系数字段修改
This commit is contained in:
parent
21d3f0379c
commit
fc26a87bee
@ -1844,3 +1844,367 @@ function numberFormatter(num, decimalNum) {
|
|||||||
return big;
|
return big;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let data1 = {
|
||||||
|
name: 'test001',
|
||||||
|
writer: '张三',// 编制人
|
||||||
|
reviewer: '李四',// 复核人
|
||||||
|
company: '测试公司',// 公司名称
|
||||||
|
date: '2021-09-24',// 编制日期
|
||||||
|
industry: 0,// 0为公路工程,1为铁路工程,2为水运工程
|
||||||
|
fee: 10000,
|
||||||
|
scaleCost: 100000,// scale的cost的合计数
|
||||||
|
scale: [// 规模信息
|
||||||
|
{
|
||||||
|
major: 0,
|
||||||
|
cost: 100000,
|
||||||
|
area: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
major: 1,
|
||||||
|
cost: 100000,
|
||||||
|
area: 200,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
serviceCoes: [// 项目咨询分类系数
|
||||||
|
{
|
||||||
|
serviceid: 0,
|
||||||
|
coe: 1.1,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceid: 1,
|
||||||
|
coe: 1.2,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
majorCoes: [// 项目工程专业系数
|
||||||
|
{
|
||||||
|
majorid: 0,
|
||||||
|
coe: 1.1,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
majorid: 1,
|
||||||
|
coe: 1.2,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
contracts: [// 合同段信息
|
||||||
|
{
|
||||||
|
name: 'A合同段',
|
||||||
|
serviceFee: 100000,
|
||||||
|
addtionalFee: 0,
|
||||||
|
reserveFee: 0,
|
||||||
|
fee: 10000,
|
||||||
|
scale: [
|
||||||
|
{
|
||||||
|
major: 0,
|
||||||
|
cost: 100000,
|
||||||
|
area: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
major: 1,
|
||||||
|
cost: 100000,
|
||||||
|
area: 200,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
serviceCoes: [// 合同段咨询分类系数
|
||||||
|
{
|
||||||
|
serviceid: 0,
|
||||||
|
coe: 1.1,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceid: 1,
|
||||||
|
coe: 1.2,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
majorCoes: [// 合同段工程专业系数
|
||||||
|
{
|
||||||
|
majorid: 0,
|
||||||
|
coe: 1.1,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
majorid: 1,
|
||||||
|
coe: 1.2,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
fee: 100000,
|
||||||
|
process: 0,// 工作环节,0为编制,1为审核
|
||||||
|
method1: { // 投资规模法
|
||||||
|
cost: 100000,
|
||||||
|
basicFee: 200,
|
||||||
|
basicFee_basic: 200,
|
||||||
|
basicFee_optional: 0,
|
||||||
|
fee: 250000,
|
||||||
|
proAmount: 3,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
proNum: 1,
|
||||||
|
major: 0,
|
||||||
|
cost: 100000,
|
||||||
|
basicFee: 200,
|
||||||
|
basicFormula: '856,000+(1,000,000,000-500,000,000)×1‰',
|
||||||
|
basicFee_basic: 200,
|
||||||
|
optionalFormula: '171,200+(1,000,000,000-500,000,000)×0.2‰',
|
||||||
|
basicFee_optional: 0,
|
||||||
|
serviceCoe: 1.1,
|
||||||
|
majorCoe: 1.2,
|
||||||
|
processCoe: 1,// 工作环节系数(编审系数)
|
||||||
|
proportion: 0.5,// 工作占比
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
method2: { // 用地规模法
|
||||||
|
area: 1200,
|
||||||
|
basicFee: 200,
|
||||||
|
basicFee_basic: 200,
|
||||||
|
basicFee_optional: 0,
|
||||||
|
fee: 250000,
|
||||||
|
proAmount: 3,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
proNum: 1,
|
||||||
|
major: 0,
|
||||||
|
area: 1200,
|
||||||
|
basicFee: 200,
|
||||||
|
basicFormula: '106,000+(1,200-1,000)×60',
|
||||||
|
basicFee_basic: 200,
|
||||||
|
optionalFormula: '21,200+(1,200-1,000)×12',
|
||||||
|
basicFee_optional: 0,
|
||||||
|
serviceCoe: 1.1,
|
||||||
|
majorCoe: 1.2,
|
||||||
|
processCoe: 1,// 工作环节系数(编审系数)
|
||||||
|
proportion: 0.5,// 工作占比
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
method3: { // 工作量法
|
||||||
|
basicFee: 200,
|
||||||
|
fee: 250000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
task: 0,
|
||||||
|
price: 100000,
|
||||||
|
amount: 10,
|
||||||
|
basicFee: 200,
|
||||||
|
serviceCoe: 1.1,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
task: 1,
|
||||||
|
price: 100000,
|
||||||
|
amount: 10,
|
||||||
|
basicFee: 200,
|
||||||
|
serviceCoe: 1.1,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
method4: { // 工时法
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 10,
|
||||||
|
fee: 250000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
expert: 0,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expert: 1,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
addtional: {// 附加工作费
|
||||||
|
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'C' }] },
|
||||||
|
name: '附加工作',
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] },
|
||||||
|
name: '人员驻场服务及其他附加工作',
|
||||||
|
fee: 10000,
|
||||||
|
m4: {
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
expert: 0,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expert: 1,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
m5: {
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'X' }] },
|
||||||
|
name: '咨询服务协调工作',
|
||||||
|
fee: 10000,
|
||||||
|
m0: {
|
||||||
|
coe: 0.03,
|
||||||
|
fee: 10000,
|
||||||
|
},
|
||||||
|
m4: {
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
expert: 0,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expert: 1,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
m5: {
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
reserve: {// 预备费
|
||||||
|
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' }] },
|
||||||
|
name: '预备费',
|
||||||
|
fee: 10000,
|
||||||
|
m0: {
|
||||||
|
coe: 0.03,
|
||||||
|
fee: 10000,
|
||||||
|
},
|
||||||
|
m4: {
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
expert: 0,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expert: 1,
|
||||||
|
price: 100000,
|
||||||
|
person_num: 10,
|
||||||
|
work_day: 3,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
m5: {
|
||||||
|
fee: 10000,
|
||||||
|
det: [
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '×××项',
|
||||||
|
unit: '项',
|
||||||
|
amount: 10,
|
||||||
|
price: 100000,
|
||||||
|
fee: 100000,
|
||||||
|
remark: '',// 用户输入的说明
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
|||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { parseNumberOrNull } from '@/lib/number'
|
import { parseNumberOrNull } from '@/lib/number'
|
||||||
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { roundTo, toDecimal } from '@/lib/decimal'
|
import { roundTo, toDecimal } from '@/lib/decimal'
|
||||||
|
|
||||||
interface FeeRow {
|
interface FeeRow {
|
||||||
@ -50,17 +50,17 @@ const formatEditableText = (params: any) => {
|
|||||||
|
|
||||||
const formatEditableQuantity = (params: any) => {
|
const formatEditableQuantity = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return '点击输入'
|
if (params.value == null || params.value === '') return '点击输入'
|
||||||
return formatThousandsFlexible(params.value, 4)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatEditableUnitPrice = (params: any) => {
|
const formatEditableUnitPrice = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return '点击输入'
|
if (params.value == null || params.value === '') return '点击输入'
|
||||||
return formatThousands(params.value, 2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatReadonlyBudgetFee = (params: any) => {
|
const formatReadonlyBudgetFee = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return ''
|
if (params.value == null || params.value === '') return ''
|
||||||
return formatThousands(params.value, 2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncComputedValuesToRows = () => {
|
const syncComputedValuesToRows = () => {
|
||||||
@ -172,7 +172,7 @@ const columnDefs: ColDef<FeeRow>[] = [
|
|||||||
headerClass: 'ag-right-aligned-header',
|
headerClass: 'ag-right-aligned-header',
|
||||||
cellClass: 'ag-right-aligned-cell editable-cell-line',
|
cellClass: 'ag-right-aligned-cell editable-cell-line',
|
||||||
editable: true,
|
editable: true,
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 4 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableQuantity,
|
valueFormatter: formatEditableQuantity,
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params => params.value == null || params.value === ''
|
'editable-cell-empty': params => params.value == null || params.value === ''
|
||||||
@ -186,7 +186,7 @@ const columnDefs: ColDef<FeeRow>[] = [
|
|||||||
headerClass: 'ag-right-aligned-header',
|
headerClass: 'ag-right-aligned-header',
|
||||||
cellClass: 'ag-right-aligned-cell editable-cell-line',
|
cellClass: 'ag-right-aligned-cell editable-cell-line',
|
||||||
editable: true,
|
editable: true,
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableUnitPrice,
|
valueFormatter: formatEditableUnitPrice,
|
||||||
cellClassRules: {
|
cellClassRules: {
|
||||||
'editable-cell-empty': params => params.value == null || params.value === ''
|
'editable-cell-empty': params => params.value == null || params.value === ''
|
||||||
|
|||||||
@ -46,12 +46,16 @@ const gridApi = ref<GridApi<FactorRow> | null>(null)
|
|||||||
|
|
||||||
const formatReadonlyFactor = (value: unknown) => {
|
const formatReadonlyFactor = (value: unknown) => {
|
||||||
if (value == null || value === '') return ''
|
if (value == null || value === '') return ''
|
||||||
return Number(value).toFixed(2)
|
const parsed = parseNumberOrNull(value, { precision: 3 })
|
||||||
|
if (parsed == null) return ''
|
||||||
|
return String(Number(parsed))
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatEditableFactor = (params: any) => {
|
const formatEditableFactor = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return '点击输入'
|
if (params.value == null || params.value === '') return '点击输入'
|
||||||
return Number(params.value).toFixed(2)
|
const parsed = parseNumberOrNull(params.value, { precision: 3 })
|
||||||
|
if (parsed == null) return ''
|
||||||
|
return String(Number(parsed))
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedDictEntries = () =>
|
const sortedDictEntries = () =>
|
||||||
@ -169,7 +173,7 @@ const columnDefs: ColDef<FactorRow>[] = [
|
|||||||
if (!props.disableBudgetEditWhenStandardNull) return true
|
if (!props.disableBudgetEditWhenStandardNull) return true
|
||||||
return params.data?.standardFactor != null
|
return params.data?.standardFactor != null
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
const disabled = props.disableBudgetEditWhenStandardNull && params.data?.standardFactor == null
|
const disabled = props.disableBudgetEditWhenStandardNull && params.data?.standardFactor == null
|
||||||
if (disabled && (params.value == null || params.value === '')) return ''
|
if (disabled && (params.value == null || params.value === '')) return ''
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
|||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { industryTypeList, getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
|
import { industryTypeList, getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
|
||||||
import { SwitchRoot, SwitchThumb } from 'reka-ui'
|
import { SwitchRoot, SwitchThumb } from 'reka-ui'
|
||||||
|
|
||||||
@ -295,13 +295,13 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
valueParser: params => {
|
valueParser: params => {
|
||||||
if (params.newValue === '' || params.newValue == null) return null
|
if (params.newValue === '' || params.newValue == null) return null
|
||||||
const v = Number(params.newValue)
|
const v = Number(params.newValue)
|
||||||
return Number.isFinite(v) ? roundTo(v, 2) : null
|
return Number.isFinite(v) ? roundTo(v, 3) : null
|
||||||
},
|
},
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (roughCalcEnabled.value) {
|
if (roughCalcEnabled.value) {
|
||||||
if (!params.node?.rowPinned) return ''
|
if (!params.node?.rowPinned) return ''
|
||||||
if (params.value == null || params.value === '') return '点击输入'
|
if (params.value == null || params.value === '') return '点击输入'
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
|
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
|
||||||
return ''
|
return ''
|
||||||
@ -310,7 +310,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -344,7 +344,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return formatThousands(params.value, 3)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import localforage from 'localforage'
|
|||||||
import { expertList } from '@/sql'
|
import { expertList } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
|
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { parseNumberOrNull } from '@/lib/number'
|
import { parseNumberOrNull } from '@/lib/number'
|
||||||
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
||||||
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
||||||
@ -190,7 +190,7 @@ const formatEditableNumber = (params: any) => {
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return Number(params.value).toFixed(2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatEditableInteger = (params: any) => {
|
const formatEditableInteger = (params: any) => {
|
||||||
@ -233,7 +233,7 @@ const editableNumberCol = <K extends keyof DetailRow>(
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableNumber,
|
valueFormatter: formatEditableNumber,
|
||||||
...extra
|
...extra
|
||||||
})
|
})
|
||||||
@ -254,13 +254,13 @@ const editableMoneyCol = <K extends keyof DetailRow>(
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
|
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
|
||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
},
|
},
|
||||||
...extra
|
...extra
|
||||||
})
|
})
|
||||||
@ -325,7 +325,7 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
|||||||
valueGetter: params => (params.node?.rowPinned ? params.data?.serviceBudget ?? null : calcServiceBudget(params.data)),
|
valueGetter: params => (params.node?.rowPinned ? params.data?.serviceBudget ?? null : calcServiceBudget(params.data)),
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (params.value == null || params.value === '') return ''
|
if (params.value == null || params.value === '') return ''
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
|||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { getMajorDictEntries, getServiceDictItemById, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
import { getMajorDictEntries, getServiceDictItemById, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
import { addNumbers, decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
||||||
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
||||||
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
|
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
|
||||||
@ -212,8 +212,9 @@ const detailDict: DictGroup[] = (() => {
|
|||||||
|
|
||||||
const hasCost = item.hasCost !== false
|
const hasCost = item.hasCost !== false
|
||||||
const hasArea = item.hasArea !== false
|
const hasArea = item.hasArea !== false
|
||||||
// 特殊规则:投资规模法中,hasCost && hasArea 的专业不参与明细行
|
// 投资规模法仅保留可按造价计价且非用地规模的专业
|
||||||
if (hasCost && hasArea) continue
|
if (!hasCost) continue
|
||||||
|
if (hasArea) continue
|
||||||
|
|
||||||
groupMap.get(parentCode)!.children.push({
|
groupMap.get(parentCode)!.children.push({
|
||||||
id: key,
|
id: key,
|
||||||
@ -292,7 +293,18 @@ const getOnlyCostScaleMajorFactorDefault = () => {
|
|||||||
|
|
||||||
const buildOnlyCostScaleRow = (
|
const buildOnlyCostScaleRow = (
|
||||||
amount: number | null,
|
amount: number | null,
|
||||||
fromDb?: Partial<Pick<DetailRow, 'consultCategoryFactor' | 'majorFactor' | 'workStageFactor' | 'workRatio' | 'remark'>>
|
fromDb?: Partial<
|
||||||
|
Pick<
|
||||||
|
DetailRow,
|
||||||
|
| 'consultCategoryFactor'
|
||||||
|
| 'majorFactor'
|
||||||
|
| 'workStageFactor'
|
||||||
|
| 'workRatio'
|
||||||
|
| 'remark'
|
||||||
|
| 'benchmarkBudgetBasicChecked'
|
||||||
|
| 'benchmarkBudgetOptionalChecked'
|
||||||
|
>
|
||||||
|
>
|
||||||
): DetailRow => ({
|
): DetailRow => ({
|
||||||
id: ONLY_COST_SCALE_ROW_ID,
|
id: ONLY_COST_SCALE_ROW_ID,
|
||||||
groupCode: 'TOTAL',
|
groupCode: 'TOTAL',
|
||||||
@ -305,8 +317,10 @@ const buildOnlyCostScaleRow = (
|
|||||||
benchmarkBudget: null,
|
benchmarkBudget: null,
|
||||||
benchmarkBudgetBasic: null,
|
benchmarkBudgetBasic: null,
|
||||||
benchmarkBudgetOptional: null,
|
benchmarkBudgetOptional: null,
|
||||||
benchmarkBudgetBasicChecked: true,
|
benchmarkBudgetBasicChecked:
|
||||||
benchmarkBudgetOptionalChecked: true,
|
typeof fromDb?.benchmarkBudgetBasicChecked === 'boolean' ? fromDb.benchmarkBudgetBasicChecked : true,
|
||||||
|
benchmarkBudgetOptionalChecked:
|
||||||
|
typeof fromDb?.benchmarkBudgetOptionalChecked === 'boolean' ? fromDb.benchmarkBudgetOptionalChecked : true,
|
||||||
basicFormula: '',
|
basicFormula: '',
|
||||||
optionalFormula: '',
|
optionalFormula: '',
|
||||||
consultCategoryFactor:
|
consultCategoryFactor:
|
||||||
@ -416,7 +430,7 @@ const formatEditableNumber = (params: any) => {
|
|||||||
return '请输入'
|
return '请输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return Number(params.value).toFixed(2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatConsultCategoryFactor = (params: any) => {
|
const formatConsultCategoryFactor = (params: any) => {
|
||||||
@ -431,7 +445,7 @@ const formatEditableMoney = (params: any) => {
|
|||||||
if (isOnlyCostScaleService.value) {
|
if (isOnlyCostScaleService.value) {
|
||||||
if (!params.node?.rowPinned) return ''
|
if (!params.node?.rowPinned) return ''
|
||||||
if (params.value == null || params.value === '') return '点击输入'
|
if (params.value == null || params.value === '') return '点击输入'
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
|
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
|
||||||
return ''
|
return ''
|
||||||
@ -440,12 +454,12 @@ const formatEditableMoney = (params: any) => {
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return formatThousands(params.value)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatReadonlyMoney = (params: any) => {
|
const formatReadonlyMoney = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return ''
|
if (params.value == null || params.value === '') return ''
|
||||||
return formatThousands(roundTo(params.value, 2))
|
return formatThousandsFlexible(roundTo(params.value, 3), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
|
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
|
||||||
@ -453,14 +467,14 @@ type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptional
|
|||||||
const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => {
|
const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (params: any) => {
|
||||||
const valueText = formatReadonlyMoney(params)
|
const valueText = formatReadonlyMoney(params)
|
||||||
const hasValue = params.value != null && params.value !== ''
|
const hasValue = params.value != null && params.value !== ''
|
||||||
if (params.node?.group || params.node?.rowPinned || !params.data || !hasValue) {
|
if (params.node?.group || (params.node?.rowPinned && !isOnlyCostScaleService.value) || !params.data || !hasValue) {
|
||||||
return valueText
|
return valueText
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = document.createElement('div')
|
const wrapper = document.createElement('div')
|
||||||
wrapper.style.display = 'flex'
|
wrapper.style.display = 'flex'
|
||||||
wrapper.style.alignItems = 'center'
|
wrapper.style.alignItems = 'center'
|
||||||
wrapper.style.justifyContent = 'flex-end'
|
wrapper.style.justifyContent = 'space-between'
|
||||||
wrapper.style.gap = '6px'
|
wrapper.style.gap = '6px'
|
||||||
wrapper.style.width = '100%'
|
wrapper.style.width = '100%'
|
||||||
|
|
||||||
@ -471,8 +485,27 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
|
|||||||
checkbox.checked = params.data[checkField] !== false
|
checkbox.checked = params.data[checkField] !== false
|
||||||
checkbox.addEventListener('click', event => event.stopPropagation())
|
checkbox.addEventListener('click', event => event.stopPropagation())
|
||||||
checkbox.addEventListener('change', () => {
|
checkbox.addEventListener('change', () => {
|
||||||
params.data[checkField] = checkbox.checked
|
const isOnlyCostScalePinned = isOnlyCostScaleService.value && Boolean(params.node?.rowPinned)
|
||||||
|
const targetRow =
|
||||||
|
isOnlyCostScalePinned
|
||||||
|
? detailRows.value[0]
|
||||||
|
: (params.data as DetailRow | undefined)
|
||||||
|
if (!targetRow) return
|
||||||
|
|
||||||
|
targetRow[checkField] = checkbox.checked
|
||||||
|
params.node?.setDataValue?.(checkField, checkbox.checked)
|
||||||
|
|
||||||
|
if (!checkbox.checked) {
|
||||||
|
const budgetField = checkField === 'benchmarkBudgetBasicChecked' ? 'benchmarkBudgetBasic' : 'benchmarkBudgetOptional'
|
||||||
|
targetRow[budgetField] = 0
|
||||||
|
params.node?.setDataValue?.(budgetField, 0)
|
||||||
|
}
|
||||||
|
|
||||||
handleCellValueChanged()
|
handleCellValueChanged()
|
||||||
|
params.api?.refreshCells?.({
|
||||||
|
rowNodes: params.node ? [params.node] : undefined,
|
||||||
|
force: true
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const valueSpan = document.createElement('span')
|
const valueSpan = document.createElement('span')
|
||||||
@ -485,10 +518,34 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
|
|||||||
const getBenchmarkBudgetSplitByAmount = (row?: Pick<DetailRow, 'amount'>) =>
|
const getBenchmarkBudgetSplitByAmount = (row?: Pick<DetailRow, 'amount'>) =>
|
||||||
getBenchmarkBudgetSplitByScale(row?.amount, 'cost')
|
getBenchmarkBudgetSplitByScale(row?.amount, 'cost')
|
||||||
|
|
||||||
const getBudgetFee = (
|
const getCheckedBenchmarkBudgetSplitByAmount = (
|
||||||
row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
|
row?: Pick<DetailRow, 'amount' | 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'>
|
||||||
) => {
|
) => {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
|
const split = getBenchmarkBudgetSplitByAmount(row)
|
||||||
|
if (!split) return null
|
||||||
|
const basic = row?.benchmarkBudgetBasicChecked === false ? 0 : split.basic
|
||||||
|
const optional = row?.benchmarkBudgetOptionalChecked === false ? 0 : split.optional
|
||||||
|
return {
|
||||||
|
...split,
|
||||||
|
basic,
|
||||||
|
optional,
|
||||||
|
total: roundTo(addNumbers(basic, optional), 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBudgetFee = (
|
||||||
|
row?: Pick<
|
||||||
|
DetailRow,
|
||||||
|
| 'amount'
|
||||||
|
| 'benchmarkBudgetBasicChecked'
|
||||||
|
| 'benchmarkBudgetOptionalChecked'
|
||||||
|
| 'majorFactor'
|
||||||
|
| 'consultCategoryFactor'
|
||||||
|
| 'workStageFactor'
|
||||||
|
| 'workRatio'
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
|
||||||
if (!benchmarkBudgetSplit) return null
|
if (!benchmarkBudgetSplit) return null
|
||||||
|
|
||||||
const splitBudgetFee = getScaleBudgetFeeSplit({
|
const splitBudgetFee = getScaleBudgetFeeSplit({
|
||||||
@ -503,9 +560,18 @@ const getBudgetFee = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getBudgetFeeSplit = (
|
const getBudgetFeeSplit = (
|
||||||
row?: Pick<DetailRow, 'amount' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
|
row?: Pick<
|
||||||
|
DetailRow,
|
||||||
|
| 'amount'
|
||||||
|
| 'benchmarkBudgetBasicChecked'
|
||||||
|
| 'benchmarkBudgetOptionalChecked'
|
||||||
|
| 'majorFactor'
|
||||||
|
| 'consultCategoryFactor'
|
||||||
|
| 'workStageFactor'
|
||||||
|
| 'workRatio'
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
|
||||||
if (!benchmarkBudgetSplit) return null
|
if (!benchmarkBudgetSplit) return null
|
||||||
return getScaleBudgetFeeSplit({
|
return getScaleBudgetFeeSplit({
|
||||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||||
@ -517,6 +583,17 @@ const getBudgetFeeSplit = (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getMergeColSpanBeforeTotal = (params: any) => {
|
||||||
|
if (!params.node?.group && !params.node?.rowPinned) return 1
|
||||||
|
if (isOnlyCostScaleService.value && params.node?.rowPinned) return 1
|
||||||
|
const displayedColumns = params.api?.getAllDisplayedColumns?.()
|
||||||
|
if (!Array.isArray(displayedColumns) || !params.column) return 1
|
||||||
|
const currentIndex = displayedColumns.findIndex((column: any) => column.getColId() === params.column.getColId())
|
||||||
|
const totalIndex = displayedColumns.findIndex((column: any) => column.getColId() === 'budgetFeeTotal')
|
||||||
|
if (currentIndex < 0 || totalIndex <= currentIndex) return 1
|
||||||
|
return totalIndex - currentIndex
|
||||||
|
}
|
||||||
|
|
||||||
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
||||||
{
|
{
|
||||||
headerName: '造价金额(万元)',
|
headerName: '造价金额(万元)',
|
||||||
@ -541,7 +618,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableMoney
|
valueFormatter: formatEditableMoney
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -558,8 +635,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null : null)
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
|
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.basic ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
},
|
},
|
||||||
@ -573,8 +650,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null : null)
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
|
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.optional ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
},
|
},
|
||||||
@ -588,8 +665,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? (isOnlyCostScaleService.value ? getCheckedBenchmarkBudgetSplitByAmount(params.data)?.total ?? null : null)
|
||||||
: getBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
|
: getCheckedBenchmarkBudgetSplitByAmount(params.data)?.total ?? null,
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -620,7 +697,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatConsultCategoryFactor
|
valueFormatter: formatConsultCategoryFactor
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -645,7 +722,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatMajorFactor
|
valueFormatter: formatMajorFactor
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -670,7 +747,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -695,7 +772,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
? params.value == null || params.value === ''
|
? params.value == null || params.value === ''
|
||||||
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
: !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -738,8 +815,8 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
const autoGroupColumnDef: ColDef = {
|
const autoGroupColumnDef: ColDef = {
|
||||||
headerName: '专业编码以及工程专业名称',
|
headerName: '专业编码以及工程专业名称',
|
||||||
minWidth: 250,
|
minWidth: 250,
|
||||||
pinned: 'left',
|
|
||||||
flex: 2,
|
flex: 2,
|
||||||
|
|
||||||
// wrapText: true,
|
// wrapText: true,
|
||||||
// cellStyle: { whiteSpace: 'normal', lineHeight: '1.5', padding: '2px' },
|
// cellStyle: { whiteSpace: 'normal', lineHeight: '1.5', padding: '2px' },
|
||||||
|
|
||||||
@ -747,6 +824,7 @@ const autoGroupColumnDef: ColDef = {
|
|||||||
cellRendererParams: {
|
cellRendererParams: {
|
||||||
suppressCount: true
|
suppressCount: true
|
||||||
},
|
},
|
||||||
|
colSpan: getMergeColSpanBeforeTotal,
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (params.node?.rowPinned) {
|
if (params.node?.rowPinned) {
|
||||||
return totalLabel.value
|
return totalLabel.value
|
||||||
@ -766,9 +844,7 @@ const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amou
|
|||||||
const visibleDetailRows = computed(() => (isOnlyCostScaleService.value ? [] : detailRows.value))
|
const visibleDetailRows = computed(() => (isOnlyCostScaleService.value ? [] : detailRows.value))
|
||||||
const onlyCostScaleSourceRow = computed(() => detailRows.value[0] ?? buildOnlyCostScaleRow(null))
|
const onlyCostScaleSourceRow = computed(() => detailRows.value[0] ?? buildOnlyCostScaleRow(null))
|
||||||
|
|
||||||
const totalBenchmarkBudgetBasic = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.basic))
|
|
||||||
const totalBenchmarkBudgetOptional = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.optional))
|
|
||||||
const totalBenchmarkBudget = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByAmount(row)?.total))
|
|
||||||
|
|
||||||
const totalBudgetFeeBasic = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.basic))
|
const totalBudgetFeeBasic = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.basic))
|
||||||
const totalBudgetFeeOptional = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.optional))
|
const totalBudgetFeeOptional = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.optional))
|
||||||
@ -786,8 +862,8 @@ const pinnedTopRowData = computed(() => [
|
|||||||
benchmarkBudget: null,
|
benchmarkBudget: null,
|
||||||
benchmarkBudgetBasic: null,
|
benchmarkBudgetBasic: null,
|
||||||
benchmarkBudgetOptional: null,
|
benchmarkBudgetOptional: null,
|
||||||
benchmarkBudgetBasicChecked: true,
|
benchmarkBudgetBasicChecked: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.benchmarkBudgetBasicChecked !== false : true,
|
||||||
benchmarkBudgetOptionalChecked: true,
|
benchmarkBudgetOptionalChecked: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.benchmarkBudgetOptionalChecked !== false : true,
|
||||||
basicFormula: '',
|
basicFormula: '',
|
||||||
optionalFormula: '',
|
optionalFormula: '',
|
||||||
consultCategoryFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.consultCategoryFactor : null,
|
consultCategoryFactor: isOnlyCostScaleService.value ? onlyCostScaleSourceRow.value.consultCategoryFactor : null,
|
||||||
@ -804,7 +880,8 @@ const pinnedTopRowData = computed(() => [
|
|||||||
|
|
||||||
const syncComputedValuesToDetailRows = () => {
|
const syncComputedValuesToDetailRows = () => {
|
||||||
for (const row of detailRows.value) {
|
for (const row of detailRows.value) {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByAmount(row)
|
const benchmarkBudgetRawSplit = getBenchmarkBudgetSplitByAmount(row)
|
||||||
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByAmount(row)
|
||||||
const budgetFeeSplit = benchmarkBudgetSplit
|
const budgetFeeSplit = benchmarkBudgetSplit
|
||||||
? getScaleBudgetFeeSplit({
|
? getScaleBudgetFeeSplit({
|
||||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||||
@ -819,8 +896,8 @@ const syncComputedValuesToDetailRows = () => {
|
|||||||
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
||||||
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
||||||
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
||||||
row.basicFormula = benchmarkBudgetSplit?.basicFormula ?? ''
|
row.basicFormula = benchmarkBudgetRawSplit?.basicFormula ?? ''
|
||||||
row.optionalFormula = benchmarkBudgetSplit?.optionalFormula ?? ''
|
row.optionalFormula = benchmarkBudgetRawSplit?.optionalFormula ?? ''
|
||||||
row.budgetFee = budgetFeeSplit?.total ?? null
|
row.budgetFee = budgetFeeSplit?.total ?? null
|
||||||
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
||||||
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
||||||
@ -951,7 +1028,7 @@ let persistTimer: ReturnType<typeof setTimeout> | null = null
|
|||||||
|
|
||||||
let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
|
let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
const applyOnlyCostScalePinnedValue = (field: string, rawValue: unknown) => {
|
const applyOnlyCostScalePinnedValue = (field: string, rawValue: unknown) => {
|
||||||
const parsedValue = parseNumberOrNull(rawValue, { precision: 2 })
|
const parsedValue = parseNumberOrNull(rawValue, { precision: 3 })
|
||||||
const current = detailRows.value[0]
|
const current = detailRows.value[0]
|
||||||
if (!current) {
|
if (!current) {
|
||||||
detailRows.value = [buildOnlyCostScaleRow(field === 'amount' ? parsedValue : null)]
|
detailRows.value = [buildOnlyCostScaleRow(field === 'amount' ? parsedValue : null)]
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import type { ColDef, ColGroupDef } from 'ag-grid-community'
|
|||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import { getMajorDictEntries, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
import { getMajorDictEntries, industryTypeList, isMajorIdInIndustryScope } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
import { addNumbers, decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
||||||
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
||||||
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
|
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
|
||||||
@ -195,12 +195,15 @@ const detailDict: DictGroup[] = (() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasArea = item.hasArea !== false
|
||||||
|
if (!hasArea) continue
|
||||||
|
|
||||||
groupMap.get(parentCode)!.children.push({
|
groupMap.get(parentCode)!.children.push({
|
||||||
id: key,
|
id: key,
|
||||||
code,
|
code,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
hasCost: item.hasCost !== false,
|
hasCost: item.hasCost !== false,
|
||||||
hasArea: item.hasArea !== false
|
hasArea
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +345,7 @@ const formatEditableNumber = (params: any) => {
|
|||||||
return '请输入'
|
return '请输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return Number(params.value).toFixed(2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatConsultCategoryFactor = (params: any) => {
|
const formatConsultCategoryFactor = (params: any) => {
|
||||||
@ -355,7 +358,7 @@ const formatMajorFactor = (params: any) => {
|
|||||||
|
|
||||||
const formatReadonlyMoney = (params: any) => {
|
const formatReadonlyMoney = (params: any) => {
|
||||||
if (params.value == null || params.value === '') return ''
|
if (params.value == null || params.value === '') return ''
|
||||||
return formatThousands(roundTo(params.value, 2))
|
return formatThousandsFlexible(roundTo(params.value, 3), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
|
type BudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
|
||||||
@ -370,7 +373,7 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
|
|||||||
const wrapper = document.createElement('div')
|
const wrapper = document.createElement('div')
|
||||||
wrapper.style.display = 'flex'
|
wrapper.style.display = 'flex'
|
||||||
wrapper.style.alignItems = 'center'
|
wrapper.style.alignItems = 'center'
|
||||||
wrapper.style.justifyContent = 'flex-end'
|
wrapper.style.justifyContent = 'space-between'
|
||||||
wrapper.style.gap = '6px'
|
wrapper.style.gap = '6px'
|
||||||
wrapper.style.width = '100%'
|
wrapper.style.width = '100%'
|
||||||
|
|
||||||
@ -381,6 +384,10 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
|
|||||||
checkbox.addEventListener('click', event => event.stopPropagation())
|
checkbox.addEventListener('click', event => event.stopPropagation())
|
||||||
checkbox.addEventListener('change', () => {
|
checkbox.addEventListener('change', () => {
|
||||||
params.data[checkField] = checkbox.checked
|
params.data[checkField] = checkbox.checked
|
||||||
|
if (!checkbox.checked) {
|
||||||
|
const budgetField = checkField === 'benchmarkBudgetBasicChecked' ? 'benchmarkBudgetBasic' : 'benchmarkBudgetOptional'
|
||||||
|
params.data[budgetField] = 0
|
||||||
|
}
|
||||||
handleCellValueChanged()
|
handleCellValueChanged()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -394,10 +401,34 @@ const createBudgetCellRendererWithCheck = (checkField: BudgetCheckField) => (par
|
|||||||
const getBenchmarkBudgetSplitByLandArea = (row?: Pick<DetailRow, 'landArea'>) =>
|
const getBenchmarkBudgetSplitByLandArea = (row?: Pick<DetailRow, 'landArea'>) =>
|
||||||
getBenchmarkBudgetSplitByScale(row?.landArea, 'area')
|
getBenchmarkBudgetSplitByScale(row?.landArea, 'area')
|
||||||
|
|
||||||
const getBudgetFee = (
|
const getCheckedBenchmarkBudgetSplitByLandArea = (
|
||||||
row?: Pick<DetailRow, 'landArea' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
|
row?: Pick<DetailRow, 'landArea' | 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'>
|
||||||
) => {
|
) => {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
|
const split = getBenchmarkBudgetSplitByLandArea(row)
|
||||||
|
if (!split) return null
|
||||||
|
const basic = row?.benchmarkBudgetBasicChecked === false ? 0 : split.basic
|
||||||
|
const optional = row?.benchmarkBudgetOptionalChecked === false ? 0 : split.optional
|
||||||
|
return {
|
||||||
|
...split,
|
||||||
|
basic,
|
||||||
|
optional,
|
||||||
|
total: roundTo(addNumbers(basic, optional), 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBudgetFee = (
|
||||||
|
row?: Pick<
|
||||||
|
DetailRow,
|
||||||
|
| 'landArea'
|
||||||
|
| 'benchmarkBudgetBasicChecked'
|
||||||
|
| 'benchmarkBudgetOptionalChecked'
|
||||||
|
| 'majorFactor'
|
||||||
|
| 'consultCategoryFactor'
|
||||||
|
| 'workStageFactor'
|
||||||
|
| 'workRatio'
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByLandArea(row)
|
||||||
if (!benchmarkBudgetSplit) return null
|
if (!benchmarkBudgetSplit) return null
|
||||||
|
|
||||||
const splitBudgetFee = getScaleBudgetFeeSplit({
|
const splitBudgetFee = getScaleBudgetFeeSplit({
|
||||||
@ -412,9 +443,18 @@ const getBudgetFee = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getBudgetFeeSplit = (
|
const getBudgetFeeSplit = (
|
||||||
row?: Pick<DetailRow, 'landArea' | 'majorFactor' | 'consultCategoryFactor' | 'workStageFactor' | 'workRatio'>
|
row?: Pick<
|
||||||
|
DetailRow,
|
||||||
|
| 'landArea'
|
||||||
|
| 'benchmarkBudgetBasicChecked'
|
||||||
|
| 'benchmarkBudgetOptionalChecked'
|
||||||
|
| 'majorFactor'
|
||||||
|
| 'consultCategoryFactor'
|
||||||
|
| 'workStageFactor'
|
||||||
|
| 'workRatio'
|
||||||
|
>
|
||||||
) => {
|
) => {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByLandArea(row)
|
||||||
if (!benchmarkBudgetSplit) return null
|
if (!benchmarkBudgetSplit) return null
|
||||||
return getScaleBudgetFeeSplit({
|
return getScaleBudgetFeeSplit({
|
||||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||||
@ -426,6 +466,16 @@ const getBudgetFeeSplit = (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getMergeColSpanBeforeTotal = (params: any) => {
|
||||||
|
if (!params.node?.group && !params.node?.rowPinned) return 1
|
||||||
|
const displayedColumns = params.api?.getAllDisplayedColumns?.()
|
||||||
|
if (!Array.isArray(displayedColumns) || !params.column) return 1
|
||||||
|
const currentIndex = displayedColumns.findIndex((column: any) => column.getColId() === params.column.getColId())
|
||||||
|
const totalIndex = displayedColumns.findIndex((column: any) => column.getColId() === 'budgetFeeTotal')
|
||||||
|
if (currentIndex < 0 || totalIndex <= currentIndex) return 1
|
||||||
|
return totalIndex - currentIndex
|
||||||
|
}
|
||||||
|
|
||||||
const formatEditableFlexibleNumber = (params: any) => {
|
const formatEditableFlexibleNumber = (params: any) => {
|
||||||
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasArea) {
|
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasArea) {
|
||||||
return ''
|
return ''
|
||||||
@ -434,7 +484,7 @@ const formatEditableFlexibleNumber = (params: any) => {
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return formatThousands(params.value, 3)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
||||||
@ -476,7 +526,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.basic ?? null,
|
: getCheckedBenchmarkBudgetSplitByLandArea(params.data)?.basic ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetBasicChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
},
|
},
|
||||||
@ -491,7 +541,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.optional ?? null,
|
: getCheckedBenchmarkBudgetSplitByLandArea(params.data)?.optional ?? null,
|
||||||
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
cellRenderer: createBudgetCellRendererWithCheck('benchmarkBudgetOptionalChecked'),
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
},
|
},
|
||||||
@ -506,7 +556,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
valueGetter: params =>
|
valueGetter: params =>
|
||||||
params.node?.rowPinned
|
params.node?.rowPinned
|
||||||
? null
|
? null
|
||||||
: getBenchmarkBudgetSplitByLandArea(params.data)?.total ?? null,
|
: getCheckedBenchmarkBudgetSplitByLandArea(params.data)?.total ?? null,
|
||||||
valueFormatter: formatReadonlyMoney
|
valueFormatter: formatReadonlyMoney
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -527,7 +577,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatConsultCategoryFactor
|
valueFormatter: formatConsultCategoryFactor
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -542,7 +592,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatMajorFactor
|
valueFormatter: formatMajorFactor
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -557,7 +607,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -572,7 +622,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
'editable-cell-empty': params =>
|
'editable-cell-empty': params =>
|
||||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
|
valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
|
||||||
valueFormatter: formatEditableNumber
|
valueFormatter: formatEditableNumber
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -615,12 +665,12 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
|||||||
const autoGroupColumnDef: ColDef = {
|
const autoGroupColumnDef: ColDef = {
|
||||||
headerName: '专业编码以及工程专业名称',
|
headerName: '专业编码以及工程专业名称',
|
||||||
minWidth: 250,
|
minWidth: 250,
|
||||||
pinned: 'left',
|
|
||||||
flex: 2,
|
flex: 2,
|
||||||
|
|
||||||
cellRendererParams: {
|
cellRendererParams: {
|
||||||
suppressCount: true
|
suppressCount: true
|
||||||
},
|
},
|
||||||
|
colSpan: getMergeColSpanBeforeTotal,
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (params.node?.rowPinned) {
|
if (params.node?.rowPinned) {
|
||||||
return totalLabel.value
|
return totalLabel.value
|
||||||
@ -678,7 +728,8 @@ const pinnedTopRowData = computed(() => [
|
|||||||
|
|
||||||
const syncComputedValuesToDetailRows = () => {
|
const syncComputedValuesToDetailRows = () => {
|
||||||
for (const row of detailRows.value) {
|
for (const row of detailRows.value) {
|
||||||
const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
|
const benchmarkBudgetRawSplit = getBenchmarkBudgetSplitByLandArea(row)
|
||||||
|
const benchmarkBudgetSplit = getCheckedBenchmarkBudgetSplitByLandArea(row)
|
||||||
const budgetFeeSplit = benchmarkBudgetSplit
|
const budgetFeeSplit = benchmarkBudgetSplit
|
||||||
? getScaleBudgetFeeSplit({
|
? getScaleBudgetFeeSplit({
|
||||||
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
|
||||||
@ -693,8 +744,8 @@ const syncComputedValuesToDetailRows = () => {
|
|||||||
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
row.benchmarkBudget = benchmarkBudgetSplit?.total ?? null
|
||||||
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
row.benchmarkBudgetBasic = benchmarkBudgetSplit?.basic ?? null
|
||||||
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
row.benchmarkBudgetOptional = benchmarkBudgetSplit?.optional ?? null
|
||||||
row.basicFormula = benchmarkBudgetSplit?.basicFormula ?? ''
|
row.basicFormula = benchmarkBudgetRawSplit?.basicFormula ?? ''
|
||||||
row.optionalFormula = benchmarkBudgetSplit?.optionalFormula ?? ''
|
row.optionalFormula = benchmarkBudgetRawSplit?.optionalFormula ?? ''
|
||||||
row.budgetFee = budgetFeeSplit?.total ?? null
|
row.budgetFee = budgetFeeSplit?.total ?? null
|
||||||
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
row.budgetFeeBasic = budgetFeeSplit?.basic ?? null
|
||||||
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
row.budgetFeeOptional = budgetFeeSplit?.optional ?? null
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import localforage from 'localforage'
|
|||||||
import { taskList } from '@/sql'
|
import { taskList } from '@/sql'
|
||||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||||
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
|
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
|
||||||
import { formatThousands } from '@/lib/numberFormat'
|
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import { parseNumberOrNull } from '@/lib/number'
|
import { parseNumberOrNull } from '@/lib/number'
|
||||||
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
||||||
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
||||||
@ -187,7 +187,10 @@ const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parseSanitizedNumberOrNull = (value: unknown) =>
|
const parseSanitizedNumberOrNull = (value: unknown) =>
|
||||||
parseNumberOrNull(value, { sanitize: true, precision: 2 })
|
parseNumberOrNull(value, { sanitize: true, precision: 3 })
|
||||||
|
|
||||||
|
const parseSanitizedAdoptedPriceOrNull = (value: unknown) =>
|
||||||
|
parseNumberOrNull(value, { sanitize: true, precision: 6 })
|
||||||
|
|
||||||
const calcBasicFee = (row: DetailRow | undefined) => {
|
const calcBasicFee = (row: DetailRow | undefined) => {
|
||||||
if (!row || isNoTaskRow(row)) return null
|
if (!row || isNoTaskRow(row)) return null
|
||||||
@ -227,7 +230,7 @@ const formatEditableNumber = (params: any) => {
|
|||||||
return '点击输入'
|
return '点击输入'
|
||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
return Number(params.value).toFixed(2)
|
return formatThousandsFlexible(params.value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
const spanRowsByTaskName = (params: any) => {
|
const spanRowsByTaskName = (params: any) => {
|
||||||
@ -294,7 +297,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
!isNoTaskRow(params.data) &&
|
!isNoTaskRow(params.data) &&
|
||||||
(params.value == null || params.value === '')
|
(params.value == null || params.value === '')
|
||||||
},
|
},
|
||||||
valueParser: params => parseSanitizedNumberOrNull(params.newValue),
|
valueParser: params => parseSanitizedAdoptedPriceOrNull(params.newValue),
|
||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (isNoTaskRow(params.data)) return '无'
|
if (isNoTaskRow(params.data)) return '无'
|
||||||
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
|
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
|
||||||
@ -302,7 +305,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
}
|
}
|
||||||
if (params.value == null) return ''
|
if (params.value == null) return ''
|
||||||
const unit = params.data?.unit || ''
|
const unit = params.data?.unit || ''
|
||||||
return `${formatThousands(params.value)}${unit}`
|
return `${formatThousandsFlexible(params.value, 6)}${unit}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -354,7 +357,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
valueFormatter: params => {
|
valueFormatter: params => {
|
||||||
if (isNoTaskRow(params.data)) return '无'
|
if (isNoTaskRow(params.data)) return '无'
|
||||||
if (params.value == null || params.value === '') return ''
|
if (params.value == null || params.value === '') return ''
|
||||||
return formatThousands(roundTo(params.value, 2))
|
return formatThousandsFlexible(roundTo(params.value, 3), 3)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,8 +8,9 @@ import { myTheme ,gridOptions} from '@/lib/diyAgGridOptions'
|
|||||||
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
||||||
import { addNumbers } from '@/lib/decimal'
|
import { addNumbers } from '@/lib/decimal'
|
||||||
import { parseNumberOrNull } from '@/lib/number'
|
import { parseNumberOrNull } from '@/lib/number'
|
||||||
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
|
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||||
import {
|
import {
|
||||||
|
ensurePricingMethodDetailRowsForServices,
|
||||||
getPricingMethodTotalsForService,
|
getPricingMethodTotalsForService,
|
||||||
getPricingMethodTotalsForServices,
|
getPricingMethodTotalsForServices,
|
||||||
type PricingMethodTotals
|
type PricingMethodTotals
|
||||||
@ -310,7 +311,7 @@ const dragRectStyle = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const numericParser = (newValue: any): number | null => {
|
const numericParser = (newValue: any): number | null => {
|
||||||
return parseNumberOrNull(newValue, { precision: 2 })
|
return parseNumberOrNull(newValue, { precision: 3 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueOrZero = (v: number | null | undefined) => (typeof v === 'number' ? v : 0)
|
const valueOrZero = (v: number | null | undefined) => (typeof v === 'number' ? v : 0)
|
||||||
@ -395,6 +396,11 @@ const clearRowValues = async (row: DetailRow) => {
|
|||||||
// 若该服务编辑页已打开,先关闭,避免子页面卸载时把旧数据写回缓? tabStore.removeTab(`zxfw-edit-${props.contractId}-${row.id}`)
|
// 若该服务编辑页已打开,先关闭,避免子页面卸载时把旧数据写回缓? tabStore.removeTab(`zxfw-edit-${props.contractId}-${row.id}`)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await clearPricingPaneValues(row.id)
|
await clearPricingPaneValues(row.id)
|
||||||
|
await ensurePricingMethodDetailRowsForServices({
|
||||||
|
contractId: props.contractId,
|
||||||
|
serviceIds: [row.id],
|
||||||
|
options: PRICING_TOTALS_OPTIONS
|
||||||
|
})
|
||||||
const totals = await getPricingMethodTotalsForService({
|
const totals = await getPricingMethodTotalsForService({
|
||||||
contractId: props.contractId,
|
contractId: props.contractId,
|
||||||
serviceId: row.id,
|
serviceId: row.id,
|
||||||
@ -502,7 +508,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
return params.data.investScale
|
return params.data.investScale
|
||||||
},
|
},
|
||||||
valueParser: params => numericParser(params.newValue),
|
valueParser: params => numericParser(params.newValue),
|
||||||
valueFormatter: params => (params.value == null ? '' : formatThousands(params.value))
|
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '用地规模法',
|
headerName: '用地规模法',
|
||||||
@ -519,7 +525,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
return params.data.landScale
|
return params.data.landScale
|
||||||
},
|
},
|
||||||
valueParser: params => numericParser(params.newValue),
|
valueParser: params => numericParser(params.newValue),
|
||||||
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 2))
|
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '工作量法',
|
headerName: '工作量法',
|
||||||
@ -537,7 +543,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
},
|
},
|
||||||
// editable: params => !params.node?.rowPinned && !isFixedRow(params.data),
|
// editable: params => !params.node?.rowPinned && !isFixedRow(params.data),
|
||||||
valueParser: params => numericParser(params.newValue),
|
valueParser: params => numericParser(params.newValue),
|
||||||
valueFormatter: params => (params.value == null ? '' : formatThousands(params.value))
|
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '工时法',
|
headerName: '工时法',
|
||||||
@ -555,7 +561,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
},
|
},
|
||||||
// editable: params => !params.node?.rowPinned && !isFixedRow(params.data),
|
// editable: params => !params.node?.rowPinned && !isFixedRow(params.data),
|
||||||
valueParser: params => numericParser(params.newValue),
|
valueParser: params => numericParser(params.newValue),
|
||||||
valueFormatter: params => (params.value == null ? '' : formatThousands(params.value))
|
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '小计',
|
headerName: '小计',
|
||||||
@ -576,7 +582,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
|||||||
valueOrZero(params.data.hourly)
|
valueOrZero(params.data.hourly)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
valueFormatter: params => (params.value == null ? '' : formatThousands(params.value))
|
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '操作',
|
headerName: '操作',
|
||||||
@ -634,6 +640,21 @@ const applyFixedRowTotals = (rows: DetailRow[]) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSelectedServiceIdsWithoutFixed = () =>
|
||||||
|
detailRows.value
|
||||||
|
.filter(row => !isFixedRow(row))
|
||||||
|
.map(row => String(row.id))
|
||||||
|
|
||||||
|
const ensurePricingDetailRowsForCurrentSelection = async () => {
|
||||||
|
const serviceIds = getSelectedServiceIdsWithoutFixed()
|
||||||
|
if (serviceIds.length === 0) return
|
||||||
|
await ensurePricingMethodDetailRowsForServices({
|
||||||
|
contractId: props.contractId,
|
||||||
|
serviceIds,
|
||||||
|
options: PRICING_TOTALS_OPTIONS
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const fillPricingTotalsForServiceIds = async (serviceIds: string[]) => {
|
const fillPricingTotalsForServiceIds = async (serviceIds: string[]) => {
|
||||||
const targetIds = Array.from(
|
const targetIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
@ -648,6 +669,12 @@ const fillPricingTotalsForServiceIds = async (serviceIds: string[]) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await ensurePricingMethodDetailRowsForServices({
|
||||||
|
contractId: props.contractId,
|
||||||
|
serviceIds: targetIds,
|
||||||
|
options: PRICING_TOTALS_OPTIONS
|
||||||
|
})
|
||||||
|
|
||||||
const totalsByServiceId = await getPricingMethodTotalsForServices({
|
const totalsByServiceId = await getPricingMethodTotalsForServices({
|
||||||
contractId: props.contractId,
|
contractId: props.contractId,
|
||||||
serviceIds: targetIds,
|
serviceIds: targetIds,
|
||||||
@ -734,6 +761,7 @@ const handleServiceSelectionChange = async (ids: string[]) => {
|
|||||||
const nextSelectedSet = new Set(selectedIds.value)
|
const nextSelectedSet = new Set(selectedIds.value)
|
||||||
const addedIds = selectedIds.value.filter(id => !prevIds.includes(id) && nextSelectedSet.has(id))
|
const addedIds = selectedIds.value.filter(id => !prevIds.includes(id) && nextSelectedSet.has(id))
|
||||||
await fillPricingTotalsForServiceIds(addedIds)
|
await fillPricingTotalsForServiceIds(addedIds)
|
||||||
|
await ensurePricingDetailRowsForCurrentSelection()
|
||||||
await saveToIndexedDB()
|
await saveToIndexedDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,6 +944,7 @@ const loadFromIndexedDB = async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
detailRows.value = applyFixedRowTotals(detailRows.value)
|
detailRows.value = applyFixedRowTotals(detailRows.value)
|
||||||
|
await ensurePricingDetailRowsForCurrentSelection()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('loadFromIndexedDB failed:', error)
|
console.error('loadFromIndexedDB failed:', error)
|
||||||
selectedIds.value = []
|
selectedIds.value = []
|
||||||
|
|||||||
@ -121,6 +121,20 @@ const getDefaultMajorFactorById = (id: string) => {
|
|||||||
return toFiniteNumberOrNull(major?.defCoe)
|
return toFiniteNumberOrNull(major?.defCoe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isCostMajorById = (id: string) => {
|
||||||
|
const resolvedId = majorById.has(id) ? id : majorIdAliasMap.get(id) || id
|
||||||
|
const major = majorById.get(resolvedId)
|
||||||
|
if (!major) return false
|
||||||
|
return major.hasCost !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAreaMajorById = (id: string) => {
|
||||||
|
const resolvedId = majorById.has(id) ? id : majorIdAliasMap.get(id) || id
|
||||||
|
const major = majorById.get(resolvedId)
|
||||||
|
if (!major) return false
|
||||||
|
return major.hasArea !== false
|
||||||
|
}
|
||||||
|
|
||||||
const isDualScaleMajorById = (id: string) => {
|
const isDualScaleMajorById = (id: string) => {
|
||||||
const resolvedId = majorById.has(id) ? id : majorIdAliasMap.get(id) || id
|
const resolvedId = majorById.has(id) ? id : majorIdAliasMap.get(id) || id
|
||||||
const major = majorById.get(resolvedId)
|
const major = majorById.get(resolvedId)
|
||||||
@ -299,6 +313,44 @@ const getOnlyCostScaleBudgetFee = (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buildOnlyCostScaleDetailRows = (
|
||||||
|
serviceId: string,
|
||||||
|
rowsFromDb: Array<Record<string, unknown>> | undefined,
|
||||||
|
consultCategoryFactorMap?: Map<string, number | null>,
|
||||||
|
majorFactorMap?: Map<string, number | null>,
|
||||||
|
industryId?: string | null
|
||||||
|
) => {
|
||||||
|
const totalAmount = sumByNumber(rowsFromDb || [], row =>
|
||||||
|
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
|
||||||
|
)
|
||||||
|
const onlyRow = (rowsFromDb || []).find(row => String(row?.id || '') === ONLY_COST_SCALE_ROW_ID)
|
||||||
|
const consultCategoryFactor =
|
||||||
|
toFiniteNumberOrNull(onlyRow?.consultCategoryFactor) ??
|
||||||
|
consultCategoryFactorMap?.get(String(serviceId)) ??
|
||||||
|
getDefaultConsultCategoryFactor(serviceId)
|
||||||
|
const industryMajorEntry = getIndustryMajorEntryByIndustryId(industryId)
|
||||||
|
const majorFactor =
|
||||||
|
toFiniteNumberOrNull(onlyRow?.majorFactor) ??
|
||||||
|
(industryMajorEntry ? majorFactorMap?.get(industryMajorEntry.id) ?? null : null) ??
|
||||||
|
toFiniteNumberOrNull(industryMajorEntry?.item?.defCoe) ??
|
||||||
|
1
|
||||||
|
const workStageFactor = toFiniteNumberOrNull(onlyRow?.workStageFactor) ?? 1
|
||||||
|
const workRatio = toFiniteNumberOrNull(onlyRow?.workRatio) ?? 100
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: ONLY_COST_SCALE_ROW_ID,
|
||||||
|
amount: totalAmount,
|
||||||
|
consultCategoryFactor,
|
||||||
|
majorFactor,
|
||||||
|
workStageFactor,
|
||||||
|
workRatio,
|
||||||
|
benchmarkBudgetBasicChecked: typeof onlyRow?.benchmarkBudgetBasicChecked === 'boolean' ? onlyRow.benchmarkBudgetBasicChecked : true,
|
||||||
|
benchmarkBudgetOptionalChecked: typeof onlyRow?.benchmarkBudgetOptionalChecked === 'boolean' ? onlyRow.benchmarkBudgetOptionalChecked : true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const getLandBudgetFee = (row: ScaleRow) => {
|
const getLandBudgetFee = (row: ScaleRow) => {
|
||||||
return getScaleBudgetFee({
|
return getScaleBudgetFee({
|
||||||
benchmarkBudget: getBenchmarkBudgetByLandArea(row.landArea),
|
benchmarkBudget: getBenchmarkBudgetByLandArea(row.landArea),
|
||||||
@ -493,6 +545,7 @@ export const getPricingMethodTotalsForService = async (params: {
|
|||||||
majorFactorMap
|
majorFactorMap
|
||||||
)
|
)
|
||||||
return sumByNumber(investRows, row => {
|
return sumByNumber(investRows, row => {
|
||||||
|
if (!isCostMajorById(row.id)) return null
|
||||||
if (excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return null
|
if (excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return null
|
||||||
return getInvestmentBudgetFee(row)
|
return getInvestmentBudgetFee(row)
|
||||||
})
|
})
|
||||||
@ -505,7 +558,7 @@ export const getPricingMethodTotalsForService = async (params: {
|
|||||||
consultCategoryFactorMap,
|
consultCategoryFactorMap,
|
||||||
majorFactorMap
|
majorFactorMap
|
||||||
)
|
)
|
||||||
const landScale = sumByNumber(landRows, row => getLandBudgetFee(row))
|
const landScale = sumByNumber(landRows, row => (isAreaMajorById(row.id) ? getLandBudgetFee(row) : null))
|
||||||
|
|
||||||
const defaultWorkloadRows = buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap)
|
const defaultWorkloadRows = buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap)
|
||||||
const workload =
|
const workload =
|
||||||
@ -532,6 +585,103 @@ export const getPricingMethodTotalsForService = async (params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ensurePricingMethodDetailRowsForServices = async (params: {
|
||||||
|
contractId: string
|
||||||
|
serviceIds: Array<string | number>
|
||||||
|
options?: PricingMethodTotalsOptions
|
||||||
|
}) => {
|
||||||
|
const uniqueServiceIds = Array.from(new Set(params.serviceIds.map(serviceId => String(serviceId))))
|
||||||
|
if (uniqueServiceIds.length === 0) return
|
||||||
|
|
||||||
|
const htDbKey = `ht-info-v3-${params.contractId}`
|
||||||
|
const consultFactorDbKey = `ht-consult-category-factor-v1-${params.contractId}`
|
||||||
|
const majorFactorDbKey = `ht-major-factor-v1-${params.contractId}`
|
||||||
|
const baseInfoDbKey = 'xm-base-info-v1'
|
||||||
|
|
||||||
|
const [htData, consultFactorData, majorFactorData, baseInfo] = await Promise.all([
|
||||||
|
localforage.getItem<StoredDetailRowsState>(htDbKey),
|
||||||
|
localforage.getItem<StoredFactorState>(consultFactorDbKey),
|
||||||
|
localforage.getItem<StoredFactorState>(majorFactorDbKey),
|
||||||
|
localforage.getItem<XmBaseInfoState>(baseInfoDbKey)
|
||||||
|
])
|
||||||
|
|
||||||
|
const consultCategoryFactorMap = buildConsultCategoryFactorMap(consultFactorData)
|
||||||
|
const majorFactorMap = buildMajorFactorMap(majorFactorData)
|
||||||
|
const industryId = typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''
|
||||||
|
const excludeInvestmentCostAndAreaRows = params.options?.excludeInvestmentCostAndAreaRows === true
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
uniqueServiceIds.map(async serviceId => {
|
||||||
|
const investDbKey = `tzGMF-${params.contractId}-${serviceId}`
|
||||||
|
const landDbKey = `ydGMF-${params.contractId}-${serviceId}`
|
||||||
|
const workloadDbKey = `gzlF-${params.contractId}-${serviceId}`
|
||||||
|
const hourlyDbKey = `hourlyPricing-${params.contractId}-${serviceId}`
|
||||||
|
const [investData, landData, workloadData, hourlyData] = await Promise.all([
|
||||||
|
localforage.getItem<StoredDetailRowsState>(investDbKey),
|
||||||
|
localforage.getItem<StoredDetailRowsState>(landDbKey),
|
||||||
|
localforage.getItem<StoredDetailRowsState>(workloadDbKey),
|
||||||
|
localforage.getItem<StoredDetailRowsState>(hourlyDbKey)
|
||||||
|
])
|
||||||
|
|
||||||
|
const shouldInitInvest = !Array.isArray(investData?.detailRows) || investData!.detailRows!.length === 0
|
||||||
|
const shouldInitLand = !Array.isArray(landData?.detailRows) || landData!.detailRows!.length === 0
|
||||||
|
const shouldInitWorkload = !Array.isArray(workloadData?.detailRows) || workloadData!.detailRows!.length === 0
|
||||||
|
const shouldInitHourly = !Array.isArray(hourlyData?.detailRows) || hourlyData!.detailRows!.length === 0
|
||||||
|
|
||||||
|
const writeTasks: Promise<unknown>[] = []
|
||||||
|
|
||||||
|
if (shouldInitInvest) {
|
||||||
|
const onlyCostScale = isOnlyCostScaleService(serviceId)
|
||||||
|
const investRows = onlyCostScale
|
||||||
|
? buildOnlyCostScaleDetailRows(
|
||||||
|
serviceId,
|
||||||
|
(htData?.detailRows as Array<Record<string, unknown>> | undefined),
|
||||||
|
consultCategoryFactorMap,
|
||||||
|
majorFactorMap,
|
||||||
|
industryId
|
||||||
|
)
|
||||||
|
: resolveScaleRows(
|
||||||
|
serviceId,
|
||||||
|
null,
|
||||||
|
htData,
|
||||||
|
consultCategoryFactorMap,
|
||||||
|
majorFactorMap
|
||||||
|
).filter(row => {
|
||||||
|
if (!isCostMajorById(row.id)) return false
|
||||||
|
if (excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
writeTasks.push(localforage.setItem(investDbKey, { detailRows: investRows }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldInitLand) {
|
||||||
|
const landRows = resolveScaleRows(
|
||||||
|
serviceId,
|
||||||
|
null,
|
||||||
|
htData,
|
||||||
|
consultCategoryFactorMap,
|
||||||
|
majorFactorMap
|
||||||
|
).filter(row => isAreaMajorById(row.id))
|
||||||
|
writeTasks.push(localforage.setItem(landDbKey, { detailRows: landRows }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldInitWorkload) {
|
||||||
|
const workloadRows = buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap)
|
||||||
|
writeTasks.push(localforage.setItem(workloadDbKey, { detailRows: workloadRows }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldInitHourly) {
|
||||||
|
const hourlyRows = buildDefaultHourlyRows()
|
||||||
|
writeTasks.push(localforage.setItem(hourlyDbKey, { detailRows: hourlyRows }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writeTasks.length > 0) {
|
||||||
|
await Promise.all(writeTasks)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const getPricingMethodTotalsForServices = async (params: {
|
export const getPricingMethodTotalsForServices = async (params: {
|
||||||
contractId: string
|
contractId: string
|
||||||
serviceIds: Array<string | number>
|
serviceIds: Array<string | number>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
RowAutoHeightModule,
|
RowAutoHeightModule,
|
||||||
TextEditorModule,
|
TextEditorModule,
|
||||||
TooltipModule,
|
TooltipModule,
|
||||||
UndoRedoEditModule,RenderApiModule
|
UndoRedoEditModule,RenderApiModule ,ColumnApiModule ,CellSpanModule
|
||||||
|
|
||||||
} from 'ag-grid-community'
|
} from 'ag-grid-community'
|
||||||
import {
|
import {
|
||||||
@ -19,7 +19,7 @@ import {
|
|||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
LicenseManager,
|
LicenseManager,
|
||||||
RowGroupingModule,
|
RowGroupingModule,
|
||||||
TreeDataModule,ContextMenuModule
|
TreeDataModule,ContextMenuModule,ValidationModule
|
||||||
} from 'ag-grid-enterprise'
|
} from 'ag-grid-enterprise'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
@ -40,14 +40,14 @@ const AG_GRID_MODULES = [
|
|||||||
LargeTextEditorModule,
|
LargeTextEditorModule,
|
||||||
UndoRedoEditModule,
|
UndoRedoEditModule,
|
||||||
CellStyleModule,
|
CellStyleModule,
|
||||||
PinnedRowModule,RenderApiModule ,
|
PinnedRowModule,RenderApiModule ,ColumnApiModule ,
|
||||||
TooltipModule,
|
TooltipModule,
|
||||||
TreeDataModule,
|
TreeDataModule,
|
||||||
AggregationModule,
|
AggregationModule,
|
||||||
RowGroupingModule,
|
RowGroupingModule,
|
||||||
CellSelectionModule,
|
CellSelectionModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
LocaleModule,
|
LocaleModule,ValidationModule ,CellSpanModule
|
||||||
]
|
]
|
||||||
|
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|||||||
@ -501,7 +501,6 @@ export function getBasicFeeFromScale(
|
|||||||
* @returns 导出流程完成后的 Promise
|
* @returns 导出流程完成后的 Promise
|
||||||
*/
|
*/
|
||||||
export async function exportFile(fileName: string, data: any): Promise<void> {
|
export async function exportFile(fileName: string, data: any): Promise<void> {
|
||||||
console.log(data)
|
|
||||||
if (window.showSaveFilePicker) {
|
if (window.showSaveFilePicker) {
|
||||||
const handle = await window.showSaveFilePicker({
|
const handle = await window.showSaveFilePicker({
|
||||||
suggestedName: fileName,
|
suggestedName: fileName,
|
||||||
@ -727,7 +726,7 @@ async function generateTemplate(data) {
|
|||||||
try {
|
try {
|
||||||
// 获取模板
|
// 获取模板
|
||||||
let templateExcel = 'template20260226001test010';
|
let templateExcel = 'template20260226001test010';
|
||||||
let templateUrl = `./public/${templateExcel}.xlsx`;
|
let templateUrl = `./${templateExcel}.xlsx`;
|
||||||
let buf = await (await fetch(templateUrl)).arrayBuffer();
|
let buf = await (await fetch(templateUrl)).arrayBuffer();
|
||||||
let workbook = new ExcelJS.Workbook();
|
let workbook = new ExcelJS.Workbook();
|
||||||
await workbook.xlsx.load(buf);
|
await workbook.xlsx.load(buf);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user