|
|
@@ -0,0 +1,257 @@
|
|
|
+<template>
|
|
|
+ <div class="knowledge-manage">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <!-- 左侧筛选面板 -->
|
|
|
+ <el-col :span="5">
|
|
|
+ <el-card>
|
|
|
+ <div slot="header"><span>维度筛选</span></div>
|
|
|
+ <el-tree
|
|
|
+ :data="dimensionTree"
|
|
|
+ :props="{ label: 'dimensionName', children: 'children' }"
|
|
|
+ node-key="id"
|
|
|
+ default-expand-all
|
|
|
+ highlight-current
|
|
|
+ @node-click="handleDimensionClick"
|
|
|
+ />
|
|
|
+ </el-card>
|
|
|
+ <el-card style="margin-top: 12px">
|
|
|
+ <div slot="header"><span>标签筛选</span></div>
|
|
|
+ <div style="padding: 4px 0">
|
|
|
+ <el-tag
|
|
|
+ v-for="tag in tags"
|
|
|
+ :key="tag.id"
|
|
|
+ :type="selectedTagId === tag.id ? 'primary' : 'info'"
|
|
|
+ style="cursor: pointer; margin: 2px"
|
|
|
+ @click="handleTagClick(tag)"
|
|
|
+ >
|
|
|
+ {{ tag.tagName }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <el-button v-if="selectedTagId" type="text" size="small" @click="clearTagFilter">清除筛选</el-button>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 右侧知识库列表 -->
|
|
|
+ <el-col :span="19">
|
|
|
+ <el-card>
|
|
|
+ <div slot="header">
|
|
|
+ <span>知识库管理</span>
|
|
|
+ <el-button style="float: right" type="primary" size="small" @click="handleCreate">+ 新增知识</el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <div style="margin-bottom: 16px; display: flex; gap: 12px">
|
|
|
+ <el-input v-model="searchKeyword" placeholder="搜索标题/内容" style="width: 300px" clearable @keyup.enter.native="handleSearch" />
|
|
|
+ <el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
|
+ <el-button @click="resetSearch">重置</el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <knowledge-table
|
|
|
+ :list="knowledgeList"
|
|
|
+ :loading="knowledgeLoading"
|
|
|
+ @edit="handleEdit"
|
|
|
+ @delete="handleDelete"
|
|
|
+ @bind-dimension="handleBindDimension"
|
|
|
+ @bind-tag="handleBindTag"
|
|
|
+ />
|
|
|
+
|
|
|
+ <el-pagination
|
|
|
+ style="margin-top: 16px; text-align: right"
|
|
|
+ :current-page="page"
|
|
|
+ :page-size="size"
|
|
|
+ :total="knowledgeTotal"
|
|
|
+ layout="total, prev, pager, next"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ />
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 新增/编辑知识弹窗 -->
|
|
|
+ <knowledge-dialog
|
|
|
+ :visible.sync="dialogVisible"
|
|
|
+ :knowledge="editingKnowledge"
|
|
|
+ @saved="handleDialogSaved"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 绑定维度弹窗 -->
|
|
|
+ <el-dialog :visible.sync="dimensionDialogVisible" title="绑定维度" width="500px">
|
|
|
+ <dimension-selector v-model="bindingDimensionIds" :multiple="true" />
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="dimensionDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="bindingDimLoading" @click="submitBindDimensions">保存</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 绑定标签弹窗 -->
|
|
|
+ <el-dialog :visible.sync="tagDialogVisible" title="绑定标签" width="500px">
|
|
|
+ <tag-selector v-model="bindingTagIds" />
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="tagDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="bindingTagLoading" @click="submitBindTags">保存</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { mapState, mapActions } from 'vuex'
|
|
|
+import { getDimensionTree } from '@/api/dimension'
|
|
|
+import { getKnowledge, deleteKnowledge, bindKnowledgeDimensions, bindKnowledgeTags } from '@/api/dimension'
|
|
|
+import KnowledgeTable from '@/components/KnowledgeTable'
|
|
|
+import KnowledgeDialog from '@/components/KnowledgeDialog'
|
|
|
+import DimensionSelector from '@/components/DimensionSelector'
|
|
|
+import TagSelector from '@/components/TagSelector'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'KnowledgeManage',
|
|
|
+ components: { KnowledgeTable, KnowledgeDialog, DimensionSelector, TagSelector },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ dimensionTree: [],
|
|
|
+ searchKeyword: '',
|
|
|
+ page: 1,
|
|
|
+ size: 20,
|
|
|
+ // 筛选
|
|
|
+ selectedDimensionId: null,
|
|
|
+ selectedTagId: null,
|
|
|
+ // 编辑
|
|
|
+ dialogVisible: false,
|
|
|
+ editingKnowledge: null,
|
|
|
+ // 绑定维度
|
|
|
+ dimensionDialogVisible: false,
|
|
|
+ bindingKnowledgeId: null,
|
|
|
+ bindingDimensionIds: [],
|
|
|
+ bindingDimLoading: false,
|
|
|
+ // 绑定标签
|
|
|
+ tagDialogVisible: false,
|
|
|
+ bindingKnowledgeIdForTag: null,
|
|
|
+ bindingTagIds: [],
|
|
|
+ bindingTagLoading: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...mapState('dimension', ['knowledgeList', 'knowledgeTotal', 'knowledgeLoading', 'tags'])
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.fetchDimensionTree()
|
|
|
+ this.$store.dispatch('dimension/fetchTags')
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ ...mapActions('dimension', ['fetchKnowledgeList']),
|
|
|
+ async fetchDimensionTree() {
|
|
|
+ const res = await getDimensionTree()
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.dimensionTree = res.data || []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fetchList() {
|
|
|
+ const params = { page: this.page, size: this.size }
|
|
|
+ if (this.searchKeyword) params.keyword = this.searchKeyword
|
|
|
+ if (this.selectedDimensionId) params.dimensionId = this.selectedDimensionId
|
|
|
+ if (this.selectedTagId) params.tagId = this.selectedTagId
|
|
|
+ this.fetchKnowledgeList(params)
|
|
|
+ },
|
|
|
+ handleDimensionClick(node) {
|
|
|
+ this.selectedDimensionId = node.id
|
|
|
+ this.page = 1
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ handleTagClick(tag) {
|
|
|
+ this.selectedTagId = this.selectedTagId === tag.id ? null : tag.id
|
|
|
+ this.page = 1
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ clearTagFilter() {
|
|
|
+ this.selectedTagId = null
|
|
|
+ this.page = 1
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ handleSearch() {
|
|
|
+ this.page = 1
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ resetSearch() {
|
|
|
+ this.searchKeyword = ''
|
|
|
+ this.selectedDimensionId = null
|
|
|
+ this.selectedTagId = null
|
|
|
+ this.page = 1
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ handlePageChange(p) {
|
|
|
+ this.page = p
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ handleCreate() {
|
|
|
+ this.editingKnowledge = null
|
|
|
+ this.dialogVisible = true
|
|
|
+ },
|
|
|
+ async handleEdit(row) {
|
|
|
+ const res = await getKnowledge(row.id)
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.editingKnowledge = res.data
|
|
|
+ this.dialogVisible = true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async handleDelete(row) {
|
|
|
+ this.$confirm('确定删除该知识吗?', '提示', { type: 'warning' }).then(async () => {
|
|
|
+ const res = await deleteKnowledge(row.id)
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success('删除成功')
|
|
|
+ this.fetchList()
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.message || '删除失败')
|
|
|
+ }
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
+ handleDialogSaved() {
|
|
|
+ this.fetchList()
|
|
|
+ },
|
|
|
+ async handleBindDimension(row) {
|
|
|
+ this.bindingKnowledgeId = row.id
|
|
|
+ this.bindingDimensionIds = []
|
|
|
+ this.dimensionDialogVisible = true
|
|
|
+ },
|
|
|
+ async submitBindDimensions() {
|
|
|
+ this.bindingDimLoading = true
|
|
|
+ try {
|
|
|
+ const res = await bindKnowledgeDimensions({
|
|
|
+ knowledgeId: this.bindingKnowledgeId,
|
|
|
+ dimensionIds: this.bindingDimensionIds
|
|
|
+ })
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success('绑定成功')
|
|
|
+ this.dimensionDialogVisible = false
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.message || '绑定失败')
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.bindingDimLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async handleBindTag(row) {
|
|
|
+ this.bindingKnowledgeIdForTag = row.id
|
|
|
+ this.bindingTagIds = []
|
|
|
+ this.tagDialogVisible = true
|
|
|
+ },
|
|
|
+ async submitBindTags() {
|
|
|
+ this.bindingTagLoading = true
|
|
|
+ try {
|
|
|
+ const res = await bindKnowledgeTags({
|
|
|
+ knowledgeId: this.bindingKnowledgeIdForTag,
|
|
|
+ tagIds: this.bindingTagIds
|
|
|
+ })
|
|
|
+ if (res.code === 200) {
|
|
|
+ this.$message.success('绑定成功')
|
|
|
+ this.tagDialogVisible = false
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.message || '绑定失败')
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.bindingTagLoading = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|