6.1
This commit is contained in:
@@ -74,13 +74,14 @@ def create_text_document(
|
|||||||
@router.get("", response_model=DocumentListResponse)
|
@router.get("", response_model=DocumentListResponse)
|
||||||
def list_documents(
|
def list_documents(
|
||||||
kb_id: str = Query(...),
|
kb_id: str = Query(...),
|
||||||
|
category_id: str = Query(None),
|
||||||
page: int = Query(1, ge=1),
|
page: int = Query(1, ge=1),
|
||||||
page_size: int = Query(50, ge=1, le=200),
|
page_size: int = Query(50, ge=1, le=200),
|
||||||
user: User = Depends(get_current_user),
|
user: User = Depends(get_current_user),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
) -> DocumentListResponse:
|
) -> DocumentListResponse:
|
||||||
svc = DocumentService(db)
|
svc = DocumentService(db)
|
||||||
items, total = svc.list_by_knowledge_base(kb_id, user, page=page, page_size=page_size)
|
items, total = svc.list_by_knowledge_base(kb_id, user, category_id=category_id, page=page, page_size=page_size)
|
||||||
return DocumentListResponse(
|
return DocumentListResponse(
|
||||||
items=[_to_response(doc) for doc in items],
|
items=[_to_response(doc) for doc in items],
|
||||||
total=total,
|
total=total,
|
||||||
|
|||||||
@@ -144,13 +144,13 @@ class DocumentService:
|
|||||||
return doc
|
return doc
|
||||||
|
|
||||||
def list_by_knowledge_base(
|
def list_by_knowledge_base(
|
||||||
self, kb_id: str, user: User, *, page: int = 1, page_size: int = 50
|
self, kb_id: str, user: User, *, category_id: str | None = None, page: int = 1, page_size: int = 50
|
||||||
):
|
):
|
||||||
# 校验 KB 归属
|
# 校验 KB 归属
|
||||||
kb = self._kb_repo.get_by_id(kb_id)
|
kb = self._kb_repo.get_by_id(kb_id)
|
||||||
if kb is None or kb.user_id != user.id or kb.status == "DELETED":
|
if kb is None or kb.user_id != user.id or kb.status == "DELETED":
|
||||||
raise NotFoundError("知识库不存在。")
|
raise NotFoundError("知识库不存在。")
|
||||||
return self._doc_repo.list_by_knowledge_base(kb_id, page=page, page_size=page_size)
|
return self._doc_repo.list_by_knowledge_base(kb_id, category_id=category_id, page=page, page_size=page_size)
|
||||||
|
|
||||||
def delete(self, doc_id: str, user: User) -> None:
|
def delete(self, doc_id: str, user: User) -> None:
|
||||||
doc = self.get_or_404(doc_id, user)
|
doc = self.get_or_404(doc_id, user)
|
||||||
|
|||||||
+135
-87
@@ -10,6 +10,7 @@ const kbId = route.params.id as string
|
|||||||
const kb = ref<any>(null)
|
const kb = ref<any>(null)
|
||||||
const docs = ref<any[]>([])
|
const docs = ref<any[]>([])
|
||||||
const categories = ref<any[]>([])
|
const categories = ref<any[]>([])
|
||||||
|
const allCategoriesFlat = ref<any[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const uploading = ref(false)
|
const uploading = ref(false)
|
||||||
const aiUrl = ref('')
|
const aiUrl = ref('')
|
||||||
@@ -32,6 +33,9 @@ const catForm = ref({ name: '', parent_id: null as string | null, is_folder: tru
|
|||||||
const catLoading = ref(false)
|
const catLoading = ref(false)
|
||||||
const editingCatId = ref<string | null>(null)
|
const editingCatId = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 编辑文档分类
|
||||||
|
const editingDocId = ref<string | null>(null)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadKb()
|
await loadKb()
|
||||||
await loadCategories()
|
await loadCategories()
|
||||||
@@ -50,6 +54,9 @@ async function loadCategories() {
|
|||||||
try {
|
try {
|
||||||
const { data } = await apiClient.get(`/knowledge-bases/${kbId}/categories/tree`)
|
const { data } = await apiClient.get(`/knowledge-bases/${kbId}/categories/tree`)
|
||||||
categories.value = data
|
categories.value = data
|
||||||
|
// 同时加载平铺列表
|
||||||
|
const { data: flat } = await apiClient.get(`/knowledge-bases/${kbId}/categories`)
|
||||||
|
allCategoriesFlat.value = flat
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +136,16 @@ async function handleCreateText() {
|
|||||||
textLoading.value = false
|
textLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改文档分类
|
||||||
|
async function handleChangeDocCategory(doc: any, newCategoryId: string) {
|
||||||
|
try {
|
||||||
|
await apiClient.put(`/documents/${doc.id}`, { category_id: newCategoryId })
|
||||||
|
ElMessage.success('目录已更新。')
|
||||||
|
loadDocs()
|
||||||
|
loadCategories()
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDeleteDoc(doc: any) {
|
async function handleDeleteDoc(doc: any) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确定删除「${doc.original_filename}」?`, '确认删除', { type: 'warning' })
|
await ElMessageBox.confirm(`确定删除「${doc.original_filename}」?`, '确认删除', { type: 'warning' })
|
||||||
@@ -154,6 +171,16 @@ async function handleCopyLink() {
|
|||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重新生成 AI 链接
|
||||||
|
async function handleRegenerateLink() {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('重新生成链接后,旧链接将立即失效。确定继续?', '确认', { type: 'warning' })
|
||||||
|
const { data } = await apiClient.post(`/knowledge-bases/${kbId}/regenerate-token`)
|
||||||
|
aiUrl.value = `${window.location.origin}/k/${data.token}`
|
||||||
|
ElMessage.success('链接已重新生成!')
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
// 目录管理
|
// 目录管理
|
||||||
function openAddCategory(parentId: string | null = null) {
|
function openAddCategory(parentId: string | null = null) {
|
||||||
editingCatId.value = null
|
editingCatId.value = null
|
||||||
@@ -196,15 +223,7 @@ async function handleDeleteCategory(cat: any) {
|
|||||||
clearCategoryFilter()
|
clearCategoryFilter()
|
||||||
}
|
}
|
||||||
loadCategories()
|
loadCategories()
|
||||||
} catch { /* ignore */ }
|
loadDocs()
|
||||||
}
|
|
||||||
|
|
||||||
async function handleRegenerate() {
|
|
||||||
try {
|
|
||||||
await ElMessageBox.confirm('重新生成链接后,旧链接将立即失效。确定继续?', '确认', { type: 'warning' })
|
|
||||||
const { data } = await apiClient.post(`/knowledge-bases/${kbId}/regenerate-token`)
|
|
||||||
aiUrl.value = `${window.location.origin}/k/${data.token}`
|
|
||||||
ElMessage.success('链接已重新生成。')
|
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,117 +239,147 @@ function statusType(status: string) {
|
|||||||
return 'warning'
|
return 'warning'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归获取所有叶子节点(用于文本对话框的分类选择器)
|
// 获取分类名称
|
||||||
function flattenCategories(nodes: any[], level = 0): any[] {
|
function getCategoryName(catId: string) {
|
||||||
const result: any[] = []
|
const cat = allCategoriesFlat.value.find((c: any) => c.id === catId)
|
||||||
for (const node of nodes) {
|
return cat ? cat.name : '未分类'
|
||||||
result.push({ ...node, level })
|
|
||||||
if (node.children?.length) {
|
|
||||||
result.push(...flattenCategories(node.children, level + 1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const flatCategories = computed(() => flattenCategories(categories.value))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="kb" style="display: flex; gap: 20px">
|
<div v-if="kb" style="display: flex; gap: 24px; height: calc(100vh - 120px)">
|
||||||
<!-- 左侧:目录树 -->
|
<!-- 左侧:目录树 -->
|
||||||
<div style="width: 280px; flex-shrink: 0">
|
<div style="width: 300px; flex-shrink: 0; overflow-y: auto; border-right: 1px solid #e4e7ed; padding-right: 20px">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px">
|
||||||
<h3 style="margin: 0">目录</h3>
|
<h3 style="margin: 0; font-size: 16px">📁 目录结构</h3>
|
||||||
<el-button size="small" @click="openAddCategory(null)">+ 新建</el-button>
|
<el-button size="small" type="primary" @click="openAddCategory(null)">+ 新建</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 全部文档 -->
|
||||||
|
<div
|
||||||
|
style="padding: 10px 12px; cursor: pointer; border-radius: 6px; margin-bottom: 8px; font-size: 14px; transition: all 0.2s"
|
||||||
|
:style="{ background: !selectedCategoryId ? '#ecf5ff' : '#f5f7fa', color: !selectedCategoryId ? '#409eff' : '#333', fontWeight: !selectedCategoryId ? 'bold' : 'normal' }"
|
||||||
|
@click="clearCategoryFilter"
|
||||||
|
>
|
||||||
|
📋 全部文档
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 目录树 -->
|
||||||
<el-tree
|
<el-tree
|
||||||
:data="categories"
|
:data="categories"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
default-expand-all
|
default-expand-all
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
|
:props="{ children: 'children', label: 'name' }"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%; padding: 4px 0">
|
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%; padding: 6px 0">
|
||||||
<span
|
<span
|
||||||
:style="{ cursor: 'pointer', color: selectedCategoryId === data.id ? '#409eff' : '#333', fontWeight: selectedCategoryId === data.id ? 'bold' : 'normal' }"
|
style="cursor: pointer; font-size: 14px; flex: 1"
|
||||||
|
:style="{ color: selectedCategoryId === data.id ? '#409eff' : '#333', fontWeight: selectedCategoryId === data.id ? 'bold' : 'normal' }"
|
||||||
@click="selectCategory(data)"
|
@click="selectCategory(data)"
|
||||||
>
|
>
|
||||||
{{ data.name }}
|
{{ data.is_folder ? '📁' : '📄' }} {{ data.name }}
|
||||||
<span v-if="data.doc_count > 0" style="color: #999; font-size: 0.85em">({{ data.doc_count }})</span>
|
<span v-if="data.doc_count > 0" style="color: #999; font-size: 12px; margin-left: 4px">({{ data.doc_count }})</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span style="display: flex; gap: 2px">
|
||||||
<el-button size="small" text @click.stop="openAddCategory(data.id)">+</el-button>
|
<el-button size="small" text @click.stop="openAddCategory(data.id)" style="font-size: 12px">+</el-button>
|
||||||
<el-button size="small" text @click.stop="openEditCategory(data)">✎</el-button>
|
<el-button size="small" text @click.stop="openEditCategory(data)" style="font-size: 12px">✎</el-button>
|
||||||
<el-button size="small" text type="danger" @click.stop="handleDeleteCategory(data)">×</el-button>
|
<el-button size="small" text type="danger" @click.stop="handleDeleteCategory(data)" style="font-size: 12px">×</el-button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
|
|
||||||
<!-- 全部文档 -->
|
|
||||||
<div
|
|
||||||
style="margin-top: 10px; padding: 8px 10px; cursor: pointer; border-radius: 4px; background: #f5f7fa"
|
|
||||||
:style="{ background: !selectedCategoryId ? '#ecf5ff' : '#f5f7fa' }"
|
|
||||||
@click="clearCategoryFilter"
|
|
||||||
>
|
|
||||||
全部文档
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧:文档列表 -->
|
<!-- 右侧:文档列表 -->
|
||||||
<div style="flex: 1">
|
<div style="flex: 1; overflow-y: auto">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px">
|
||||||
<h1 style="margin: 0">{{ kb.name }}</h1>
|
<h1 style="margin: 0; font-size: 24px">{{ kb.name }}</h1>
|
||||||
<el-button type="primary" @click="handleCopyLink">复制 AI 链接</el-button>
|
<div style="display: flex; gap: 12px">
|
||||||
|
<el-button @click="handleCopyLink" size="large">📋 复制 AI 链接</el-button>
|
||||||
|
<el-button type="warning" @click="handleRegenerateLink" size="large">🔄 重新生成链接</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card style="margin-bottom: 20px">
|
<!-- AI 链接显示 -->
|
||||||
<el-descriptions :column="2" border size="small">
|
<el-card style="margin-bottom: 20px" shadow="hover">
|
||||||
<el-descriptions-item label="描述">{{ kb.description || '无' }}</el-descriptions-item>
|
<div style="display: flex; align-items: center; gap: 12px">
|
||||||
<el-descriptions-item label="状态">
|
<span style="font-weight: bold; font-size: 14px; white-space: nowrap">AI 访问链接:</span>
|
||||||
<el-tag :type="kb.enabled ? 'success' : 'danger'">{{ kb.enabled ? '启用' : '禁用' }}</el-tag>
|
<el-input :model-value="aiUrl" readonly style="flex: 1" size="large">
|
||||||
</el-descriptions-item>
|
<template #append>
|
||||||
</el-descriptions>
|
<el-button @click="handleCopyLink">复制</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card>
|
<el-card shadow="hover">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||||
<span>
|
<span style="font-size: 16px; font-weight: bold">
|
||||||
文档列表
|
📄 文档列表
|
||||||
<el-tag v-if="selectedCategoryPath" closable @close="clearCategoryFilter" style="margin-left: 8px">
|
<el-tag v-if="selectedCategoryPath" closable @close="clearCategoryFilter" style="margin-left: 8px" size="large">
|
||||||
{{ selectedCategoryPath }}
|
{{ selectedCategoryPath }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</span>
|
</span>
|
||||||
<div style="display: flex; gap: 8px">
|
<div style="display: flex; gap: 10px">
|
||||||
<el-button @click="showTextDialog = true">添加文本</el-button>
|
<el-button size="large" @click="showTextDialog = true">✏️ 添加文本</el-button>
|
||||||
<el-upload :show-file-list="false" :http-request="handleUpload" accept=".docx,.pdf">
|
<el-upload :show-file-list="false" :http-request="handleUpload" accept=".docx,.pdf">
|
||||||
<el-button type="primary" :loading="uploading">上传文档</el-button>
|
<el-button type="primary" :loading="uploading" size="large">📤 上传文档</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-table :data="docs" v-loading="loading" style="width: 100%">
|
<el-table :data="docs" v-loading="loading" style="width: 100%" size="large">
|
||||||
<el-table-column prop="original_filename" label="标题/文件名" min-width="200" show-overflow-tooltip />
|
<el-table-column prop="original_filename" label="标题/文件名" min-width="200" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="font-size: 14px; font-weight: 500">{{ row.original_filename }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="所属目录" width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select
|
||||||
|
:model-value="row.category_id"
|
||||||
|
@change="(val: string) => handleChangeDocCategory(row, val)"
|
||||||
|
placeholder="选择目录"
|
||||||
|
size="default"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="cat in allCategoriesFlat"
|
||||||
|
:key="cat.id"
|
||||||
|
:label="cat.name"
|
||||||
|
:value="cat.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="100" align="center">
|
<el-table-column label="状态" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="statusType(row.status)" size="small">{{ row.status }}</el-tag>
|
<el-tag :type="statusType(row.status)" size="default">{{ row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="类型" width="80" align="center">
|
<el-table-column label="类型" width="80" align="center">
|
||||||
<template #default="{ row }">{{ row.file_ext }}</template>
|
<template #default="{ row }">
|
||||||
|
<span style="font-size: 13px">{{ row.file_ext }}</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="大小" width="100" align="center">
|
<el-table-column label="大小" width="100" align="center">
|
||||||
<template #default="{ row }">{{ formatSize(row.file_size) }}</template>
|
<template #default="{ row }">
|
||||||
|
<span style="font-size: 13px">{{ formatSize(row.file_size) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="title" label="解析标题" min-width="150" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="font-size: 13px; color: #666">{{ row.title || '-' }}</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="title" label="标题" min-width="150" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="keywords" label="关键词" min-width="150" show-overflow-tooltip />
|
|
||||||
<el-table-column label="操作" width="200" align="center">
|
<el-table-column label="操作" width="200" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button size="small" @click="handleReprocess(row)">重新解析</el-button>
|
<el-button size="default" @click="handleReprocess(row)">🔄 重新解析</el-button>
|
||||||
<el-button size="small" type="danger" @click="handleDeleteDoc(row)">删除</el-button>
|
<el-button size="default" type="danger" @click="handleDeleteDoc(row)">🗑️ 删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -338,25 +387,23 @@ const flatCategories = computed(() => flattenCategories(categories.value))
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文本内容对话框 -->
|
<!-- 文本内容对话框 -->
|
||||||
<el-dialog v-model="showTextDialog" title="添加文本内容" width="700px">
|
<el-dialog v-model="showTextDialog" title="✏️ 添加文本内容" width="700px">
|
||||||
<el-form :model="textForm" label-position="top">
|
<el-form :model="textForm" label-position="top">
|
||||||
<el-form-item label="标题" required>
|
<el-form-item label="标题" required>
|
||||||
<el-input v-model="textForm.title" placeholder="文档标题" />
|
<el-input v-model="textForm.title" placeholder="文档标题" size="large" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属目录">
|
<el-form-item label="所属目录">
|
||||||
<el-select v-model="textForm.category_id" placeholder="选择目录(可选)" clearable style="width: 100%">
|
<el-select v-model="textForm.category_id" placeholder="选择目录(可选)" clearable style="width: 100%" size="large">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="cat in flatCategories"
|
v-for="cat in allCategoriesFlat"
|
||||||
:key="cat.id"
|
:key="cat.id"
|
||||||
:label="cat.name"
|
:label="cat.name"
|
||||||
:value="cat.id"
|
:value="cat.id"
|
||||||
>
|
/>
|
||||||
<span :style="{ paddingLeft: cat.level * 20 + 'px' }">{{ cat.name }}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="格式">
|
<el-form-item label="格式">
|
||||||
<el-radio-group v-model="textForm.content_format">
|
<el-radio-group v-model="textForm.content_format" size="large">
|
||||||
<el-radio value="markdown">Markdown</el-radio>
|
<el-radio value="markdown">Markdown</el-radio>
|
||||||
<el-radio value="text">纯文本</el-radio>
|
<el-radio value="text">纯文本</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@@ -365,33 +412,34 @@ const flatCategories = computed(() => flattenCategories(categories.value))
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="textForm.content"
|
v-model="textForm.content"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="12"
|
:rows="15"
|
||||||
placeholder="输入文本内容(支持 Markdown 格式)"
|
placeholder="输入文本内容(支持 Markdown 格式)"
|
||||||
|
size="large"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="showTextDialog = false">取消</el-button>
|
<el-button @click="showTextDialog = false" size="large">取消</el-button>
|
||||||
<el-button type="primary" :loading="textLoading" @click="handleCreateText">创建</el-button>
|
<el-button type="primary" :loading="textLoading" @click="handleCreateText" size="large">创建</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 目录管理对话框 -->
|
<!-- 目录管理对话框 -->
|
||||||
<el-dialog v-model="showCatDialog" :title="editingCatId ? '编辑目录' : '新建目录'" width="400px">
|
<el-dialog v-model="showCatDialog" :title="editingCatId ? '✏️ 编辑目录' : '📁 新建目录'" width="450px">
|
||||||
<el-form :model="catForm" label-position="top">
|
<el-form :model="catForm" label-position="top">
|
||||||
<el-form-item label="目录名称" required>
|
<el-form-item label="目录名称" required>
|
||||||
<el-input v-model="catForm.name" placeholder="如:公司基本信息" />
|
<el-input v-model="catForm.name" placeholder="如:公司基本信息" size="large" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="catForm.is_folder">
|
<el-radio-group v-model="catForm.is_folder" size="large">
|
||||||
<el-radio :value="true">文件夹(可包含子目录)</el-radio>
|
<el-radio :value="true">📁 文件夹(可包含子目录)</el-radio>
|
||||||
<el-radio :value="false">叶子分类(存放文档)</el-radio>
|
<el-radio :value="false">📄 叶子分类(存放文档)</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="showCatDialog = false">取消</el-button>
|
<el-button @click="showCatDialog = false" size="large">取消</el-button>
|
||||||
<el-button type="primary" :loading="catLoading" @click="handleSaveCategory">
|
<el-button type="primary" :loading="catLoading" @click="handleSaveCategory" size="large">
|
||||||
{{ editingCatId ? '保存' : '创建' }}
|
{{ editingCatId ? '保存' : '创建' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+652
-509
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user