This commit is contained in:
ygx
2026-03-29 16:07:17 +08:00
parent 9343c110d3
commit 564eb09b40
5 changed files with 82 additions and 71 deletions

View File

@@ -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,
});
}; };
/** 获取我的文档列表(由我创建的所有文档) */ /** 获取我的文档列表(由我创建的所有文档) */

View File

@@ -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',

View File

@@ -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
@@ -494,7 +503,7 @@ const handleSelectDocument = async (doc: Document) => {
Message.warning('请先保存或取消当前编辑') Message.warning('请先保存或取消当前编辑')
return return
} }
try { try {
loading.value = true loading.value = true
const res: any = await fetchDocumentDetail(doc.id) const res: any = await fetchDocumentDetail(doc.id)
@@ -502,9 +511,17 @@ const handleSelectDocument = async (doc: Document) => {
// 兼容两种响应格式:直接返回文档对象 或 { type, resource } 格式 // 兼容两种响应格式:直接返回文档对象 或 { type, resource } 格式
const docData = res.details.resource || res.details const docData = res.details.resource || res.details
currentDocument.value = docData currentDocument.value = docData
// 从文档详情中获取收藏状态 // 从文档详情中获取收藏状态
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'
@@ -669,9 +687,9 @@ const handleDelete = () => {
// 确认删除 // 确认删除
const handleConfirmDelete = async () => { 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

View File

@@ -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)
} }

View File

@@ -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('删除失败')
} }
}, },