|
|
|
@ -5,35 +5,56 @@
|
|
|
|
|
<img style="width: 1.2rem;height: 1.13rem;" src="@/assets/images/标签管理.png" alt="">
|
|
|
|
|
<span style="margin-top: -0.1rem;">标签管理:</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="headright" @click="showAddTagModal">
|
|
|
|
|
<div class="headright" @click="showAddTagModal" v-if="(action === 'fill' || !action || action === 'okay') && checkRole(['admin', 'common'])">
|
|
|
|
|
添加标签
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 按照type分组显示 -->
|
|
|
|
|
<div class="type-group" v-for="group in groupedTags" :key="group.type">
|
|
|
|
|
<div class="group-title">
|
|
|
|
|
<!-- 下拉展示 -->
|
|
|
|
|
<i class="el-icon-arrow-down"></i>
|
|
|
|
|
<!-- 上拉收起 -->
|
|
|
|
|
<i class="el-icon-arrow-up"></i>
|
|
|
|
|
类型 {{ group.type }}</div>
|
|
|
|
|
<div class="tablebody">
|
|
|
|
|
<el-table :data="group.list" style="width: 100%" v-loading="loading">
|
|
|
|
|
<el-table-column prop="name" label="标签名称" width="220"></el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="160">
|
|
|
|
|
<div class="group-title" @click="toggleGroup(group.type)">
|
|
|
|
|
<div>
|
|
|
|
|
<i :class="isGroupOpen(group.type) ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
|
|
|
|
{{ TypeMap[group.type] }}
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="(action === 'fill' || !action || action === 'okay') && checkRole(['admin', 'common'])">
|
|
|
|
|
<el-button size="mini" type="text" icon="el-icon-delete" style="color: #F25353;"
|
|
|
|
|
@click.stop="handleDeleteType(group.type)" :disabled="isSaving">删除</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tablebody" v-if="isGroupOpen(group.type)">
|
|
|
|
|
<el-table :data="group.list" style="width: 100%;" v-loading="loading">
|
|
|
|
|
<el-table-column prop="name" label="标签名称" width="260">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<div v-if="!scope.row.editing">{{ scope.row.name }}</div>
|
|
|
|
|
<el-input v-else v-model="scope.row.editName" size="small"
|
|
|
|
|
@keyup.enter.native="saveEdit(scope.row)" @blur="saveEdit(scope.row)"></el-input>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="155">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
|
|
|
|
|
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
|
|
<div
|
|
|
|
|
v-if="(action === 'fill' || !action || action === 'okay') && checkRole(['admin', 'common'])">
|
|
|
|
|
<el-button size="mini" @click="startEdit(scope.row)" v-if="!scope.row.editing"
|
|
|
|
|
:disabled="isSaving">编辑</el-button>
|
|
|
|
|
<el-button size="mini" type="success" @click="saveEdit(scope.row)" v-else
|
|
|
|
|
:loading="isSaving">保存</el-button>
|
|
|
|
|
<el-button size="mini" type="danger" @click="handleDelete(scope.row)"
|
|
|
|
|
:disabled="scope.row.editing || isSaving">删除</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else style="color: #ccc;">
|
|
|
|
|
当前不可操作
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 添加/编辑标签弹窗 -->
|
|
|
|
|
<el-dialog :title="isEdit ? '编辑标签' : '添加标签'" :visible.sync="tagModalVisible" width="30%"
|
|
|
|
|
:close-on-click-modal="false" @closed="resetForm">
|
|
|
|
|
<!-- 添加标签弹窗 -->
|
|
|
|
|
<el-dialog title="添加标签" :visible.sync="tagModalVisible" width="30%" :close-on-click-modal="false"
|
|
|
|
|
@closed="resetForm">
|
|
|
|
|
<el-form :model="tagForm" :rules="rules" ref="tagForm" label-width="100px">
|
|
|
|
|
<el-form-item label="标签类型" prop="type">
|
|
|
|
|
<el-select v-model="tagForm.type" placeholder="请选择标签类型" style="width: 27.2rem;">
|
|
|
|
@ -43,7 +64,7 @@
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="标签名称" prop="name">
|
|
|
|
|
<el-input v-model="tagForm.name" placeholder="请输入标签名称"></el-input>
|
|
|
|
|
<el-input v-model="tagForm.name" placeholder="请输入标签名称" style="width: 27.2rem;"></el-input>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<span slot="footer" class="dialog-footer">
|
|
|
|
@ -56,6 +77,7 @@
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { getallspan, addspan, updatspan, deletespan } from '@/api/manageApitwo/index';
|
|
|
|
|
import { checkPermi, checkRole } from "@/utils/permission";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
dicts: ['bqlx'],
|
|
|
|
@ -63,15 +85,18 @@ export default {
|
|
|
|
|
xmId: {
|
|
|
|
|
type: Number,
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
action: {
|
|
|
|
|
type: String,
|
|
|
|
|
required: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
loading: false,
|
|
|
|
|
groupedTags: [], // 改为存储分组后的标签数据
|
|
|
|
|
groupedTags: [],
|
|
|
|
|
isSaving: false,
|
|
|
|
|
tagModalVisible: false,
|
|
|
|
|
isEdit: false,
|
|
|
|
|
currentTagId: null,
|
|
|
|
|
tagForm: {
|
|
|
|
|
type: '',
|
|
|
|
|
name: '',
|
|
|
|
@ -87,11 +112,21 @@ export default {
|
|
|
|
|
rules: {
|
|
|
|
|
type: [{ required: true, message: '请选择标签类型', trigger: 'change' }],
|
|
|
|
|
name: [{ required: true, message: '请输入标签名称', trigger: 'blur' }]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 类型映射
|
|
|
|
|
TypeMap: {
|
|
|
|
|
1: "新一代信息技术",
|
|
|
|
|
2: "高端装备制造",
|
|
|
|
|
3: "生物医药及大健康",
|
|
|
|
|
4: "纳米技术应用及新材料",
|
|
|
|
|
5: "人工智能及数字产业",
|
|
|
|
|
6: "新能源及绿色产业"
|
|
|
|
|
},
|
|
|
|
|
openGroups: []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
// 将分组数据扁平化,用于编辑/删除操作
|
|
|
|
|
// 分组数据扁平化
|
|
|
|
|
tagList() {
|
|
|
|
|
return this.groupedTags.flatMap(group => group.list);
|
|
|
|
|
}
|
|
|
|
@ -107,14 +142,44 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
checkPermi,
|
|
|
|
|
checkRole,
|
|
|
|
|
// 判断分组是否展开
|
|
|
|
|
isGroupOpen(type) {
|
|
|
|
|
return this.openGroups.includes(type);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 切换分组展开状态
|
|
|
|
|
toggleGroup(type) {
|
|
|
|
|
const index = this.openGroups.indexOf(type);
|
|
|
|
|
if (index >= 0) {
|
|
|
|
|
this.openGroups.splice(index, 1);
|
|
|
|
|
} else {
|
|
|
|
|
this.openGroups.push(type);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取标签列表
|
|
|
|
|
async fetchTags() {
|
|
|
|
|
this.loading = true;
|
|
|
|
|
try {
|
|
|
|
|
const res = await getallspan({ xmId: this.xmId });
|
|
|
|
|
// 直接使用后端返回的分组数据
|
|
|
|
|
this.groupedTags = res.data || [];
|
|
|
|
|
console.log('分组标签数据:', this.groupedTags);
|
|
|
|
|
// 处理数据,添加编辑状态
|
|
|
|
|
this.groupedTags = (res.data || []).map(group => {
|
|
|
|
|
return {
|
|
|
|
|
...group,
|
|
|
|
|
list: group.list.map(item => ({
|
|
|
|
|
...item,
|
|
|
|
|
editing: false,
|
|
|
|
|
editName: item.name
|
|
|
|
|
}))
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 默认展开第一个分组
|
|
|
|
|
if (this.groupedTags.length > 0 && this.openGroups.length === 0) {
|
|
|
|
|
this.openGroups.push(this.groupedTags[0].type);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取标签列表失败:', error);
|
|
|
|
|
this.$message.error('获取标签列表失败');
|
|
|
|
@ -123,35 +188,73 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 其他方法保持不变...
|
|
|
|
|
showAddTagModal() {
|
|
|
|
|
this.isEdit = false;
|
|
|
|
|
this.currentTagId = null;
|
|
|
|
|
this.tagModalVisible = true;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.$refs.tagForm && this.$refs.tagForm.clearValidate();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleEdit(row) {
|
|
|
|
|
this.isEdit = true;
|
|
|
|
|
this.currentTagId = row.id;
|
|
|
|
|
this.tagForm = {
|
|
|
|
|
type: row.type,
|
|
|
|
|
name: row.name,
|
|
|
|
|
xmId: this.xmId,
|
|
|
|
|
createBy: row.createBy || '',
|
|
|
|
|
createTime: row.createTime || '',
|
|
|
|
|
createId: row.createId || 0,
|
|
|
|
|
id: row.id || 0,
|
|
|
|
|
updateBy: row.updateBy || '',
|
|
|
|
|
updateTime: row.updateTime || '',
|
|
|
|
|
updateId: row.updateId || 0
|
|
|
|
|
};
|
|
|
|
|
this.tagModalVisible = true;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.$refs.tagForm && this.$refs.tagForm.clearValidate();
|
|
|
|
|
// 开始编辑
|
|
|
|
|
startEdit(row) {
|
|
|
|
|
// 如果正在保存,则不允许编辑
|
|
|
|
|
if (this.isSaving) return;
|
|
|
|
|
|
|
|
|
|
// 先取消其他行的编辑状态
|
|
|
|
|
this.groupedTags.forEach(group => {
|
|
|
|
|
group.list.forEach(item => {
|
|
|
|
|
if (item.editing && item.id !== row.id) {
|
|
|
|
|
item.editing = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
row.editing = true;
|
|
|
|
|
row.editName = row.name;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 保存编辑
|
|
|
|
|
async saveEdit(row) {
|
|
|
|
|
// 如果正在保存,则直接返回
|
|
|
|
|
if (this.isSaving) return;
|
|
|
|
|
|
|
|
|
|
if (!row.editName || row.editName.trim() === '') {
|
|
|
|
|
this.$message.warning('标签名称不能为空');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (row.editName === row.name) {
|
|
|
|
|
row.editing = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置保存状态
|
|
|
|
|
this.isSaving = true;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const formattedTime = now.toISOString().replace('T', ' ').substring(0, 19);
|
|
|
|
|
|
|
|
|
|
const submitData = {
|
|
|
|
|
...row,
|
|
|
|
|
name: row.editName,
|
|
|
|
|
updateTime: formattedTime,
|
|
|
|
|
updateBy: this.$store.state.user.name || '',
|
|
|
|
|
updateId: this.$store.state.user.id || 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await updatspan(submitData);
|
|
|
|
|
this.$message.success('标签更新成功');
|
|
|
|
|
|
|
|
|
|
// 更新数据
|
|
|
|
|
row.name = row.editName;
|
|
|
|
|
row.editing = false;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('更新失败:', error);
|
|
|
|
|
this.$message.error('标签更新失败');
|
|
|
|
|
} finally {
|
|
|
|
|
this.isSaving = false;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submitForm() {
|
|
|
|
@ -164,29 +267,22 @@ export default {
|
|
|
|
|
|
|
|
|
|
const submitData = {
|
|
|
|
|
...this.tagForm,
|
|
|
|
|
createTime: this.isEdit ? this.tagForm.createTime : formattedTime,
|
|
|
|
|
createTime: formattedTime,
|
|
|
|
|
updateTime: formattedTime,
|
|
|
|
|
createBy: this.isEdit ? this.tagForm.createBy : this.$store.state.user.name || '',
|
|
|
|
|
createBy: this.$store.state.user.name || '',
|
|
|
|
|
updateBy: this.$store.state.user.name || '',
|
|
|
|
|
createId: this.isEdit ? this.tagForm.createId : this.$store.state.user.id || 0,
|
|
|
|
|
createId: this.$store.state.user.id || 0,
|
|
|
|
|
updateId: this.$store.state.user.id || 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log('准备提交的完整数据:', JSON.stringify(submitData, null, 2));
|
|
|
|
|
|
|
|
|
|
if (this.isEdit) {
|
|
|
|
|
await updatspan(submitData);
|
|
|
|
|
this.$message.success('标签更新成功');
|
|
|
|
|
} else {
|
|
|
|
|
await addspan(submitData);
|
|
|
|
|
this.$message.success('标签添加成功');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.tagModalVisible = false;
|
|
|
|
|
this.fetchTags();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('操作失败:', error);
|
|
|
|
|
this.$message.error(this.isEdit ? '标签更新失败' : '标签添加失败');
|
|
|
|
|
console.error('添加失败:', error);
|
|
|
|
|
this.$message.error('标签添加失败');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
@ -205,9 +301,39 @@ export default {
|
|
|
|
|
console.error('删除失败:', error);
|
|
|
|
|
this.$message.error('删除失败');
|
|
|
|
|
}
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
this.$message.info('已取消删除');
|
|
|
|
|
});
|
|
|
|
|
}).catch(() => { });
|
|
|
|
|
},
|
|
|
|
|
// 删除整个标签类型
|
|
|
|
|
async handleDeleteType(type) {
|
|
|
|
|
// 检查该类型下是否有标签
|
|
|
|
|
const group = this.groupedTags.find(g => g.type === type);
|
|
|
|
|
if (!group || group.list.length === 0) {
|
|
|
|
|
this.$message.warning('该类型下没有标签');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.$confirm(`确定要删除"${this.TypeMap[type]}"类型及其所有标签吗?`, '提示', {
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
type: 'warning'
|
|
|
|
|
}).then(async () => {
|
|
|
|
|
this.isSaving = true;
|
|
|
|
|
try {
|
|
|
|
|
// 获取该类型下所有标签的ID
|
|
|
|
|
const tagIds = group.list.map(tag => tag.id);
|
|
|
|
|
|
|
|
|
|
// 调用批量删除接口
|
|
|
|
|
await deletespan(tagIds);
|
|
|
|
|
|
|
|
|
|
this.$message.success('删除成功');
|
|
|
|
|
this.fetchTags(); // 重新获取标签列表
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除失败:', error);
|
|
|
|
|
this.$message.error('删除失败');
|
|
|
|
|
} finally {
|
|
|
|
|
this.isSaving = false;
|
|
|
|
|
}
|
|
|
|
|
}).catch(() => { });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
resetForm() {
|
|
|
|
@ -231,14 +357,16 @@ export default {
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.descriptionsdivtwo {
|
|
|
|
|
width: 24rem;
|
|
|
|
|
width: 27rem;
|
|
|
|
|
padding: 0 0.5rem 0 0;
|
|
|
|
|
margin-left: 3rem;
|
|
|
|
|
height: auto; /* 改为自动高度 */
|
|
|
|
|
height: auto;
|
|
|
|
|
min-height: 25.31rem;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tablehead {
|
|
|
|
|
width: 100%;
|
|
|
|
|
width: 90%;
|
|
|
|
|
height: 2.38rem;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
@ -273,13 +401,25 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.group-title {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
margin: 1rem 0 0.5rem 0;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
/* font-size: 1rem; */
|
|
|
|
|
font-family: alimedium;
|
|
|
|
|
color: #333;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
width: 90%;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tablebody {
|
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-input {
|
|
|
|
|
width: 180px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|