feat
This commit is contained in:
@@ -177,8 +177,11 @@ export const publishDocument = (id: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** 移入回收站 */
|
/** 移入回收站 */
|
||||||
export const moveToTrash = (id: number) => {
|
export const moveToTrash = (resourceId: number, resourceType: string) => {
|
||||||
return request.post<ApiResponse<string>>("/Kb/v1/trash/move", { id, type: 'document' });
|
return request.post<ApiResponse<string>>("/Kb/v1/trash/move", {
|
||||||
|
resource_id: resourceId,
|
||||||
|
resource_type: resourceType,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 获取我的文档列表(由我创建的所有文档) */
|
/** 获取我的文档列表(由我创建的所有文档) */
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<!-- 资源类型 -->
|
<!-- 资源类型 -->
|
||||||
<template #resource_type="{ record }">
|
<template #resource_type="{ record }">
|
||||||
<a-tag :color="record.resource_type === 'document' ? 'arc-blue' : 'arc-green'">
|
<a-tag :color="record.resource_type === 'document' ? 'blue' : 'green'">
|
||||||
{{ record.resource_type === 'document' ? '文档' : 'FAQ' }}
|
{{ record.resource_type === 'document' ? '文档' : 'FAQ' }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
@@ -69,12 +69,12 @@
|
|||||||
>
|
>
|
||||||
<div v-if="currentResource" class="detail-content">
|
<div v-if="currentResource" class="detail-content">
|
||||||
<a-descriptions :column="2" bordered>
|
<a-descriptions :column="2" bordered>
|
||||||
<a-descriptions-item label="文档编号">
|
<a-descriptions-item label="资源名称">
|
||||||
{{ currentResource.doc_no || '-' }}
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="标题">
|
|
||||||
{{ currentResource.title || '-' }}
|
{{ currentResource.title || '-' }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="资源类型">
|
||||||
|
<a-tag color="blue">文档</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="作者">
|
<a-descriptions-item label="作者">
|
||||||
{{ currentResource.author_name || '-' }}
|
{{ currentResource.author_name || '-' }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
@@ -109,23 +109,20 @@
|
|||||||
>
|
>
|
||||||
<div v-if="currentFaq" class="detail-content">
|
<div v-if="currentFaq" class="detail-content">
|
||||||
<a-descriptions :column="2" bordered>
|
<a-descriptions :column="2" bordered>
|
||||||
<a-descriptions-item label="FAQ编号">
|
<a-descriptions-item label="资源名称">
|
||||||
{{ currentFaq.faq_no || '-' }}
|
{{ currentFaq.question || '-' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="资源类型">
|
||||||
|
<a-tag color="green">FAQ</a-tag>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="状态">
|
<a-descriptions-item label="状态">
|
||||||
<a-tag color="green">{{ currentFaq.status || '已发布' }}</a-tag>
|
<a-tag color="green">{{ currentFaq.status || '已发布' }}</a-tag>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="问题" :span="2">
|
|
||||||
{{ currentFaq.question || '-' }}
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="答案" :span="2">
|
|
||||||
<div class="content-preview" v-html="currentFaq.answer || '-'"></div>
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="浏览次数">
|
<a-descriptions-item label="浏览次数">
|
||||||
{{ currentFaq.view_count || 0 }}
|
{{ currentFaq.view_count || 0 }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="有用次数">
|
<a-descriptions-item label="答案" :span="2">
|
||||||
{{ currentFaq.helpful_count || 0 }}
|
<div class="content-preview" v-html="currentFaq.answer || '-'"></div>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,13 +175,6 @@ const columns = computed<TableColumnData[]>(() => [
|
|||||||
width: 100,
|
width: 100,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
dataIndex: 'remarks',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
width: 200,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '收藏时间',
|
title: '收藏时间',
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'created_at',
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ const searchKeyword = ref('')
|
|||||||
|
|
||||||
// 文档列表
|
// 文档列表
|
||||||
const documentList = ref<Document[]>([])
|
const documentList = ref<Document[]>([])
|
||||||
|
const resourceTypeMap = ref<Record<number, string>>({}) // 保存 id -> resource_type 的映射
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(20)
|
||||||
@@ -337,6 +338,7 @@ const total = ref(0)
|
|||||||
|
|
||||||
// 当前选中的文档
|
// 当前选中的文档
|
||||||
const currentDocument = ref<Document | null>(null)
|
const currentDocument = ref<Document | null>(null)
|
||||||
|
const currentResourceType = ref<string>('document') // 外层类型:document 或 faq
|
||||||
const isFavorited = ref(false)
|
const isFavorited = ref(false)
|
||||||
|
|
||||||
// 编辑状态
|
// 编辑状态
|
||||||
@@ -433,7 +435,14 @@ const fetchData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理数据:提取 resource 字段(document 和 faq 都作为文档处理)
|
// 处理数据:提取 resource 字段(document 和 faq 都作为文档处理)
|
||||||
documentList.value = rawData.map((item: any) => item.resource)
|
// 同时保存每个文档对应的 resource_type
|
||||||
|
documentList.value = rawData.map((item: any) => {
|
||||||
|
// 保存 id -> resource_type 的映射
|
||||||
|
if (item.resource?.id && item.type) {
|
||||||
|
resourceTypeMap.value[item.resource.id] = item.type
|
||||||
|
}
|
||||||
|
return item.resource
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
documentList.value = []
|
documentList.value = []
|
||||||
total.value = 0
|
total.value = 0
|
||||||
@@ -505,6 +514,14 @@ const handleSelectDocument = async (doc: Document) => {
|
|||||||
|
|
||||||
// 从文档详情中获取收藏状态
|
// 从文档详情中获取收藏状态
|
||||||
isFavorited.value = docData.is_favorited || false
|
isFavorited.value = docData.is_favorited || false
|
||||||
|
|
||||||
|
// 获取 resource_type:优先从列表映射中获取(因为详情接口可能不返回外层 type)
|
||||||
|
if (resourceTypeMap.value[doc.id]) {
|
||||||
|
currentResourceType.value = resourceTypeMap.value[doc.id]
|
||||||
|
} else if (res.details.type) {
|
||||||
|
// 备用:从详情响应中获取(注意:这可能是内层 type,如 common/guide)
|
||||||
|
currentResourceType.value = res.details.type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取文档详情失败:', error)
|
console.error('获取文档详情失败:', error)
|
||||||
@@ -517,6 +534,7 @@ const handleSelectDocument = async (doc: Document) => {
|
|||||||
// 新建文档
|
// 新建文档
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
currentDocument.value = null
|
currentDocument.value = null
|
||||||
|
currentResourceType.value = 'document' // 新建时默认为 document
|
||||||
isFavorited.value = false
|
isFavorited.value = false
|
||||||
editForm.title = ''
|
editForm.title = ''
|
||||||
editForm.type = 'common'
|
editForm.type = 'common'
|
||||||
@@ -671,7 +689,7 @@ const handleConfirmDelete = async () => {
|
|||||||
if (!currentDocument.value?.id) return
|
if (!currentDocument.value?.id) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await moveToTrash(currentDocument.value.id)
|
await moveToTrash(currentDocument.value.id, currentResourceType.value)
|
||||||
Message.success('删除成功,已移入回收站')
|
Message.success('删除成功,已移入回收站')
|
||||||
deleteConfirmVisible.value = false
|
deleteConfirmVisible.value = false
|
||||||
currentDocument.value = null
|
currentDocument.value = null
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:title="isEdit ? '编辑分类' : '新增分类'"
|
:title="isEdit ? '编辑标签' : '新增标签'"
|
||||||
width="600px"
|
width="600px"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
@@ -12,42 +12,42 @@
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
label="分类名称"
|
label="标签名称"
|
||||||
field="name"
|
field="name"
|
||||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
:rules="[{ required: true, message: '请输入标签名称' }]"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
placeholder="请输入分类名称"
|
placeholder="请输入标签名称"
|
||||||
:max-length="200"
|
:max-length="200"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="分类类型" field="type">
|
<a-form-item label="标签类型" field="type">
|
||||||
<a-select
|
<a-select
|
||||||
v-model="form.type"
|
v-model="form.type"
|
||||||
placeholder="请选择分类类型"
|
placeholder="请选择标签类型"
|
||||||
allow-clear
|
allow-clear
|
||||||
>
|
>
|
||||||
<a-option value="document">文档分类</a-option>
|
<a-option value="document">文档标签</a-option>
|
||||||
<a-option value="faq">FAQ分类</a-option>
|
<a-option value="faq">FAQ标签</a-option>
|
||||||
<a-option value="general">通用分类</a-option>
|
<a-option value="general">通用标签</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-form-item label="分类描述" field="description">
|
<a-form-item label="标签描述" field="description">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model="form.description"
|
v-model="form.description"
|
||||||
placeholder="请输入分类描述"
|
placeholder="请输入标签描述"
|
||||||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||||
:max-length="500"
|
:max-length="500"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="分类颜色" field="color">
|
<a-form-item label="标签颜色" field="color">
|
||||||
<div class="color-picker-wrapper">
|
<div class="color-picker-wrapper">
|
||||||
<a-input
|
<a-input
|
||||||
v-model="form.color"
|
v-model="form.color"
|
||||||
@@ -208,11 +208,11 @@ const handleOk = async () => {
|
|||||||
|
|
||||||
let res: any
|
let res: any
|
||||||
if (isEdit.value && props.category?.id) {
|
if (isEdit.value && props.category?.id) {
|
||||||
// 编辑分类
|
// 编辑标签
|
||||||
data.id = props.category.id
|
data.id = props.category.id
|
||||||
res = await updateCategory(data)
|
res = await updateCategory(data)
|
||||||
} else {
|
} else {
|
||||||
// 新建分类
|
// 新建标签
|
||||||
res = await createCategory(data)
|
res = await createCategory(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:columns="columns"
|
:columns="columns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
title="分类管理"
|
title="标签管理"
|
||||||
search-button-text="查询"
|
search-button-text="查询"
|
||||||
reset-button-text="重置"
|
reset-button-text="重置"
|
||||||
@update:form-model="handleFormModelUpdate"
|
@update:form-model="handleFormModelUpdate"
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
>
|
>
|
||||||
<template #toolbar-left>
|
<template #toolbar-left>
|
||||||
<a-button type="primary" @click="handleCreate">
|
<a-button type="primary" @click="handleCreate">
|
||||||
新增分类
|
新增标签
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
{{ rowIndex + 1 }}
|
{{ rowIndex + 1 }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 分类类型 -->
|
<!-- 标签类型 -->
|
||||||
<template #type="{ record }">
|
<template #type="{ record }">
|
||||||
<a-tag :color="getTypeColor(record.type)">
|
<a-tag :color="getTypeColor(record.type)">
|
||||||
{{ getTypeLabel(record.type) }}
|
{{ getTypeLabel(record.type) }}
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
|
|
||||||
<!-- 分类表单对话框(新增/编辑) -->
|
<!-- 标签表单对话框(新增/编辑) -->
|
||||||
<category-form-dialog
|
<category-form-dialog
|
||||||
v-model:visible="formVisible"
|
v-model:visible="formVisible"
|
||||||
:category="editingCategory"
|
:category="editingCategory"
|
||||||
@@ -89,17 +89,17 @@ const formItems = computed<FormItem[]>(() => [
|
|||||||
field: 'keyword',
|
field: 'keyword',
|
||||||
label: '关键词',
|
label: '关键词',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
placeholder: '请输入分类名称',
|
placeholder: '请输入标签名称',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'type',
|
field: 'type',
|
||||||
label: '分类类型',
|
label: '标签类型',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
placeholder: '请选择分类类型',
|
placeholder: '请选择标签类型',
|
||||||
options: [
|
options: [
|
||||||
{ label: '文档分类', value: 'document' },
|
{ label: '文档标签', value: 'document' },
|
||||||
{ label: 'FAQ分类', value: 'faq' },
|
{ label: 'FAQ标签', value: 'faq' },
|
||||||
{ label: '通用分类', value: 'general' },
|
{ label: '通用标签', value: 'general' },
|
||||||
],
|
],
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
},
|
},
|
||||||
@@ -115,19 +115,19 @@ const columns = computed(() => [
|
|||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '分类名称',
|
title: '标签名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '分类描述',
|
title: '标签描述',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '分类类型',
|
title: '标签类型',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
slotName: 'type',
|
slotName: 'type',
|
||||||
width: 120,
|
width: 120,
|
||||||
@@ -153,23 +153,23 @@ const columns = computed(() => [
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
// 当前选中的分类
|
// 当前选中的标签
|
||||||
const editingCategory = ref<Category | null>(null)
|
const editingCategory = ref<Category | null>(null)
|
||||||
|
|
||||||
// 对话框可见性
|
// 对话框可见性
|
||||||
const formVisible = ref(false)
|
const formVisible = ref(false)
|
||||||
|
|
||||||
// 获取分类类型标签
|
// 获取标签类型标签
|
||||||
const getTypeLabel = (type: string) => {
|
const getTypeLabel = (type: string) => {
|
||||||
const typeMap: Record<string, string> = {
|
const typeMap: Record<string, string> = {
|
||||||
document: '文档分类',
|
document: '文档标签',
|
||||||
faq: 'FAQ分类',
|
faq: 'FAQ标签',
|
||||||
general: '通用分类',
|
general: '通用标签',
|
||||||
}
|
}
|
||||||
return typeMap[type] || type
|
return typeMap[type] || type
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取分类类型颜色
|
// 获取标签类型颜色
|
||||||
const getTypeColor = (type: string) => {
|
const getTypeColor = (type: string) => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
document: 'blue',
|
document: 'blue',
|
||||||
@@ -187,7 +187,7 @@ const updateTableData = () => {
|
|||||||
tableData.value = allData.value.slice(start, end)
|
tableData.value = allData.value.slice(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取分类列表
|
// 获取标签列表
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
@@ -217,14 +217,14 @@ const fetchCategories = async () => {
|
|||||||
pagination.value.current = 1
|
pagination.value.current = 1
|
||||||
updateTableData()
|
updateTableData()
|
||||||
} else {
|
} else {
|
||||||
Message.error(res.message || '获取分类列表失败')
|
Message.error(res.message || '获取标签列表失败')
|
||||||
allData.value = []
|
allData.value = []
|
||||||
tableData.value = []
|
tableData.value = []
|
||||||
pagination.value.total = 0
|
pagination.value.total = 0
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取分类列表失败:', error)
|
console.error('获取标签列表失败:', error)
|
||||||
Message.error('获取分类列表失败')
|
Message.error('获取标签列表失败')
|
||||||
allData.value = []
|
allData.value = []
|
||||||
tableData.value = []
|
tableData.value = []
|
||||||
pagination.value.total = 0
|
pagination.value.total = 0
|
||||||
@@ -271,23 +271,23 @@ const handlePageSizeChange = (pageSize: number) => {
|
|||||||
updateTableData()
|
updateTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增分类
|
// 新增标签
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
editingCategory.value = null
|
editingCategory.value = null
|
||||||
formVisible.value = true
|
formVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑分类
|
// 编辑标签
|
||||||
const handleEdit = (record: Category) => {
|
const handleEdit = (record: Category) => {
|
||||||
editingCategory.value = { ...record }
|
editingCategory.value = { ...record }
|
||||||
formVisible.value = true
|
formVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除分类
|
// 删除标签
|
||||||
const handleDelete = async (record: Category) => {
|
const handleDelete = async (record: Category) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: `确认删除分类「${record.name}」吗?`,
|
content: `确认删除标签「${record.name}」吗?`,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
try {
|
try {
|
||||||
const res: any = await deleteCategory(record.id)
|
const res: any = await deleteCategory(record.id)
|
||||||
@@ -298,7 +298,7 @@ const handleDelete = async (record: Category) => {
|
|||||||
Message.error(res.message || '删除失败')
|
Message.error(res.message || '删除失败')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('删除分类失败:', error)
|
console.error('删除标签失败:', error)
|
||||||
Message.error('删除失败')
|
Message.error('删除失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user