This commit is contained in:
liang.chao 2025-12-05 16:07:52 +08:00
parent aef5c1d2f6
commit 8dbe92c1b6
7 changed files with 935 additions and 25 deletions

View File

@ -0,0 +1,11 @@
import request from '@/axios';
// 查询项目数据列表
export function list(data) {
return request({
url: '/blade-system/archivingManage/list',
method: 'POST',
data: data,
})
}

View File

@ -98,6 +98,14 @@ export default [
title: '档案数据管理'
}
},
{
path: '/archivingManager/fileData',
name: 'FileData1',
component: () => import('@/views/archivingManager/file-data.vue'),
meta: {
title: '档案数据管理'
}
},
{
path: '/archivesManagement/detailData',

View File

@ -0,0 +1,318 @@
<template>
<div>
<el-card style="height: calc(100vh - 190px)">
<el-row :gutter="24" style="display: flex; align-items: center">
<el-col :span="14">
<el-input v-model="filterText" placeholder="输入关键字" @keyup.enter="onHandleSearch">
</el-input>
</el-col>
<el-col :span="10">
<el-button type="primary" @click="onHandleSearch">
查询
</el-button>
</el-col>
</el-row>
<div class="tree-container">
<el-tree ref="leftTreeRef" :data="treeDataList" default-expand-all class="left-tree-list"
@node-click="onHandleNodeClick" :filter-node-method="filterNode" highlight-current node-key="id">
<template #default="{ node, data }">
<span class="custom-tree-node">
<template v-if="isTruncated(node.label)">
<el-tooltip effect="dark" :content="node.label" placement="right">
<span class="node-label">{{ truncateLabel(node.label) }}</span>
</el-tooltip>
</template>
<template v-else>
<span class="node-label">{{ node.label }}</span>
</template>
</span>
</template>
</el-tree>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { getFileManageTreeApi } from '@/api/archivesManagement/fileManager/fileManager.js'
const props = defineProps({
projectId: {
type: String,
default: ''
},
fileStatus: {
type: String,
default: ''
}
})
const emit = defineEmits(['handleNodeClick'])
//
const treeDataList = ref([])
const filterText = ref('')
const originalTreeData = ref([])
const isflag = ref(false)
const isAdd = ref('')
const title = ref('')
const row = ref({})
const selectedNodeId = ref(null)
const leftTreeRef = ref()
//
const filteredTreeData = computed(() => {
if (!filterText.value) {
return treeDataList.value
}
return filterTreeData(treeDataList.value)
})
//
const handleQuery = async () => {
const currentNode = leftTreeRef.value?.getCurrentNode()
if (currentNode) {
selectedNodeId.value = currentNode.id
}
await getLeftTreeList()
nextTick(() => {
if (selectedNodeId.value && leftTreeRef.value) {
leftTreeRef.value.setCurrentKey(selectedNodeId.value)
}
})
}
const closeDialog = () => {
isflag.value = false
handleQuery()
}
const showColose = () => {
isflag.value = false
}
const onHandleNodeClick = (data) => {
selectedNodeId.value = data.id
emit('handleNodeClick', data)
}
const convertToVueTree = (data) => {
if (!data || !Array.isArray(data)) {
return []
}
return data.map(item => {
const node = {
id: item.id,
label: item.contentName,
level: item.level,
sort: item.sort,
parentId: item.parentId,
parentName: item.parentName,
isUnique: item.isUnique
}
if (item.children && Array.isArray(item.children) && item.children.length > 0) {
const children = convertToVueTree(item.children)
if (children.length > 0) {
node.children = children
}
}
return node
})
}
const truncateLabel = (text) => {
if (!text) return ''
const max = 100
return text.length > max ? text.slice(0, max) + '...' : text
}
const isTruncated = (text) => {
if (!text) return false
return text.length > 100
}
const getLeftTreeList = async () => {
const res = await getFileManageTreeApi({ proId: props.projectId })
const transformedData = convertToVueTree(res.data.data)
treeDataList.value = transformedData
originalTreeData.value = JSON.parse(JSON.stringify(treeDataList.value))
}
const onHandleSearch = () => {
if (filterText.value) {
ElMessage.error('搜索内容包含非法字符,请重新输入')
return
}
leftTreeRef.value?.filter(filterText.value)
}
const filterNode = (value, data) => {
if (!value) return true
return data.label.indexOf(value) !== -1
}
const filterTreeData = (treeData) => {
const result = []
for (const node of treeData) {
const newNode = { ...node }
if (node.children && node.children.length > 0) {
const filteredChildren = filterTreeData(node.children)
if (filteredChildren.length > 0) {
newNode.children = filteredChildren
result.push(newNode)
} else if (node.label.indexOf(filterText.value) !== -1) {
result.push(node)
}
} else if (node.label.indexOf(filterText.value) !== -1) {
result.push(newNode)
}
}
return result
}
//
watch(filterText, (val) => {
leftTreeRef.value?.filter(val)
})
//
onMounted(() => {
getLeftTreeList()
})
</script>
<style scoped>
.tree-container {
margin-top: 10px;
margin-bottom: 10px;
max-height: calc(100vh - 270px);
overflow-x: hidden;
overflow-y: auto;
}
.tree-container::-webkit-scrollbar {
width: 0px;
background: transparent;
}
.tree-container::-webkit-scrollbar-track {
background: transparent;
}
.tree-container::-webkit-scrollbar-thumb {
background: transparent;
}
.left-tree-list {
width: 100%;
}
.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
min-height: 20px;
padding: 1px 0;
font-size: 14px;
}
.node-label {
flex: 1;
min-width: 0;
overflow: visible;
text-overflow: initial;
white-space: normal;
word-break: break-word;
word-wrap: break-word;
line-height: 1.2;
padding-right: 4px;
}
.left-tree-list .el-tree-node__content {
display: flex;
align-items: center;
height: auto;
min-height: 20px;
padding: 1px 4px;
line-height: 1.2;
}
.left-tree-list .el-tree-node__content:has(.node-label[style*="height"]) {
align-items: flex-start;
}
.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
height: auto !important;
align-items: center;
line-height: 1.2;
}
.left-tree-list .el-tree-node.is-current>.el-tree-node__content {
background-color: #b3d9ff;
color: #006e6a !important;
}
.left-tree-list .el-tree-node.is-current>.el-tree-node__content .el-tree-node__label {
color: #006e6a !important;
}
.left-tree-list .el-tree-node.is-current>.el-tree-node__content .custom-tree-node {
color: #006e6a !important;
}
.left-tree-list .el-tree-node__content:hover {
background-color: #f5f5f5;
}
.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
height: auto;
}
.left-tree-list .el-tree-node.is-current>.el-tree-node__content:hover {
background-color: #8cc8ff;
}
.btn-box {
margin-left: 4px;
opacity: 0;
transition: opacity 0.15s ease-in-out;
flex-shrink: 0;
display: flex;
align-items: center;
gap: 1px;
}
.left-tree-list .el-tree-node__content:hover .btn-box {
opacity: 1;
}
:deep(.el-tree-node__content) {
display: flex;
align-items: center;
cursor: pointer;
height: auto;
min-height: 38px;
padding: 1px 4px;
}
:deep(.el-tree-node__expand-icon) {
padding: 1px;
margin-right: 1px;
flex-shrink: 0;
}
:deep(.el-tree-node__content > .el-tree-node__expand-icon) {
flex-shrink: 0;
}
</style>

View File

@ -0,0 +1,248 @@
<template>
<div>
<el-card style="min-height: calc(100vh - 190px);">
<avue-crud
:option="option"
:data="tableData"
:page="page"
:table-loading="loading"
@search-change="searchChange"
@search-reset="searchReset"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #fileName="{ row }">
<span class="file-name-link" @click="viewFile(row)">{{ row.fileName }}</span>
</template>
</avue-crud>
<!-- 预览文件 -->
<ViewFile v-if="isViewflag" :rowData="row" :title="title" :isAdd="isAdd" @closeDialog="closeDialog"
@showColose="showColose" :width="600" />
</el-card>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
import { ref, reactive, watch, nextTick } from 'vue';
import {
getFileManageApi
} from '@/api/archivesManagement/fileManager/fileManager.js';
import ViewFile from '@/views/viewFile/viewFile.vue';
const props = defineProps({
projectId: {
type: String,
default: ''
},
selectedNode: {
type: Object,
default: null
},
auditStatus: {
type: String,
default: ''
}
});
//
const title = ref('');
const isflag = ref(false);
const isflagRect = ref(false);
const isViewflag = ref(false);
const isAdd = ref('');
const isRect = ref('');
const row = ref({});
const loading = ref(false);
const addBtnIsShow = ref(true);
const defaultParams = ref({});
//
const tableData = ref([]);
const page = reactive({
currentPage: 1,
pageSize: 10,
total: 0
});
const query = reactive({});
// Avue
const option = reactive({
height: 'auto',
calcHeight: 32,
tip: false,
searchShow: true,
searchMenuSpan: 6,
menu: false,
border: true,
index: true,
viewBtn: false,
selection: false,
addBtn: false,
editBtn: false,
delBtn: false,
dialogClickModal: false,
column: [
{
label: '档案名称',
prop: 'contentName',
search: true
},
{
label: '档案文件',
prop: 'fileName',
slot: true
},
{
label: '上传人',
prop: 'createUserName'
},
{
label: '保管期限',
prop: 'term'
},
{
label: '来源',
prop: 'dataSource',
slot: true
},
{
label: '责任单位',
prop: 'unitName'
},
{
label: '上传时间',
prop: 'createTime',
width: 160
}
]
});
//
const closeDialog = () => {
isflag.value = false;
isflagRect.value = false;
isViewflag.value = false;
};
const showColose = () => {
isflag.value = false;
isViewflag.value = false;
};
/** 详情操作 */
const handleDetail = (rowData) => {
title.value = '详情';
isAdd.value = 'detail';
row.value = rowData;
isflag.value = true;
};
/** 修改操作 */
const handleUpdate = (rowData) => {
title.value = '修改';
isAdd.value = 'edit';
row.value = rowData;
row.value.belongName = props.selectedNode.parentName + '/' + props.selectedNode.label;
row.value.detailStatus = false;
isflag.value = true;
};
const handleRect = (rowData) => {
title.value = '加入整改清单';
isRect.value = 'rect';
row.value = rowData;
row.value.belongName = props.selectedNode.parentName + '/' + props.selectedNode.label + '/' + row.value.fileName;
row.value.detailStatus = false;
isflagRect.value = true;
};
//
const viewFile = (rowData) => {
title.value = '预览';
isAdd.value = 'view';
row.value = rowData;
isViewflag.value = true;
};
/* 搜索操作 */
const handleQuery = () => {
onLoad(page, query);
};
//
const searchChange = (params, done) => {
Object.assign(query, params);
page.currentPage = 1;
onLoad(page, params);
done();
};
const searchReset = () => {
Object.keys(query).forEach(key => delete query[key]);
onLoad(page);
};
const currentChange = (currentPage) => {
page.currentPage = currentPage;
};
const sizeChange = (pageSize) => {
page.pageSize = pageSize;
};
const refreshChange = () => {
onLoad(page, query);
};
const onLoad = async (pageParam, params = {}) => {
loading.value = true;
try {
const requestData = {
...params,
...defaultParams.value,
pageNum: pageParam.currentPage,
pageSize: pageParam.pageSize
};
const res = await getFileManageApi(requestData);
if (res.data.code === 200) {
tableData.value = res.data.rows;
page.total = res.data.total;
}
} catch (error) {
} finally {
loading.value = false;
}
};
//
watch(() => props.selectedNode, (newVal) => {
addBtnIsShow.value = !(newVal && Number(newVal.level) === 4);
const parentId = newVal && newVal.id ? newVal.id : 0;
const proId = props.projectId;
defaultParams.value = { parentId, proId };
onLoad(page, { parentId, proId });
}, { immediate: true });
</script>
<style scoped>
.file-name-link {
color: #409EFF;
cursor: pointer;
}
.file-name-link:hover {
text-decoration: underline;
}
</style>

View File

@ -0,0 +1,157 @@
<template>
<div class="app-container">
<el-card class="toolbar-card">
<div class="toolbar">
<div class="toolbar-left">
<el-button type="danger" plain icon="Close" @click="handleClose">返回</el-button>
</div>
</div>
</el-card>
<el-row :gutter="24" class="content-row" v-if="projectId && auditStatus">
<el-col :span="8" class="pane-left">
<LeftTree @handleNodeClick="handleNodeClick" :projectId="projectId" :auditStatus="auditStatus" />
</el-col>
<el-col :span="16" class="pane-right">
<RightTable :selectedNode="selectedNode" :projectId="projectId" :auditStatus="auditStatus" />
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import LeftTree from './components/leftTree.vue'
import RightTable from './components/rightTable.vue'
import {
getProjectNameByIdApi,
} from '@/api/archivesManagement/fileManager/fileManager';
const route = useRoute()
const router = useRouter()
//
const projectId = ref(null)
const auditStatus = ref(null)
const selectedNode = ref(null)
const archiveForm = ref({
projectName: '',
description: ''
})
//
onMounted(() => {
projectId.value = route.query.id
auditStatus.value = route.query.auditStatus
//
getProjectName()
})
//
const getProjectName = async () => {
try {
const res = await getProjectNameByIdApi({
id: projectId.value
})
if (res.data?.code === 200) {
const project = res.data.data
archiveForm.value.projectName = project.proName || '未知项目'
} else {
archiveForm.value.projectName = '未知项目'
}
} catch (error) {
console.error('获取项目名称失败:', error)
archiveForm.value.projectName = '未知项目'
}
}
//
const handleClose = () => {
router.go(-1)
}
const handleNodeClick = (data) => {
selectedNode.value = data
}
</script>
<style scoped>
.app-container {
padding: 12px;
}
.toolbar-card {
margin-bottom: 12px;
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
}
.toolbar-left :deep(.el-button) + :deep(.el-button) {
margin-left: 8px;
}
.toolbar-right :deep(.el-tag) {
border-color: #dcdfe6;
}
.content-row {
min-height: calc(100vh - 200px);
}
.pane-left,
.pane-right {
background: #fff;
border-radius: 6px;
height: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.pane-left {
overflow: auto;
}
.pane-right {
overflow: hidden;
}
.dialog-footer {
text-align: right !important;
}
.dialog-footer .el-button+.el-button {
margin-left: 8px;
}
.sync-icon {
font-size: 48px;
margin-bottom: 16px;
}
.sync-icon .el-icon-loading {
color: #409EFF;
animation: rotating 2s linear infinite;
}
.sync-text p {
margin: 0;
font-size: 16px;
color: #606266;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,193 @@
<template>
<basic-container>
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
:permission="permissionList"
:before-open="beforeOpen"
v-model="form"
ref="crud"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<!-- 自定义档案状态显示 -->
<template #auditStatus="{ row }">
<el-tag :type="getauditStatusType(row.auditStatus)" effect="light">
{{ formatauditStatus(row.auditStatus) }}
</el-tag>
</template>
<!-- 操作列 -->
<template #menu="{ row }">
<el-button
type="primary"
@click="openFileManager(row)"
>
详情
</el-button>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import {
list,
} from '@/api/archivesManagement/archivingManage';
import { mapGetters } from 'vuex';
import website from '@/config/website';
export default {
data() {
return {
form: {},
query: {},
loading: true,
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
option: {
height: 'auto',
calcHeight: 32,
tip: false,
searchShow: true,
searchMenuSpan: 6,
border: true,
index: true,
viewBtn: false,
selection: false,
addBtn: false,
editBtn: false,
delBtn: false,
dialogClickModal: false,
column: [
{
label: '归档工程',
prop: 'proName',
search: true,
},
{
label: '审核状态',
prop: 'auditStatus',
search: true,
slot: true, //
},
],
},
data: [],
};
},
computed: {
...mapGetters(['permission']),
permissionList() {
return {
addBtn: this.validData(this.permission.post_add, false),
viewBtn: this.validData(this.permission.post_view, false),
delBtn: this.validData(this.permission.post_delete, false),
editBtn: this.validData(this.permission.post_edit, false),
};
},
ids() {
let ids = [];
this.selectionList.forEach(ele => {
ids.push(ele.id);
});
return ids.join(',');
},
},
methods: {
beforeOpen(done, type, row) {
done(); // done()
},
formatauditStatus(status) {
switch (status) {
case '0':
return '待审核';
case '1':
return '审核通过';
case '2':
return '已下发整改';
default:
return '未知状态';
}
},
getauditStatusType(status) {
switch (status) {
case '0':
return 'warning';
case '1':
return 'success';
case '2':
return 'error';
default:
return 'info';
}
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
this.$refs.crud.toggleSelection();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page, params = {}) {
this.loading = true;
let data = {
...params,
pageNum:page.currentPage,
pageSize:page.pageSize
};
list(data).then(res => {
const data = res.data;
this.page.total = data.total;
this.data = data.rows;
this.loading = false;
this.selectionClear();
});
},
openFileManager(row) {
this.$router.push({
path: '/archivingManager/fileData',
query: {
id: row.id, // ID
auditStatus: row.auditStatus //
}
});
},
},
};
</script>
<style></style>

View File

@ -1,25 +0,0 @@
export const formLabel = [
{
isShow: false,
f_type: 'ipt',
f_label: '文件名称',
f_model: 'contentName',
f_max: 32,
},
{
isShow: false,
f_type: 'date',
f_label: '上传时间',
f_model: 'uploadTime',
},
]
export const columnsList = [
{ t_props: 'contentName', t_label: '档案名称', t_width: 200 },
{ t_slot: 'fileName', t_label: '档案文件', t_width: 140 },
{ t_props: 'createUserName', t_label: '上传人' },
{ t_props: 'term', t_label: '保管期限' },
{ t_slot: 'dataSource', t_label: '来源' },
{ t_props: 'unitName', t_label: '责任单位' },
{ t_props: 'createTime', t_label: '上传时间', t_width: 160 }
]