资料档案库添加删除文件功能

This commit is contained in:
2026-10-13 10:48:05 +08:00
parent 842f7f0941
commit 91536b193c
2 changed files with 229 additions and 182 deletions
+8
View File
@@ -66,6 +66,14 @@ export function docHistoryListApi(doc_id:number) {
});
}
// 删除资料
export function deleteDocApi(id: number) {
return request({
url: `/api/knowledge/${id}`,
method: 'delete',
});
}
// 归档目录树
export function docArchiveTreeListApi(params: {from:string | number | null,to:string | number | null,org_id: number,type: number | string,change_no?:string}) {
return request({
+212 -173
View File
@@ -13,7 +13,8 @@ import {
docListApi,
uploadDocApi,
updateDocApi,
docHistoryListApi
docHistoryListApi,
deleteDocApi
} from '@/service/api/doc';
const themeStore = useThemeStore();
@@ -315,6 +316,7 @@ const searchHandle = () => {
if(!keyword.value) {
// 搜索框为空时,显示树状列表
showTree.value = true;
getFileList(currentNode.value.id);
}else{
// 搜索框不为空时,显示搜索结果
showTree.value = false;
@@ -464,6 +466,20 @@ const viewHistory = async (d:any) => {
}
historyLoading.value = false;
}
// 删除资料
const deleteFile = async (d:any) => {
loading.value = true;
const {error} = await deleteDocApi(d.id);
if(!error){
window.$message?.success('删除成功');
if(showTree.value){
getFileList(currentNode.value.id);
}else{
getFileList(0);
}
}
loading.value = false;
}
onMounted(() => {
getDocList();
@@ -471,227 +487,250 @@ onMounted(() => {
</script>
<template>
<NSpace vertical :size="16">
<NSpin :show="loading" size="small">
<!-- 上传资料 -->
<input ref="uploadDocRef" type="file" accept="image/*,.pdf,.docx,.xlsx" class="hidden" @change="uploadFiles" />
<!-- 更新资料 -->
<input ref="updateDocRef" type="file" accept="image/*,.pdf,.docx,.xlsx" class="hidden" @change="updateDoc" />
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<NSpace vertical :size="16">
<NSpin :show="loading" size="small">
<!-- 上传资料 -->
<input ref="uploadDocRef" type="file" accept="image/*,.pdf,.docx,.xlsx" class="hidden" @change="uploadFiles" />
<!-- 更新资料 -->
<input ref="updateDocRef" type="file" accept="image/*,.pdf,.docx,.xlsx" class="hidden" @change="updateDoc" />
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
<NGrid :x-gap="16" :y-gap="16" responsive="screen" item-responsive class="pb-[12px] border-b">
<NGi span="24 s:24 m:16">
<div class="h-100% flex items-center gap-2">
<Icon icon="mage:book" class="size-20px" :style="{ color: themeStore.themeColor }" />
<p class="text-base font-semibold">资料档案库(按组织 / 专业类别建立文件夹)</p>
</div>
</NGi>
<NGi span="24 s:24 m:8" class="flex items-center justify-end gap-12px">
<div class="flex items-center gap-2">
<NInput v-model:value="keyword" size="small" type="text" class="w-40 text-xs" clearable placeholder="全文检索…" @clear="keyword = ''; searchHandle();">
<template #suffix>
<Icon icon="akar-icons:search" class="size-14px text-slate-400 cursor-pointer" @click="searchHandle" />
</template>
</NInput>
</div>
</NGi>
<NGi span="24 s:24 m:16">
<div class="h-100% flex items-center gap-2">
<Icon icon="mage:book" class="size-20px" :style="{ color: themeStore.themeColor }" />
<p class="text-base font-semibold">资料档案库(按组织 / 专业类别建立文件夹)</p>
</div>
</NGi>
<NGi span="24 s:24 m:8" class="flex items-center justify-end gap-12px">
<div class="flex items-center gap-2">
<NInput v-model:value="keyword" size="small" type="text" class="w-40 text-xs" clearable placeholder="全文检索…" @clear="keyword = ''; searchHandle();">
<template #suffix>
<Icon icon="akar-icons:search" class="size-14px text-slate-400 cursor-pointer" @click="searchHandle" />
</template>
</NInput>
</div>
</NGi>
</NGrid>
<div class="mt-4 mb-2 rounded-lg bg-slate-50 px-3 py-1.5 text-[11px] text-slate-400">
按「组织 → 专业类别 → 资料文件夹」归档具体资料(操作规程 / PID 图纸 / 工艺卡片 / 设备台账 / 安全阀台账 / 报警台账 / 联锁台账 / 总图 / 现场气体报警仪布置图 / 防爆分区图等);资料由指定维护人(资料管理员 / 各专业专责)上传维护,版本受控;「调用」可将资料引用为当前变更的 AI 知识来源,调用记录留痕。
按「组织 → 专业类别 → 资料文件夹」归档具体资料(操作规程 / PID 图纸 / 工艺卡片 / 设备台账 / 安全阀台账 / 报警台账 / 联锁台账 / 总图 / 现场气体报警仪布置图 / 防爆分区图等);资料由指定维护人(资料管理员 / 各专业专责)上传维护,版本受控;「调用」可将资料引用为当前变更的 AI 知识来源,调用记录留痕。
</div>
<!-- 全文检索:跨文件夹平铺结果 -->
<div v-if="!showTree">
<NSpin :show="fileLoading" size="small">
<div class="mb-1.5 text-xs text-slate-400">全文检索「{{ keyword }}」:跨全部文件夹命中 {{ queryList.length }} 份</div>
<div class="top-scroll">
<div class="divide-y rounded-md border bg-white h-100%">
<div v-for="d in queryList" :key="d.name" class="flex flex-wrap items-center gap-3 px-2 py-2.5">
<Icon icon="akar-icons:file" class="size-16px" />
<div class="min-w-0 flex-1">
<div class="truncate text-sm">{{ d.doc_name }}</div>
<div class="mt-0.5 flex flex-wrap items-center gap-x-2 text-xs text-slate-500">
<NTag size="small" type="info" class="text-[10px]">版本 {{ d.version }}</NTag>
<span>{{ d.path.join(' · ') }}</span>
<span>更新 {{ d?.updated_at?.slice(0, 10) }}</span>
</div>
</div>
<div class="flex shrink-0 gap-3 text-xs">
<NButton type="primary" text class="hover:underline text-xs" @click="viewHistory(d)">查看历史</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="updateFile(d)">更新</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="preview(d)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(d)">下载</NButton>
</div>
</div>
<div v-if="queryList.length === 0" class="h-100% flex items-center justify-center">
<NEmpty description="未匹配到相关资料" class="text-xs"></NEmpty>
</div>
<NSpin :show="fileLoading" size="small">
<div class="mb-1.5 text-xs text-slate-400">全文检索「{{ keyword }}」:跨全部文件夹命中 {{ queryList.length }} 份</div>
<div class="top-scroll">
<div class="divide-y rounded-md border bg-white h-100%">
<div v-for="d in queryList" :key="d.name" class="flex flex-wrap items-center gap-3 px-2 py-2.5">
<Icon icon="akar-icons:file" class="size-16px" />
<div class="min-w-0 flex-1">
<div class="truncate text-sm">{{ d.doc_name }}</div>
<div class="mt-0.5 flex flex-wrap items-center gap-x-2 text-xs text-slate-500">
<NTag size="small" type="info" class="text-[10px]">版本 {{ d.version }}</NTag>
<span>{{ d.path.join(' · ') }}</span>
<span>更新 {{ d?.updated_at?.slice(0, 10) }}</span>
</div>
</div>
<div class="flex shrink-0 gap-3 text-xs">
<NButton type="primary" text class="hover:underline text-xs" @click="viewHistory(d)">查看历史</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="updateFile(d)">更新</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="preview(d)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(d)">下载</NButton>
<NPopconfirm
positive-text="确定"
:positive-button-props="{ size: 'tiny' }"
:negative-button-props="{ size: 'tiny' }"
@positive-click="deleteFile(d)"
>
<template #trigger>
<NButton type="error" text class="hover:underline text-xs">删除</NButton>
</template>
确定删除吗?
</NPopconfirm>
</div>
</div>
</NSpin>
<div v-if="queryList.length === 0" class="h-100% flex items-center justify-center">
<NEmpty description="未匹配到相关资料" class="text-xs"></NEmpty>
</div>
</div>
</div>
</NSpin>
</div>
<!-- 文件夹树 + 当前文件夹资料 -->
<div v-else class="flex gap-3">
<!-- 左侧 -->
<div class="w-300px border rounded-md p-2">
<div class="scroll-left">
<NTree
class="h-100% flex flex-col"
block-line
:data="docList"
label-field="label"
key-field="key"
:expanded-keys="expandedKeys"
:selected-keys="selectedKeys"
:render-switcher-icon="renderSwitcherIcon"
selectable
:cancelable="false"
:on-update:expanded-keys="handleExpandKeys"
:on-update:selected-keys="handleSelectKeys"
:node-props="treeProps"
>
<template #empty>
<div class="h-100% flex items-center justify-center">
<NEmpty description="暂无数据" class="text-xs"></NEmpty>
</div>
</template>
</NTree>
<NDropdown
:show="menuVisible"
:options="menuOptions"
size="small"
:x="menuX"
:y="menuY"
placement="bottom-start"
trigger="manual"
@clickoutside="closeMenu"
@select="handleMenuSelect"
/>
</div>
<!-- 左侧 -->
<div class="w-300px border rounded-md p-2">
<div class="scroll-left">
<NTree
class="h-100% flex flex-col"
block-line
:data="docList"
label-field="label"
key-field="key"
:expanded-keys="expandedKeys"
:selected-keys="selectedKeys"
:render-switcher-icon="renderSwitcherIcon"
selectable
:cancelable="false"
:on-update:expanded-keys="handleExpandKeys"
:on-update:selected-keys="handleSelectKeys"
:node-props="treeProps"
>
<template #empty>
<div class="h-100% flex items-center justify-center">
<NEmpty description="暂无数据" class="text-xs"></NEmpty>
</div>
</template>
</NTree>
<NDropdown
:show="menuVisible"
:options="menuOptions"
size="small"
:x="menuX"
:y="menuY"
placement="bottom-start"
trigger="manual"
@clickoutside="closeMenu"
@select="handleMenuSelect"
/>
</div>
<!-- 右侧 -->
<div class="min-w-0 flex-1">
<div class="mb-1.5 flex flex-wrap items-center gap-1 text-xs text-slate-400">
当前位置:
<div class="flex items-center gap-1">
<template v-for="(item,index) in currentPath" :key="index">
<span v-if="index !== (currentPath.length - 1)">{{ item }} </span>
<span v-else class="font-medium text-slate-600">{{ item }}</span>
<Icon v-if="index !== (currentPath.length - 1)" icon="formkit:right" class="size-12px" />
</template>
</div>
</div>
<!-- 右侧 -->
<div class="min-w-0 flex-1">
<div class="mb-1.5 flex flex-wrap items-center gap-1 text-xs text-slate-400">
当前位置:
<div class="flex items-center gap-1">
<template v-for="(item,index) in currentPath" :key="index">
<span v-if="index !== (currentPath.length - 1)">{{ item }} </span>
<span v-else class="font-medium text-slate-600">{{ item }}</span>
<Icon v-if="index !== (currentPath.length - 1)" icon="formkit:right" class="size-12px" />
</template>
</div>
<span class="ml-auto">共 {{ fileList.length }} 份 · 版本受控</span>
</div>
<NSpin :show="fileLoading" size="small">
<div class="scroll-right">
<div class="divide-y rounded-md border bg-white h-100%">
<div v-for="d in fileList" :key="d.name" class="flex flex-wrap items-center gap-3 px-2 py-2.5">
<Icon icon="akar-icons:file" class="size-16px" />
<div class="min-w-0 flex-1">
<div class="truncate text-sm">{{ d.doc_name }}</div>
<div class="mt-0.5 flex flex-wrap items-center gap-x-2 text-xs text-slate-500">
<NTag size="small" type="info" class="text-[10px]">版本 {{ d.version }}</NTag>
<span>更新 {{ d?.updated_at?.slice(0, 10) }}</span>
</div>
</div>
<div class="flex shrink-0 gap-3 text-xs">
<NButton type="primary" text class="hover:underline text-xs" @click="viewHistory(d)">查看历史</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="updateFile(d)">更新</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="preview(d)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(d)">下载</NButton>
</div>
</div>
<div v-if="fileList.length===0" class="h-100% flex items-center justify-center">
<NEmpty description="暂无资料数据" class="text-xs"></NEmpty>
</div>
</div>
</div>
</NSpin>
<span class="ml-auto">共 {{ fileList.length }} 份 · 版本受控</span>
</div>
<NSpin :show="fileLoading" size="small">
<div class="scroll-right">
<div class="divide-y rounded-md border bg-white h-100%">
<div v-for="d in fileList" :key="d.name" class="flex flex-wrap items-center gap-3 px-2 py-2.5">
<Icon icon="akar-icons:file" class="size-16px" />
<div class="min-w-0 flex-1">
<div class="truncate text-sm">{{ d.doc_name }}</div>
<div class="mt-0.5 flex flex-wrap items-center gap-x-2 text-xs text-slate-500">
<NTag size="small" type="info" class="text-[10px]">版本 {{ d.version }}</NTag>
<span>更新 {{ d?.updated_at?.slice(0, 10) }}</span>
</div>
</div>
<div class="flex shrink-0 gap-3 text-xs">
<NButton type="primary" text class="hover:underline text-xs" @click="viewHistory(d)">查看历史</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="updateFile(d)">更新</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="preview(d)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(d)">下载</NButton>
<NPopconfirm
positive-text="确定"
:positive-button-props="{ size: 'tiny' }"
:negative-button-props="{ size: 'tiny' }"
@positive-click="deleteFile(d)"
>
<template #trigger>
<NButton type="error" text class="hover:underline text-xs">删除</NButton>
</template>
确定删除吗?
</NPopconfirm>
</div>
</div>
<div v-if="fileList.length===0" class="h-100% flex items-center justify-center">
<NEmpty description="暂无资料数据" class="text-xs"></NEmpty>
</div>
</div>
</div>
</NSpin>
</div>
</div>
</NCard>
<!-- 新增/编辑弹窗 -->
<NModal
</NCard>
<!-- 新增/编辑弹窗 -->
<NModal
v-model:show="showModal"
preset="card"
:title="modalType==='add'?'新增文件夹':'编辑文件夹'"
:auto-focus="true"
:style="{ width: '450px', height: 'auto' }"
:segmented="{ content: true, footer: true }"
>
>
<div>
<NInput v-model:value="name" placeholder="请输入文件夹名称" />
<NInput v-model:value="name" placeholder="请输入文件夹名称" />
</div>
<template #footer>
<div class="flex justify-end gap-[10px]">
<div class="flex justify-end gap-[10px]">
<NButton size="small" @click="showModal = false">取消</NButton>
<NButton size="small" type="primary" :loading="saveLoading" @click="modalSave">确定</NButton>
</div>
</div>
</template>
</NModal>
<!-- 历史版本查看 -->
<NDrawer v-model:show="historyDrawer" :width="600" placement="right" :auto-focus="false">
</NModal>
<!-- 历史版本查看 -->
<NDrawer v-model:show="historyDrawer" :width="600" placement="right" :auto-focus="false">
<NDrawerContent title="历史版本" closable>
<NSpin :show="historyLoading" size="small">
<NTable size="small">
<thead>
<tr>
<th>名称</th>
<th class="!text-center w-80px">版本</th>
<th class="!text-center w-120px">更新时间</th>
<th class="!text-center w-80px">操作</th>
</tr>
</thead>
<tbody>
<tr v-if="historyList.length===0">
<td colspan="4" class="text-center !text-slate-400 !py-20px">暂无历史版本数据</td>
</tr>
<tr v-for="item in historyList" :key="item.id">
<td>{{ item.doc_name }}</td>
<td class="text-center">{{ item.version }}</td>
<td class="text-center">{{ item.updated_at?.slice(0, 10) }}</td>
<td>
<div class="flex items-center justify-center gap-2">
<NButton type="primary" text class="hover:underline text-xs" @click="preview(item)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(item)">下载</NButton>
</div>
</td>
</tr>
</tbody>
<thead>
<tr>
<th>名称</th>
<th class="!text-center w-80px">版本</th>
<th class="!text-center w-120px">更新时间</th>
<th class="!text-center w-80px">操作</th>
</tr>
</thead>
<tbody>
<tr v-if="historyList.length===0">
<td colspan="4" class="text-center !text-slate-400 !py-20px">暂无历史版本数据</td>
</tr>
<tr v-for="item in historyList" :key="item.id">
<td>{{ item.doc_name }}</td>
<td class="text-center">{{ item.version }}</td>
<td class="text-center">{{ item.updated_at?.slice(0, 10) }}</td>
<td>
<div class="flex items-center justify-center gap-2">
<NButton type="primary" text class="hover:underline text-xs" @click="preview(item)">预览</NButton>
<NButton type="primary" text class="hover:underline text-xs" @click="download(item)">下载</NButton>
</div>
</td>
</tr>
</tbody>
</NTable>
</NSpin>
</NDrawerContent>
</NDrawer>
<!-- 附件在线预览弹窗 -->
<NModal
</NDrawer>
<!-- 附件在线预览弹窗 -->
<NModal
v-model:show="previewModal"
preset="card"
:auto-focus="false"
:style="{ width: '80%', height: 'auto' }"
:segmented="{ content: true, footer: true }"
>
>
<template #header>
<div class="flex items-center gap-2 text-base font-semibold text-slate-800">
<div class="flex items-center gap-2 text-base font-semibold text-slate-800">
<Icon icon="akar-icons:file" class="size-18px" /> {{ previewFile?.doc_name }}
</div>
</div>
</template>
<template #default>
<div class="space-y-3">
<div class="flex flex-wrap gap-x-4 gap-y-1 text-xs text-slate-400">
<span>上传人:{{ previewFile?.user_name }}</span>
<span>上传时间:{{ previewFile?.created_at?.slice(0, 16) }}</span>
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
</div>
<previewComponent v-if="previewFile" :previewFile="previewFile" />
<div class="space-y-3">
<div class="flex flex-wrap gap-x-4 gap-y-1 text-xs text-slate-400">
<span>上传人:{{ previewFile?.user_name }}</span>
<span>上传时间:{{ previewFile?.created_at?.slice(0, 16) }}</span>
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
</div>
<PreviewComponent v-if="previewFile" :preview-file="previewFile" />
</div>
</template>
</NModal>
</NSpin>
</NSpace>
</NModal>
</NSpin>
</NSpace>
</template>
<style scoped>
</style>
<style scoped lang="scss">
.scroll-left {
height: calc(100vh - 276px);