This commit is contained in:
670788339
2025-11-03 13:24:48 +08:00
parent a901e488b4
commit b00e2c80b0
2 changed files with 449 additions and 243 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
<template> <template>
<div class="tag-container"> <div class="tag-container">
<el-select style="width: 100%;" <el-select style="width: 100%;"
v-model="selectedTagIds" v-model="selectedTags"
multiple multiple
filterable filterable
value-key="id" value-key="id"
@@ -21,13 +22,13 @@
v-for="item in searchResults" v-for="item in searchResults"
:key="item.id" :key="item.id"
:label="item.tagName" :label="item.tagName"
:value="item.id" :value="item"
:disabled="isTagDisabled(item)" :disabled="isTagDisabled(item)"
/> />
</el-select> </el-select>
<!-- 添加标签计数显示 --> <!-- 添加标签计数显示 -->
<div class="tag-count"> <div class="tag-count">
{{ selectedTagIds.length }}/5 {{ selectedTags.length }}/5
</div> </div>
</div> </div>
</template> </template>
@@ -60,7 +61,7 @@ export default {
}, },
data() { data() {
return { return {
selectedTagIds: [], // 修改存储标签ID数组 selectedTags: [],
searchResults: [], searchResults: [],
loading: false, loading: false,
tagMap: new Map(), tagMap: new Map(),
@@ -73,13 +74,10 @@ export default {
}, },
computed: { computed: {
...mapGetters(['userInfo']), ...mapGetters(['userInfo']),
// 修改:计算属性,返回完整的标签对象数组用于显示
displayTags() { displayTags() {
return this.selectedTagIds.map(tagId => this.tagMap.get(tagId)).filter(Boolean) return this.selectedTags.map(tag =>
}, typeof tag === 'object' ? tag : this.tagMap.get(tag)
// 新增计算标签ID字符串用于传参 ).filter(Boolean)
tagIdsString() {
return this.selectedTagIds.join(',')
} }
}, },
created() { created() {
@@ -90,7 +88,14 @@ export default {
// 添加:挂载时初始化标签数据 // 添加:挂载时初始化标签数据
mounted() { mounted() {
if (this.initialTags && this.initialTags.length > 0) { if (this.initialTags && this.initialTags.length > 0) {
this.initTags(this.initialTags); this.selectedTags = this.initialTags;
this.searchResults = this.initialTags;
// 将初始标签添加到tagMap中确保删除功能正常
this.initialTags.forEach(tag => {
if (tag.id) {
this.tagMap.set(tag.id, tag);
}
});
} }
}, },
watch: { watch: {
@@ -100,13 +105,12 @@ export default {
}, },
// 监听初始标签变化,重新加载 // 监听初始标签变化,重新加载
initialTags(newVal) { initialTags(newVal) {
if (newVal && newVal.length > 0) { this.selectedTags = newVal || [];
this.initTags(newVal); this.searchResults = newVal || [];
} else { this.tagMap.clear(); // 清空旧缓存
this.selectedTagIds = []; newVal.forEach(tag => {
this.searchResults = []; if (tag.id) this.tagMap.set(tag.id, tag);
this.tagMap.clear(); });
}
}, },
// 监听分类变化,重新加载搜索结果 // 监听分类变化,重新加载搜索结果
sysTypeList: { sysTypeList: {
@@ -117,37 +121,22 @@ export default {
} }
}, },
deep: true deep: true
},
// 监听标签ID变化触发change事件
tagIdsString(newVal) {
this.$emit('change', this.displayTags, newVal);
} }
}, },
methods: { methods: {
// 新增:初始化标签方法
initTags(tags) {
this.selectedTagIds = tags.map(tag => tag.id);
this.searchResults = [...tags];
this.tagMap.clear();
tags.forEach(tag => {
if (tag.id) {
this.tagMap.set(tag.id, tag);
}
});
},
// 新增:检查标签是否应该被禁用 // 新增:检查标签是否应该被禁用
isTagDisabled(tag) { isTagDisabled(tag) {
// 如果标签已经被选中,不应该禁用(允许取消选择) // 如果标签已经被选中,不应该禁用(允许取消选择)
const isSelected = this.selectedTagIds.some(selectedTagId => selectedTagId === tag.id); const isSelected = this.selectedTags.some(selectedTag => selectedTag.id === tag.id);
if (isSelected) { if (isSelected) {
return false; return false;
} }
// 如果标签未被选中且已达到最大数量,则禁用 // 如果标签未被选中且已达到最大数量,则禁用
return this.selectedTagIds.length >= this.maxTags; return this.selectedTags.length >= this.maxTags;
}, },
// 新增:处理输入框获得焦点事件 // 新增:处理输入框获得焦点事件
async handleFocus() { async handleFocus() {
this.previousTags = [...this.selectedTagIds]; this.previousTags = [...this.selectedTags];
// 当输入框获得焦点时,加载默认的搜索结果 // 当输入框获得焦点时,加载默认的搜索结果
if (this.sysTypeList.length > 0) { if (this.sysTypeList.length > 0) {
await this.doSearch(''); await this.doSearch('');
@@ -155,15 +144,15 @@ export default {
}, },
// 新增:重置标签状态的方法 // 新增:重置标签状态的方法
resetTagState() { resetTagState() {
this.selectedTagIds = []; this.selectedTags = [];
this.searchResults = []; this.searchResults = [];
this.tagMap.clear(); this.tagMap.clear();
this.loading = false; this.loading = false;
this.params = {}; this.params = {};
}, },
handleTagRemove(tagId) { handleTagRemove(tagId) {
this.selectedTagIds = this.selectedTagIds.filter(id => id !== tagId) this.selectedTags = this.selectedTags.filter(id => id !== tagId)
this.$emit('change', this.displayTags, this.tagIdsString) this.$emit('change', this.displayTags)
this.clearInput(); this.clearInput();
}, },
removeTag(tagId) { removeTag(tagId) {
@@ -182,19 +171,17 @@ export default {
handleEnterKey(event) { handleEnterKey(event) {
const inputVal = event.target.value?.trim() const inputVal = event.target.value?.trim()
if (!inputVal) return; if (!inputVal) return;
// 检查是否与已选择的标签重复 // 检查是否与已选择的标签重复
const isDuplicate = this.displayTags.some(tag => tag.tagName === inputVal); const isDuplicate = this.selectedTags.some(tag => tag.tagName === inputVal);
if (isDuplicate) { if (isDuplicate) {
this.$message.warning('该标签已存在,无需重复创建'); this.$message.warning('该标签已存在,无需重复创建');
event.target.value = ''; event.target.value = '';
return; return;
} }
if (!this.searchResults.length && inputVal && this.selectedTags.length < this.maxTags) {
if (!this.searchResults.length && inputVal && this.selectedTagIds.length < this.maxTags) {
this.createNewTag(event.target.value.trim()) this.createNewTag(event.target.value.trim())
this.clearInput(); this.clearInput();
} else if (this.selectedTagIds.length >= this.maxTags) { } else if (this.selectedTags.length >= this.maxTags) {
this.$message.warning('最多只能添加5个标签') this.$message.warning('最多只能添加5个标签')
this.clearInput(); this.clearInput();
} else { } else {
@@ -202,20 +189,20 @@ export default {
} }
}, },
// 修改:处理选择变化事件 // 新增:处理选择变化事件
handleSelectionChange(newValues) { handleSelectionChange(newValues) {
// 检查数量限制 // 检查数量限制
if (newValues.length > this.maxTags) { if (newValues.length > this.maxTags) {
this.$message.warning(`最多只能选择${this.maxTags}个标签`); this.$message.warning(`最多只能选择${this.maxTags}个标签`);
// 回滚到之前的状态 // 回滚到之前的状态
this.selectedTagIds = [...this.previousTags]; this.selectedTags = [...this.previousTags];
return; return;
} }
// 更新前保存当前状态 // 更新前保存当前状态
this.previousTags = [...newValues]; this.previousTags = [...newValues];
this.$emit('change', this.displayTags);
// 自动触发change事件通过watch监听tagIdsString的变化
this.clearInput(); this.clearInput();
}, },
@@ -228,34 +215,30 @@ export default {
} }
}, },
//修改:创建新标签 - 修复显示问题 //创建新标签
async createNewTag(tagName) { async createNewTag(tagName) {
// 标签不能超过八个字 // 标签不能超过八个字
if (tagName.length > 8) { if (tagName.length > 8) {
this.$message.error('标签不能超过8个字') this.$message.error('标签不能超过8个字')
return; return;
} }
// 首先检查是否与已选择的标签重复 // 首先检查是否与已选择的标签重复
const isDuplicate = this.displayTags.some(tag => tag.tagName === tagName); const isDuplicate = this.selectedTags.some(tag => tag.tagName === tagName);
if (isDuplicate) { if (isDuplicate) {
this.$message.warning('该标签已存在,无需重复创建'); this.$message.warning('该标签已存在,无需重复创建');
return; return;
} }
// 标签格式验证:仅支持中文、英文、数字、下划线、中横线 // 标签格式验证:仅支持中文、英文、数字、下划线、中横线
const tagPattern = /^[\u4e00-\u9fa5a-zA-Z0-9_-]+$/; const tagPattern = /^[\u4e00-\u9fa5a-zA-Z0-9_-]+$/;
if (!tagPattern.test(tagName)) { if (!tagPattern.test(tagName)) {
this.$message.error('标签名称仅支持中文、英文、数字、下划线(_)和中横线(-),不支持空格、点和特殊字符'); this.$message.error('标签名称仅支持中文、英文、数字、下划线(_)和中横线(-),不支持空格、点和特殊字符');
return; return;
} }
// 添加标签数量限制检查 // 添加标签数量限制检查
if (this.selectedTagIds.length >= this.maxTags) { if (this.selectedTags.length >= this.maxTags) {
this.$message.warning('最多只能添加5个标签') this.$message.warning('最多只能添加5个标签')
return; return;
} }
this.loading = true this.loading = true
try { try {
this.params.courseId = this.courseId; this.params.courseId = this.courseId;
@@ -270,39 +253,23 @@ export default {
if (this.sysTypeList.length > 2) { if (this.sysTypeList.length > 2) {
this.params.sysType3 = this.sysTypeList[2]; //三级的id this.params.sysType3 = this.sysTypeList[2]; //三级的id
} }
const {result:newTag} = await apiCourseTag.createTag(this.params) const {result:newTag} = await apiCourseTag.createTag(this.params)
this.$message.success('标签创建成功'); this.$message.success('标签创建成功',newTag);
this.searchResults.push(newTag)
// 修改:将新标签添加到选中列表和搜索结果中 console.log("----------newTag---------->",this.searchResults)
this.selectedTagIds.push(newTag.id); this.tagMap.set(newTag.id, newTag)
this.$emit('change', this.displayTags)
// 创建完整的标签对象包含id和tagName
const completeTag = {
id: newTag.id,
tagName: newTag.tagName
};
this.searchResults.push(completeTag);
this.tagMap.set(newTag.id, completeTag);
console.log("----------新创建标签---------->", completeTag)
console.log("----------当前选中标签ID---------->", this.selectedTagIds)
console.log("----------标签映射表---------->", this.tagMap)
// 触发change事件传递标签对象数组和ID字符串
this.$emit('change', this.displayTags, this.tagIdsString);
// 清空输入框
this.clearInput();
} finally { } finally {
this.loading = false this.loading = false
} }
}, },
// 修改doSearch方法添加搜索结果为空时的提示 // 修改doSearch方法添加搜索结果为空时的提示
async doSearch(query) { async doSearch(query) {
// 不再在空查询时清空搜索结果
// if (!query.trim()) {
// this.searchResults = []
// return
// }
console.log("---- doSearch ------ query = " + query ) console.log("---- doSearch ------ query = " + query )
this.loading = true this.loading = true
try { try {
@@ -314,18 +281,12 @@ export default {
const {result:tags} = await apiCourseTag.searchTags({tagName:query,typeId:typeId}) const {result:tags} = await apiCourseTag.searchTags({tagName:query,typeId:typeId})
console.log("-- searchTags 查询结果 tags = " + tags ) console.log("-- searchTags 查询结果 tags = " + tags )
// 确保每个标签对象都有完整的属性 tags.forEach(item => {
const completeTags = tags.map(tag => ({
id: tag.id,
tagName: tag.tagName
}));
completeTags.forEach(item => {
this.tagMap.set(item.id, item) this.tagMap.set(item.id, item)
}) })
this.searchResults = completeTags this.searchResults = tags
// 当搜索结果为空时,提示用户可以按回车键创建标签 // 当搜索结果为空时,提示用户可以按回车键创建标签
if (tags.length === 0 && query.trim()) { if (tags.length === 0) {
this.$message.info('无此标签,按回车键创建') this.$message.info('无此标签,按回车键创建')
} }
} finally { } finally {