mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-06 09:26:43 +08:00
Merge branch 'master' into ebiz-uat-2025-11-06
This commit is contained in:
@@ -440,6 +440,12 @@ const queryCrowd=function(query){
|
||||
const ids=function (data){
|
||||
return ajax.postJson('/xboe/m/course/manage/ids',data);
|
||||
}
|
||||
|
||||
const saveTip = function() {
|
||||
return ajax.postJson('/xboe/m/course/manage/saveTip');
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
saveBase,
|
||||
submitCourse,
|
||||
@@ -482,6 +488,7 @@ export default {
|
||||
exportCourseAudit,
|
||||
exportCourse,
|
||||
queryCrowd,
|
||||
ids
|
||||
ids,
|
||||
saveTip
|
||||
|
||||
}
|
||||
|
||||
64
src/api/modules/courseTag.js
Normal file
64
src/api/modules/courseTag.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**课程标签模块的相关处理*/
|
||||
import ajax from '@/utils/xajax.js'
|
||||
|
||||
/**
|
||||
* 分页查询:标签列表
|
||||
* @param {Object} query
|
||||
*/
|
||||
const portalPageList = function(query) {
|
||||
return ajax.post('/xboe/m/coursetag/page', query);
|
||||
}
|
||||
|
||||
//改变标签的公共属性
|
||||
const changeTagPublic = function (row){
|
||||
// 返回 Promise 的 API 调用
|
||||
return ajax.post('/xboe/m/coursetag/changePublicStatus', {
|
||||
id: row.id,
|
||||
isPublic: row.isPublic
|
||||
});
|
||||
}
|
||||
|
||||
//改变标签的热点属性
|
||||
const changeTagHot = function (row){
|
||||
// 返回 Promise 的 API 调用
|
||||
return ajax.post('/xboe/m/coursetag/changeHotStatus', {
|
||||
id: row.id,
|
||||
isHot: row.isHot
|
||||
});
|
||||
}
|
||||
|
||||
//查询指定id的标签关联的所有课程
|
||||
const showCourseByTag = function (query){
|
||||
return ajax.post('/xboe/m/coursetag/showCourseByTag', query);
|
||||
}
|
||||
|
||||
//解除指定id的课程和某个标签之间的关联关系
|
||||
const unbindCourseTagRelation = function (params){
|
||||
return ajax.post('/xboe/m/coursetag/unbind', params);
|
||||
}
|
||||
|
||||
//编辑课程:标签模糊查询
|
||||
const searchTags = function (params){
|
||||
return ajax.post('/xboe/m/coursetag/searchTags', params);
|
||||
}
|
||||
|
||||
//编辑课程:创建标签(与当前课程关联)
|
||||
const createTag = function (params){
|
||||
return ajax.post('/xboe/m/coursetag/createTag', params);
|
||||
}
|
||||
|
||||
//获取最新前10个热点标签
|
||||
const getHotTagList = function (params){
|
||||
return ajax.post('/xboe/m/coursetag/getHotTagList', params);
|
||||
}
|
||||
|
||||
export default {
|
||||
portalPageList,
|
||||
changeTagPublic,
|
||||
changeTagHot,
|
||||
showCourseByTag,
|
||||
unbindCourseTagRelation,
|
||||
searchTags,
|
||||
createTag,
|
||||
getHotTagList
|
||||
}
|
||||
BIN
src/assets/images/project/title-bg.png
Normal file
BIN
src/assets/images/project/title-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
File diff suppressed because it is too large
Load Diff
400
src/components/Course/courseTag.vue
Normal file
400
src/components/Course/courseTag.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<div class="tag-container" @click="handleContainerClick">
|
||||
<el-select style="width: 100%;"
|
||||
v-model="selectedTags"
|
||||
multiple
|
||||
filterable
|
||||
value-key="id"
|
||||
remote
|
||||
reserve-keyword
|
||||
:remote-method="debouncedSearch"
|
||||
:loading="loading"
|
||||
:placeholder="'回车创建新标签'"
|
||||
:no-data-text="'无此标签,按回车键创建'"
|
||||
@remove-tag="handleTagRemove"
|
||||
@change="handleSelectionChange"
|
||||
@keyup.enter.native="handleEnterKey"
|
||||
@keyup.delete.native="handleDeleteKey"
|
||||
@focus="handleFocus"
|
||||
ref="tagSelect"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in searchResults"
|
||||
:key="item.id"
|
||||
:label="item.tagName"
|
||||
:value="item"
|
||||
:disabled="isTagDisabled(item)"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- 添加标签计数显示 -->
|
||||
<div class="tag-count">
|
||||
{{ selectedTags.length }}/5
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash'
|
||||
import apiCourseTag from '@/api/modules/courseTag.js'
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
courseId:{
|
||||
type:String,
|
||||
require:true,
|
||||
},
|
||||
sysTypeList:{
|
||||
type:Array,
|
||||
require:true,
|
||||
default: []
|
||||
},
|
||||
maxTags: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
// 添加:接收初始标签数据的props
|
||||
initialTags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTags: [],
|
||||
searchResults: [],
|
||||
loading: false,
|
||||
tagMap: new Map(),
|
||||
inputBuffer: '',
|
||||
params: {},
|
||||
tag: {},
|
||||
// 添加临时存储用于回滚
|
||||
previousTags: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
displayTags() {
|
||||
return this.selectedTags.map(tag =>
|
||||
typeof tag === 'object' ? tag : this.tagMap.get(tag)
|
||||
).filter(Boolean)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.debouncedSearch = debounce(this.doSearch, 500)
|
||||
console.log("----------sysTypeList.length---------->"+this.sysTypeList.length)
|
||||
console.log("----------sysTypeList.length---------->"+(this.sysTypeList.length===0))
|
||||
},
|
||||
// 添加:挂载时初始化标签数据
|
||||
mounted() {
|
||||
if (this.initialTags && this.initialTags.length > 0) {
|
||||
this.selectedTags = this.initialTags;
|
||||
this.searchResults = this.initialTags;
|
||||
// 将初始标签添加到tagMap中,确保删除功能正常
|
||||
this.initialTags.forEach(tag => {
|
||||
if (tag.id) {
|
||||
this.tagMap.set(tag.id, tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听课程ID变化,重置所有状态
|
||||
courseId(newVal) {
|
||||
this.resetTagState();
|
||||
},
|
||||
// 监听初始标签变化,重新加载
|
||||
initialTags(newVal) {
|
||||
this.selectedTags = newVal || [];
|
||||
this.searchResults = newVal || [];
|
||||
this.tagMap.clear(); // 清空旧缓存
|
||||
newVal.forEach(tag => {
|
||||
if (tag.id) this.tagMap.set(tag.id, tag);
|
||||
});
|
||||
},
|
||||
// 监听分类变化,重新加载搜索结果
|
||||
sysTypeList: {
|
||||
handler() {
|
||||
// 只有在已选择分类且有焦点时才重新加载
|
||||
if (this.sysTypeList.length > 0 && this.$refs.tagSelect && this.$refs.tagSelect.visible) {
|
||||
this.doSearch('');
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 新增:检查标签是否应该被禁用
|
||||
isTagDisabled(tag) {
|
||||
// 如果标签已经被选中,不应该禁用(允许取消选择)
|
||||
const isSelected = this.selectedTags.some(selectedTag => selectedTag.id === tag.id);
|
||||
if (isSelected) {
|
||||
return false;
|
||||
}
|
||||
// 如果标签未被选中且已达到最大数量,则禁用
|
||||
return this.selectedTags.length >= this.maxTags;
|
||||
},
|
||||
// 新增:处理输入框获得焦点事件
|
||||
async handleFocus() {
|
||||
this.previousTags = [...this.selectedTags];
|
||||
// 当输入框获得焦点时,加载默认的搜索结果
|
||||
if (this.sysTypeList.length > 0) {
|
||||
await this.doSearch('');
|
||||
}
|
||||
this.$emit('focus');
|
||||
},
|
||||
handleContainerClick() {
|
||||
// 容器点击时也触发焦点事件
|
||||
this.$emit('focus');
|
||||
},
|
||||
// 新增:重置标签状态的方法
|
||||
resetTagState() {
|
||||
this.selectedTags = [];
|
||||
this.searchResults = [];
|
||||
this.tagMap.clear();
|
||||
this.loading = false;
|
||||
this.params = {};
|
||||
},
|
||||
handleTagRemove(tagId) {
|
||||
this.selectedTags = this.selectedTags.filter(id => id !== tagId)
|
||||
this.$emit('change', this.displayTags)
|
||||
this.clearInput();
|
||||
},
|
||||
removeTag(tagId) {
|
||||
this.handleTagRemove(tagId)
|
||||
},
|
||||
|
||||
// 新增:处理删除键事件
|
||||
handleDeleteKey(event) {
|
||||
// 如果输入框内容为空,不执行任何搜索
|
||||
if (!event.target.value.trim()) {
|
||||
this.searchResults = []
|
||||
}
|
||||
},
|
||||
|
||||
//按回车键,创建新标签
|
||||
handleEnterKey(event) {
|
||||
const inputVal = event.target.value?.trim()
|
||||
if (!inputVal) return;
|
||||
// 检查是否与已选择的标签重复
|
||||
const isDuplicate = this.selectedTags.some(tag => tag.tagName === inputVal);
|
||||
if (isDuplicate) {
|
||||
this.$message.warning('该标签已存在,无需重复创建');
|
||||
event.target.value = '';
|
||||
return;
|
||||
}
|
||||
if (!isDuplicate && inputVal && this.selectedTags.length < this.maxTags) {
|
||||
this.createNewTag(event.target.value.trim())
|
||||
this.clearInput();
|
||||
} else if (this.selectedTags.length >= this.maxTags) {
|
||||
this.$message.warning('最多只能添加5个标签')
|
||||
this.clearInput();
|
||||
} else {
|
||||
this.clearInput();
|
||||
}
|
||||
},
|
||||
|
||||
// 新增:处理选择变化事件
|
||||
handleSelectionChange(newValues) {
|
||||
|
||||
// 检查每个标签对象是否完整
|
||||
newValues.forEach((tag, index) => {
|
||||
if (!tag.tagName) {
|
||||
console.error(`第${index}个标签缺少tagName:`, tag);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查数量限制
|
||||
if (newValues.length > this.maxTags) {
|
||||
this.$message.warning(`最多只能选择${this.maxTags}个标签`);
|
||||
// 回滚到之前的状态
|
||||
this.selectedTags = [...this.previousTags];
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新前保存当前状态
|
||||
this.previousTags = [...newValues];
|
||||
this.$emit('change', this.displayTags);
|
||||
|
||||
this.clearInput();
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tagSelect.visible = false;
|
||||
});
|
||||
},
|
||||
|
||||
clearInput() {
|
||||
if (this.$refs.tagSelect) {
|
||||
const input = this.$refs.tagSelect.$refs.input;
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//创建新标签
|
||||
async createNewTag(tagName) {
|
||||
// 标签不能超过八个字
|
||||
if (tagName.length > 8) {
|
||||
this.$message.error('标签不能超过8个字')
|
||||
return;
|
||||
}
|
||||
// 检查标签是否在下拉框中已存在
|
||||
const isExistInSearch = this.searchResults.some(tag => tag.tagName === tagName);
|
||||
if (isExistInSearch) {
|
||||
this.$message.warning('已存在此标签,请选择');
|
||||
return;
|
||||
}
|
||||
// 首先检查是否与已选择的标签重复
|
||||
const isDuplicate = this.selectedTags.some(tag => tag.tagName === tagName);
|
||||
if (isDuplicate) {
|
||||
this.$message.warning('该标签已存在,无需重复创建');
|
||||
return;
|
||||
}
|
||||
// 标签格式验证:仅支持中文、英文、数字、下划线、中横线
|
||||
const tagPattern = /^[\u4e00-\u9fa5a-zA-Z0-9_-]+$/;
|
||||
if (!tagPattern.test(tagName)) {
|
||||
this.$message.error('标签名称仅支持中文、英文、数字、下划线(_)和中横线(-),不支持空格、点和特殊字符');
|
||||
return;
|
||||
}
|
||||
// 添加标签数量限制检查
|
||||
if (this.selectedTags.length >= this.maxTags) {
|
||||
this.$message.warning('最多只能添加5个标签')
|
||||
return;
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
this.params.courseId = this.courseId;
|
||||
this.params.tagName = tagName;
|
||||
// 分类
|
||||
if (this.sysTypeList.length > 0) {
|
||||
this.params.sysType1 = this.sysTypeList[0]; //一级的id
|
||||
}
|
||||
if (this.sysTypeList.length > 1) {
|
||||
this.params.sysType2 = this.sysTypeList[1]; //二级的id
|
||||
}
|
||||
if (this.sysTypeList.length > 2) {
|
||||
this.params.sysType3 = this.sysTypeList[2]; //三级的id
|
||||
}
|
||||
const {result:newTag} = await apiCourseTag.createTag(this.params)
|
||||
this.$message.success('标签创建成功',newTag);
|
||||
|
||||
this.selectedTags = [...this.selectedTags, newTag];
|
||||
// 更新搜索结果的逻辑保持不变
|
||||
this.searchResults = [newTag, ...this.searchResults];
|
||||
this.tagMap.set(newTag.id, newTag)
|
||||
this.$emit('change', this.displayTags)
|
||||
|
||||
this.$nextTick(() => {
|
||||
// 强制重新设置selectedTags来触发更新
|
||||
const tempTags = [...this.selectedTags];
|
||||
this.selectedTags = [];
|
||||
this.$nextTick(() => {
|
||||
this.selectedTags = tempTags;
|
||||
});
|
||||
this.$refs.tagSelect.visible = false;
|
||||
});
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
// 修改doSearch方法,添加搜索结果为空时的提示
|
||||
async doSearch(query) {
|
||||
// 不再在空查询时清空搜索结果
|
||||
// if (!query.trim()) {
|
||||
// this.searchResults = []
|
||||
// return
|
||||
// }
|
||||
console.log("---- doSearch ------ query = " + query )
|
||||
this.loading = true
|
||||
try {
|
||||
// 获取 typeId:取 sysTypeList 最后一个有效的值
|
||||
const typeId = this.sysTypeList.length > 2 ? this.sysTypeList[2] :
|
||||
this.sysTypeList.length > 1 ? this.sysTypeList[1] :
|
||||
this.sysTypeList.length > 0 ? this.sysTypeList[0] : null;
|
||||
console.log("---- doSearch searchTags ------ query = " + query + " , typeId = " + typeId )
|
||||
const {result:tags} = await apiCourseTag.searchTags({tagName:query,typeId:typeId})
|
||||
console.log("-- searchTags 查询结果 tags = " + tags )
|
||||
|
||||
tags.forEach(item => {
|
||||
this.tagMap.set(item.id, item)
|
||||
})
|
||||
this.searchResults = tags
|
||||
// 当搜索结果为空时,提示用户可以按回车键创建标签
|
||||
if (tags.length === 0) {
|
||||
// this.$message.info('无此标签,按回车键创建')
|
||||
}
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tag-container {
|
||||
position: relative;
|
||||
}
|
||||
.tag-preview {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.el-tag {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* 添加标签计数样式 */
|
||||
.tag-count {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 47%;
|
||||
transform: translateY(-40%);
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
background: white;
|
||||
padding: 0 5px;
|
||||
pointer-events: none;
|
||||
/* 添加高度限制 */
|
||||
height: 25px;
|
||||
line-height: 25px; /* 垂直居中文字 */
|
||||
box-sizing: border-box; /* 确保padding包含在height内 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
::v-deep(.el-select__tags) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
/*
|
||||
::v-deep(.el-tag) {
|
||||
flex: 0 0 calc(50% - 8px);
|
||||
max-width: calc(50% - 8px);
|
||||
box-sizing: border-box;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}*/
|
||||
|
||||
::v-deep(.el-tag) {
|
||||
flex: 1 1 auto; /* 自动调整宽度 */
|
||||
min-width: 30%; /* 设置最小宽度 */
|
||||
max-width: 48%; /* 设置最大宽度,留出边距 */
|
||||
box-sizing: border-box;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
::v-deep(.el-select__input) {
|
||||
flex: 1;
|
||||
min-width: 60px;
|
||||
}
|
||||
</style>
|
||||
@@ -128,7 +128,8 @@ export const iframes=[
|
||||
{title:'查看受众', path:'/iframe/ugroup/view',hidden:false,component:'manage/AudienceView'},
|
||||
{title:'问答管理', path:'/iframe/qa/manages',hidden:false,component:'qa/ManageList'},
|
||||
{title:'待审核课程', path:'/iframe/course/noapproved',hidden:false,component:'examine/NotApproved'},
|
||||
{title:'已审核课程', path:'/iframe/course/reviewed',hidden:false,component:'examine/Reviewed'}
|
||||
{title:'已审核课程', path:'/iframe/course/reviewed',hidden:false,component:'examine/Reviewed'},
|
||||
{title:'标签管理', path:'/iframe/tag/manages',hidden:false,component:'tag/TagManageList'},
|
||||
|
||||
]
|
||||
|
||||
|
||||
@@ -30,13 +30,19 @@
|
||||
<!-- <div class="course-title-right"> -->
|
||||
<!-- <interactBar :readonly="!stuStusts || stuStusts==0" :type="1" :data="courseInfo" :comments="false" :views="false"></interactBar> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="label-div">
|
||||
<el-tag class="label-item" effect="plain" v-for="(item,tagIdx) in tagArray" :key="tagIdx">{{item}}</el-tag>
|
||||
</div>-->
|
||||
<div class="label-div">
|
||||
<div v-for="(item, tagIdx) in tagArray" :key="tagIdx" class="keyword-tag">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="study-count">{{courseInfo.studys}}人学习</div>
|
||||
<!-- <div><span style="font-size:20px;color:#ff8e00">{{courseInfo.score ? courseInfo.score.toFixed(1) : 0}}</span><span style="font-size:12px;color:#ff8e00">分</span></div> -->
|
||||
</div>
|
||||
<div class="label-div">
|
||||
<el-tag class="label-item" effect="plain" v-for="(item,tagIdx) in tagArray" :key="tagIdx">{{item}}</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- <div style="width:160px;height:50px"> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="label-div">
|
||||
@@ -419,7 +425,7 @@ export default {
|
||||
|
||||
.course-title{
|
||||
position: relative;
|
||||
height: 90px;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.title {
|
||||
@@ -452,18 +458,43 @@ export default {
|
||||
padding: 24px 24px 5px 24px;
|
||||
// margin-right: 361px;
|
||||
.study-count {
|
||||
margin-top: 10px;
|
||||
margin-top: 30px;
|
||||
font-size: 16px;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
.label-div {
|
||||
/*.label-div {
|
||||
margin: 5px 0;
|
||||
min-height: 20px;
|
||||
.label-item {
|
||||
padding: 0 7px;
|
||||
padding: 0px 8px;
|
||||
margin-top: 5px;
|
||||
float: left;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 0px;
|
||||
color: #2C68FF;
|
||||
height: 24px;
|
||||
background: rgba(44, 104, 255, 0.06);
|
||||
border: none; // 或者使用 border-color: transparent;
|
||||
}
|
||||
}*/
|
||||
.label-div {
|
||||
margin: 5px 0;
|
||||
min-height: 20px;
|
||||
|
||||
.keyword-tag {
|
||||
padding: 0px 10px;
|
||||
margin-top: 7px;
|
||||
float: left;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
margin-right: 10px;
|
||||
color: #2C68FF;
|
||||
height: 24px;
|
||||
background: rgba(44, 104, 255, 0.06);
|
||||
}
|
||||
}
|
||||
::v-deep .el-rate__icon {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
389
src/views/tag/TagManageList.vue
Normal file
389
src/views/tag/TagManageList.vue
Normal file
@@ -0,0 +1,389 @@
|
||||
<template>
|
||||
<div class="u-page" style="padding-right:32px">
|
||||
<div style="width: 100%; margin-left: 12px;padding: 2px 0px 10px 12px;background-color: white">
|
||||
<el-form :inline="true" style="margin-left: 12px;" :model="pageData" class="demo-form-inline">
|
||||
<el-form-item label="标签ID:" label-width="60px">
|
||||
<el-input id="tag-id" placeholder="请输入标签ID" v-model="pageData.id" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签名称:" label-width="80px">
|
||||
<el-input id="tag-id" placeholder="请输入标签名称" v-model="pageData.tagName" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="热点标签:" label-width="80px">
|
||||
<el-select v-model="pageData.isHot" style="width: 120px;" clearable placeholder="请选择状态">
|
||||
<el-option label="开启" value="true"></el-option>
|
||||
<el-option label="关闭" value="false"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="getsearch" icon="el-icon-search" type="primary">查询</el-button>
|
||||
<!-- 添加重置按钮 -->
|
||||
<el-button @click="resetSearch" icon="el-icon-refresh">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div style="padding: 5px 0px 2px 12px;">
|
||||
<!-- <el-checkbox label="前台公共显示"></el-checkbox>-->
|
||||
<!-- <el-checkbox label="热点标签展示"></el-checkbox>-->
|
||||
</div>
|
||||
|
||||
<div style="width: 100%; margin-left: 12px;padding: 2px 0px 10px 12px;background-color: white">
|
||||
<el-table style="width: 96%; margin:2px 32px 10px 12px;" :data="pageData.list" border stripe
|
||||
:header-cell-style="{ background: '#E9F0FF' }"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange">
|
||||
<el-table-column type="selection" width="80px"></el-table-column>
|
||||
<el-table-column label="标签ID" width="200px" prop="id"></el-table-column>
|
||||
<el-table-column label="标签名称" width="235px" prop="tagName"></el-table-column>
|
||||
<el-table-column label="已关联课程" width="220px"
|
||||
prop="useCount"
|
||||
sortable="custom"
|
||||
:sort-orders="['descending', 'ascending']"
|
||||
>
|
||||
<template #default="scope">
|
||||
<a v-if="scope.row.useCount > 0"
|
||||
@click="showCourseByTag(`${scope.row.id}`)"
|
||||
style="font-weight:bold; color: #409EFF; text-decoration: underline;">
|
||||
{{ scope.row.useCount }}
|
||||
</a>
|
||||
<span style="font-weight:bold; color: #409EFF; text-decoration: underline;" v-else>0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="前台公共显示" width="220px" prop="isPublic">
|
||||
<template #default="scope"><!-- 开关状态会直接修改 pageData.list 中的数据 -->
|
||||
<el-switch
|
||||
v-model="scope.row.isPublic"
|
||||
:disabled="scope.row.isHot==1?true:false"
|
||||
@change="handlePublicChange(scope.row)"
|
||||
>
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="热点标签展示" width="220px" prop="isHot">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.isHot"
|
||||
:disabled="scope.row.isPublic==0?true:false"
|
||||
@change="handleHotChange(scope.row)"
|
||||
>
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="pageData.list.length > 0" style="text-align: center;margin-top: 50px;">
|
||||
<el-pagination
|
||||
background
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pageData.pageIndex"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="pageData.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 标签关联课程弹窗 -->
|
||||
<el-dialog custom-class="g-dialog" title="关联课程"
|
||||
width="850px" top="20px"
|
||||
:visible.sync="dialogVisible"
|
||||
:modal-append-to-body="true"
|
||||
:append-to-body="true">
|
||||
<div class="dialog-content-container">
|
||||
<el-table
|
||||
:data="pageData.list2"
|
||||
border stripe style="width: 100%"
|
||||
:header-cell-style="{ background: '#E9F0FF' }"
|
||||
@sort-change="handleSortChange2">
|
||||
<el-table-column label="序号" width="60px" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联课程名称" width="200px" prop="courseName"></el-table-column>
|
||||
<el-table-column label="关联课程ID" width="100px" prop="courseId"></el-table-column>
|
||||
<el-table-column label="关联人" width="80px" prop="sysCreateBy"></el-table-column>
|
||||
<el-table-column label="关联时间" width="110px" prop="sysCreateTime"
|
||||
:formatter="dateFormat" sortable="custom"
|
||||
:sort-orders="['descending', 'ascending']"></el-table-column>
|
||||
<el-table-column label="本课程绑定的其他标签" width="200px" prop="otherTags"></el-table-column>
|
||||
<el-table-column label="操作" width="60px">
|
||||
<template #default="scope">
|
||||
<a @click="unbindCurrentTag(scope.row)"
|
||||
style="font-weight:bold; color: #409EFF;">
|
||||
解绑
|
||||
</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<div v-if="pageData.list2.length > 0" class="pagination-container">
|
||||
<el-pagination
|
||||
background
|
||||
@size-change="handleSizeChange2"
|
||||
@current-change="handleCurrentChange2"
|
||||
:current-page="pageData.pageIndex2"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="pageData.pageSize2"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total2">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import apiCourseTag from '@/api/modules/courseTag.js'
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'courseTagItems',
|
||||
computed: {
|
||||
...mapGetters(['userInfo'])
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageData: {
|
||||
pageIndex: 1,
|
||||
pageIndex2: 1,
|
||||
pageSize: 10,
|
||||
pageSize2: 10,
|
||||
list:[],
|
||||
list2:[],
|
||||
orderField: null,
|
||||
orderAsc: null,
|
||||
orderField2: null,
|
||||
orderAsc2: null,
|
||||
},
|
||||
total: 0,
|
||||
total2: 0,
|
||||
dialogVisible: false,
|
||||
tagId: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getCourseTagList()
|
||||
},
|
||||
methods: {
|
||||
|
||||
//重置搜索条件
|
||||
resetSearch() {
|
||||
this.pageData.id = '';
|
||||
this.pageData.tagName = '';
|
||||
this.pageData.isHot = '';
|
||||
this.pageData.pageIndex = 1;
|
||||
this.getCourseTagList(); // 重置后重新加载列表
|
||||
},
|
||||
|
||||
//初始化:课程标签列表
|
||||
getsearch(){
|
||||
this.pageData.pageIndex = 1;
|
||||
this.getCourseTagList()
|
||||
},
|
||||
|
||||
//课程标签列表:排序
|
||||
handleSortChange({ prop, order }) {
|
||||
this.pageData.orderField = prop; // 当前排序字段
|
||||
this.pageData.orderAsc = order === 'ascending'; // 排序方向
|
||||
this.getCourseTagList(); // 重新获取数据
|
||||
},
|
||||
|
||||
//TODO:课程标签列表:监听选中项变化(批量的设置标签公共显示|热点标签)
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection; // 更新选中的行数据
|
||||
},
|
||||
|
||||
//课程标签列表:获取课程标签列表数据
|
||||
getCourseTagList() {
|
||||
const { pageIndex, pageSize, orderField, orderAsc } = this.pageData
|
||||
let query = { pageIndex, pageSize, orderField, orderAsc}
|
||||
//拼接查询条件
|
||||
if (this.pageData.id) {
|
||||
const { id } = this.pageData
|
||||
query.id = id
|
||||
}
|
||||
if (this.pageData.tagName) {
|
||||
const { tagName } = this.pageData
|
||||
query.tagName = tagName
|
||||
}
|
||||
if (this.pageData.isHot) {
|
||||
const { isHot } = this.pageData
|
||||
query.isHot = isHot
|
||||
}
|
||||
apiCourseTag.portalPageList(query).then((res) => {
|
||||
if (res.status == 200) {
|
||||
this.total = res.result.count
|
||||
this.pageData.list = res.result.list
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error('获取数据失败')
|
||||
})
|
||||
},
|
||||
|
||||
//课程标签列表:改变标签的公共属性
|
||||
async handlePublicChange(row) {
|
||||
// 保存原始状态用于回滚
|
||||
const originalStatus = row.isPublic;
|
||||
try {
|
||||
// 调用 API 更新状态
|
||||
await apiCourseTag.changeTagPublic(row);
|
||||
this.$message.success('更新成功');
|
||||
} catch (error) {
|
||||
// 发生错误时回滚状态
|
||||
row.isPublic = originalStatus;
|
||||
this.$message.error('更新失败:' + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
//课程标签列表:改变标签的热点属性
|
||||
async handleHotChange(row) {
|
||||
const isPublic=row.isPublic;
|
||||
// 保存原始状态用于回滚
|
||||
const originalStatus = row.isHot;
|
||||
try {
|
||||
// 调用 API 更新状态
|
||||
await apiCourseTag.changeTagHot(row).then((res)=>{
|
||||
if (res.status == 200){
|
||||
this.$message.success(res.message);
|
||||
}else {
|
||||
row.isHot=false;
|
||||
this.$message.warning(res.message);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// 发生错误时回滚状态
|
||||
row.isHot = originalStatus;
|
||||
this.$message.error('更新失败:' + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
//课程标签列表:改变条数的回调
|
||||
handleSizeChange(value) {
|
||||
this.pageData.pageIndex = 1;
|
||||
this.pageData.pageSize = value;
|
||||
this.getCourseTagList();
|
||||
},
|
||||
//课程标签列表:改变页数的回调
|
||||
handleCurrentChange(value) {
|
||||
this.pageData.pageIndex = value;
|
||||
this.getCourseTagList();
|
||||
},
|
||||
|
||||
//标签关联的所有课程弹出框:显示指定标签id关联的课程列表
|
||||
showCourseByTag(tagId) {
|
||||
this.tagId=tagId;
|
||||
this.getCourseOfTagList(tagId);
|
||||
this.dialogVisible=true;
|
||||
},
|
||||
|
||||
//分页查询指定标签关联的所有课程
|
||||
getCourseOfTagList(){
|
||||
const { pageIndex2:pageIndex, pageSize2:pageSize, orderField2:orderField, orderAsc2:orderAsc } = this.pageData
|
||||
let query = { pageIndex, pageSize, orderField, orderAsc }
|
||||
//拼接查询条件
|
||||
if (this.tagId) {
|
||||
query.id = this.tagId
|
||||
apiCourseTag.showCourseByTag(query).then((res) => {
|
||||
if (res.status == 200) {
|
||||
this.total2 = res.result.count
|
||||
this.pageData.list2 = res.result.list
|
||||
if (this.total2==0){
|
||||
this.dialogVisible=false
|
||||
this.getCourseTagList(); // 重新获取课程标签列表数据
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error('获取数据失败')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
//标签关联课程列表:排序
|
||||
handleSortChange2({ prop, order }) {
|
||||
this.pageData.orderField2 = prop; // 当前排序字段
|
||||
this.pageData.orderAsc2 = order === 'ascending'; // 排序方向
|
||||
this.getCourseOfTagList(); // 重新获取数据
|
||||
},
|
||||
|
||||
//标签关联的所有课程列表:改变条数的回调
|
||||
handleSizeChange2(value) {
|
||||
this.pageData.pageIndex2= 1;
|
||||
this.pageData.pageSize2 = value;
|
||||
this.getCourseOfTagList();
|
||||
},
|
||||
//标签关联的所有课程列表:改变页数的回调
|
||||
handleCurrentChange2(value) {
|
||||
this.pageData.pageIndex2 = value;
|
||||
this.getCourseOfTagList();
|
||||
},
|
||||
//关联时间格式化
|
||||
dateFormat(row, column) {
|
||||
return row[column.property] ?
|
||||
moment(row[column.property]).format('YYYY-MM-DD') : '';
|
||||
},
|
||||
|
||||
//解除指定课程和当前标签的关联关系
|
||||
unbindCurrentTag (row) {
|
||||
let id = row.id;
|
||||
let tagId = this.tagId;
|
||||
let courseId = row.courseId;
|
||||
//拼接查询条件
|
||||
if (tagId && courseId) {
|
||||
let params = { id, tagId, courseId }
|
||||
apiCourseTag.unbindCourseTagRelation(params).then((res) => {
|
||||
if (res.status == 200) {
|
||||
//刷新列表
|
||||
this.getCourseOfTagList(this.tagId);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error('解绑失败!')
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.demo-form-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px; /* 间距 */
|
||||
}
|
||||
|
||||
.demo-form-inline .el-form-item {
|
||||
margin-bottom: 0; /* 消除默认底部间距 */
|
||||
}
|
||||
|
||||
.dialog-content-container {
|
||||
padding: 10px;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.g-dialog .el-dialog__header {
|
||||
background-color: #409EFF;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.g-dialog .el-dialog__title {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.g-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user