6637b003 by lxs

入表功调整

1 parent f44693c3
...@@ -8,10 +8,9 @@ export const getAssetCatalog = (params= {}) => request({ ...@@ -8,10 +8,9 @@ export const getAssetCatalog = (params= {}) => request({
8 }) 8 })
9 9
10 /** 获取成本项列表 */ 10 /** 获取成本项列表 */
11 export const getCostList = (params) => request({ 11 export const getCostList = () => request({
12 url: `${import.meta.env.VITE_API_NEW_PORTAL}/tableentry-index-classify/page-list`, 12 url: `${import.meta.env.VITE_API_NEW_PORTAL}/tableentry-index-classify/list`,
13 method: 'post', 13 method: 'post',
14 data: params
15 }) 14 })
16 15
17 /** 获取成本项详情 */ 16 /** 获取成本项详情 */
...@@ -83,4 +82,4 @@ export const deleteValuationMode = (params) => request({ ...@@ -83,4 +82,4 @@ export const deleteValuationMode = (params) => request({
83 export const getValuationModelDetail = (params) => request({ 82 export const getValuationModelDetail = (params) => request({
84 url: `${import.meta.env.VITE_API_NEW_PORTAL}/valuation-model/detail?guid=${params.guid}`, 83 url: `${import.meta.env.VITE_API_NEW_PORTAL}/valuation-model/detail?guid=${params.guid}`,
85 method: 'get' 84 method: 'get'
86 })
...\ No newline at end of file ...\ No newline at end of file
85 })
......
...@@ -6,13 +6,9 @@ ...@@ -6,13 +6,9 @@
6 import { ref, inject } from "vue"; 6 import { ref, inject } from "vue";
7 import { ElMessage, ElMessageBox, translate } from "element-plus"; 7 import { ElMessage, ElMessageBox, translate } from "element-plus";
8 import { MoreFilled } from '@element-plus/icons-vue' 8 import { MoreFilled } from '@element-plus/icons-vue'
9 import StepBar from "@/components/StepBar/index.vue";
10 import TableTools from "@/components/Tools/table_tools.vue";
11 import Table from "@/components/Table/index.vue";
12 import Form from "@/components/Form/index.vue";
13 import useUserStore from "@/store/modules/user"; 9 import useUserStore from "@/store/modules/user";
14 import { changeNum } from "@/utils/common"; 10 import { changeNum } from "@/utils/common";
15 import { getAssetCatalog, getCostList, sendEntryMsg } from "@/api/modules/dataEntry"; 11 import { costDelete, getAssetCatalog, getCostList, sendEntryMsg } from "@/api/modules/dataEntry";
16 import * as XLSXS from 'xlsx-js-style'; 12 import * as XLSXS from 'xlsx-js-style';
17 import { saveAs } from 'file-saver'; 13 import { saveAs } from 'file-saver';
18 14
...@@ -20,43 +16,17 @@ const { proxy } = getCurrentInstance() as any; ...@@ -20,43 +16,17 @@ const { proxy } = getCurrentInstance() as any;
20 const userStore = useUserStore(); 16 const userStore = useUserStore();
21 const userData = JSON.parse(userStore.userData); 17 const userData = JSON.parse(userStore.userData);
22 18
23 const itemWidth = ref('56px'); 19 const reload: any = inject("reload");
24 const itemLeft = ref('-56px');
25 const reload = inject("reload");
26 const loading = ref(false); 20 const loading = ref(false);
27 const companyName = ref(''); 21 const companyName = ref('');
28 /** 默认显示步骤 */ 22 /** 默认显示步骤 */
29 const step = ref(0); 23 const step = ref(0);
30 /** 步骤条配置信息 */
31 const stepsInfo = ref({
32 step: step.value,
33 list: [
34 { title: '设置成本项', value: 1 },
35 { title: '填写成本项', value: 2 },
36 { title: '入表', value: 3 },
37 ]
38 })
39 24
40 const allMapList = ref([]); 25 const showAdd = ref(false);
41 const showLevel4 = ref(false);
42 const gridList = ref([
43 { name1: '获取成本', code1: '0', name2: '外购成本', code2: '01', name3: '外购成本', code3: '011' },
44 { name1: '获取成本', code1: '0', name2: '外购成本', code2: '01', name3: '数据加工', code3: '012' },
45 { name1: '获取成本', code1: '0', name2: '数据加工', code2: '02', name3: '数据采集', code3: '021' },
46 { name1: '获取成本', code1: '0', name2: '数据加工', code2: '02', name3: '数据脱敏', code3: '022' },
47 { name1: '获取成本', code1: '0', name2: '数据加工', code2: '02', name3: '数据清洗', code3: '023' },
48 { name1: '获取成本', code1: '0', name2: '数据加工', code2: '02', name3: '数据分析', code3: '024' },
49 { name1: '存储成本', code1: '1', name2: '数据存储', code2: '11', name3: '数据存储成本', code3: '111' },
50 { name1: '管理成本', code1: '2', name2: '数据维护', code2: '21', name3: '数据质检成本', code3: '211' },
51 { name1: '管理成本', code1: '2', name2: '数据维护', code2: '21', name3: '数据维护', code3: '212' },
52 { name1: '管理成本', code1: '2', name2: '数据维护', code2: '21', name3: '采购维护', code3: '213' },
53 { name1: '管理成本', code1: '2', name2: '数据维护', code2: '21', name3: '采购租赁', code3: '214' },
54 ]);
55 const checkedList = ref([]);
56 const productList = ref([]); 26 const productList = ref([]);
57 const costFormRef = ref(); 27 const costFormRef = ref();
58 const entryFormRef = ref(); 28 const entryFormRef = ref();
59 const costFormInfo = ref({}); 29 const costFormInfo: any = ref({});
60 const costForm = ref({ 30 const costForm = ref({
61 id: 'step-form-one', 31 id: 'step-form-one',
62 col: '', 32 col: '',
...@@ -113,7 +83,7 @@ const costForm = ref({ ...@@ -113,7 +83,7 @@ const costForm = ref({
113 options: [ 83 options: [
114 { label: '确认', value: 'besure', type: 'primary', style: { height: '32px' } }, 84 { label: '确认', value: 'besure', type: 'primary', style: { height: '32px' } },
115 ], 85 ],
116 visible: false, 86 visible: true,
117 style: { width: 'unset', 'align-self': 'end', 'margin-right': '0px' } 87 style: { width: 'unset', 'align-self': 'end', 'margin-right': '0px' }
118 }, 88 },
119 ], 89 ],
...@@ -142,7 +112,7 @@ const costForm = ref({ ...@@ -142,7 +112,7 @@ const costForm = ref({
142 ] 112 ]
143 } 113 }
144 }); 114 });
145 const entryForm = ref({ 115 const entryForm: any = ref({
146 id: 'step-form-two', 116 id: 'step-form-two',
147 col: '', 117 col: '',
148 items: [ 118 items: [
...@@ -215,15 +185,8 @@ const entryForm = ref({ ...@@ -215,15 +185,8 @@ const entryForm = ref({
215 }); 185 });
216 186
217 const amount = ref(0); 187 const amount = ref(0);
218 const sumAmount = ref([]); 188 const sumAmount: any = ref([]);
219 const initCheckedList = ref([]);
220 const checkedData = ref([]);
221 const costFileds = ref({}); 189 const costFileds = ref({});
222 const rowCount = {
223 level1: { index: 0, rowspan: [] },
224 level2: { index: 0, rowspan: [] },
225 level3: { index: 0, rowspan: [] },
226 }
227 const convertConfig = { 190 const convertConfig = {
228 intangible: { 191 intangible: {
229 originName: '一、账面原值', 192 originName: '一、账面原值',
...@@ -262,16 +225,23 @@ const convertConfig = { ...@@ -262,16 +225,23 @@ const convertConfig = {
262 } 225 }
263 } 226 }
264 const costTableRef = ref(null); 227 const costTableRef = ref(null);
265 const isInitField = ref(true); 228 const initField: any = ref([
266 const initField = ref([]); //第二步初始化的表头数据 229 { label: "一级指标", field: "name1", level: 1, width: 120 },
267 const tableField = ref([ 230 { label: "一级分类", field: "name2", level: 2, width: 120 },
268 { label: "一级指标", field: "name1", width: 120 }, 231 { label: "二级分类", field: "name3", level: 3, width: 200 },
269 { label: "一级分类", field: "name2", width: 120 }, 232 { label: "三级分类", field: "name4", level: 4, width: 200, visible: false },
270 { label: "二级分类", field: "name3", width: 120 } 233 {
271 ]); 234 label: "累计投入", field: "total", width: 120, align: 'right', visible: false, columClass: 'inverse_cell', getName: (scope) => {
272 const tableData = ref([]) 235 return changeNum(scope.row.total, 2, true);
236 }
237 },
238 ]); //第二步初始化的表头数据
239 const tableField: any = ref([]);
240 const tableData: any = ref([])
273 const besure = ref(false); 241 const besure = ref(false);
274 const mergeRowCount = ref({}); 242 const mergeRowCount: any = ref({});
243 const checkedData: any = ref([]);
244
275 const entryItem = { 245 const entryItem = {
276 year: '', 246 year: '',
277 originName: '', 247 originName: '',
...@@ -300,10 +270,17 @@ const entryItem = { ...@@ -300,10 +270,17 @@ const entryItem = {
300 }; 270 };
301 271
302 const entryTableRef = ref(null); 272 const entryTableRef = ref(null);
303 const entryData = [] 273 const entryData: any = ref([]);
304 const headers = ref([]); 274 const bookHeaders: any = ref([
275 { label: '项目', field: 'title', width: 140, align: 'left' }
276 ]);
305 const transposedData = ref([]); 277 const transposedData = ref([]);
306 278
279 const popoverVisible = ref(false);
280 const popoverTriggerRef = ref(null)
281 const currentRow: any = ref({})
282
283 // 获取产品数据
307 const getProducts = () => { 284 const getProducts = () => {
308 getAssetCatalog().then((res: any) => { 285 getAssetCatalog().then((res: any) => {
309 if (res.code == proxy.$passCode) { 286 if (res.code == proxy.$passCode) {
...@@ -316,148 +293,100 @@ const getProducts = () => { ...@@ -316,148 +293,100 @@ const getProducts = () => {
316 }) 293 })
317 } 294 }
318 295
296 // 获取原始指标数据
319 const getCostData = () => { 297 const getCostData = () => {
320 loading.value = true; 298 loading.value = true;
321 getCostList({ 299 getCostList().then((res: any) => {
322 pageIndex: 1,
323 pageSize: -1,
324 }).then((res: any) => {
325 loading.value = false; 300 loading.value = false;
326 const data = res.data.records || []; 301 tableData.value = res.data || [];
327 let levelList = { level1: [], level2: [], level3: [], level4: [] }; 302 getMergeRow();
328 data.map(item => {
329 item.checked = false;
330 item.children = [];
331 levelList['level' + item.level].push(item);
332 })
333 levelList.level1.map(l => allMapList.value.push(l));
334 setAllMapList(levelList, allMapList.value, 2);
335
336 }).catch((res) => { 303 }).catch((res) => {
337 loading.value = false; 304 loading.value = false;
338 }); 305 });
339 }; 306 };
340 307
341 // 整理成本项数据 308 // 单元格多选框选中监听
342 const setAllMapList = (mapObj, arr, level) => { 309 const cellCheckboxChange = (val, scope) => {
343 arr.map(item => { 310 const { rIndex, level } = scope;
344 const code = item.code; 311 let rowData = tableData.value[rIndex];
345 item.children = mapObj['level' + level].filter(a => a.parentId == code); 312 rowData[`checked${level}`] = val;
346 level < 3 && setAllMapList(mapObj, item.children, level + 1);
347 })
348 // console.log(allMapList.value);
349 }
350
351 const setStep = async () => {
352 const info = costFormInfo.value;
353 await setFormItems(info, 'cost');
354 // costForm.value.items[0].disabled = step.value > 0;
355 // costForm.value.items[1].visible = step.value == 1;
356 // costForm.value.items[2].visible = step.value == 1;
357 // costForm.value.items[3].visible = step.value == 1;
358 if (step.value == 1) {
359 setSumRow();
360 }
361 stepsInfo.value.step = step.value;
362 nextTick(() => {
363 const contentDom = document.getElementsByClassName('content_main');
364 if (contentDom[0]) {
365 contentDom[0].scrollTo({
366 behavior: 'smooth',
367 top: 0,
368 });
369 }
370 })
371 }
372
373 const checkboxChange = (val, scope, level) => {
374 const { item, grid, child, opt } = scope;
375 let row = {};
376 switch (level) {
377 case 1:
378 row = item;
379 break;
380 case 2:
381 row = grid;
382 break;
383 case 3:
384 row = child;
385 break;
386 case 4:
387 row = opt;
388 break;
389 }
390 let list = checkedList.value, tData = checkedData.value, tRow = { ...row, total: '0.00', memo: '', level1: item.name, level2: grid ? grid.name : '', level3: child ? child.name : '', level4: opt ? opt.name : '' };
391 const eIndex = list.findIndex(item => item.guid == row.guid);
392 const tIndex = tData.findIndex(t => t.guid == row.guid);
393
394 if (val) { 313 if (val) {
395 eIndex === -1 && list.push(row); 314 // 选中上级
396 if (tIndex === -1) { 315 for (let l = level - 1; l >= 1; l--) {
397 tRow[`level${row.level}`] = row.name; 316 const tIndex = l;
398 tData.push(tRow); 317 const rCode = rowData[`code${l}`];
399 } 318 (() => {
400 if (level > 1) { 319 setChecked(tIndex, rCode, val);
401 item.checked = true; 320 })();
402 const x = list.findIndex(l => l.guid == item.guid);
403 x === -1 && list.push(item);
404 const i = tData.findIndex(t => t.guid == item.guid);
405 i > -1 && tData.splice(i, 1);
406 if (level > 2) {
407 grid.checked = true;
408 const y = list.findIndex(l => l.guid == grid.guid);
409 y === -1 && list.push(grid);
410 const g = tData.findIndex(t => t.guid == grid.guid);
411 g > -1 && tData.splice(g, 1);
412 if (level > 3) {
413 child.checked = true;
414 const z = list.findIndex(l => l.guid == child.guid);
415 z === -1 && list.push(child);
416 const c = tData.findIndex(t => t.guid == child.guid);
417 c > -1 && tData.splice(c, 1);
418 }
419 }
420 } 321 }
421 } else { 322 } else {
422 eIndex > -1 && list.splice(eIndex, 1); 323 // 取消选中下级
423 tIndex > -1 && tData.splice(tIndex, 1); 324 const rowList = tableData.value.filter(t => t[`code${level}`] == rowData[`code${level}`]);
424 if (level > 1) { 325 rowList.forEach(rData => {
425 const targetObj = level === 2 ? item : level === 3 ? grid : child; 326 for (let l = level; l <= 4; l++) {
426 const hasChildItem = targetObj.children.find(r => r.checked); 327 rData[`checked${l}`] = val;
427 const nIndex = tData.findIndex(t => t.guid == targetObj.guid);
428 if (!hasChildItem && nIndex === -1) {
429 let nRow = { ...targetObj, total: '0.00', memo: '', level1: item.name, level2: grid ? grid.name : '', level3: child ? child.name : '', level4: opt ? opt.name : '' };
430 nRow.level4 = '';
431 if (level === 2) {
432 nRow.level2 = '';
433 nRow.level3 = '';
434 } else if (level === 3) {
435 nRow.level3 = '';
436 }
437 tData.push(nRow);
438 } 328 }
439 } 329 })
440 level < 4 && setChecked(row.children);
441 } 330 }
331 rowData.edit = rowData.code4 ? rowData.checked4 : rowData.checked3; // 数据是否可编辑
332 checkedData.value = tableData.value.filter(t => t.edit);
442 }; 333 };
443 334
444 const setChecked = (arr) => { 335 // 设置单元格选中
445 let list = checkedList.value, tData = checkedData.value; 336 const setChecked = (level, code, check) => {
446 arr.forEach(a => { 337 const rowList = tableData.value.filter(t => t[`code${level}`] == code);
447 a.checked = false; 338 rowList.forEach(rData => rData[`checked${level}`] = check)
448 const lIndex = list.findIndex(t => t.guid == a.guid);
449 lIndex > -1 && list.splice(lIndex, 1);
450 const tIndex = tData.findIndex(t => t.guid == a.guid);
451 tIndex > -1 && tData.splice(tIndex, 1);
452 if (a.children.length) {
453 setChecked(a.children);
454 }
455 });
456 }; 339 };
457 340
341 // 点击外部区域关闭处理
342 const handleClickOutside = (event) => {
343 const popoverEl = document.querySelector('.tree-item-edit-menu')
344 const triggerEl: any = popoverTriggerRef.value
345
346 if (!popoverVisible.value || !popoverEl || !triggerEl) return
347
348 const clickedInPopover = popoverEl.contains(event.target)
349 const clickedOnTrigger = triggerEl === event.target || triggerEl.contains(event.target)
350
351 if (!clickedInPopover && !clickedOnTrigger) {
352 popoverVisible.value = false
353 }
354 }
355
356 // 切换Popover显示/隐藏
357 const togglePopover = (row, event) => {
358 // 如果点击的是当前已激活的触发器,则关闭popover
359 if (popoverVisible.value && popoverTriggerRef.value === event.currentTarget) {
360 popoverVisible.value = false
361 return
362 }
363
364 // 设置当前行和触发器引用
365 currentRow.value = row
366 popoverTriggerRef.value = event.currentTarget
367 const level = Number(row.field.split('name')[1]);
368 showAdd.value = level == 3 ? true : false;
369
370 // 打开popover
371 popoverVisible.value = true
372 }
373
374 // 处理菜单点击
375 const handleMenuClick = (action) => {
376 // 关闭popover
377 popoverVisible.value = false;
378 btnClick({ value: action, row: currentRow.value })
379 }
380
381 // Popover关闭后的处理
382 const handlePopoverClose = () => {
383 currentRow.value = null
384 popoverTriggerRef.value = null
385 }
386
458 const selectChange = async (val, row, info) => { 387 const selectChange = async (val, row, info) => {
459 if (row.field == 'entryType') { 388 if (row.field == 'entryType') {
460 await setFormItems(info); 389 await setFormItems(info, 'entry');
461 entryForm.value.items.at(0).default = val; 390 entryForm.value.items.at(0).default = val;
462 entryForm.value.items.at(1).visible = val == 'intangible' ? true : false; 391 entryForm.value.items.at(1).visible = val == 'intangible' ? true : false;
463 entryForm.value.items.at(2).visible = val == 'intangible' ? true : false; 392 entryForm.value.items.at(2).visible = val == 'intangible' ? true : false;
...@@ -497,13 +426,48 @@ const inputEventChange = (val, scope, field) => { ...@@ -497,13 +426,48 @@ const inputEventChange = (val, scope, field) => {
497 row[field] = row[field].toString().replace(/^\D*(\d{0,12}(?:\.\d{0,2})?).*$/g, "$1") 426 row[field] = row[field].toString().replace(/^\D*(\d{0,12}(?:\.\d{0,2})?).*$/g, "$1")
498 } 427 }
499 428
429 // 重命名
430 const setName = (name, level) => {
431 const rIndex = currentRow.value.$index;
432 let rowData = tableData.value[rIndex];
433 rowData[`name${level}`] = name;
434 }
435
436 // 新增本级
437 const addSameData = (rIndex, name, level, tData) => {
438 let rowData = JSON.parse(JSON.stringify(tData));
439 const lastCode = rowData[`code${level - 1}`];
440 const tCode = tData[`code${level}`].split(lastCode)[1];
441 const codeVal = parseInt(tCode, 10) < 10 ? `0${parseInt(tCode, 10) + 1}` : parseInt(tCode, 10) + 1;
442 rowData[`name${level}`] = name;
443 rowData[`code${level}`] = `${lastCode}${codeVal}`;
444 delete rowData.name4;
445 delete rowData.code4;
446 tableData.value.splice(rIndex + 1, 0, rowData);
447 getMergeRow();
448 }
449
450 // 新增下级
451 const addLowerData = (rIndex, name, level, len) => {
452 let rowData = JSON.parse(JSON.stringify(tableData.value[rIndex]));
453 const hasLowerItem = rowData[`code${level + 1}`] ? true : false;
454 const lastCode = rowData[`code${level}`];
455 const codeVal = (len + 1) < 10 ? '0' + (len + 1) : len + 1;
456 rowData[`name${level + 1}`] = name;
457 rowData[`code${level + 1}`] = `${lastCode}${codeVal}`;
458 hasLowerItem ? tableData.value.splice(rIndex + 1, 0, rowData) : tableData.value.splice(rIndex, 1, rowData);
459 tableField.value[3].visible = true;
460 getMergeRow();
461 }
462
463 // 按钮点击事件
500 const btnClick = async (btn, bType = null) => { 464 const btnClick = async (btn, bType = null) => {
501 const type = btn.value; 465 const type = btn.value;
502 if (type == 'add-same' || type == 'add-lower' || type == 'edit') { 466 if (type == 'add-same' || type == 'add-lower' || type == 'edit') {
503 const row = btn.row; 467 const row = btn.row;
504 const parent = btn.parent; 468 const { rIndex, level } = row;
505 const inputVal = type == 'edit' ? row.name : ''; 469 const inputVal = type == 'edit' ? row[`name${level}`] : '';
506 let isChange = false, curGridList = type == 'add-lower' ? row.children : parent.children; 470 let isChange = false, rowList = tableData.value.filter(t => t[`code${level - 1}`] == row[`code${level - 1}`]);
507 ElMessageBox.prompt('', '节点名称', { 471 ElMessageBox.prompt('', '节点名称', {
508 confirmButtonText: '确定', 472 confirmButtonText: '确定',
509 cancelButtonText: '取消', 473 cancelButtonText: '取消',
...@@ -524,7 +488,7 @@ const btnClick = async (btn, bType = null) => { ...@@ -524,7 +488,7 @@ const btnClick = async (btn, bType = null) => {
524 if (name.length > 10) { 488 if (name.length > 10) {
525 return '节点名称长度不能超过10个字符' 489 return '节点名称长度不能超过10个字符'
526 } 490 }
527 const isExist = curGridList.find(a => a.name == name); 491 const isExist = rowList.find(a => a[`name${level}`] == name);
528 if (isExist) { 492 if (isExist) {
529 return '节点名称已存在,请填写其他名称' 493 return '节点名称已存在,请填写其他名称'
530 } 494 }
...@@ -534,7 +498,7 @@ const btnClick = async (btn, bType = null) => { ...@@ -534,7 +498,7 @@ const btnClick = async (btn, bType = null) => {
534 beforeClose: (action, instance, done) => { 498 beforeClose: (action, instance, done) => {
535 if (action === 'confirm') { 499 if (action === 'confirm') {
536 if (!instance.inputValue) { 500 if (!instance.inputValue) {
537 const dom = document.querySelectorAll('.prompt_msg_box .el-message-box__errormsg'); 501 const dom: any = document.querySelectorAll('.prompt_msg_box .el-message-box__errormsg');
538 if (dom[0]) { 502 if (dom[0]) {
539 dom[0].innerHTML = '请填写节点名称'; 503 dom[0].innerHTML = '请填写节点名称';
540 dom[0].style.visibility = 'visible'; 504 dom[0].style.visibility = 'visible';
...@@ -552,61 +516,13 @@ const btnClick = async (btn, bType = null) => { ...@@ -552,61 +516,13 @@ const btnClick = async (btn, bType = null) => {
552 const name = value.trim(); 516 const name = value.trim();
553 if (type == 'edit') { 517 if (type == 'edit') {
554 if (value == inputVal) return 518 if (value == inputVal) return
555 row.name = name; 519 setName(name, level);
556 let list = checkedList.value, tData = checkedData.value;
557 tData.forEach((t) => {
558 if (t.guid == row.guid) {
559 t['level' + row.level] = name;
560 t.name = name;
561 }
562 if (t.parentId == row.code) {
563 t['level' + row.level] = name;
564 }
565 })
566 list.forEach((l) => {
567 if (l.guid == row.guid) {
568 l.name = name;
569 }
570 })
571 } else if (type == 'add-same') { 520 } else if (type == 'add-same') {
572 let code = ''; 521 const tData = rowList.at(-1);
573 const sameCode = curGridList.filter(c => c.parentId == row.parentId); 522 const tIndex = tableData.value.findIndex(t => (tData.code4 ? t.code4 === tData.code4 : t.code3 === tData.code3));
574 sameCode.sort((a, b) => a.sortNum - b.sortNum); 523 addSameData(tIndex, name, level, tData);
575 const lastCode = sameCode.at(-1);
576 const codeVal = (sameCode.length + 1) < 10 ? '0' + (sameCode.length + 1) : sameCode.length + 1;
577 code = `${lastCode.parentId}${codeVal}`;
578 curGridList.push({
579 ...JSON.parse(JSON.stringify(row)),
580 code: code,
581 name: name,
582 guid: `new-${code}`,
583 sortNum: lastCode.sortNum + 1,
584 children: [],
585 checked: false
586 })
587 } else if (type == 'add-lower') { 524 } else if (type == 'add-lower') {
588 let code = '', sortNum = 1; 525 addLowerData(rIndex, name, level, rowList.length);
589 if (curGridList.length) {
590 curGridList.sort((a, b) => a.sortNum - b.sortNum);
591 const lastCode = curGridList.at(-1);
592 const codeVal = (curGridList.length + 1) < 10 ? '0' + (curGridList.length + 1) : curGridList.length + 1;
593 code = `${lastCode.parentId}${codeVal}`;
594 sortNum = lastCode.sortNum + 1;
595 } else {
596 code = `${row.code}01`;
597 }
598 curGridList.push({
599 ...JSON.parse(JSON.stringify(row)),
600 code: code,
601 name: name,
602 sortNum: sortNum,
603 level: 4,
604 guid: `new-${code}`,
605 parentId: row.code,
606 children: [],
607 checked: false
608 })
609 if (!showLevel4.value) showLevel4.value = true;
610 } 526 }
611 }).catch(() => { 527 }).catch(() => {
612 ElMessage({ 528 ElMessage({
...@@ -616,8 +532,9 @@ const btnClick = async (btn, bType = null) => { ...@@ -616,8 +532,9 @@ const btnClick = async (btn, bType = null) => {
616 }) 532 })
617 } else if (type == 'remove') { 533 } else if (type == 'remove') {
618 const row = btn.row; 534 const row = btn.row;
619 const hasChild = row.children; 535 const { rIndex, level } = row;
620 const msg = hasChild.length ? '删除该节点会同步删除其子节点,是否确定删除?' : '确定删除该节点吗?'; 536 const rowList = tableData.value.filter(t => t[`code${level}`] == row[`code${level}`]);
537 const msg = level == 3 && rowList[0].code4 ? '删除该节点会同步删除其子节点,是否确定删除?' : '确定删除该节点吗?';
621 ElMessageBox.confirm( 538 ElMessageBox.confirm(
622 msg, 539 msg,
623 '提示', 540 '提示',
...@@ -627,57 +544,21 @@ const btnClick = async (btn, bType = null) => { ...@@ -627,57 +544,21 @@ const btnClick = async (btn, bType = null) => {
627 type: 'warning', 544 type: 'warning',
628 } 545 }
629 ).then(() => { 546 ).then(() => {
630 let curGridList = btn.parent.children; 547 if (level == 3) {
631 const rIndex = curGridList.findIndex(c => c.guid == row.guid); 548 if (rowList.length > 1) {
632 rIndex > -1 && curGridList.splice(rIndex, 1); 549 // 获取所有code3不等于当前code3的节点
633 let list = checkedList.value, tData = checkedData.value; 550 tableData.value = tableData.value.filter(t => t.code3 !== row.code3);
634 let cRow = tData.find(c => c.guid == row.guid);
635 const hasChild = row.children.find(r => r.checked);
636 if (!cRow && hasChild) {
637 cRow = tData.find(c => c.guid == hasChild.guid);
638 }
639 const tIndex = tData.findIndex(c => c.guid == row.guid);
640 tIndex > -1 && tData.splice(tIndex, 1);
641 const lIndex = list.findIndex(c => c.guid == row.guid);
642 lIndex > -1 && list.splice(lIndex, 1);
643 const level = row.level;
644 if (level > 1 && cRow) {
645 const targetObj = btn.parent;
646 const hasChildItem = targetObj.children.find(r => r.checked);
647 const nIndex = tData.findIndex(t => t.guid == targetObj.guid);
648 if (!hasChildItem && nIndex === -1) {
649 let nRow = { ...cRow, ...targetObj, total: '0.00', level: targetObj.level };
650 nRow[`level${targetObj.level}`] = targetObj.name;
651 nRow.level4 = '';
652 if (level === 2) {
653 nRow.level2 = '';
654 nRow.level3 = '';
655 } else if (level === 3) {
656 nRow.level3 = '';
657 }
658 tData.push(nRow);
659 }
660 }
661 level < 4 && setChecked(row.children);
662 for (let c = 0; c < curGridList.length; c++) {
663 const item = curGridList[c];
664 if (item.sortNum > row.sortNum) {
665 const code = parseInt(item.code.split(item.parentId)[1], 10) - 1;
666 tData.forEach(t => {
667 if (t.guid == item.guid) {
668 t.code = `${t.parentId}${code < 10 ? '0' + code : code}`;
669 t.guid = t.guid.indexOf('new') > -1 ? `new-${t.code}` : t.guid;
670 t.sortNum = t.sortNum - 1;
671 }
672 })
673 item.code = `${item.parentId}${code < 10 ? '0' + code : code}`;
674 item.guid = item.guid.indexOf('new') > -1 ? `new-${item.code}` : item.guid;
675 item.sortNum = item.sortNum - 1;
676 } else { 551 } else {
677 continue; 552 tableData.value.splice(rIndex, 1);
678 } 553 }
554 } else {
555 let rowData = tableData.value[rIndex];
556 delete rowData[`name${level}`];
557 delete rowData[`code${level}`];
558 const level4 = tableData.value.find(t => t.code4);
559 !level4 && (tableField.value.at(-2).visible = false);
679 } 560 }
680 setShowLevel4(); 561 getMergeRow();
681 }).catch(() => { 562 }).catch(() => {
682 ElMessage({ 563 ElMessage({
683 type: 'info', 564 type: 'info',
...@@ -690,104 +571,35 @@ const btnClick = async (btn, bType = null) => { ...@@ -690,104 +571,35 @@ const btnClick = async (btn, bType = null) => {
690 costFormInfo.value = { ...costFormInfo.value, ...JSON.parse(JSON.stringify(fInfo)) }; 571 costFormInfo.value = { ...costFormInfo.value, ...JSON.parse(JSON.stringify(fInfo)) };
691 setStep(); 572 setStep();
692 } else if (type == 'next') { 573 } else if (type == 'next') {
693 if (step.value == 0) { 574 const formEl1 = entryFormRef.value.ruleFormRef;
694 const formEl = costFormRef.value.ruleFormRef; 575 const fInfo1 = entryFormRef.value.formInline;
695 const fInfo = costFormRef.value.formInline; 576 if (!formEl1) return;
696 costFormInfo.value = { ...costFormInfo.value, ...JSON.parse(JSON.stringify(fInfo)) }; 577 const valid1 = await submitForm(formEl1);
697 if (!formEl) return; 578 if (valid1) {
698 const valid = await submitForm(formEl); 579 const formEl2 = costFormRef.value.ruleFormRef;
699 if (valid) { 580 const fInfo2 = costFormRef.value.formInline;
700 if (checkedList.value.length == 0) { 581 const changeTable = JSON.stringify(costFormInfo.value) != JSON.stringify(fInfo2);
701 ElMessage.warning('请勾选成本项'); 582 if (changeTable) {
583 besure.value = false;
584 }
585 costFormInfo.value = JSON.parse(JSON.stringify(fInfo2));
586 if (!formEl2) return;
587 const valid2 = await submitForm(formEl2);
588 if (valid2) {
589 if (!besure.value) {
590 ElMessage.warning('成本年有变动请点击确认按钮,更新表数据');
702 return 591 return
703 } 592 }
704 593 await setEntryData(fInfo1, fInfo2);
705 const nameArr = checkedList.value.map(c => c.name); 594 await transposeData(fInfo1.entryType);
706 isInitField.value = initCheckedList.value.toString() != nameArr.toString(); 595 costForm.value.items.map((cost, c) => {
707 const tGuids = tableData.value.map(d => d.guid); 596 if (c == 0) {
708 if (isInitField.value) { 597 cost.disabled = true
709 initCheckedList.value = nameArr; 598 } else {
710 let fields = [ 599 cost.visible = false
711 {
712 label: "累计投入", field: "total", width: 120, align: 'right', getName: (scope) => {
713 return changeNum(scope.row.total, 2, true);
714 }
715 },
716 ]
717 const hasLevel1 = checkedList.value.find(c => c.level == 1);
718 if (hasLevel1) {
719 fields.splice(-1, 0, { label: "指标", field: "level1", width: 120 });
720 }
721 const hasLevel2 = checkedList.value.find(c => c.level == 2);
722 if (hasLevel2) {
723 fields.splice(-1, 0, { label: "一级分类", field: "level2", width: 120 });
724 }
725 const hasLevel3 = checkedList.value.find(c => c.level == 3);
726 if (hasLevel3) {
727 fields.splice(-1, 0, { label: "二级分类", field: "level3", width: 120 });
728 }
729 const hasLevel4 = checkedList.value.find(c => c.level == 4);
730 if (hasLevel4) {
731 fields.splice(-1, 0, { label: "三级分类", field: "level4", width: 120 });
732 }
733 tableField.value.splice(0);
734 tableField.value.push(...fields);
735 initField.value = JSON.parse(JSON.stringify(tableField.value));
736 const datas = JSON.parse(JSON.stringify(checkedData.value));
737 datas.sort((a, b) => a.code.localeCompare(b.code));
738 const dGuid = datas.map(d => d.guid);
739 let tDatas = tableData.value.filter(t => dGuid.indexOf(t.guid) > -1);
740 datas.forEach((td, t) => {
741 if (tGuids.indexOf(td.guid) == -1) {
742 tDatas.splice(t, 0, td)
743 } else {
744 tDatas.map(d => {
745 if (d.guid == td.guid) {
746 d.name = td.name;
747 d.level1 = td.level1;
748 d.level2 = td.level2;
749 d.level3 = td.level3;
750 d.level4 = td.level4;
751 }
752 })
753 }
754 })
755 tableData.value.splice(0);
756 tableData.value = tDatas;
757 if (tGuids.length > 0 && costFormInfo.value.baseDate && costFormInfo.value.investYear) {
758 setTableFields(costFormInfo.value);
759 besure.value = true;
760 } 600 }
761 getMergeRow(); 601 })
762 }
763 step.value++; 602 step.value++;
764 setStep();
765 }
766 } else if (step.value == 1) {
767 const formEl1 = entryFormRef.value.ruleFormRef;
768 const fInfo1 = entryFormRef.value.formInline;
769 if (!formEl1) return;
770 const valid1 = await submitForm(formEl1);
771 if (valid1) {
772 const formEl2 = costFormRef.value.ruleFormRef;
773 const fInfo2 = costFormRef.value.formInline;
774 const changeTable = JSON.stringify(costFormInfo.value) != JSON.stringify(fInfo2);
775 if (changeTable) {
776 besure.value = false;
777 }
778 costFormInfo.value = JSON.parse(JSON.stringify(fInfo2));
779 if (!formEl2) return;
780 const valid2 = await submitForm(formEl2);
781 if (valid2) {
782 if (!besure.value) {
783 ElMessage.warning('成本年有变动请点击确认按钮,更新表数据');
784 return
785 }
786 await setEntryData(fInfo1, fInfo2);
787 await transposeData(fInfo1.entryType);
788 step.value++;
789 setStep();
790 }
791 } 603 }
792 } 604 }
793 } else if (type == 'refresh') { 605 } else if (type == 'refresh') {
...@@ -805,66 +617,13 @@ const btnClick = async (btn, bType = null) => { ...@@ -805,66 +617,13 @@ const btnClick = async (btn, bType = null) => {
805 } 617 }
806 }; 618 };
807 619
808 const initTableFileds = () => { 620 const setStep = async () => {
809 let fields = [ 621 const info = costFormInfo.value;
810 { 622 await setFormItems(info, 'cost');
811 label: "累计投入", field: "total", width: 120, align: 'right', getName: (scope) => { 623 costForm.value.items[0].disabled = step.value > 0;
812 return changeNum(scope.row.total, 2, true); 624 costForm.value.items[1].visible = step.value == 0;
813 } 625 costForm.value.items[2].visible = step.value == 0;
814 }, 626 costForm.value.items[3].visible = step.value == 0;
815 ]
816 const hasLevel1 = checkedList.value.find(c => c.level == 1);
817 if (hasLevel1) {
818 fields.splice(-1, 0, { label: "指标", field: "level1", width: 120 });
819 }
820 const hasLevel2 = checkedList.value.find(c => c.level == 2);
821 if (hasLevel2) {
822 fields.splice(-1, 0, { label: "一级分类", field: "level2", width: 120 });
823 }
824 const hasLevel3 = checkedList.value.find(c => c.level == 3);
825 if (hasLevel3) {
826 fields.splice(-1, 0, { label: "二级分类", field: "level3", width: 120 });
827 }
828 const hasLevel4 = checkedList.value.find(c => c.level == 4);
829 if (hasLevel4) {
830 fields.splice(-1, 0, { label: "三级分类", field: "level4", width: 120 });
831 }
832 tableField.value.splice(0);
833 tableField.value.push(...fields);
834 initField.value = JSON.parse(JSON.stringify(tableField.value));
835 const datas = JSON.parse(JSON.stringify(checkedData.value));
836 datas.sort((a, b) => a.code.localeCompare(b.code));
837 const dGuid = datas.map(d => d.guid);
838 let tDatas = tableData.value.filter(t => dGuid.indexOf(t.guid) > -1);
839 datas.forEach((td, t) => {
840 if (tGuids.indexOf(td.guid) == -1) {
841 tDatas.splice(t, 0, td)
842 } else {
843 tDatas.map(d => {
844 if (d.guid == td.guid) {
845 d.name = td.name;
846 d.level1 = td.level1;
847 d.level2 = td.level2;
848 d.level3 = td.level3;
849 d.level4 = td.level4;
850 }
851 })
852 }
853 })
854 tableData.value.splice(0);
855 tableData.value = tDatas;
856 // if (tGuids.length > 0 && costFormInfo.value.baseDate && costFormInfo.value.investYear) {
857 // setTableFields(costFormInfo.value);
858 // besure.value = true;
859 // }
860 getMergeRow();
861 };
862
863 const setShowLevel4 = () => {
864 nextTick(() => {
865 const box4 = document.querySelectorAll('.grid-panel-box.box3 .grid-items');
866 showLevel4.value = box4.length > 0
867 })
868 } 627 }
869 628
870 const setFormItems = (row: any = null, type) => { 629 const setFormItems = (row: any = null, type) => {
...@@ -906,10 +665,10 @@ const submitForm = (formEl) => { ...@@ -906,10 +665,10 @@ const submitForm = (formEl) => {
906 }; 665 };
907 666
908 const setEntryData = (fInfo1, fInfo2) => { 667 const setEntryData = (fInfo1, fInfo2) => {
909 let datas = [], lastCount = -1, lastYear = {}; 668 let datas: any = [], lastCount = -1, lastYear: any = {};
910 for (var f in costFileds.value) { 669 for (var f in costFileds.value) {
911 lastCount++; 670 lastCount++;
912 let row = { ...entryItem }; 671 let row: any = { ...entryItem };
913 if (f != 'baseNum') { 672 if (f != 'baseNum') {
914 row.year = `${f}年`; 673 row.year = `${f}年`;
915 } else { 674 } else {
...@@ -930,7 +689,7 @@ const setEntryData = (fInfo1, fInfo2) => { ...@@ -930,7 +689,7 @@ const setEntryData = (fInfo1, fInfo2) => {
930 } else { 689 } else {
931 let amortize1 = 0; 690 let amortize1 = 0;
932 for (var d = datas.length - 1; d > (lastCount - fInfo1.shareYears); d--) { 691 for (var d = datas.length - 1; d > (lastCount - fInfo1.shareYears); d--) {
933 const da = datas[d]; 692 const da: any = datas[d];
934 amortize1 += da.amortize2 693 amortize1 += da.amortize2
935 } 694 }
936 row.amortize1 = amortize1; 695 row.amortize1 = amortize1;
...@@ -956,7 +715,7 @@ const setTableFields = (info) => { ...@@ -956,7 +715,7 @@ const setTableFields = (info) => {
956 //结束年 715 //结束年
957 let nowYears = Number(info.baseDate.split('-')[0]); 716 let nowYears = Number(info.baseDate.split('-')[0]);
958 let Years = nowYears - smallYears 717 let Years = nowYears - smallYears
959 let arrYear = [], fields = [], row = { baseNum: '' }; 718 let arrYear: any = [], fields: any = [], row: any = { baseNum: '' };
960 for (let i = 0; i < Years; i++) { 719 for (let i = 0; i < Years; i++) {
961 arrYear.push(smallYears++) 720 arrYear.push(smallYears++)
962 } 721 }
...@@ -968,13 +727,13 @@ const setTableFields = (info) => { ...@@ -968,13 +727,13 @@ const setTableFields = (info) => {
968 costFileds.value = row; 727 costFileds.value = row;
969 const date = new Date(info.baseDate); 728 const date = new Date(info.baseDate);
970 const currDay = date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long' }); 729 const currDay = date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long' });
971 tableField.value = JSON.parse(JSON.stringify(initField.value)); 730 tableField.value.at(-1).visible = true;
972 tableField.value.splice(-1, 0, ...fields, { label: currDay, field: 'baseNum', type: 'input', width: 120, columClass: 'edit_cell' }); 731 tableField.value.splice(-1, 0, ...fields, { label: currDay, field: 'baseNum', type: 'input', width: 120, columClass: 'edit_cell' });
973 732 // 设置table数据
974 let datas = []; 733 let datas: any = [];
975 tableData.value.map(t => { 734 tableData.value.map(t => {
976 let tRow = { ...row, ...t }; 735 let tRow = { ...row, ...t };
977 const tKey = Object.keys(t); 736 const tKey: any = Object.keys(t);
978 tKey.forEach(k => { 737 tKey.forEach(k => {
979 if ((k == 'baseNum' || !isNaN(k)) && row[k] === undefined) { 738 if ((k == 'baseNum' || !isNaN(k)) && row[k] === undefined) {
980 delete tRow[k]; 739 delete tRow[k];
...@@ -984,87 +743,107 @@ const setTableFields = (info) => { ...@@ -984,87 +743,107 @@ const setTableFields = (info) => {
984 }) 743 })
985 tableData.value.splice(0); 744 tableData.value.splice(0);
986 tableData.value = datas; 745 tableData.value = datas;
746 getMergeRow();
987 } 747 }
988 748
989 // 设置表格合并下标 749 // 设置表格合并下标
990 const getMergeRow = () => { 750 const getMergeRow = () => {
991 mergeRowCount.value = JSON.parse(JSON.stringify(rowCount)); 751 let list = tableData.value, info = {}
992 let list = tableData.value; 752 // 为每个层级初始化合并信息
993 for (var i = 0; i < list.length; i++) { 753 for (let level = 1; level <= 3; level++) {
994 if (i === 0) { 754 const nameKey = `name${level}`
995 //第一个数据 默认合并1行,开始位置下标默认为0 755 const codeKey = `code${level}`
996 for (var m in mergeRowCount.value) { 756 info[nameKey] = []
997 mergeRowCount.value[m].rowspan.push(1); 757
998 mergeRowCount.value[m].index = 0; 758 // 处理每行数据
999 } 759 list.forEach((row, index) => {
1000 } else { 760 if (index === 0) {
1001 // 根据拥有子级数量进行合并 761 // 第一行默认合并1行,开始位置0
1002 for (var m in mergeRowCount.value) { 762 info[nameKey][index] = 1
1003 let mergeRow = mergeRowCount.value[m]; 763 } else {
1004 const e = Number(m.split('level')[1]); 764 // 当前行与上一行的code和name比较
1005 if (list[i][m] && list[i]['level' + (e - 1)] == list[i - 1]['level' + (e - 1)] && list[i][m] === list[i - 1][m]) { 765 const sameCode = row[codeKey] === list[index - 1][codeKey]
1006 mergeRow.rowspan[mergeRow.index] += 1; 766 const sameName = row[nameKey] === list[index - 1][nameKey]
1007 mergeRow.rowspan.push(0); 767
768 if (sameCode && sameName) {
769 // 找到当前分组的起始位置
770 let groupStart = index - 1
771 while (groupStart > 0 && info[nameKey][groupStart] === 0) {
772 groupStart--
773 }
774 // 增加合并行数
775 info[nameKey][groupStart]++
776 info[nameKey][index] = 0
1008 } else { 777 } else {
1009 mergeRow.rowspan.push(1); 778 // 新分组,合并1行
1010 mergeRow.index = i; 779 info[nameKey][index] = 1
1011 } 780 }
1012 } 781 }
1013 } 782 })
1014 } 783 }
784
785 mergeRowCount.value = info;
1015 } 786 }
1016 787
1017 // 表格行合并 788 // 表格行合并
1018 const tableSpanMethod = ({ row, column, rowIndex, columnIndex }) => { 789 const tableSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
1019 if (column.property.indexOf('level') > -1 && column.property != 'level4') { 790 const colName = column.property
1020 const rowspan = mergeRowCount.value[column.property].rowspan; 791 if (mergeRowCount.value[colName]) {
1021 const _row = rowspan[rowIndex]; 792 const rowspan = mergeRowCount.value[colName][rowIndex]
1022 const _col = _row == 0 ? 0 : 1;
1023 return { 793 return {
1024 rowspan: _row, 794 rowspan: rowspan,
1025 colspan: _col 795 colspan: rowspan > 0 ? 1 : 0
1026 } 796 }
1027 } 797 }
798 return { rowspan: 1, colspan: 1 }
1028 } 799 }
1029 800
1030 const isMergedCell = ({ rowIndex, columnIndex }) => { 801 const isMergedCell = ({ row, column, rowIndex, columnIndex }) => {
1031 const property = tableField.value[columnIndex].field; 802 const property = column.property;
1032 const rowspan = mergeRowCount.value[property]?.rowspan[rowIndex]; 803 // 只处理需要合并的列 (name1/name2/name3)
1033 return rowspan > 1 ? 'merged-cell' : ''; 804 if (['name1', 'name2', 'name3'].includes(property)) {
805 // 检查当前单元格是否属于合并范围
806 const rowspan = mergeRowCount.value[property]?.[rowIndex];
807 // 无论是合并的起始单元格(rowspan>1)还是被合并的隐藏单元格(rowspan===0)
808 return rowspan !== 1 ? 'merged-cell' : '';
809 }
810 return '';
1034 } 811 }
1035 812
1036 // 表格合计行 813 // 优化后的合计计算方法
1037 const tableSummaryMethod = ({ columns, data }) => { 814 const tableSummaryMethod = ({ columns, data }) => {
1038 let sums = []; 815 if (!besure.value) return;
816 let sums: any = [], numericColumns: any = []; // 记录需要计算的数字列
1039 columns.forEach((column, index) => { 817 columns.forEach((column, index) => {
1040 if (index === 0) { //需要显示'总金额'的列 坐标 :0 818 if (index === 0) {
1041 sums[index] = '合计' 819 sums[index] = '合计';
1042 return 820 return;
821 }
822 // 识别需要计算的数字列
823 if (!column.property.includes('name') && column.property !== 'memo') {
824 numericColumns.push({
825 index,
826 property: column.property
827 });
1043 } else { 828 } else {
1044 if (column.property.indexOf('level') == -1 && column.property != 'memo') { 829 sums[index] = ''; // 非数字列留空
1045 const values = data.map(item => parseFloat(item[column.property] ? item[column.property].replace(/,/g, "") : 0));
1046 if (!values.every(value => isNaN(value))) {
1047 const sum = values.reduce((prev, curr) => {
1048 const value = parseFloat(curr || 0)
1049 if (!isNaN(value)) {
1050 return prev + curr
1051 } else {
1052 return prev
1053 }
1054 }, 0)
1055 sums[index] = changeNum(sum, 2, true)
1056 if (column.property == 'total') {
1057 amount.value = sum
1058 }
1059 sumAmount.value[column.property] = sum;
1060 } else {
1061 sums[index] = 'N/A'
1062 }
1063 }
1064 } 830 }
1065 }) 831 });
1066 return sums 832 // 计算数字列合计
1067 } 833 numericColumns.forEach(({ index, property }) => {
834 const sum = data.reduce((total, item) => {
835 const value = parseFloat(String(item[property]).replace(/,/g, '')) || 0;
836 return total + value;
837 }, 0);
838 sums[index] = changeNum(sum, 2, true);
839 // 特殊字段处理
840 if (property === 'total') {
841 amount.value = sum;
842 }
843 sumAmount.value[property] = sum;
844 });
845 return sums;
846 };
1068 847
1069 const isExist = (newArr, name) => { 848 const isExist = (newArr, name) => {
1070 for (let i = 0; i < newArr.length; i++) { 849 for (let i = 0; i < newArr.length; i++) {
...@@ -1077,19 +856,23 @@ const isExist = (newArr, name) => { ...@@ -1077,19 +856,23 @@ const isExist = (newArr, name) => {
1077 856
1078 // 表格数据行列转置 857 // 表格数据行列转置
1079 const transposeData = (type) => { 858 const transposeData = (type) => {
859 bookHeaders.value.splice(1);
1080 // 提取原始数据的头部作为转置后数据的列 860 // 提取原始数据的头部作为转置后数据的列
1081 headers.value = entryData.value.map((t) => { 861 const list = entryData.value.map((t) => {
1082 return { 862 return {
1083 label: t.year, 863 label: t.year,
1084 value: t.guid || t.year 864 field: t.guid || t.year,
865 width: 120,
866 align: 'right'
1085 } 867 }
1086 }); 868 });
869 bookHeaders.value.push(...list);
1087 870
1088 /** 871 /**
1089 * 定义映射字段表(最好取全量字段) 872 * 定义映射字段表(最好取全量字段)
1090 * */ 873 * */
1091 const mapObj = convertConfig[type] 874 const mapObj = convertConfig[type]
1092 const newArr = []; 875 const newArr: any = [];
1093 for (const t in mapObj) { 876 for (const t in mapObj) {
1094 for (let i = 0; i < entryData.value.length; i++) { 877 for (let i = 0; i < entryData.value.length; i++) {
1095 const item = entryData.value[i] 878 const item = entryData.value[i]
...@@ -1097,7 +880,7 @@ const transposeData = (type) => { ...@@ -1097,7 +880,7 @@ const transposeData = (type) => {
1097 if (result) { 880 if (result) {
1098 result[item.year] = item[t] 881 result[item.year] = item[t]
1099 } else { 882 } else {
1100 const obj = {} 883 const obj: any = {}
1101 obj.title = mapObj[t] 884 obj.title = mapObj[t]
1102 obj[item.year] = item[t] 885 obj[item.year] = item[t]
1103 newArr.push(obj) 886 newArr.push(obj)
...@@ -1121,248 +904,290 @@ const setLabel = (val) => { ...@@ -1121,248 +904,290 @@ const setLabel = (val) => {
1121 } 904 }
1122 } 905 }
1123 906
1124 const getClass = (i, checked) => { 907 // 下载表格
1125 return checked ? `active${i + 1}` : ''; 908 const s2ab = (s) => {
909 const buf = new ArrayBuffer(s.length);
910 const view = new Uint8Array(buf);
911 for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
912 return buf;
1126 } 913 }
1127 914
1128 const gridPanel = ref(null); 915 // 生成成本设置表
1129 const gridItem = ref(null); 916 const exportDetailsToExcel = async () => {
1130 const setItemLine = () => { 917 // 准备工作表数据
1131 nextTick(() => { 918 const wsData = []
1132 setTimeout(() => { 919
1133 if (gridItem.value) { 920 // 表头
1134 const width1 = gridPanel.value[0].offsetWidth; 921 const headers = tableField.value.map(item => item.label)
1135 const width2 = gridItem.value[0].offsetWidth; 922 wsData.push(headers)
1136 // 设置::before伪元素的宽度 923
1137 itemWidth.value = `${(width1 - width2) / 2 + (showLevel4.value ? 6 : 8)}px`; 924 // 处理数据行
1138 itemLeft.value = `-${(width1 - width2) / 2 + (showLevel4.value ? 6 : 8)}px`; 925 checkedData.value.forEach(item => {
1139 document.documentElement.style.setProperty('--item-width', itemWidth.value); 926 const row = tableField.value.map(f => {
1140 document.documentElement.style.setProperty('--item-left', itemLeft.value); 927 if (f.field.indexOf('name') == -1) {
928 // 分类字段
929 return changeNum(item[f.field])
930 } else {
931 // 其他字段
932 return item[f.field] || ''
1141 } 933 }
1142 }, 100) 934 })
935 wsData.push(row)
1143 }) 936 })
1144 }
1145 937
1146 const spanHeight = (obj) => { 938 // 添加合计行
1147 return `${obj.children.length ? obj.children.length * 48 : 48}px`; 939 const totalRow = ['合计', '', '', ''];
1148 } 940 const numField = tableField.value.filter(item => item.field.indexOf('name') == -1);
941 const numericFields = numField.map(f => f.field);
942 let totalValues = {}
1149 943
1150 const showPanel = (arr, row) => { 944 // 初始化合计值
1151 arr.forEach(a => { 945 numericFields.forEach(field => {
1152 if (a.level < 3) { 946 totalValues[field] = 0
1153 a.children.length && showPanel(a.children, row) 947 })
1154 } else { 948
1155 if (a.children.length) { 949 // 计算合计值
1156 row.show4 = true; 950 checkedData.value.forEach(item => {
1157 return; 951 numericFields.forEach(field => {
952 if (item[field]) {
953 const num = parseFloat(item[field].replace(/,/g, '')) || 0
954 totalValues[field] += num
1158 } 955 }
956 })
957 })
958
959 // 将合计值添加到合计行
960 tableField.value.forEach((field, index) => {
961 if (index < 4) return // 前4列已经处理
962 if (numericFields.includes(field.field)) {
963 const value = totalValues[field.field]
964 // 格式化数字,保留2位小数
965 totalRow.push(value.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","))
966 } else {
967 totalRow.push('')
1159 } 968 }
1160 }) 969 })
1161 return row.show4 || false;
1162 }
1163 970
1164 // 设置二级分类 item的margin-bottom值 971 wsData.push(totalRow)
1165 const setItemStyle = (row, indexObj) => {
1166 if (indexObj) {
1167 const { o } = indexObj;
1168 setGroupStyle();
1169 setItemsStyle(o);
1170 return { height: 'auto' }
1171 } else {
1172 setHeight();
1173 return { 'margin-bottom': row.children.length > 1 ? ((row.children.length - 1) * 48) + 8 + 'px' : '0px' }
1174 }
1175 }
1176 972
1177 // 设置三级分类 group高度 973 // 创建工作表
1178 const setGroupStyle = () => { 974 const ws = XLSXS.utils.aoa_to_sheet(wsData)
1179 nextTick(() => { 975
1180 const box3 = document.querySelectorAll('.grid-panel-box.box2 .grid-panel'); 976 // 自动调整列宽
1181 const box4 = document.querySelectorAll('.grid-panel-box.box3 .grid-group'); 977 const colWidths = tableField.value.map((field, index) => {
1182 box4.forEach((box, b) => { 978 const defaultWidth = field.width || 100 // 默认宽度100
1183 box.style.height = box3[b].offsetHeight + 'px'; 979 const colWidth = defaultWidth / 14 + 2; // 留一些余地
1184 }) 980
981 // 取表头宽度和内容宽度的较大值
982 return { wpx: colWidth * 12 } // 使用像素宽度
1185 }) 983 })
1186 }
1187 984
1188 // 设置三级分类 item高度 985 ws['!cols'] = colWidths
1189 const setItemsStyle = (i) => { 986
1190 nextTick(() => { 987 // 设置合并单元格
1191 const box3 = document.querySelectorAll('.grid-panel-box.box2 .grid-items'); 988 const merges = []
1192 const box4 = document.querySelectorAll('.grid-panel-box.box3 .group-items'); 989
1193 box4.forEach((box, b) => { 990 // 按code1合并一级指标
1194 const bStyle = window.getComputedStyle(box3[b]); 991 const level1Groups = groupBy(checkedData.value, 'code1')
1195 const h = parseFloat(bStyle.height) + parseFloat(bStyle.marginBottom); 992 Object.values(level1Groups).forEach(group => {
1196 box.style.height = h <= 40 ? (h + 8) + 'px' : h + 'px'; 993 const startRow = checkedData.value.findIndex(item => item.code1 === group[0].code1) + 1 // +1因为第一行是表头
1197 }) 994 const endRow = startRow + group.length - 1
995 merges.push({ s: { r: startRow, c: 0 }, e: { r: endRow, c: 0 } })
1198 }) 996 })
1199 }
1200 997
1201 // 设置一级分类 panel高度 998 // 按code2合并一级分类
1202 const setHeight = () => { 999 const level2Groups = groupBy(checkedData.value, 'code2')
1203 nextTick(() => { 1000 Object.values(level2Groups).forEach(group => {
1204 const boxArr = document.querySelectorAll('.grid-panel-box'); 1001 const startRow = checkedData.value.findIndex(item => item.code2 === group[0].code2) + 1
1205 if (boxArr.length) { 1002 const endRow = startRow + group.length - 1
1206 const box2 = boxArr[1].querySelectorAll('.grid-panel'); 1003 merges.push({ s: { r: startRow, c: 1 }, e: { r: endRow, c: 1 } })
1207 const box3 = boxArr[2].querySelectorAll('.grid-panel');
1208 box2.forEach((box, b) => {
1209 box.style.height = box3[b].offsetHeight + 'px';
1210 })
1211 }
1212 }) 1004 })
1213 }
1214 1005
1215 // 下载表格 1006 // 按code3合并二级分类
1216 const s2ab = (s) => { 1007 const level3Groups = groupBy(checkedData.value, 'code3')
1217 const buf = new ArrayBuffer(s.length); 1008 Object.values(level3Groups).forEach(group => {
1218 const view = new Uint8Array(buf); 1009 const startRow = tableData.value.findIndex(item => item.code3 === group[0].code3) + 1
1219 for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; 1010 const endRow = startRow + group.length - 1
1220 return buf; 1011 merges.push({ s: { r: startRow, c: 2 }, e: { r: endRow, c: 2 } })
1221 } 1012 })
1222 1013
1223 // 获取所有单元格对象 1014 ws['!merges'] = merges
1224 function getAllCells(pRef, type) { 1015
1225 const allCells = []; 1016 // 设置单元格样式
1226 let columns = [], allColsNum = 0; 1017 const range = XLSXS.utils.decode_range(ws['!ref'])
1227 if (type == 'cost') { 1018
1228 allColsNum = tableField.value.length; 1019 for (let R = range.s.r; R <= range.e.r; ++R) {
1229 tableField.value.map((tc, c) => { 1020 for (let C = range.s.c; C <= range.e.c; ++C) {
1230 if (tc.field.indexOf('level') == -1 || tc.field == 'memo') { 1021 const cellAddress = XLSXS.utils.encode_cell({ r: R, c: C })
1231 columns.push({ ...tc, cIndex: c }) 1022
1023 // 默认样式
1024 const defaultStyle = {
1025 alignment: {
1026 vertical: 'center',
1027 horizontal: 'left'
1028 }
1232 } 1029 }
1233 }); 1030
1234 } else { 1031 // 表头样式
1235 headers.value.map((th, h) => { 1032 if (R === 0) {
1236 columns.push({ ...th, cIndex: h + 1 }) 1033 ws[cellAddress].s = {
1237 }); 1034 ...defaultStyle,
1035 }
1036 continue
1037 }
1038
1039 // 合计行样式
1040 if (R === range.e.r) {
1041 ws[cellAddress].s = {
1042 ...defaultStyle,
1043 alignment: {
1044 vertical: 'center',
1045 horizontal: C > 3 ? 'right' : 'left'
1046 }
1047 }
1048 continue
1049 }
1050
1051 // 数据行样式
1052 const fieldIndex = C
1053 const field = tableField.value[fieldIndex]
1054
1055 // 数值列右对齐
1056 if (numericFields.includes(field.field)) {
1057 ws[cellAddress].s = {
1058 ...defaultStyle,
1059 alignment: {
1060 vertical: 'center',
1061 horizontal: 'right'
1062 }
1063 }
1064 } else {
1065 // 其他列左对齐
1066 ws[cellAddress].s = defaultStyle
1067 }
1068 }
1238 } 1069 }
1239 const colIndex = columns.map(c => c.cIndex); 1070
1240 // 获取所有行 1071 return ws;
1241 const rows = pRef.$el.querySelectorAll('.el-table__row'); 1072 };
1242 1073
1243 rows.forEach((rowElement, rowIndex) => { 1074 // 辅助函数:按属性分组
1244 const cells = rowElement.querySelectorAll('.el-table__cell'); 1075 const groupBy = (array, key) => {
1245 1076 return array.reduce((acc, obj) => {
1246 cells.forEach((cellElement, cellIndex) => { 1077 const groupKey = obj[key]
1247 const cell = { 1078 if (!acc[groupKey]) {
1248 rowIndex: (rowIndex + 1), 1079 acc[groupKey] = []
1249 cellIndex: cellIndex, 1080 }
1250 value: cellElement.innerText.trim(), 1081 acc[groupKey].push(obj)
1251 style: window.getComputedStyle(cellElement) 1082 return acc
1252 }; 1083 }, {})
1253 let format = 'General'; 1084 };
1254 if (type == 'entry') { 1085
1255 format = colIndex.indexOf(cellIndex) > -1 ? '#,##0.00' : ''; 1086 // 生成成本设置表
1087 const exportBookToExcel = async () => {
1088 // 准备工作表数据
1089 const wsData = []
1090
1091 // 表头
1092 const headers = bookHeaders.value.map(item => item.label)
1093 wsData.push(headers)
1094
1095 // 处理数据行
1096 transposedData.value.forEach(item => {
1097 const row = bookHeaders.value.map(f => {
1098 if (f.field.indexOf('title') == -1) {
1099 // 分类字段
1100 return changeNum(item[f.field])
1256 } else { 1101 } else {
1257 if (rowIndex == rows.length - 1) { 1102 // 其他字段
1258 format = cell.value != '合计' && cell.value ? '#,##0.00' : ''; 1103 return item[f.field] || ''
1259 } else {
1260 format = colIndex.indexOf(cellIndex + (allColsNum - cells.length)) > -1 ? '#,##0.00' : '';
1261 cell.cellIndex = cellIndex + (allColsNum - cells.length)
1262 }
1263 } 1104 }
1264 cell.format = format;
1265 allCells.push(cell);
1266 }) 1105 })
1106 wsData.push(row)
1267 }) 1107 })
1268 return allCells;
1269 }
1270 1108
1271 // 设置单元格样式 1109 // 创建工作表
1272 const applyStyles = (ws, pRef, type) => { 1110 const ws = XLSXS.utils.aoa_to_sheet(wsData)
1273 // 获取所有带有样式的单元格 1111
1274 const styledCells = getAllCells(pRef, type) 1112 // 自动调整列宽
1113 const colWidths = bookHeaders.value.map((field, index) => {
1114 const defaultWidth = field.width || 100 // 默认宽度100
1115 const colWidth = defaultWidth / 14 + 2; // 留一些余地
1116
1117 // 取表头宽度和内容宽度的较大值
1118 return { wpx: colWidth * 12 } // 使用像素宽度
1119 })
1120
1121 ws['!cols'] = colWidths
1275 1122
1276 // 设置单元格样式 1123 // 设置单元格样式
1277 styledCells.forEach((cell) => { 1124 const range = XLSXS.utils.decode_range(ws['!ref'])
1278 const cellRef = XLSXS.utils.encode_cell({ r: cell.rowIndex, c: cell.cellIndex }); 1125 // 数值字段(除title外的所有字段)
1279 const style = cell.style; 1126 const numericFields = bookHeaders.value.slice(1).map(f => f.field);
1280
1281 // 设置单元格样式
1282 const cellStyle = {
1283 alignment: {
1284 horizontal: cell.format ? 'right' : style.textAlign,
1285 vertical: 'center', // 垂直居中
1286 },
1287 };
1288 1127
1289 ws[cellRef] = ws[cellRef] || {}; 1128 for (let R = range.s.r; R <= range.e.r; ++R) {
1290 ws[cellRef].s = cellStyle; 1129 for (let C = range.s.c; C <= range.e.c; ++C) {
1291 if (cell.format) { 1130 const cellAddress = XLSXS.utils.encode_cell({ r: R, c: C })
1292 ws[cellRef].z = cell.format;
1293 } else {
1294 ws[cellRef].t = 's';
1295 ws[cellRef].v = cell.value;
1296 }
1297 });
1298 // 合并单元格
1299 const mergedCells = document.querySelectorAll('.merged-cell');
1300 mergedCells.forEach((cell) => {
1301 // 设置合并单元格的样式
1302 const mergedCellStyle = {
1303 alignment: {
1304 vertical: 'center', // 垂直居中
1305 },
1306 };
1307 1131
1308 const topLeftCellRef = XLSXS.utils.encode_cell({ r: cell.rowIndex, c: cell.cellIndex }); 1132 // 确保单元格存在(有些空值单元格可能不存在)
1309 ws[topLeftCellRef] = ws[topLeftCellRef] || {}; 1133 if (!ws[cellAddress]) {
1310 ws[topLeftCellRef].s = mergedCellStyle; 1134 ws[cellAddress] = {};
1311 }); 1135 }
1312 }
1313 1136
1314 // 创建一个隐藏的 div 用于测量文本宽度 1137 // 默认样式(垂直居中)
1315 const measureDiv = document.createElement('div'); 1138 const defaultStyle = {
1316 measureDiv.style.position = 'absolute'; 1139 alignment: {
1317 measureDiv.style.visibility = 'hidden'; 1140 vertical: 'center',
1318 measureDiv.style.whiteSpace = 'nowrap'; 1141 horizontal: 'left'
1319 document.body.appendChild(measureDiv); 1142 }
1320 1143 }
1321 // 测量文本宽度的函数
1322 function measureTextWidth(text, fontSize, fontFamily) {
1323 measureDiv.style.fontSize = `${fontSize}px`;
1324 measureDiv.style.fontFamily = fontFamily;
1325 measureDiv.textContent = text;
1326 return measureDiv.offsetWidth;
1327 }
1328 1144
1329 const adjustColumnWidth = (ws) => { 1145 // 表头样式(第1行)
1330 const range = XLSXS.utils.decode_range(ws['!ref']); 1146 if (R === 0) {
1331 const defaultFontSize = 11; // 默认字体大小 1147 ws[cellAddress].s = {
1332 const defaultFontFamily = 'Arial'; // 默认字体族 1148 ...defaultStyle,
1333 1149 // font: { bold: true } // 表头加粗
1334 for (let C = range.s.c; C <= range.e.c; ++C) { 1150 }
1335 let maxWidth = 0; 1151 continue
1336 for (let R = range.s.r; R <= range.e.r; ++R) { 1152 }
1337 const cellRef = XLSXS.utils.encode_cell({ r: R, c: C }); 1153
1338 const cellValue = ws[cellRef]?.v?.toString() || ''; 1154 // 获取当前列对应的字段
1339 const width = measureTextWidth(cellValue, defaultFontSize, defaultFontFamily); 1155 const field = bookHeaders.value[C];
1340 maxWidth = Math.max(maxWidth, width); 1156
1341 } 1157 // 判断是否为数值列(包括最后一行)
1158 const isNumeric = numericFields.includes(field?.field);
1159 const cellValue = ws[cellAddress].v;
1342 1160
1343 // 根据测量的宽度计算列宽 1161 // 数值列特殊处理(无论是否最后一行)
1344 const colWidth = maxWidth / defaultFontSize + 2; // 留一些余地 1162 if (isNumeric) {
1345 ws['!cols'] = ws['!cols'] || []; 1163 ws[cellAddress].s = {
1346 ws['!cols'][C] = { wpx: colWidth * defaultFontSize }; // 使用像素宽度 1164 alignment: {
1165 vertical: 'center',
1166 horizontal: 'right'
1167 }
1168 };
1169
1170 // 确保数值被正确识别为数字类型
1171 if (cellValue !== undefined && cellValue !== "" && !isNaN(cellValue)) {
1172 ws[cellAddress].t = 'n'; // 数字类型
1173 ws[cellAddress].z = '#,##0.00'; // 数字格式
1174 }
1175 } else {
1176 // 文本列保持默认样式
1177 ws[cellAddress].s = defaultStyle;
1178 }
1179 }
1347 } 1180 }
1181
1182 return ws;
1348 }; 1183 };
1349 1184
1350 const exportClick = () => { 1185 const exportClick = async () => {
1351 // 获取第一个表格的数据 1186 // 获取第一个表格的数据
1352 const table1 = document.getElementById('cost-table'); 1187 const ws1 = await exportDetailsToExcel();
1353 const ws1 = XLSXS.utils.table_to_sheet(table1);
1354 1188
1355 // 获取第二个表格的数据 1189 // 获取第二个表格的数据
1356 const table2 = document.getElementById('entry-table'); 1190 const ws2 = await exportBookToExcel();
1357 const ws2 = XLSXS.utils.table_to_sheet(table2);
1358
1359 // 调整列宽
1360 adjustColumnWidth(ws1);
1361 adjustColumnWidth(ws2);
1362
1363 // 添加样式
1364 applyStyles(ws1, costTableRef.value, 'cost');
1365 applyStyles(ws2, entryTableRef.value, 'entry');
1366 1191
1367 // 创建工作簿并添加两个工作表 1192 // 创建工作簿并添加两个工作表
1368 const wb = XLSXS.utils.book_new(); 1193 const wb = XLSXS.utils.book_new();
...@@ -1409,35 +1234,30 @@ const setSumRow = () => { ...@@ -1409,35 +1234,30 @@ const setSumRow = () => {
1409 } 1234 }
1410 1235
1411 onActivated(() => { 1236 onActivated(() => {
1237 tableField.value = JSON.parse(JSON.stringify(initField.value));
1412 getProducts(); 1238 getProducts();
1413 getCostData(); 1239 getCostData();
1414 }); 1240 });
1415 1241
1416 1242
1417 onMounted(() => { 1243 onMounted(() => {
1418 setItemLine(); 1244 // 添加/移除全局点击监听
1419 window.addEventListener('resize', setItemLine); 1245 document.addEventListener('click', handleClickOutside)
1420 }); 1246 })
1421 1247
1422 onUpdated(() => { 1248 onBeforeUnmount(() => {
1423 setItemLine(); 1249 document.removeEventListener('click', handleClickOutside)
1424 }); 1250 })
1425 1251
1426 onUnmounted(() => { 1252 onUpdated(() => {
1427 window.removeEventListener('resize', setItemLine); 1253 // setItemLine();
1428 }); 1254 });
1429 1255
1430 watch(showLevel4, (newVal, oldVal) => {
1431 setItemLine();
1432 })
1433 </script> 1256 </script>
1434 1257
1435 <template> 1258 <template>
1436 <div class="container_wrap"> 1259 <div class="container_wrap">
1437 <div class="content_main"> 1260 <div class="content_main">
1438 <!-- <div class="top_tool_wrap">
1439 <StepBar :steps-info="stepsInfo" />
1440 </div> -->
1441 <div class="operator_panel_wrap" :style="{ 'min-height': 'unset' }" v-loading="loading"> 1261 <div class="operator_panel_wrap" :style="{ 'min-height': 'unset' }" v-loading="loading">
1442 <div class="v-tip" v-show="step == 0"> 1262 <div class="v-tip" v-show="step == 0">
1443 <div class="tip-icon"></div> 1263 <div class="tip-icon"></div>
...@@ -1455,152 +1275,71 @@ watch(showLevel4, (newVal, oldVal) => { ...@@ -1455,152 +1275,71 @@ watch(showLevel4, (newVal, oldVal) => {
1455 <!-- <el-button type="primary" plain @click="senMessage">入表咨询</el-button> --> 1275 <!-- <el-button type="primary" plain @click="senMessage">入表咨询</el-button> -->
1456 </div> 1276 </div>
1457 </div> 1277 </div>
1458 <div class="grid-box-wrap" v-show="step == 3">
1459 <!-- 头部标题 -->
1460 <div class="grid-panel-wrap header" :class="{ col4: showLevel4 }">
1461 <template v-for="(list, l) in gridList" :key="'panel'+l">
1462 <div class="panel-header" v-if="l < 3 || showLevel4">{{ list.name }}</div>
1463 </template>
1464 </div>
1465 <div class="grid-panel-wrap" :class="{ col4: showLevel4 }" v-for="(item, i) in allMapList"
1466 :key="'panel-' + i">
1467 <!-- 一级指标 -->
1468 <div class="grid-panel-box">
1469 <div class="grid-panel" ref="gridPanel">
1470 <div class="grid-items after" ref="gridItem" :class="getClass(i, item.checked)">
1471 <el-checkbox v-model="item.checked" @change="val => checkboxChange(val, { item }, 1)" />
1472 <span class="item-label">{{ item.name }}</span>
1473 <span class="circle"></span>
1474 </div>
1475 </div>
1476 </div>
1477 <!-- 一级分类 -->
1478 <div class="grid-panel-box box1">
1479 <template v-for="(grid, g) in item.children" :key="'grid' + i + '-' + g">
1480 <div class="grid-panel" :class="{ before: g < item.children.length - 1, left8: showLevel4 }">
1481 <div class="grid-items before after" :class="getClass(i, grid.checked)">
1482 <el-checkbox v-model="grid.checked" @change="val => checkboxChange(val, { item, grid }, 2)" />
1483 <span class="item-label">{{ grid.name }}</span>
1484 <span class="circle"></span>
1485 </div>
1486 </div>
1487 </template>
1488 </div>
1489 <!-- 二级分类 -->
1490 <div class="grid-panel-box box2 before" :class="{ after: showLevel4 }">
1491 <template v-for="(grid, g) in item.children" :key="'grid' + i + '-' + g">
1492 <div class="grid-panel height_auto" :class="{ left8: showLevel4 }" v-if="grid.children.length">
1493 <div class="grid-items before"
1494 :class="[getClass(i, child.checked), { after: child.children.length }]"
1495 v-for="(child, c) in grid.children" :key="'item' + i + '-' + g + '-' + c"
1496 :style="setItemStyle(child, null)">
1497 <el-checkbox v-model="child.checked"
1498 @change="val => checkboxChange(val, { item, grid, child }, 3)" />
1499 <span class="item-label">{{ child.name }}</span>
1500 <div class="item-tool">
1501 <el-popover placement="bottom-end" :width="108" trigger="click" popper-class="popover_btns">
1502 <template #reference>
1503 <el-icon>
1504 <MoreFilled />
1505 </el-icon>
1506 </template>
1507 <template #default>
1508 <div class="tool-btns">
1509 <div @click="btnClick({ value: 'add-same', row: child, parent: grid })">新增本级</div>
1510 <div @click="btnClick({ value: 'add-lower', row: child, parent: grid })">新增下级</div>
1511 <div @click="btnClick({ value: 'edit', row: child, parent: grid })">重命名</div>
1512 <div v-if="grid.children.length > 1"
1513 @click="btnClick({ value: 'remove', row: child, parent: grid })">删除</div>
1514 </div>
1515 </template>
1516 </el-popover>
1517 </div>
1518 <span class="circle" :style="{ height: spanHeight(child) }"></span>
1519 </div>
1520 </div>
1521 </template>
1522 </div>
1523 <!-- 三级分类 -->
1524 <div class="grid-panel-box box3" v-if="showLevel4">
1525 <div class="grid-panel no_padding" v-if="showPanel(item.children, item)">
1526 <template v-for="(grid, g) in item.children" :key="'grid' + i + '-' + g">
1527 <div class="grid-group">
1528 <template v-for="(child, c) in grid.children" :key="'item' + i + '-' + g + '-' + c">
1529 <div class="group-items before">
1530 <div class="grid-items before" :class="getClass(i, opt.checked)"
1531 v-for="(opt, o) in child.children" :key="'pis' + i + '-' + g + '-' + c + '-' + o"
1532 :style="setItemStyle(opt, { o })">
1533 <el-checkbox v-model="opt.checked"
1534 @change="val => checkboxChange(val, { item, grid, child, opt }, 4)" />
1535 <span class="item-label">{{ opt.name }}</span>
1536 <div class="item-tool">
1537 <el-popover placement="bottom-end" :width="108" trigger="click"
1538 popper-class="popover_btns">
1539 <template #reference>
1540 <el-icon>
1541 <MoreFilled />
1542 </el-icon>
1543 </template>
1544 <template #default>
1545 <div class="tool-btns">
1546 <div @click="btnClick({ value: 'edit', row: opt, parent: child })">重命名</div>
1547 <div @click="btnClick({ value: 'remove', row: opt, parent: child })">删除</div>
1548 </div>
1549 </template>
1550 </el-popover>
1551 </div>
1552 </div>
1553 </div>
1554 </template>
1555 </div>
1556 </template>
1557 </div>
1558 </div>
1559 </div>
1560 </div>
1561 <div class="table_panel_wrap"> 1278 <div class="table_panel_wrap">
1562 <div class="amount_tool"> 1279 <div class="amount_tool" v-if="besure">
1563 <span class="amount_text">累计投入:</span> 1280 <span class="amount_text">累计投入:</span>
1564 <span class="amount_num">{{ changeNum(amount, 2, true) }}</span> 1281 <span class="amount_num">{{ changeNum(amount, 2, true) }}</span>
1565 <span></span> 1282 <span></span>
1566 </div> 1283 </div>
1567 <div class="table_panel"> 1284 <div class="table_panel">
1568 <el-table id="cost-table" v-show="step == 0" ref="costTableRef" :data="tableData" 1285 <el-table id="cost-table" v-show="step == 0" ref="costTableRef" :data="tableData"
1569 :span-method="tableSpanMethod" :summary-method="tableSummaryMethod" show-summary border 1286 :span-method="tableSpanMethod" :show-summary="besure" :summary-method="tableSummaryMethod"
1570 :cell-class-name="isMergedCell"> 1287 :cell-class-name="isMergedCell" border>
1571 <el-table-column v-for="(item, i) in tableField" :label="item.label" :width="item.width" 1288 <template v-for="(item, i) in tableField" :key="'tField' + i">
1572 :min-width="item.minWidth" :fixed="item.fixed" :align="item.align" :sortable="item.sortable ?? false" 1289 <el-table-column v-if="item.visible ?? true" :label="item.label" :width="item.width"
1573 :prop="item.field" :class-name="item.columClass" show-overflow-tooltip> 1290 :min-width="item.minWidth" :fixed="item.fixed" :align="item.align"
1574 <template #default="scope"> 1291 :sortable="item.sortable ?? false" :prop="item.field" :class-name="item.columClass"
1575 <div class="input_cell" v-if="item.type == 'input'"> 1292 show-overflow-tooltip>
1576 <el-input v-model.trim="scope.row[item.field]" placeholder="请输入" :maxlength="item.maxlength ?? ''" 1293 <template #default="scope">
1577 @change="(val) => inputChange(val, scope, item.field)" 1294 <div class="input_cell" v-if="item.type == 'input'">
1578 @input="(val) => inputEventChange(val, scope, item.field)" clearable></el-input> 1295 <el-input v-model.trim="scope.row[item.field]" placeholder="请输入"
1579 <span>{{ scope.row[item.field] ? changeNum(scope.row[item.field], 2, true) : 1296 :maxlength="item.maxlength ?? ''" @change="(val) => inputChange(val, scope, item.field)"
1580 Number(scope.row[item.field]) == 0 ? '0.00' : '' }}</span> 1297 @input="(val) => inputEventChange(val, scope, item.field)"
1581 </div> 1298 :disabled="scope.row.edit ? false : true" clearable></el-input>
1582 <span v-else> 1299 <span>{{ scope.row[item.field] ? changeNum(scope.row[item.field], 2, true) :
1583 {{ item.getName ? item.getName(scope) : scope.row[item.field] !== 0 && !scope.row[item.field] ? 1300 Number(scope.row[item.field]) == 0 ? '0.00' : '' }}</span>
1584 "--" : scope.row[item.field] }} 1301 </div>
1585 </span> 1302 <div class="cell-tool" v-else>
1586 </template> 1303 <span class="cell_text_group">
1587 </el-table-column> 1304 <el-checkbox v-if="item.field != 'total' && scope.row[item.field]"
1305 v-model="scope.row['checked' + item.level]"
1306 @change="val => cellCheckboxChange(val, { ...item, ...scope.row, rIndex: scope.$index })" />
1307 <span class="cell_text">
1308 {{ item.getName ? item.getName(scope) : scope.row[item.field] !== 0 &&
1309 !scope.row[item.field]
1310 ?
1311 "--" : scope.row[item.field] }}
1312 </span>
1313 </span>
1314 <el-icon v-if="item.field == 'name3' || (item.field == 'name4' && scope.row[item.field])"
1315 class="list-more" color="#666"
1316 @click="e => togglePopover({ ...item, ...scope.row, rIndex: scope.$index }, e)">
1317 <MoreFilled />
1318 </el-icon>
1319 </div>
1320 </template>
1321 </el-table-column>
1322 </template>
1588 </el-table> 1323 </el-table>
1589 <el-table id="entry-table" ref="entryTableRef" :data="transposedData" stripe border v-show="step == 1"> 1324 <el-table id="entry-table" ref="entryTableRef" :data="transposedData" stripe border v-show="step == 1">
1590 <el-table-column prop="title" label="项目" :width="140"> 1325 <!-- <el-table-column prop="title" label="项目" :width="140">
1591 <template v-slot="{ row }"> 1326 <template v-slot="{ row }">
1592 <span>{{ setLabel(row.title) }}</span> 1327 <span>{{ setLabel(row.title) }}</span>
1593 </template> 1328 </template>
1594 </el-table-column> 1329 </el-table-column> -->
1595 <el-table-column v-for="(item, index) in headers" :key="index" :prop="item.value" :label="item.label" 1330 <el-table-column v-for="(item, index) in bookHeaders" :key="index" :prop="item.field" :label="item.label"
1596 :width="120" align="right"> 1331 :width="item.width" :align="item.align">
1597 <template v-slot:header> 1332 <template #default="scope">
1333 <span v-if="item.field == 'title'">{{ setLabel(scope.row[item.field]) }}</span>
1334 <span v-else>{{ changeNum(scope.row[item.field], 2) }}</span>
1335 </template>
1336 <!-- <template v-slot:header>
1598 <span v-html="item.label"></span> 1337 <span v-html="item.label"></span>
1599 </template> 1338 </template>
1600 <template v-slot="{ row }"> 1339 <template v-slot="{ row }">
1601 <span v-if="typeof row[item.value] == 'number'">{{ changeNum(row[item.value], 2, true) }}</span> 1340 <span v-if="typeof row[item.field] == 'number'">{{ changeNum(row[item.field], 2, true) }}</span>
1602 <span v-else></span> 1341 <span v-else></span>
1603 </template> 1342 </template> -->
1604 </el-table-column> 1343 </el-table-column>
1605 </el-table> 1344 </el-table>
1606 </div> 1345 </div>
...@@ -1615,13 +1354,21 @@ watch(showLevel4, (newVal, oldVal) => { ...@@ -1615,13 +1354,21 @@ watch(showLevel4, (newVal, oldVal) => {
1615 <div class="tool_btns"> 1354 <div class="tool_btns">
1616 <div class="btns"> 1355 <div class="btns">
1617 <el-button @click="btnClick({ value: 'refresh' })">重置</el-button> 1356 <el-button @click="btnClick({ value: 'refresh' })">重置</el-button>
1618 <el-button @click="btnClick({ value: 'next' })" v-if="step == 0">入表</el-button> 1357 <el-button type="primary" @click="btnClick({ value: 'next' })" v-if="step == 0">入表</el-button>
1619 <!-- <el-button @click="btnClick({ value: 'prev' })" v-if="step == 1">上一步</el-button> 1358 <el-button type="primary" @click="btnClick({ value: 'prev' })" v-if="step == 1">上一步</el-button>
1620 <el-button type="primary" @click="btnClick({ value: 'prev' })" v-if="step == 2">上一步</el-button>
1621 <el-button type="primary" @click="btnClick({ value: 'next' })" v-if="step < 2">下一步</el-button> -->
1622 </div> 1359 </div>
1623 </div> 1360 </div>
1624 </div> 1361 </div>
1362 <el-popover :visible="popoverVisible" placement="bottom-start" width="110" trigger="click"
1363 popper-class="tree-item-edit-menu" :virtual-ref="popoverTriggerRef" virtual-triggering :hide-after="0" :offset="8"
1364 @after-leave="handlePopoverClose">
1365 <div class="levitation-ul" @mousedown.stop>
1366 <span class="levitation-li" @click="handleMenuClick('add-same')" v-show="showAdd">新增本级分类</span>
1367 <span class="levitation-li" @click="handleMenuClick('add-lower')" v-show="showAdd">新增下级分类</span>
1368 <span class="levitation-li" @click="handleMenuClick('edit')">重命名</span>
1369 <span class="levitation-li" @click="handleMenuClick('remove')">删除</span>
1370 </div>
1371 </el-popover>
1625 </template> 1372 </template>
1626 1373
1627 <style lang="scss" scoped> 1374 <style lang="scss" scoped>
...@@ -1968,6 +1715,25 @@ watch(showLevel4, (newVal, oldVal) => { ...@@ -1968,6 +1715,25 @@ watch(showLevel4, (newVal, oldVal) => {
1968 1715
1969 :deep(.el-table) { 1716 :deep(.el-table) {
1970 td.el-table__cell { 1717 td.el-table__cell {
1718 .cell-tool {
1719 display: flex;
1720 justify-content: space-between;
1721 align-items: center;
1722
1723 .cell_text_group {
1724 display: flex;
1725
1726 .cell_text {
1727 margin-left: 4px;
1728 line-height: 32px;
1729 }
1730 }
1731
1732 .list-more {
1733 cursor: pointer;
1734 }
1735 }
1736
1971 &.edit_cell { 1737 &.edit_cell {
1972 padding: 4px 0; 1738 padding: 4px 0;
1973 1739
...@@ -1985,6 +1751,13 @@ watch(showLevel4, (newVal, oldVal) => { ...@@ -1985,6 +1751,13 @@ watch(showLevel4, (newVal, oldVal) => {
1985 } 1751 }
1986 } 1752 }
1987 } 1753 }
1754
1755 &.inverse_cell {
1756 .cell_text_group {
1757 display: block;
1758 width: 100%;
1759 }
1760 }
1988 } 1761 }
1989 1762
1990 .el-table__footer-wrapper tr td { 1763 .el-table__footer-wrapper tr td {
...@@ -1993,4 +1766,10 @@ watch(showLevel4, (newVal, oldVal) => { ...@@ -1993,4 +1766,10 @@ watch(showLevel4, (newVal, oldVal) => {
1993 } 1766 }
1994 } 1767 }
1995 } 1768 }
1769
1770 :deep(.el-popper) {
1771 &.tree-item-edit-menu {
1772 transform: translateX(-10px);
1773 }
1774 }
1996 </style> 1775 </style>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!