档案管理

This commit is contained in:
cwchen 2025-09-18 11:16:04 +08:00
parent 095b9006b4
commit a41394d8bd
6 changed files with 304 additions and 88 deletions

View File

@ -117,11 +117,11 @@ export function getFileManageByIdApi(params) {
} }
// 档案移交确认 // 档案移交确认
export function updateIntegrityStatusApi(params) { export function updateIntegrityStatusApi(data) {
return request({ return request({
url: '/smartArchives/fileManage/updateIntegrityStatus', url: '/smartArchives/fileManage/updateIntegrityStatus',
method: 'get', method: 'post',
params data
}) })
} }
@ -133,3 +133,12 @@ export function fileExtractApi(data) {
data data
}) })
} }
// 文件预览
export function getFileAsBase64Api(params) {
return request({
url: '/smartArchives/fileManage/getFileAsBase64',
method: 'get',
params:params
})
}

View File

@ -12,7 +12,7 @@
查询 查询
</el-button> </el-button>
<el-button plain size="small" type="primary" icon="el-icon-plus" <el-button plain size="small" type="primary" icon="el-icon-plus"
v-hasPermi="['archive:catalogue:add']" @click="addTree"> v-hasPermi="['archive:catalogue:add']" @click="addTree" v-if="fileStatus === '0'">
新增 新增
</el-button> </el-button>
</el-col> </el-col>
@ -31,14 +31,14 @@
<span class="node-label">{{ node.label }}</span> <span class="node-label">{{ node.label }}</span>
</template> </template>
<span class="btn-box"> <span class="btn-box">
<el-button type="text" icon="el-icon-plus" v-if="(data.level > 1 && Number(data.isUnique) === 1) || (data.level === 1 && Number(data.isUnique) === 0)" <el-button type="text" icon="el-icon-plus" v-if="(data.level > 1 && Number(data.isUnique) === 1) || (data.level === 1 && Number(data.isUnique) === 0) && fileStatus === '0'"
@click.stop="() => addTree(data)" v-hasPermi="['file:manage:add']"> @click.stop="() => addTree(data)" v-hasPermi="['file:manage:add']">
</el-button> </el-button>
<el-button type="text" v-if="data.level > 1 && Number(data.isUnique) === 1" icon="el-icon-edit-outline" <el-button type="text" v-if="data.level > 1 && Number(data.isUnique) === 1 && fileStatus === '0'" icon="el-icon-edit-outline"
style="color: #007ce0" @click.stop="() => editTree(data, data)" style="color: #007ce0" @click.stop="() => editTree(data, data)"
v-hasPermi="['file:manage:update']"> v-hasPermi="['file:manage:update']">
</el-button> </el-button>
<el-button type="text" v-if="data.level > 1 && Number(data.isUnique) === 1" icon="el-icon-delete" style="color: #f00000;" <el-button type="text" v-if="data.level > 1 && Number(data.isUnique) === 1 && fileStatus === '0'" icon="el-icon-delete" style="color: #f00000;"
@click.stop="() => delTree(node, data)" v-hasPermi="['file:manage:del']"> @click.stop="() => delTree(node, data)" v-hasPermi="['file:manage:del']">
</el-button> </el-button>
</span> </span>
@ -59,7 +59,7 @@ import FileAddTreeData from './addTreeData.vue'
export default { export default {
name: 'FileLeftTree', name: 'FileLeftTree',
components: { FileAddTreeData }, components: { FileAddTreeData },
props: ["projectId"], props: ["projectId", "fileStatus"],
data() { data() {
return { return {
treeDataList: [], treeDataList: [],

View File

@ -2,7 +2,7 @@
<!-- 档案目录管理 --> <!-- 档案目录管理 -->
<div> <div>
<el-card style="min-height: calc(100vh - 190px);"> <el-card style="min-height: calc(100vh - 190px);">
<TableModel :formLabel="formLabel" :showOperation="true" :showRightTools="true" ref="tableRef" <TableModel :formLabel="formLabel" :showOperation="fileStatus === '0'" :showRightTools="true" ref="tableRef"
:columnsList="columnsList" :request-api="getFileManageApi" :send-params="defaultParams"> :columnsList="columnsList" :request-api="getFileManageApi" :send-params="defaultParams">
<template slot="dataSource" slot-scope="{ data }"> <template slot="dataSource" slot-scope="{ data }">
<span>{{ data.dataSource === '1' ? '本系统上传' : '智慧现场' }}</span> <span>{{ data.dataSource === '1' ? '本系统上传' : '智慧现场' }}</span>
@ -12,7 +12,7 @@
</template> </template>
<template slot="btn"> <template slot="btn">
<el-button plain size="mini" type="primary" icon="el-icon-plus" v-hasPermi="['file:manage:add']" <el-button plain size="mini" type="primary" icon="el-icon-plus" v-hasPermi="['file:manage:add']"
@click="handleAdd" :disabled="addBtnIsShow"> @click="handleAdd" :disabled="addBtnIsShow" v-if="fileStatus === '0'">
新增 新增
</el-button> </el-button>
</template> </template>
@ -58,6 +58,10 @@ export default {
selectedNode:{ selectedNode:{
type:Object, type:Object,
default:null default:null
},
fileStatus:{
type:String,
default:''
} }
}, },
components: { components: {

View File

@ -3,10 +3,12 @@
<el-card class="toolbar-card"> <el-card class="toolbar-card">
<div class="toolbar"> <div class="toolbar">
<div class="toolbar-left"> <div class="toolbar-left">
<el-button type="warning" plain icon="el-icon-bottom" size="mini" <el-button type="warning" plain icon="el-icon-bottom" size="mini" @click="handleFileExtract"
@click="handleFileExtract">档案抽取</el-button> v-if="fileStatus === '0' && !integrityStatus">档案抽取</el-button>
<el-button type="success" plain icon="el-icon-finished" size="mini" <el-button type="success" plain icon="el-icon-finished" size="mini" @click="moveListConfirm"
@click="handleUpdateIntegrityStatus">移交清单确认</el-button> v-if="fileStatus === '0' && !integrityStatus">移交清单确认</el-button>
<el-button type="success" plain icon="el-icon-finished" size="mini" @click="handleIntegrityStatus"
v-if="fileStatus === '0' && integrityStatus">完整性确认</el-button>
<el-button type="danger" plain icon="el-icon-close" size="mini" @click="handleClose">返回</el-button> <el-button type="danger" plain icon="el-icon-close" size="mini" @click="handleClose">返回</el-button>
</div> </div>
@ -14,12 +16,57 @@
</el-card> </el-card>
<el-row :gutter="24" class="content-row"> <el-row :gutter="24" class="content-row">
<el-col :span="8" class="pane-left"> <el-col :span="8" class="pane-left">
<LeftTree @handleNodeClick="handleNodeClick" :projectId="projectId" /> <LeftTree @handleNodeClick="handleNodeClick" :projectId="projectId" :fileStatus="fileStatus" />
</el-col> </el-col>
<el-col :span="16" class="pane-right"> <el-col :span="16" class="pane-right">
<RightTable :selectedNode="selectedNode" :projectId="projectId" /> <RightTable :selectedNode="selectedNode" :projectId="projectId" :fileStatus="fileStatus" />
</el-col> </el-col>
</el-row> </el-row>
<!-- 档案同步进度弹框 -->
<el-dialog :visible.sync="syncDialogVisible" :close-on-click-modal="false" :close-on-press-escape="false"
:show-close="false" width="400px">
<div slot="title" class="dialog-title">
<span>档案同步</span>
</div>
<div class="sync-content">
<div class="sync-icon">
<i class="el-icon-loading" v-if="isSyncing"></i>
<i class="el-icon-success" v-else-if="syncSuccess"></i>
<i class="el-icon-error" v-else-if="syncError"></i>
</div>
<div class="sync-text">
<p v-if="isSyncing">档案同步中请稍候...</p>
<p v-else-if="syncSuccess" class="success-text">同步档案已完成</p>
<p v-else-if="syncError" class="error-text">档案同步失败</p>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="clear-btn" @click="closeSyncDialog" v-if="!isSyncing">取消</el-button>
<el-button type="primary" class="search-btn" @click="closeSyncDialog" v-if="!isSyncing">确定</el-button>
</span>
</el-dialog>
<!-- 完整性确认弹框 -->
<el-dialog :visible.sync="confirmDialogVisible" :close-on-click-modal="false" :close-on-press-escape="false"
:show-close="true" width="450px">
<div slot="title" class="dialog-title">
<span>操作确认</span>
</div>
<div class="confirm-content">
<div class="confirm-icon">
<i class="el-icon-question"></i>
</div>
<div class="confirm-text">
<p class="main-message">确认所有档案已完整,可以进行移交?</p>
<p class="sub-message">确认后不可再上传文件</p>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="clear-btn" @click="confirmDialogVisible = false">取消</el-button>
<el-button type="primary" class="search-btn" @click="confirmIntegrityStatus">确定</el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
@ -27,6 +74,7 @@
import LeftTree from './components/leftTree.vue' import LeftTree from './components/leftTree.vue'
import RightTable from './components/rightTable.vue' import RightTable from './components/rightTable.vue'
import { fileExtractApi, updateIntegrityStatusApi } from '@/api/archivesManagement/fileManager/fileManager' import { fileExtractApi, updateIntegrityStatusApi } from '@/api/archivesManagement/fileManager/fileManager'
import { decryptWithSM4 } from '@/utils/sm'
export default { export default {
name: 'FileData', name: 'FileData',
components: { components: {
@ -36,14 +84,23 @@ export default {
data() { data() {
return { return {
projectId: null, projectId: null,
fileStatus: null,
// //
selectedNode: null, selectedNode: null,
//
syncDialogVisible: false,
isSyncing: false,
syncSuccess: false,
syncError: false,
integrityStatus: false,
//
confirmDialogVisible: false,
} }
}, },
created() { created() {
// ID // ID
this.projectId = this.$route.query.id this.projectId = decryptWithSM4(this.$route.query.id)
console.log('接收到的项目ID:', this.projectId) this.fileStatus = decryptWithSM4(this.$route.query.fileStatus)
}, },
methods: { methods: {
handleClose() { handleClose() {
@ -54,39 +111,76 @@ export default {
handleNodeClick(data) { handleNodeClick(data) {
this.selectedNode = data; this.selectedNode = data;
}, },
// //
handleFileExtract() { moveListConfirm() {
// this.integrityStatus = true;
const loadingInstance = this.$loading({ },
lock: true, //
text: '档案同步中...', handleIntegrityStatus() {
spinner: 'el-icon-loading', this.confirmDialogVisible = true;
background: 'rgba(0, 0, 0, 0.7)' },
}); //
confirmIntegrityStatus() {
// this.closeConfirmDialog();
fileExtractApi({ projectId: this.projectId }) //
updateIntegrityStatusApi({ proId: this.projectId })
.then(res => { .then(res => {
//
loadingInstance.close();
if (res.code === 200) { if (res.code === 200) {
this.$message.success('同步档案已完成'); this.$message.success('完整性确认成功');
this.closeConfirmDialog();
//
} else { } else {
this.$message.error(res.msg || '档案同步失败'); this.$message.error(res.msg || '完整性确认失败');
} }
}) })
.catch(error => { .catch(error => {
// this.$message.error('完整性确认失败,请重试');
loadingInstance.close(); console.error('完整性确认失败:', error);
this.$message.error('档案同步失败,请重试');
console.error('档案抽取失败:', error);
}); });
}, },
// //
handleUpdateIntegrityStatus() { closeConfirmDialog() {
this.confirmDialogVisible = false;
setTimeout(() => {
const obj = { path: "/archivesManagement/fileManager" }
this.$tab.closeOpenPage(obj)
}, 200);
},
//
handleFileExtract() {
//
this.syncDialogVisible = true;
this.isSyncing = true;
this.syncSuccess = false;
this.syncError = false;
//
setTimeout(() => {
fileExtractApi({ projectId: this.projectId })
.then(res => {
this.isSyncing = false;
if (res.code === 200) {
this.syncSuccess = true;
} else {
this.syncError = true;
} }
})
.catch(error => {
this.isSyncing = false;
this.syncError = true;
console.error('档案抽取失败:', error);
});
}, 2000);
},
//
closeSyncDialog() {
this.syncDialogVisible = false;
this.isSyncing = false;
this.syncSuccess = false;
this.syncError = false;
},
} }
} }
</script> </script>
@ -133,4 +227,96 @@ export default {
.pane-right { .pane-right {
overflow: hidden; overflow: hidden;
} }
/* 档案同步弹框样式 */
.dialog-title {
text-align: left;
}
.dialog-footer {
text-align: right !important;
}
.dialog-footer .el-button+.el-button {
margin-left: 8px;
}
.sync-content {
text-align: center;
padding: 20px 0;
}
.sync-icon {
font-size: 48px;
margin-bottom: 16px;
}
.sync-icon .el-icon-loading {
color: #409EFF;
animation: rotating 2s linear infinite;
}
.sync-icon .el-icon-success {
color: #67C23A;
}
.sync-icon .el-icon-error {
color: #F56C6C;
}
.sync-text p {
margin: 0;
font-size: 16px;
color: #606266;
}
.success-text {
color: #67C23A !important;
}
.error-text {
color: #F56C6C !important;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 确认弹框样式 */
.confirm-content {
display: flex;
align-items: flex-start;
padding: 20px 0;
}
.confirm-icon {
font-size: 48px;
color: #E6A23C;
margin-right: 16px;
flex-shrink: 0;
}
.confirm-text {
flex: 1;
}
.main-message {
margin: 0 0 8px 0;
font-size: 16px;
color: #303133;
line-height: 1.5;
}
.sub-message {
margin: 0;
font-size: 14px;
color: #909399;
line-height: 1.4;
}
</style> </style>

View File

@ -30,6 +30,7 @@
<script> <script>
import TableModel from '@/components/TableModel' import TableModel from '@/components/TableModel'
import { columnsList, formLabel } from './config' import { columnsList, formLabel } from './config'
import { encryptWithSM4 } from '@/utils/sm'
import { import {
getProListAPI getProListAPI
} from '@/api/archivesManagement/fileManager/fileManager.js' } from '@/api/archivesManagement/fileManager/fileManager.js'
@ -81,6 +82,7 @@ export default {
}, },
/* 搜索操作 */ /* 搜索操作 */
handleQuery() { handleQuery() {
alert(1);
this.$refs.proTableRef.getTableList() this.$refs.proTableRef.getTableList()
}, },
/* 打开档案管理 */ /* 打开档案管理 */
@ -88,7 +90,8 @@ export default {
this.$router.push({ this.$router.push({
name: 'FileData', name: 'FileData',
query: { query: {
id: row.id id: encryptWithSM4(row.id),
fileStatus: encryptWithSM4(row.fileStatus)
} }
}) })
} }

View File

@ -5,12 +5,7 @@
<div style="text-align:center"> <div style="text-align:center">
<!-- 图片预览 --> <!-- 图片预览 -->
<template v-if="isImage"> <template v-if="isImage">
<el-image <el-image :src="processedFileUrl" :preview-src-list="previewList" fit="contain" style="max-width:100%;max-height:70vh">
:src="fileUrl"
:preview-src-list="previewList"
fit="contain"
style="max-width:100%;max-height:70vh"
>
<div slot="error" class="image-slot"> <div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i> <i class="el-icon-picture-outline"></i>
</div> </div>
@ -19,17 +14,18 @@
<!-- PDF 预览 --> <!-- PDF 预览 -->
<template v-else> <template v-else>
<pdf :src="fileUrl" style="width:100%;height:70vh" /> <pdf :src="processedFileUrl" style="width:100%;height:70vh" />
</template> </template>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import pdf from 'vue-pdf' import pdf from 'vue-pdf'
import { getFileAsBase64Api } from '@/api/archivesManagement/fileManager/fileManager'
export default { export default {
name: 'ViewFile', name: 'ViewFile',
props: ['width','hight','dataForm','title','disabled','isAdd','rowData','projectId'], props: ['width', 'hight', 'dataForm', 'title', 'disabled', 'isAdd', 'rowData', 'projectId'],
components: { pdf },
data() { data() {
return { return {
dialogVisible: true, dialogVisible: true,
@ -42,40 +38,57 @@ export default {
computed: { computed: {
isImage() { isImage() {
// //
const exts = ['.jpg','.jpeg','.png','.gif','.bmp','.webp'] const exts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const url = (this.fileUrl || '').toLowerCase() const url = (this.fileUrl || '')
const name = (this.fileName || '').toLowerCase() const name = (this.fileName || '').toLowerCase()
return exts.some(ext => url.endsWith(ext) || name.endsWith(ext)) return exts.some(ext => url.endsWith(ext) || name.endsWith(ext))
},
isPdf() {
// PDF
const exts = ['.pdf']
const url = (this.fileUrl || '')
const name = (this.fileName || '').toLowerCase()
return exts.some(ext => url.endsWith(ext) || name.endsWith(ext))
},
processedFileUrl() {
// URLPDFdata
if (this.isPdf && this.fileUrl && !this.fileUrl.startsWith('data:')) {
return `data:application/pdf;base64,${this.fileUrl}`
}
return this.fileUrl
} }
}, },
created() { created() {
// rowData dataForm this.getFileAsBase64();
//
this.fileUrl = this.rowData?.fileUrl || this.dataForm?.fileUrl || ''
this.fileName = this.rowData?.fileName || this.dataForm?.fileName || ''
//
if (this.isImage && this.fileUrl) {
this.previewList = [this.fileUrl]
}
}, },
methods: { methods: {
handleClose() { handleClose() {
this.dialogVisible = false this.dialogVisible = false
this.$emit('closeDialog') this.$emit('closeDialog')
},
/* 获取文件的base64 */
getFileAsBase64() {
getFileAsBase64Api({ id: this.rowData.fileId }).then(res => {
const obj =res.data;
this.fileUrl = obj?.fileBase64 || ''
this.fileName = obj?.fileName || ''
//
if (this.isImage && this.fileUrl) {
this.previewList = [this.processedFileUrl]
}
})
} }
}, },
components: { pdf }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.w700 .el-dialog { .w700 .el-dialog {
width: 700px; width: 700px;
height: 800px;
} }
.w500 .el-dialog { .w500 .el-dialog {
width: 500px; width: 500px;
height: 800px;
} }
.w500 .el-dialog__header, .w500 .el-dialog__header,
@ -103,6 +116,7 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.image-slot { .image-slot {
width: 100%; width: 100%;
height: 240px; height: 240px;