8fbb6b15 by lihua

fix: 估值模型

1 parent c6a967a9
...@@ -586,11 +586,11 @@ const visibleChange = (visible, row) => { ...@@ -586,11 +586,11 @@ const visibleChange = (visible, row) => {
586 } 586 }
587 587
588 const panelChange = (scope, row) => { 588 const panelChange = (scope, row) => {
589 if (row.field == 'baseDate' || row.field == 'investYear') { 589 if (row.field == 'evaluateBaseDate' || row.field == 'baseDate' || row.field == 'investYear') {
590 const date = new Date(); 590 const date = new Date();
591 const year = date.getFullYear(); 591 const year = date.getFullYear();
592 const datePopperNextBtn = document.querySelector('.date-month-popper .d-arrow-right'); 592 const datePopperNextBtn = document.querySelector('.date-month-popper .d-arrow-right');
593 if (row.field == 'baseDate') { 593 if (row.field == 'baseDate' || row.field == 'evaluateBaseDate') {
594 const currentYear = scope.date.getFullYear(); 594 const currentYear = scope.date.getFullYear();
595 if (currentYear >= year) { 595 if (currentYear >= year) {
596 datePopperNextBtn.setAttribute("disabled", ''); 596 datePopperNextBtn.setAttribute("disabled", '');
...@@ -1463,8 +1463,8 @@ const panelChange = (scope, row) => { ...@@ -1463,8 +1463,8 @@ const panelChange = (scope, row) => {
1463 @panel-change="(date, mode) => panelChange({ date, mode }, item)" 1463 @panel-change="(date, mode) => panelChange({ date, mode }, item)"
1464 @visible-change="(vis) => visibleChange(vis, item)" /> 1464 @visible-change="(vis) => visibleChange(vis, item)" />
1465 <el-date-picker :class="[item.col, { is_block: item.block }]" v-else-if="item.type == 'date-month'" 1465 <el-date-picker :class="[item.col, { is_block: item.block }]" v-else-if="item.type == 'date-month'"
1466 v-model="formInline[item.field]" type="month" format="YYYY-MM" value-format="YYYY-MM" 1466 v-model="formInline[item.field]" type="month" :format="item.format ?? 'YYYY-MM'" :value-format="item.valueFormat ?? 'YYYY-MM'"
1467 :placeholder="item.placeholder" :unlink-panels="item.unlink ?? false" :shortcuts="item.shortcuts ?? []" 1467 :placeholder="item.placeholder" :unlink-panels="item.unlink ?? false" :shortcuts="item.shortcuts ?? []" :clearable="item.clearable ?? true"
1468 :default-value="item.defaultDate" :disabled="item.disabled ?? false" :disabled-date="item.disabledDate" 1468 :default-value="item.defaultDate" :disabled="item.disabled ?? false" :disabled-date="item.disabledDate"
1469 :popper-class="item.popperClass" @change="(val) => inputChange(val, item)" 1469 :popper-class="item.popperClass" @change="(val) => inputChange(val, item)"
1470 @panel-change="(date, mode) => panelChange({ date, mode }, item)" 1470 @panel-change="(date, mode) => panelChange({ date, mode }, item)"
......
...@@ -22,6 +22,17 @@ const route = useRoute(); ...@@ -22,6 +22,17 @@ const route = useRoute();
22 const fullPath = route.fullPath; 22 const fullPath = route.fullPath;
23 const fullscreenLoading = ref(false); 23 const fullscreenLoading = ref(false);
24 24
25 /** 获取当月的最后一天。 */
26 const getLastDayOfMonth = (month) => {
27 const year = parseInt(month.split('-')[0], 10);
28 const monthIndex = parseInt(month.split('-')[1], 10) - 1; // JavaScript 的月份是从0开始计数的
29 const date = new Date(year, monthIndex + 1, 0); // 使用0可以得到前一个月的最后一天
30 const yearString = date.getFullYear();
31 const monthString = String(date.getMonth() + 1).padStart(2, '0'); // JavaScript 的月份是从0开始计数的
32 const dayString = String(date.getDate()).padStart(2, '0');
33 return `${yearString}-${monthString}-${dayString}`;
34 }
35
25 /** 数据产品列表 */ 36 /** 数据产品列表 */
26 const damProductList: any = ref([]); 37 const damProductList: any = ref([]);
27 38
...@@ -47,9 +58,11 @@ const valuateFormItems: any = ref([ ...@@ -47,9 +58,11 @@ const valuateFormItems: any = ref([
47 label: "基准日", 58 label: "基准日",
48 type: "date-month", 59 type: "date-month",
49 field: "evaluateBaseDate", 60 field: "evaluateBaseDate",
50 default: moment(new Date()).format('YYYY-MM'), 61 default: getLastDayOfMonth(moment(new Date()).format('YYYY-MM')),
51 placeholder: "请选择", 62 placeholder: "请选择",
52 clearable: true, 63 format: 'YYYY-MM-DD',
64 valueFormat: 'YYYY-MM-DD',
65 clearable: false,
53 required: true, 66 required: true,
54 style: { width: 'calc(33.33% - 70px)', 'margin-right': '8px' }, 67 style: { width: 'calc(33.33% - 70px)', 'margin-right': '8px' },
55 popperClass: 'date-month-popper', 68 popperClass: 'date-month-popper',
...@@ -115,7 +128,7 @@ const valuateFormItems: any = ref([ ...@@ -115,7 +128,7 @@ const valuateFormItems: any = ref([
115 ]); 128 ]);
116 129
117 const valuateFormRules = ref({ 130 const valuateFormRules = ref({
118 // damGuid: [required('请选择数据产品名称')], 131 damGuid: [required('请选择数据产品名称')],
119 evaluateBaseDate: [required('请选择基准日')], 132 evaluateBaseDate: [required('请选择基准日')],
120 evaluateMethod: [required('请选择评估方法')], 133 evaluateMethod: [required('请选择评估方法')],
121 useYears: [{ type: 'number', min: 1, max: 10, message: "请填写年限1~10", trigger: "change", },] 134 useYears: [{ type: 'number', min: 1, max: 10, message: "请填写年限1~10", trigger: "change", },]
...@@ -126,22 +139,24 @@ const handleValudateFormChange = (val, row, info) => { ...@@ -126,22 +139,24 @@ const handleValudateFormChange = (val, row, info) => {
126 valuateFormItems.value.forEach(item => { 139 valuateFormItems.value.forEach(item => {
127 item.default = info[item.field]; 140 item.default = info[item.field];
128 if (item.field == 'useYears') { 141 if (item.field == 'useYears') {
129 item.visible = item.default != '1'; 142 item.visible = val != '1';
130 item.default = info.useYears ? info.useYears : 1; 143 item.default = info.useYears ? info.useYears : 1;
131 } 144 }
132 }) 145 })
133 } 146 }
134 } 147 }
135 148
136 /** 获取当月的最后一天。 */ 149 const handleInputChange = (val, row, info) => {
137 const getLastDayOfMonth = (month) => { 150 if (row.field == 'evaluateBaseDate') {
138 const year = parseInt(month.split('-')[0], 10); 151 valuateFormItems.value.forEach(item => {
139 const monthIndex = parseInt(month.split('-')[1], 10) - 1; // JavaScript 的月份是从0开始计数的 152 item.default = info[item.field];
140 const date = new Date(year, monthIndex + 1, 0); // 使用0可以得到前一个月的最后一天 153 if (item.field == 'useYears') {
141 const yearString = date.getFullYear(); 154 item.visible = info.evaluateMethod != '1';
142 const monthString = String(date.getMonth() + 1).padStart(2, '0'); // JavaScript 的月份是从0开始计数的 155 } else if (item.field == 'evaluateBaseDate') {
143 const dayString = String(date.getDate()).padStart(2, '0'); 156 item.default = getLastDayOfMonth(val);
144 return `${yearString}-${monthString}-${dayString}`; 157 }
158 })
159 }
145 } 160 }
146 161
147 const costTableField: any = ref([ 162 const costTableField: any = ref([
...@@ -433,10 +448,7 @@ const incomeYears = computed(() => { ...@@ -433,10 +448,7 @@ const incomeYears = computed(() => {
433 let year = parseInt(infos[0]); 448 let year = parseInt(infos[0]);
434 let month = parseInt(infos[1]); 449 let month = parseInt(infos[1]);
435 if (month == 12) { 450 if (month == 12) {
436 let a = [{ 451 let a: any = [];
437 field: year + '',
438 label: year + '年'
439 }];
440 for (var i = 1; i < useYears + 1; i++) { 452 for (var i = 1; i < useYears + 1; i++) {
441 a.push({ 453 a.push({
442 field: year + i + '', 454 field: year + i + '',
...@@ -447,7 +459,7 @@ const incomeYears = computed(() => { ...@@ -447,7 +459,7 @@ const incomeYears = computed(() => {
447 } else if (month == 1) { 459 } else if (month == 1) {
448 let a = [{ 460 let a = [{
449 field: evaluateBaseDate + '', 461 field: evaluateBaseDate + '',
450 label: year + '年' + `(1)` 462 label: year + '年' + `(2~12)`
451 }]; 463 }];
452 for (var i = 1; i < useYears + 1; i++) { 464 for (var i = 1; i < useYears + 1; i++) {
453 a.push({ 465 a.push({
...@@ -459,12 +471,12 @@ const incomeYears = computed(() => { ...@@ -459,12 +471,12 @@ const incomeYears = computed(() => {
459 } else { 471 } else {
460 let a = [{ 472 let a = [{
461 field: evaluateBaseDate + '', 473 field: evaluateBaseDate + '',
462 label: year + '年' + `(1~${month})` 474 label: year + '年' + `(${month + 1}~12)`
463 }]; 475 }];
464 for (var i = 1; i < useYears + 1; i++) { 476 for (var i = 1; i < useYears + 1; i++) {
465 a.push({ 477 a.push({
466 field: year + i + '', 478 field: year + i + '',
467 label: i == useYears ? ((year + i) + '年' + `(${month}~12)`) : ((year + i) + '年') 479 label: i == useYears ? ((year + i) + '年' + `(1~${month})`) : ((year + i) + '年')
468 }); 480 });
469 } 481 }
470 return a; 482 return a;
...@@ -560,11 +572,16 @@ const inputChange = (val, scope, field) => { ...@@ -560,11 +572,16 @@ const inputChange = (val, scope, field) => {
560 } else { 572 } else {
561 row[field] = val = parseFloat(val || 0).toFixed(2); 573 row[field] = val = parseFloat(val || 0).toFixed(2);
562 } 574 }
575 if (val == 'NaN') {
576 row[field] = '0.00';
577 }
563 } 578 }
564 579
565 /** 输入框输入触发事件 */ 580 /** 输入框输入触发事件 */
566 const inputEventChange = (val, scope, field, max: any = null) => { 581 const inputEventChange = (val, scope, field, max: any = null) => {
567 let row = scope.row; 582 let row = scope.row;
583 if (row.indexName == '数据资产分成率' || row.indexName == '衰减率' || formRef.value.formInline?.evaluateMethod == '1') { //只能输入正数
584 let row = scope.row;
568 row[field] = row[field].toString().replace(/[^\d.]/g, "") 585 row[field] = row[field].toString().replace(/[^\d.]/g, "")
569 row[field] = row[field].toString().replace(/\.{2,}/g, ".") 586 row[field] = row[field].toString().replace(/\.{2,}/g, ".")
570 row[field] = row[field].toString().replace(".", "$#$").replace(/\./g, "").replace("$#$", ".") 587 row[field] = row[field].toString().replace(".", "$#$").replace(/\./g, "").replace("$#$", ".")
...@@ -573,6 +590,45 @@ const inputEventChange = (val, scope, field, max: any = null) => { ...@@ -573,6 +590,45 @@ const inputEventChange = (val, scope, field, max: any = null) => {
573 if (max !== null && row[field] > max) { 590 if (max !== null && row[field] > max) {
574 row[field] = max; 591 row[field] = max;
575 } 592 }
593 } else { //需要支持负数
594 row[field] = row[field].toString().replace(/[^\d.-]/g, "")
595 row[field] = row[field].toString().replace(/\.{2,}/g, ".")
596 // 第四步:移除整数部分前面不必要的零,但保留小数部分的有效零
597 if (row[field].startsWith('-')) {
598 // 对于负数,去掉负号进行处理
599 let tempValue = row[field].substring(1);
600 tempValue = tempValue.replace(/-/g, '');;
601 row[field] = '-' + tempValue;
602 } else {
603 // 对于正数直接处理
604 row[field] = row[field].replace(/-/g, '');
605 }
606 row[field] = row[field].toString().replace(/^(-?\d{0,12})(\.\d{0,2})?$/, '$1$2');
607
608 let parts = row[field].split('.');
609 let integerPart = parts[0];
610 let decimalPart = parts.length > 1 ? parts[1] : '';
611
612 // 限制整数部分最多12位
613 if (integerPart.length > 12) {
614 integerPart = integerPart.substring(0, 12);
615 }
616
617 // 限制小数部分最多2位
618 if (decimalPart.length > 2) {
619 decimalPart = decimalPart.substring(0, 2);
620 }
621 row[field] = integerPart + (decimalPart ? '.' + decimalPart : (row[field].includes('.') ? '.' : ''))
622
623 if (max !== null) {
624 if (row[field] > max) {
625 row[field] = max;
626 }
627 if (row[field] < 0 && row[field] < -max) {
628 row[field] = -max;
629 }
630 }
631 }
576 } 632 }
577 633
578 const incomeCalculateData = computed(() => { //响应式不生效 634 const incomeCalculateData = computed(() => { //响应式不生效
...@@ -584,30 +640,42 @@ const incomeCalculateData = computed(() => { //响应式不生效 ...@@ -584,30 +640,42 @@ const incomeCalculateData = computed(() => { //响应式不生效
584 resultInfo['折现因子'] = []; 640 resultInfo['折现因子'] = [];
585 resultInfo['折现现值'] = []; 641 resultInfo['折现现值'] = [];
586 resultInfo['数据资产估值'] = 0; 642 resultInfo['数据资产估值'] = 0;
643 let formInline = formRef.value.formInline;
644 let evaluateBaseDate = formInline.evaluateBaseDate;
645 let infos = evaluateBaseDate.split('-');
646 let month = parseInt(infos[1]);
587 let transfer = (v, need = true) => { 647 let transfer = (v, need = true) => {
588 return v ? (need ? parseFloat(v) / 100 : parseFloat(v)) : 0; 648 return v ? (need ? parseFloat(v) / 100 : parseFloat(v)) : 0;
589 } 649 }
590 incomeYears.value.forEach((year, i) => { 650 incomeYears.value.forEach((year, i) => {
591 let C6 = transfer(data[5][year.field]) 651 let C6 = transfer(data[5][year.field])
592 let C7 = transfer(data[6][year.field]) 652 let C7 = transfer(data[6][year.field])
593 let sumC7: any = i == 0 ? C7 : incomeYears.value.slice(0, i + 1).reduce(function (prev, curr, idx, arr) { 653 let sumC7: any = 0;
594 return transfer(data[6][prev.field]) + transfer(data[6][curr.field]); 654 if (i == 0) {
655 sumC7 = C7;
656 } else {
657 incomeYears.value.slice(0, i + 1).forEach((item) => {
658 sumC7 = sumC7 + transfer(data[6][item.field]);
595 }) 659 })
660 }
596 resultInfo['综合分成率'].push(changeNum(C6 * (1 - sumC7 + C7 / 2) * 100, 2, true)); //TODO综合分成率算法有问题 661 resultInfo['综合分成率'].push(changeNum(C6 * (1 - sumC7 + C7 / 2) * 100, 2, true)); //TODO综合分成率算法有问题
597 let C1 = transfer(data[0][year.field], false) 662 let C1 = transfer(data[0][year.field], false)
598 let C5 = transfer(data[4][year.field]) 663 let C5 = transfer(data[4][year.field])
599 resultInfo['现金流'].push(changeNum(C1 * C5 * resultInfo['综合分成率'][i] / 100, 2, true)); 664 resultInfo['现金流'].push(changeNum(C1 * C5 * resultInfo['综合分成率'][i] / 100, 2, true));
600 if (i == 0) { 665 if (i == 0) {
601 resultInfo['折现年期'].push(changeNum(10 / 12 / 2, 2, true)); 666 let cnt = month == 12 ? 12 : (12 - month);
667 resultInfo['折现年期'].push(changeNum(cnt / 12 / 2, 2, true));
668 } else if (i == incomeYears.value.length - 1) {
669 resultInfo['折现年期'].push(changeNum(parseFloat(resultInfo['折现年期'][i - 1]) + (month == 12 ? 1 : (month / 12 / 2)), 2, true))
602 } else { 670 } else {
603 resultInfo['折现年期'].push(changeNum(parseFloat(resultInfo['折现年期'][i - 1]) + 1, 2, true)) 671 resultInfo['折现年期'].push(changeNum(parseFloat(resultInfo['折现年期'][i - 1]) + 1, 2, true))
604 } 672 }
605 let C10 = transfer(data[9][year.field]); 673 let C10 = transfer(data[9][year.field]);
606 resultInfo['折现因子'].push(changeNum(1 / Math.pow((1 + C10), parseFloat(resultInfo['折现年期'][i])), 2, true)); 674 resultInfo['折现因子'].push(changeNum(1 / Math.pow((1 + C10), parseFloat(resultInfo['折现年期'][i])), 2, true));
607 resultInfo['折现现值'].push(changeNum(parseFloat(resultInfo['折现因子'][i]) * parseFloat(resultInfo['现金流'][i]), 2, true)); 675 resultInfo['折现现值'].push(changeNum(parseFloat(resultInfo['折现因子'][i]) * parseFloat(resultInfo['现金流'][i]?.replace(/,/g, '')), 2, true));
608 }) 676 })
609 resultInfo['数据资产估值'] = changeNum(resultInfo['折现现值'].reduce(function (prev, curr, idx, arr) { 677 resultInfo['数据资产估值'] = changeNum(resultInfo['折现现值'].reduce(function (prev, curr, idx, arr) {
610 return parseFloat(prev) + parseFloat(curr); 678 return (typeof prev == 'string' ? parseFloat(prev?.replace(/,/g, '')) : parseFloat(prev)) + parseFloat(curr?.replace(/,/g, ''));
611 }), 2, true); 679 }), 2, true);
612 return resultInfo; 680 return resultInfo;
613 }) 681 })
...@@ -619,18 +687,25 @@ const submit = () => { ...@@ -619,18 +687,25 @@ const submit = () => {
619 if (params.evaluateMethod == '1') { 687 if (params.evaluateMethod == '1') {
620 params.valuationCostRQVOList = costTableData.value; 688 params.valuationCostRQVOList = costTableData.value;
621 params.damValuation = costTableSummaryValue.value; 689 params.damValuation = costTableSummaryValue.value;
690 if (!costTableData.value.some(table => table.amount !== '')) {
691 proxy.$ElMessage.error('成本法请至少输入一项指标金额');
692 return
693 }
622 } else { 694 } else {
623 params.valuationEarningsRQVOList = incomeTableData.value; 695 params.valuationEarningsRQVOList = incomeTableData.value;
624 params.damValuation = incomeCalculateData.value['数据资产估值']; 696 params.damValuation = incomeCalculateData.value['数据资产估值'];
625 params.valuationEarningsRQVOList.forEach(d => { 697 for (const d of params.valuationEarningsRQVOList) {
626 let years: any = {}; 698 let years: any = {};
627 incomeYears.value.forEach(y => { 699 for (const y of incomeYears.value) {
700 if (d.auto != true && d[y.field] == null) {
701 proxy.$ElMessage.error(`收益法的指标【${d.indexName}】预测年限中存在空值,请输入`);
702 return;
703 }
628 years[y.field] = d[y.field]; 704 years[y.field] = d[y.field];
629 }) 705 }
630 d.predictedYears = years; 706 d.predictedYears = years;
631 })
632 } 707 }
633 params.evaluateBaseDate = getLastDayOfMonth(params.evaluateBaseDate); 708 }
634 fullscreenLoading.value = true; 709 fullscreenLoading.value = true;
635 if (!route.query.guid) { 710 if (!route.query.guid) {
636 saveValuationMode(params).then((res: any) => { 711 saveValuationMode(params).then((res: any) => {
...@@ -714,8 +789,6 @@ onBeforeMount(() => { ...@@ -714,8 +789,6 @@ onBeforeMount(() => {
714 item.default = detailData[item.field]; 789 item.default = detailData[item.field];
715 if (item.field == 'evaluateMethod') { 790 if (item.field == 'evaluateMethod') {
716 valuateFormItems.value.at(-1).visible = item.default == '2'; 791 valuateFormItems.value.at(-1).visible = item.default == '2';
717 } else if (item.field == 'evaluateBaseDate') {
718 item.default = item.default.substring(0, 7);
719 } 792 }
720 }) 793 })
721 if (detailData.evaluateMethod == '2') { 794 if (detailData.evaluateMethod == '2') {
...@@ -761,10 +834,10 @@ onMounted(async () => { ...@@ -761,10 +834,10 @@ onMounted(async () => {
761 <div class="content_main"> 834 <div class="content_main">
762 <ContentWrap id="id-baseInfo" title="估值类型" instructions="" style="margin-top: 8px;"> 835 <ContentWrap id="id-baseInfo" title="估值类型" instructions="" style="margin-top: 8px;">
763 <Form ref="formRef" :itemList="valuateFormItems" :rules="valuateFormRules" formId="main-model-edit" 836 <Form ref="formRef" :itemList="valuateFormItems" :rules="valuateFormRules" formId="main-model-edit"
764 @select-change="handleValudateFormChange" col="col3" /> 837 @select-change="handleValudateFormChange" @input-change="handleInputChange" col="col3" />
765 </ContentWrap> 838 </ContentWrap>
766 <ContentWrap id="id-grade-info" title="填写成本明细" 839 <ContentWrap id="id-grade-info" :title="formRef?.formInline?.evaluateMethod == '1' ? '填写成本明细' : '填写预期收益'"
767 :instructions="formRef?.formInline?.evaluateMethod == '1' ? '填写时请按照所选数据产品的成本投入进行填写,跟数据产品产生的成本一致' : ''" 840 :description="formRef?.formInline?.evaluateMethod == '1' ? '填写时请按照所选数据产品的成本投入进行填写,跟数据产品产生的成本一致' : ''"
768 style="margin-top: 16px;"> 841 style="margin-top: 16px;">
769 <el-table id="cost-table" v-show="formRef?.formInline?.evaluateMethod == '1'" ref="costTableRef" 842 <el-table id="cost-table" v-show="formRef?.formInline?.evaluateMethod == '1'" ref="costTableRef"
770 :data="costTableData" :span-method="costTableSpanMethod" :summary-method="costTableSummaryMethod" show-summary 843 :data="costTableData" :span-method="costTableSpanMethod" :summary-method="costTableSummaryMethod" show-summary
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!