<script lang="ts" setup>
|
import { computed, ref, unref, watch } from 'vue';
|
|
import { useAttrs } from '@jnpf/hooks';
|
import { calcRPN, getAmountChinese, mergeNumberOfExps, thousandsFormat, toRPN } from '@jnpf/utils';
|
|
import { $t } from '@vben/locales';
|
|
import { useDebounceFn } from '@vueuse/core';
|
import { Form } from 'ant-design-vue';
|
import dayjs from 'dayjs';
|
import { ceil, floor, round } from 'lodash-es';
|
|
defineOptions({ inheritAttrs: false, name: 'JnpfCalculate' });
|
const props = defineProps({
|
componentVModel: String,
|
dateCalConfig: Object,
|
detailed: {
|
default: false,
|
type: Boolean,
|
},
|
disabled: {
|
default: false,
|
type: Boolean,
|
},
|
expression: Array,
|
formData: Object,
|
isAmountChinese: {
|
default: false,
|
type: Boolean,
|
},
|
isStorage: {
|
default: 0,
|
type: Number,
|
},
|
precision: Number,
|
roundType: {
|
default: 1,
|
type: Number,
|
},
|
rowIndex: Number,
|
tableVModel: String,
|
thousands: {
|
default: false,
|
type: Boolean,
|
},
|
type: {
|
default: 1,
|
type: Number,
|
},
|
value: [Number, String],
|
});
|
const emit = defineEmits(['update:value', 'change']);
|
const attrs: any = useAttrs({ excludeDefaultKeys: false });
|
const innerValue = ref<any>('');
|
const computeExps = ref<any>(null);
|
const startTime = ref<any>(new Date());
|
const endTime = ref<any>(new Date());
|
const formItemContext = Form.useInjectFormItemContext();
|
const unitObj = {
|
d: '天',
|
h: '时',
|
M: '月',
|
m: '分',
|
s: '秒',
|
Y: '年',
|
};
|
|
const getExp = computed(() => toRPN(mergeNumberOfExps(props.expression)));
|
const getChineseName = computed(() => (!props.isAmountChinese || (!innerValue.value && innerValue.value !== 0) ? '' : getAmountChinese(innerValue.value)));
|
const getValue = computed(() => (props.thousands ? thousandsFormat(innerValue.value) : innerValue.value));
|
const getStyle = computed(() => (Reflect.has(unref(attrs), 'style') ? unref(attrs).style : {}));
|
|
const getBindValue = computed(() => {
|
const data: any = {
|
disabled: props.disabled,
|
placeholder: props.isStorage ? $t('component.jnpf.calculate.storage') : $t('component.jnpf.calculate.unStorage'),
|
readonly: true,
|
};
|
if (props.type === 2) data.addonAfter = unitObj[props.dateCalConfig?.dateUnit];
|
return data;
|
});
|
|
watch(
|
() => props.formData,
|
(val) => {
|
if (!val) return;
|
if (!computeExps.value) {
|
// formData更新可能比较频繁
|
computeExps.value = useDebounceFn(execRPN, 100);
|
}
|
unref(computeExps)();
|
},
|
{ deep: true, immediate: true },
|
);
|
watch(
|
() => unref(props.value),
|
(val) => {
|
innerValue.value = val;
|
},
|
{ immediate: true },
|
);
|
|
/**
|
* 获取指定组件的值
|
*/
|
function getFormVal(vModel) {
|
try {
|
const formData: any = props.formData;
|
if (vModel.includes('.')) {
|
const [tableVModel, cmpVModel] = vModel.split('.');
|
if (typeof props.rowIndex === 'number') {
|
if (!Array.isArray(formData[tableVModel]) || formData[tableVModel].length < props.rowIndex + 1) return 0;
|
return formData[tableVModel][props.rowIndex][cmpVModel] || 0;
|
} else {
|
if (formData[tableVModel].length === 0) return 0;
|
return formData[tableVModel].reduce((sum, c) => (c[cmpVModel] ? Number(c[cmpVModel]) : 0) + sum, 0);
|
}
|
}
|
return formData[vModel] || 0;
|
} catch (error) {
|
console.warn('计算公式出错, 可能包含无效的组件值', error);
|
return 0;
|
}
|
}
|
/**
|
* 计算表达式
|
*/
|
function execRPN() {
|
const formData: any = props.formData;
|
if (props.type === 2) {
|
if (props.dateCalConfig?.startTimeType == 1) startTime.value = props.dateCalConfig.startTimeValue;
|
if (props.dateCalConfig?.startTimeType == 2) startTime.value = getFormVal(props.dateCalConfig.startRelationField);
|
if (props.dateCalConfig?.endTimeType == 1) endTime.value = props.dateCalConfig.endTimeValue;
|
if (props.dateCalConfig?.endTimeType == 2) endTime.value = getFormVal(props.dateCalConfig.endRelationField);
|
innerValue.value = calDateDiff(startTime.value, endTime.value, props.dateCalConfig?.dateUnit);
|
} else {
|
const temp = unref(getExp).map((t) => (typeof t === 'object' ? getFormVal(t.__vModel__) : t));
|
innerValue.value = Number.parseFloat(calcRPN(temp));
|
if (Number.isNaN(innerValue.value as unknown as number)) (innerValue.value as unknown as number) = 0;
|
innerValue.value = getRoundValue(Number.parseFloat(innerValue.value));
|
}
|
if ((props.rowIndex as any) >= 0 && props.componentVModel && props.tableVModel) {
|
if (
|
formData[props.tableVModel][props.rowIndex as any] &&
|
formData[props.tableVModel][props.rowIndex as any][props.componentVModel] !== innerValue.value &&
|
props.isStorage
|
) {
|
emit('update:value', innerValue.value);
|
emit('change', innerValue.value);
|
formItemContext.onFieldChange();
|
}
|
} else {
|
if (props.isStorage) {
|
emit('update:value', innerValue.value);
|
formItemContext.onFieldChange();
|
}
|
}
|
}
|
function calDateDiff(startDate, endDate, unit) {
|
if (!startDate || !endDate) return '';
|
const start: any = dayjs(startDate);
|
let end: any = dayjs(endDate);
|
if (end.hour() === 0 && end.minute() === 0 && end.second() === 0 && props.dateCalConfig?.dateFormat === 2) {
|
end = end.endOf('d');
|
}
|
const diff = end.diff(start, unit == 'Y' ? 'y' : unit, true);
|
const data = !!diff || diff === 0 ? getRoundValue(diff) : '';
|
return data;
|
}
|
function getRoundValue(val) {
|
const precision = props.precision || 0;
|
if (props.roundType == 2) return floor(val, precision).toFixed(precision);
|
if (props.roundType == 3) return ceil(val);
|
if (props.roundType == 4) return floor(val);
|
return round(val, precision).toFixed(precision);
|
}
|
</script>
|
|
<template>
|
<div :style="getStyle" class="jnpf-calculate">
|
<a-input v-model:value="innerValue" v-bind="getBindValue" v-if="!detailed" />
|
<p v-else :title="getValue" class="detail-txt leading-[32px]">
|
{{ getValue }}
|
<span v-if="props.type === 2">{{ unitObj[props.dateCalConfig?.dateUnit] }}</span>
|
</p>
|
<p v-if="isAmountChinese && getChineseName" class="amount-chinese-name">{{ getChineseName }}</p>
|
</div>
|
</template>
|
<style lang="scss" scoped>
|
.jnpf-calculate {
|
.amount-chinese-name {
|
margin-top: 4px;
|
font-size: 14px;
|
line-height: 20px;
|
color: var(--text-color-secondary);
|
}
|
}
|
|
.ant-table,
|
.jnpf-vxe-table {
|
.jnpf-calculate {
|
.detail-txt {
|
overflow: hidden;
|
text-overflow: ellipsis;
|
line-height: 22px !important;
|
white-space: nowrap;
|
}
|
}
|
}
|
</style>
|