2de12ec4 by lxs

入表功调整

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