Merge remote-tracking branch 'origin/master'

This commit is contained in:
liang.chao 2025-11-28 13:16:08 +08:00
commit f218a961a4
2 changed files with 777 additions and 0 deletions

View File

@ -0,0 +1,91 @@
import request from '@/axios';
// 获取档案目录树
export function getArchivalCatalogueTreeApi(params) {
return request({
url: '/blade-system/archive/getArchivalCatalogueTree',
method: 'POST',
data:{
},
})
}
// 新增档案目录
export function addArchiveLeftApi(data) {
return request({
url: '/blade-system/archive/addLeft',
method: 'POST',
data:data
})
}
// 新增档案表格数据
export function addArchiveRightApi(data) {
return request({
url: '/blade-system/archive/addRight',
method: 'POST',
data:data
})
}
// 修改档案目录
export function editArchiveLeftApi(data) {
return request({
url: '/blade-system/archive/editLeft',
method: 'POST',
data:data,
})
}
// 修改档案目录
export function editArchiveRightApi(data) {
return request({
url: '/blade-system/archive/editRight',
method: 'POST',
data:data,
})
}
// 删除档案目录
export function delArchiveApi(id) {
let data = {
id:id
};
return request({
url: '/blade-system/archive/del',
method: 'POST',
data:data,
})
}
// 编辑-详情
export function getArchivalCatalogueByIdApi(params) {
return request({
url: '/blade-system/archive/getArchivalCatalogueById',
method: 'POST',
params:params
})
}
// 列表
export function getArchivalCatalogueListApi(data) {
return request({
url: '/blade-system/archive/getArchivalCatalogue',
method: 'POST',
data:data
})
}
// 查询序号
export function getMaxSortApi(parentId) {
let data = {
parentId: parentId
};
return request({
url: '/blade-system/archive/geMaxSort',
method: 'POST',
data:data
})
}

View File

@ -0,0 +1,686 @@
<template>
<el-row class="full-height">
<!-- 左侧树 -->
<el-col :span="6" class="left-panel">
<div class="box">
<el-scrollbar>
<basic-container>
<avue-tree
:option="treeOption"
:data="treeData"
@node-click="nodeClick"
>
<template #default="{ node, data }">
<span>{{ data.contentName }}</span>
<el-icon
v-if="data.level != 3"
class="el-icon-circle-plus-outline icon-op"
@click.stop="openDialog('add',data)"
>
<CirclePlus />
</el-icon>
<el-icon
v-if="data.level > 0"
class="el-icon-edit icon-op"
@click.stop="openDialog('edit',data)"
>
<Edit />
</el-icon>
<el-icon
v-if="data.level > 0"
class="el-icon-delete icon-op"
@click.stop="deleteNode(data)"
>
<Delete />
</el-icon>
</template>
</avue-tree>
</basic-container>
</el-scrollbar>
</div>
</el-col>
<!-- 右侧表格 -->
<el-col :span="18" class="right-panel">
<basic-container class="right-basic">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
:permission="permissionList"
:before-open="beforeOpen"
v-model="form"
ref="crud"
@row-update="rowUpdate"
@row-save="rowSave"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@on-load="onLoad"
>
</avue-crud>
</basic-container>
</el-col>
</el-row>
<!-- 树的新增修改删除弹窗 -->
<el-dialog
:title="dialogTitle"
v-model="dialogVisible"
width="500px"
:close-on-click-modal="false"
>
<el-form :model="editForm" ref="editFormRef" label-width="100px">
<!-- 上级节点 -->
<el-form-item label="上级节点" prop="parentId">
<avue-tree
ref="tree"
:data="treeForDialog"
:option="treeSelectOption"
v-model="editForm.parentId"
@node-click="onParentSelect"
>
<template #default="{ node, data }">
<span>{{ data.contentName }}</span>
</template>
</avue-tree>
</el-form-item>
<!-- 名称 -->
<el-form-item label="分类名称" prop="contentName">
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 6 }" maxlength="64" v-model="editForm.contentName" placeholder="请输入分类名称" />
</el-form-item>
<!-- 案卷排序号 -->
<el-form-item label="案卷排序号" prop="sort">
<el-input-number v-model="editForm.sort" :min="1" maxlength="3" />
<span style="color:red">{{ sortTip }}</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveNode">保存</el-button>
</template>
</el-dialog>
</template>
<script>
import { ref } from "vue";
import {
getArchivalCatalogueTreeApi,
addArchiveLeftApi,
editArchiveLeftApi,
delArchiveApi,
getArchivalCatalogueListApi,
getMaxSortApi,
addArchiveRightApi, editArchiveRightApi
} from '@/api/archivesManagement/archCatalogue';
import { mapGetters } from 'vuex';
// import { add, getDetail, getList, remove, update } from '@/api/data-collect/data-class-manage';
export default {
name: "ArchCatalogue",
data() {
return {
form: {},
query: {},
loading: true,
data: [],
rawData: [],
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
option: {
height: "calc(100vh - 280px)",
tip: false,
searchShow: true,
searchMenuSpan: 6,
border: true,
index: true,
viewBtn: true,
selection: false,
columnBtn: true,
column: [
{
label: '所属案卷',
labelWidth: 120,
prop: 'path',
hide: true,
type: 'input', // static input
readonly: true, //
disabled: true, //
},
{
label: "文件名称",
labelWidth: 120,
prop: "contentName",
search: true,
type: "input",
placeholder: "请输入文件名称",
searchLabelWidth: 120,
rules: [
{
required: true,
message: '请输入文件名称',
trigger: 'blur',
},
],
},
{
label: '案卷期限',
labelWidth: 120,
prop: 'term',
rules: [
{
required: true,
message: '请输入案卷期限',
trigger: 'blur',
},
],
},
{
label: '档案标识代码',
labelWidth: 120,
prop: 'markCode', //使 key
type: 'select', //
dicUrl: '/blade-system/system/dict/data/type', //
dicMethod: 'post', // POST
dicQuery: {
dictType: 'mark_code' //bodyAvue
},
props: {
label: 'dictLabel',
value: 'dictLabel',
},
slot: true,
rules: [
{
required: true,
message: '请选择档案标识代码',
trigger: 'change',
},
],
},
{
label: '归档责任单位',
labelWidth: 120,
prop: 'unitName',
rules: [
{
required: true,
message: '请输入归档责任单位',
trigger: 'blur',
},
],
},
{
label: '所属专业',
labelWidth: 120,
prop: 'major',
rules: [
{
required: true,
message: '请输入所属专业',
trigger: 'blur',
},
],
},
{
label: '文件分类标记',
labelWidth: 120,
prop: 'classifyMarkId', //使 key
type: 'select', //
dicUrl: '/blade-system/archive/getFilesClassifyMarkSelect', //
dicMethod: 'post', // POST
props: {
label: 'name',
value: 'id',
},
slot: true,
rules: [
{
required: true,
message: '请选择文件分类标记',
trigger: 'change',
},
],
},
]
},
treeDeptId: "0",
treeLevel:0,
treeData: [],
treeOption: {
nodeKey: "id",
menu: false,
addBtn: false,
props: { label: "contentName", children: "children" },
level: null,
defaultExpandAll: true,
},
shareDialogVisible: false,
shareRowData: null,
selectedNodeId: 0,
selectedNodeName: '',
dialogVisible: false,
dialogType: "add",
editForm: { contentName:null,id:null,level:null,parentId:null,sort:null },
treeOptions: [], //
treeForDialog: [], //
treeSelectOption: {
menu: false,
addBtn: false,
searchShow: false,
nodeKey: "id",
props: {
label: "contentName",
children: "children",
value: "id"
},
defaultExpandAll: true,
multiple: false,
checkStrictly: true,
selectClick: true,
},
sortTip: "",
dialogTitle: '',
treeKey: 0, //
selectedNodePath:'',
};
},
computed: {
...mapGetters(['permission']),
permissionList() {
let isLevel3 = this.treeLevel ===3;
return {
addBtn: this.validData(this.permission.post_add, false) && isLevel3,
viewBtn: this.validData(this.permission.post_view, false),
delBtn: this.validData(this.permission.post_delete, false),
editBtn: this.validData(this.permission.post_edit, false),
};
},
},
mounted() {
this.loadFullTree();
this.onLoad(this.page, this.query);
},
methods: {
//
async loadFullTree() {
try {
const res = await getArchivalCatalogueTreeApi(0);
const rows = res.data?.data || [];
// parentId
const convert = (list, parentId = null) => list.map(item => ({
id: item.id,
contentName: item.contentName,
level: item.level,
parentId: parentId, // ID
children: item.children && item.children.length > 0 ? convert(item.children, item.id) : []
}));
this.treeData = convert(rows);
this.treeOptions = this.treeData; // treeOptions parentId
} catch (err) {
console.error("加载左树失败", err);
this.treeData = [];
this.treeOptions = [];
}
},
nodeClick(node) {
this.treeDeptId = node.id;
this.treeLevel = node.level;
this.selectedNodeId = node.id;
this.selectedNodeName = node.contentName;
this.onLoad(this.page, this.query);
},
//
async openDialog(type, node) {
this.dialogType = type;
this.dialogVisible = true;
this.dialogTitle = type === 'add' ? '新增分类' : '编辑分类';
console.log('打开弹窗 - 类型:', type, '节点:', node);
//
if (type === 'edit') {
this.editForm = { ...node };
// 使 parentId
if (!node.parentId) {
// parentId
const parentNode = this.findParentNode(this.treeData, node.id);
this.editForm.parentId = parentNode ? parentNode.id : null;
} else {
this.editForm.parentId = node.parentId;
}
} else {
//
this.editForm = {
id: null,
parentId: node.id,
level: node.level + 1,
contentName: '',
sort: 1
};
}
console.log('设置的 parentId:', this.editForm.parentId);
//
this.treeForDialog = this.filterTreeForDialog(this.treeOptions, type === 'edit' ? node : null);
//
this.treeKey += 1;
// 使 $nextTick DOM
await this.$nextTick();
//
if (this.$refs.tree && this.editForm.parentId) {
setTimeout(() => {
//
if (this.$refs.tree.setValue) {
this.$refs.tree.setValue(this.editForm.parentId);
}
if (this.$refs.tree.setCurrentKey) {
this.$refs.tree.setCurrentKey(this.editForm.parentId);
}
}, 150);
}
//
if (this.editForm.parentId) {
await this.onParentChange(this.editForm.parentId);
}
},
//
findParentNode(tree, nodeId, parent = null) {
for (const item of tree) {
if (item.id === nodeId) {
return parent;
}
if (item.children && item.children.length > 0) {
const found = this.findParentNode(item.children, nodeId, item);
if (found) return found;
}
}
return null;
},
//
filterTreeForDialog(list, excludeNode = null) {
return list
.filter(n => {
if (!excludeNode) return true;
return !this.isDescendant(excludeNode, n);
})
.filter(n => n.level < 3)
.map(n => ({
...n,
children: n.children ? this.filterTreeForDialog(n.children, excludeNode) : []
}));
},
// target excludeNode
isDescendant(excludeNode, target) {
if (excludeNode.id === target.id) return true;
if (!excludeNode.children || excludeNode.children.length === 0) return false;
return excludeNode.children.some(child => this.isDescendant(child, target));
},
async saveNode() {
try {
//
if (!this.editForm.contentName || !this.editForm.contentName.trim()) {
this.$message({ type: 'warning', message: '请输入分类名称!' });
return;
}
const api = this.dialogType === "add" ? addArchiveLeftApi : editArchiveLeftApi;
await api(this.editForm);
this.dialogVisible = false;
this.$message({ type: 'success', message: '操作成功!' });
await this.loadFullTree();
} catch (error) {
console.error('保存失败:', error);
this.$message({ type: 'error', message: '操作失败!' });
}
},
async deleteNode(data) {
try {
await this.$confirm('确定要删除该节点吗?', '提示', {
type: 'warning'
});
await delArchiveApi(data.id);
await this.loadFullTree();
this.$message({ type: 'success', message: '删除成功!' });
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error);
this.$message({ type: 'error', message: '删除失败!' });
}
}
},
async onParentChange(parentId) {
if (parentId) {
const parent = this.findNode(this.treeOptions, parentId);
this.editForm.level = parent ? parent.level + 1 : 1;
try {
const res = await getMaxSortApi(parentId);
const maxSort = res.data?.data || 0;
this.sortTip = `当前已有最大排序为 ${maxSort}`;
//
if (this.dialogType === 'add') {
this.editForm.sort = maxSort + 1;
} else {
// 使
this.editForm.sort = maxSort;
}
} catch (error) {
console.error('获取最大排序失败:', error);
this.sortTip = '获取排序信息失败';
}
} else {
this.editForm.level = 1;
this.sortTip = "";
}
},
findNode(tree, id) {
for (const node of tree) {
if (node.id === id) return node;
if (node.children) {
const found = this.findNode(node.children, id);
if (found) return found;
}
}
return null;
},
//
onParentSelect(node) {
this.editForm.parentId = node.id;
this.editForm.level = node.level + 1;
this.onParentChange(node.id);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
shareRow(row) {
const { $cellEdit, $index, ...cleanRow } = row;
cleanRow.selectedNodeName = this.selectedNodeName;
cleanRow.selectedNodeId = this.selectedNodeId;
cleanRow.jsonId = cleanRow.id;
this.shareRowData = cleanRow;
this.shareDialogVisible = true;
},
rowSave(row, done, loading) {
row.level = 4;
row.parentId = this.selectedNodeId;
row.classifyMark= row.classifyMarkId;
addArchiveRightApi(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowUpdate(row, index, done, loading) {
row.level = 4;
row.parentId = this.selectedNodeId;
row.classifyMark= row.classifyMarkId;
editArchiveRightApi(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return delArchiveApi(row.id);
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
});
},
beforeOpen(done, type) {
if (type === 'add') {
this.$nextTick(() => {
this.form.path = this.selectedNodePath;
console.log("aaa=",this.form.path)
});
}
done();
},
selectionChange(list) {
this.selectionList = list;
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
onLoad(page, params = {}) {
this.loading = true;
let data = {
...params,
pageNum:page.currentPage,
pageSize:page.pageSize,
parentId: this.treeDeptId
};
getArchivalCatalogueListApi(data).then(res => {
const data = res.data;
this.page.total = data.total;
this.data = data.rows;
this.loading = false;
if(data.rows.length){
this.selectedNodePath = data.rows[0].path;
}
this.selectionClear();
});
},
selectionClear() {
this.selectionList = [];
this.$refs.crud.toggleSelection();
},
}
};
</script>
<style scoped>
.full-height {
height: 100%;
}
.box {
height: 800px;
}
.el-scrollbar {
height: 100%;
}
.box .el-scrollbar__wrap {
overflow: scroll;
}
.icon-op {
font-size: 16px;
margin-left: 6px;
color: #409EFF;
cursor: pointer;
}
.icon-op:hover {
color: #66b1ff;
}
.left-panel {
border-right: 1px solid #e6e6e6;
}
.right-panel {
padding-left: 16px;
}
.right-basic {
height: 100%;
}
</style>