✨ 合同结算币种联动计费与运单计价单位,并完善合同与派车交互
结算币种驱动计费单价单位及运单/计划/总单计价单位;同步合同编号预览、部门前缀配置、派车备注与查看弹窗等交互优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -90,3 +90,11 @@ export const expireStats = params =>
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 按合同负责部门预览下一合同编号 */
|
||||||
|
export const nextContractNo = organizationId =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/next-contract-no`,
|
||||||
|
method: 'get',
|
||||||
|
params: { organizationId },
|
||||||
|
});
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export const contractCategoryOptions = [
|
|||||||
export const signTypeOptions = [
|
export const signTypeOptions = [
|
||||||
{ label: '新签', value: '新签' },
|
{ label: '新签', value: '新签' },
|
||||||
{ label: '续签', value: '续签' },
|
{ label: '续签', value: '续签' },
|
||||||
|
{ label: '补充协议', value: '补充协议' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const contractStageOptions = [
|
export const contractStageOptions = [
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
contractStageOptions,
|
contractStageOptions,
|
||||||
createCrudOption,
|
createCrudOption,
|
||||||
effectiveTypeOptions,
|
effectiveTypeOptions,
|
||||||
|
nonNegativeRule,
|
||||||
selectRule,
|
selectRule,
|
||||||
signTypeOptions,
|
signTypeOptions,
|
||||||
textRule,
|
textRule,
|
||||||
@@ -61,13 +62,14 @@ export const config = {
|
|||||||
['contractNo', '合同编号'],
|
['contractNo', '合同编号'],
|
||||||
['contractName', '合同名称'],
|
['contractName', '合同名称'],
|
||||||
['projectName', '所属项目'],
|
['projectName', '所属项目'],
|
||||||
['organizationName', '所属组织'],
|
['organizationName', '合同负责部门'],
|
||||||
['contractCategory', '合同类别'],
|
['contractCategory', '合同类别'],
|
||||||
['signType', '签约类型'],
|
['signType', '签约类型'],
|
||||||
['partyA', '甲方'],
|
['partyA', '甲方'],
|
||||||
['partyB', '乙方'],
|
['partyB', '乙方'],
|
||||||
['handlerUserName', '经办人'],
|
['handlerUserName', '经办人'],
|
||||||
['signDate', '签订日期'],
|
['signDate', '签订日期'],
|
||||||
|
['applyDate', '申请日期'],
|
||||||
['contractFormat', '合同格式'],
|
['contractFormat', '合同格式'],
|
||||||
['legalSealFlag', '是否需要加盖法人章'],
|
['legalSealFlag', '是否需要加盖法人章'],
|
||||||
['copyCount', '一式(份)'],
|
['copyCount', '一式(份)'],
|
||||||
@@ -76,7 +78,8 @@ export const config = {
|
|||||||
['templateFlag', '是否范本'],
|
['templateFlag', '是否范本'],
|
||||||
['originalContractNo', '原件合同编号'],
|
['originalContractNo', '原件合同编号'],
|
||||||
['electronicSealFlag', '是否电子章'],
|
['electronicSealFlag', '是否电子章'],
|
||||||
['remark', '备注', 2],
|
['mainContent', '合同主要内容', 2],
|
||||||
|
['remark', '其他事项说明', 2],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -294,17 +297,17 @@ export const option = createCrudOption([
|
|||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '所属组织',
|
label: '合同负责部门',
|
||||||
prop: 'organizationName',
|
prop: 'organizationName',
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 3,
|
searchOrder: 3,
|
||||||
searchslot: true,
|
searchslot: true,
|
||||||
searchPlaceholder: '请选择 所属组织',
|
searchPlaceholder: '请选择 合同负责部门',
|
||||||
formslot: true,
|
formslot: true,
|
||||||
order: 120,
|
order: 120,
|
||||||
dicData: [],
|
dicData: [],
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
rules: selectRule('所属组织'),
|
rules: selectRule('合同负责部门'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '合同类别',
|
label: '合同类别',
|
||||||
@@ -435,6 +438,17 @@ export const option = createCrudOption([
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
rules: [{ required: true, message: '请选择签订日期', trigger: 'change' }],
|
rules: [{ required: true, message: '请选择签订日期', trigger: 'change' }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '申请日期',
|
||||||
|
prop: 'applyDate',
|
||||||
|
type: 'date',
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
valueFormat: 'YYYY-MM-DD',
|
||||||
|
sortable: true,
|
||||||
|
minWidth: 130,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '结算类型',
|
label: '结算类型',
|
||||||
prop: 'settlementMode',
|
prop: 'settlementMode',
|
||||||
@@ -468,11 +482,10 @@ export const option = createCrudOption([
|
|||||||
{ label: '否', value: 0 },
|
{ label: '否', value: 0 },
|
||||||
{ label: '是', value: 1 },
|
{ label: '是', value: 1 },
|
||||||
],
|
],
|
||||||
value: 0,
|
clearable: true,
|
||||||
hide: true,
|
hide: true,
|
||||||
order: 70,
|
order: 70,
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
rules: selectRule('是否需要加盖法人章'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '一式(份)',
|
label: '一式(份)',
|
||||||
@@ -500,6 +513,10 @@ export const option = createCrudOption([
|
|||||||
hide: true,
|
hide: true,
|
||||||
order: 45,
|
order: 45,
|
||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: '请输入合同金额', trigger: 'blur' },
|
||||||
|
nonNegativeRule('合同金额'),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '是否范本',
|
label: '是否范本',
|
||||||
@@ -617,7 +634,19 @@ export const option = createCrudOption([
|
|||||||
minWidth: 110,
|
minWidth: 110,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '备注',
|
label: '合同主要内容',
|
||||||
|
prop: 'mainContent',
|
||||||
|
type: 'textarea',
|
||||||
|
span: 24,
|
||||||
|
minRows: 4,
|
||||||
|
hide: true,
|
||||||
|
order: 41,
|
||||||
|
maxlength: 1000,
|
||||||
|
showWordLimit: true,
|
||||||
|
rules: textRule('合同主要内容', 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '其他事项说明',
|
||||||
prop: 'remark',
|
prop: 'remark',
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
span: 24,
|
span: 24,
|
||||||
@@ -626,7 +655,7 @@ export const option = createCrudOption([
|
|||||||
order: 40,
|
order: 40,
|
||||||
maxlength: 2000,
|
maxlength: 2000,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: textRule('备注', 2000),
|
rules: textRule('其他事项说明', 2000),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '',
|
label: '',
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ export const option = {
|
|||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
viewDisplay: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '当前节点',
|
label: '当前节点',
|
||||||
@@ -108,6 +109,7 @@ export const option = {
|
|||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
viewDisplay: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '当前处理人',
|
label: '当前处理人',
|
||||||
@@ -115,6 +117,7 @@ export const option = {
|
|||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
viewDisplay: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '创建人',
|
label: '创建人',
|
||||||
@@ -122,6 +125,7 @@ export const option = {
|
|||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
viewDisplay: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
@@ -132,6 +136,7 @@ export const option = {
|
|||||||
minWidth: 170,
|
minWidth: 170,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
|
viewDisplay: false,
|
||||||
},
|
},
|
||||||
// ========== 新增/编辑表单专用(不进表格)==========
|
// ========== 新增/编辑表单专用(不进表格)==========
|
||||||
{
|
{
|
||||||
@@ -165,6 +170,7 @@ export const option = {
|
|||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
hide: true,
|
hide: true,
|
||||||
span: 24,
|
span: 24,
|
||||||
|
minRows: 2,
|
||||||
maxlength: 200,
|
maxlength: 200,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [{ max: 200, message: '备注不能超过200个字', trigger: 'blur' }],
|
rules: [{ max: 200, message: '备注不能超过200个字', trigger: 'blur' }],
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
const CURRENCY_NAME_MAP = {
|
||||||
|
CNY: '人民币',
|
||||||
|
RMB: '人民币',
|
||||||
|
USD: '美元',
|
||||||
|
EUR: '欧元',
|
||||||
|
JPY: '日元',
|
||||||
|
GBP: '英镑',
|
||||||
|
};
|
||||||
|
|
||||||
|
const CURRENCY_UNIT_MAP = {
|
||||||
|
CNY: '元',
|
||||||
|
RMB: '元',
|
||||||
|
USD: '美元',
|
||||||
|
EUR: '欧元',
|
||||||
|
JPY: '日元',
|
||||||
|
GBP: '英镑',
|
||||||
|
};
|
||||||
|
|
||||||
|
const CURRENCY_NAME_TO_UNIT = {
|
||||||
|
人民币: '元',
|
||||||
|
美元: '美元',
|
||||||
|
欧元: '欧元',
|
||||||
|
日元: '日元',
|
||||||
|
英镑: '英镑',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归一化币种代码(CNY → RMB)
|
||||||
|
*/
|
||||||
|
export function normalizeCurrencyCode(value, fallback = 'RMB') {
|
||||||
|
const raw =
|
||||||
|
typeof value === 'object'
|
||||||
|
? value?.dictKey || value?.value || value?.code || value?.dictValue || ''
|
||||||
|
: value;
|
||||||
|
const normalized = String(raw || fallback)
|
||||||
|
.trim()
|
||||||
|
.toUpperCase();
|
||||||
|
const code = normalized.includes(' - ') ? normalized.split(' - ')[0].trim() : normalized;
|
||||||
|
if (!code) return fallback;
|
||||||
|
return code === 'CNY' ? 'RMB' : code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 币种中文名称
|
||||||
|
*/
|
||||||
|
export function currencyName(value) {
|
||||||
|
const text = String(value || '');
|
||||||
|
if (text.includes(' - ')) return text.slice(text.indexOf(' - ') + 3);
|
||||||
|
return CURRENCY_NAME_MAP[text.toUpperCase()] || text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额单位文案:RMB/CNY → 元,USD → 美元
|
||||||
|
*/
|
||||||
|
export function currencyUnit(value) {
|
||||||
|
const text = String(value || '');
|
||||||
|
let code = text;
|
||||||
|
if (text.includes(' - ')) {
|
||||||
|
code = text.split(' - ')[0].trim();
|
||||||
|
}
|
||||||
|
const upperCode = code.toUpperCase();
|
||||||
|
if (CURRENCY_UNIT_MAP[upperCode]) {
|
||||||
|
return CURRENCY_UNIT_MAP[upperCode];
|
||||||
|
}
|
||||||
|
if (CURRENCY_NAME_TO_UNIT[code]) {
|
||||||
|
return CURRENCY_NAME_TO_UNIT[code];
|
||||||
|
}
|
||||||
|
return text || '元';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将计价单位前缀替换为当前币种单位,如 元/吨 → 美元/吨
|
||||||
|
*/
|
||||||
|
export function remapPriceUnitCurrency(priceUnit, currency) {
|
||||||
|
const text = String(priceUnit || '').trim();
|
||||||
|
if (!text || !text.includes('/')) return text;
|
||||||
|
const suffix = text.slice(text.indexOf('/'));
|
||||||
|
return `${currencyUnit(currency)}${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按数量单位生成默认计价单位
|
||||||
|
*/
|
||||||
|
export function resolvePriceUnitByQuantityUnit(quantityUnit, currency = 'RMB') {
|
||||||
|
const unit = currencyUnit(currency);
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
吨: `${unit}/吨`,
|
||||||
|
方: `${unit}/立方`,
|
||||||
|
件: `${unit}/件`,
|
||||||
|
}[String(quantityUnit || '').trim()] || ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字典中的计价单位选项映射到当前币种
|
||||||
|
*/
|
||||||
|
export function mapFeeUnitOptions(options = [], currency = 'RMB') {
|
||||||
|
const unit = currencyUnit(currency);
|
||||||
|
return (options || []).map(item => {
|
||||||
|
const label = String(item.label || item.value || '');
|
||||||
|
const value = String(item.value || item.label || '');
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
label: label.replace(/^元/, unit).replace(/^人民币/, unit),
|
||||||
|
value: value.replace(/^元/, unit).replace(/^人民币/, unit),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value" /></el-select></template
|
:value="item.value" /></el-select></template
|
||||||
></el-table-column>
|
></el-table-column>
|
||||||
<el-table-column label="单价(元)" width="180"
|
<el-table-column :label="unitPriceColumnLabel" width="180"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
><div v-if="readonly && usesRangeUnitPrice(row)" class="limit-list">
|
><div v-if="readonly && usesRangeUnitPrice(row)" class="limit-list">
|
||||||
<span v-for="(item, rangeIndex) in getRanges(row)" :key="rangeIndex">{{
|
<span v-for="(item, rangeIndex) in getRanges(row)" :key="rangeIndex">{{
|
||||||
@@ -379,6 +379,7 @@ import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
|||||||
import { InfoFilled } from '@element-plus/icons-vue';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import SectionCard from '@/components/section-card/main.vue';
|
import SectionCard from '@/components/section-card/main.vue';
|
||||||
|
import { currencyUnit } from '@/utils/currency';
|
||||||
|
|
||||||
const clone = value => JSON.parse(JSON.stringify(value));
|
const clone = value => JSON.parse(JSON.stringify(value));
|
||||||
/** 计费要素与计量单位维度的对应关系(这些要素可选计费单位) */
|
/** 计费要素与计量单位维度的对应关系(这些要素可选计费单位) */
|
||||||
@@ -431,6 +432,7 @@ export default {
|
|||||||
value: { type: Object, default: () => ({}) },
|
value: { type: Object, default: () => ({}) },
|
||||||
index: { type: Number, default: -1 },
|
index: { type: Number, default: -1 },
|
||||||
readonly: Boolean,
|
readonly: Boolean,
|
||||||
|
settlementCurrency: { type: String, default: 'RMB' },
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'save'],
|
emits: ['update:modelValue', 'save'],
|
||||||
data() {
|
data() {
|
||||||
@@ -500,6 +502,9 @@ export default {
|
|||||||
emitPath: true,
|
emitPath: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
unitPriceColumnLabel() {
|
||||||
|
return `单价(${currencyUnit(this.settlementCurrency)})`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadDictionaries();
|
this.loadDictionaries();
|
||||||
|
|||||||
@@ -173,13 +173,41 @@ import { downloadFileByUrl } from '@/utils/util';
|
|||||||
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
|
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
|
||||||
|
|
||||||
const CONTRACT_ATTACHMENT_TYPE_OTHER = '其他文件';
|
const CONTRACT_ATTACHMENT_TYPE_OTHER = '其他文件';
|
||||||
|
/** 用印材料历史取值兼容别名 */
|
||||||
|
export const CONTRACT_SEAL_MATERIAL_LOCATION = '用印材料';
|
||||||
|
export const CONTRACT_SEAL_MATERIAL_TYPE_ALIASES = {
|
||||||
|
合同文件: CONTRACT_SEAL_MATERIAL_LOCATION,
|
||||||
|
};
|
||||||
|
/** 佐证材料附件位置(历史:其它附件) */
|
||||||
|
export const CONTRACT_SUPPORTING_ATTACHMENT_LOCATION = '附件(佐证材料等)';
|
||||||
|
export const CONTRACT_ATTACHMENT_TYPE_DICT = 'contract_attachment_type';
|
||||||
|
export const CONTRACT_SUPPORTING_ATTACHMENT_TYPE_DICT = 'contract_supporting_attachment_type';
|
||||||
|
export const isContractSupportingAttachmentLocation = location => {
|
||||||
|
const text = String(location || '');
|
||||||
|
return (
|
||||||
|
text === CONTRACT_SUPPORTING_ATTACHMENT_LOCATION ||
|
||||||
|
text === '其它附件' ||
|
||||||
|
text === '其他附件'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const resolveAttachmentTypeDictCode = (location, fallback = CONTRACT_ATTACHMENT_TYPE_DICT) =>
|
||||||
|
isContractSupportingAttachmentLocation(location)
|
||||||
|
? CONTRACT_SUPPORTING_ATTACHMENT_TYPE_DICT
|
||||||
|
: fallback || CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||||
export const defaultContractAttachmentTypeOptions = [
|
export const defaultContractAttachmentTypeOptions = [
|
||||||
{ label: '合同文件', value: '合同文件' },
|
{ label: '用印材料', value: '用印材料' },
|
||||||
{ label: '双章归档文件', value: '双章归档文件' },
|
{ label: '双章归档文件', value: '双章归档文件' },
|
||||||
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
||||||
];
|
];
|
||||||
/** @deprecated 兼容旧引用,实际选项由业务字典 contract_attachment_type 动态加载 */
|
export const defaultContractSupportingAttachmentTypeOptions = [
|
||||||
|
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
||||||
|
];
|
||||||
|
/** @deprecated 兼容旧引用,实际选项由业务字典动态加载 */
|
||||||
export const contractAttachmentTypeOptions = defaultContractAttachmentTypeOptions;
|
export const contractAttachmentTypeOptions = defaultContractAttachmentTypeOptions;
|
||||||
|
export const isContractSealMaterialLocation = location => {
|
||||||
|
const text = String(location || '');
|
||||||
|
return text === CONTRACT_SEAL_MATERIAL_LOCATION || text === '合同文件';
|
||||||
|
};
|
||||||
export const attachmentFileTypes = [
|
export const attachmentFileTypes = [
|
||||||
'pdf',
|
'pdf',
|
||||||
'bmp',
|
'bmp',
|
||||||
@@ -234,8 +262,11 @@ const isOtherAttachmentTypeOption = item => {
|
|||||||
const attachmentTypeKeywords = fileType => {
|
const attachmentTypeKeywords = fileType => {
|
||||||
const text = String(fileType || '');
|
const text = String(fileType || '');
|
||||||
if (text.includes('双章')) return ['双章归档文件', '双章归档', '双章'];
|
if (text.includes('双章')) return ['双章归档文件', '双章归档', '双章'];
|
||||||
if (text.includes('合同') && !text.includes('其他') && !text.includes('其它')) {
|
if (
|
||||||
return ['合同文件', '合同'];
|
text.includes('用印') ||
|
||||||
|
(text.includes('合同') && !text.includes('其他') && !text.includes('其它'))
|
||||||
|
) {
|
||||||
|
return ['用印材料', '合同文件', '用印', '合同'];
|
||||||
}
|
}
|
||||||
if (isOtherAttachmentTypeOption({ label: text, value: text })) return [];
|
if (isOtherAttachmentTypeOption({ label: text, value: text })) return [];
|
||||||
return text ? [text] : [];
|
return text ? [text] : [];
|
||||||
@@ -254,8 +285,13 @@ export const resolveContractAttachmentType = (
|
|||||||
const list = options.length ? options : defaultContractAttachmentTypeOptions;
|
const list = options.length ? options : defaultContractAttachmentTypeOptions;
|
||||||
const otherType = resolveOtherAttachmentType(list);
|
const otherType = resolveOtherAttachmentType(list);
|
||||||
const values = list.map(item => item.value);
|
const values = list.map(item => item.value);
|
||||||
if (values.includes(row.fileType)) return row.fileType;
|
const rawFileType = String(row.fileType || '');
|
||||||
const matchedByLabel = list.find(item => String(item.label) === String(row.fileType));
|
const aliasedFileType = CONTRACT_SEAL_MATERIAL_TYPE_ALIASES[rawFileType] || rawFileType;
|
||||||
|
if (values.includes(aliasedFileType)) return aliasedFileType;
|
||||||
|
if (values.includes(rawFileType)) return rawFileType;
|
||||||
|
const matchedByLabel = list.find(
|
||||||
|
item => String(item.label) === aliasedFileType || String(item.label) === rawFileType
|
||||||
|
);
|
||||||
if (matchedByLabel) return matchedByLabel.value;
|
if (matchedByLabel) return matchedByLabel.value;
|
||||||
const fileName = normalizeAttachmentTypeText(attachmentName(row));
|
const fileName = normalizeAttachmentTypeText(attachmentName(row));
|
||||||
if (!fileName) return otherType;
|
if (!fileName) return otherType;
|
||||||
@@ -283,17 +319,32 @@ export const normalizeContractFileRows = (rows, options = defaultContractAttachm
|
|||||||
description: row.description || '',
|
description: row.description || '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const mapDictOptions = response => {
|
const mapDictOptions = (response, dictCode = CONTRACT_ATTACHMENT_TYPE_DICT) => {
|
||||||
const data = response?.data?.data || response?.data || [];
|
const data = response?.data?.data || response?.data || [];
|
||||||
const records = Array.isArray(data) ? data : data.records || [];
|
const records = Array.isArray(data) ? data : data.records || [];
|
||||||
|
const applySealAlias = dictCode === CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||||
return records
|
return records
|
||||||
.map(item => ({
|
.map(item => {
|
||||||
label: item.dictValue,
|
const rawLabel = String(item.dictValue || '');
|
||||||
value: item.dictKey,
|
const rawValue = String(item.dictKey || '');
|
||||||
}))
|
const label = applySealAlias
|
||||||
|
? CONTRACT_SEAL_MATERIAL_TYPE_ALIASES[rawLabel] ||
|
||||||
|
(rawLabel === '合同文件' ? CONTRACT_SEAL_MATERIAL_LOCATION : rawLabel)
|
||||||
|
: rawLabel;
|
||||||
|
const value = applySealAlias
|
||||||
|
? CONTRACT_SEAL_MATERIAL_TYPE_ALIASES[rawValue] ||
|
||||||
|
(rawValue === '合同文件' ? CONTRACT_SEAL_MATERIAL_LOCATION : rawValue)
|
||||||
|
: rawValue;
|
||||||
|
return { label, value };
|
||||||
|
})
|
||||||
.filter(item => item.label && item.value);
|
.filter(item => item.label && item.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultOptionsByDictCode = dictCode =>
|
||||||
|
dictCode === CONTRACT_SUPPORTING_ATTACHMENT_TYPE_DICT
|
||||||
|
? [...defaultContractSupportingAttachmentTypeOptions]
|
||||||
|
: [...defaultContractAttachmentTypeOptions];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ContractAttachmentSection',
|
name: 'ContractAttachmentSection',
|
||||||
components: { UploadFilled },
|
components: { UploadFilled },
|
||||||
@@ -307,8 +358,13 @@ export default {
|
|||||||
markApprovedOnUpload: Boolean,
|
markApprovedOnUpload: Boolean,
|
||||||
useUploadDialog: Boolean,
|
useUploadDialog: Boolean,
|
||||||
dialogOnly: Boolean,
|
dialogOnly: Boolean,
|
||||||
attachmentLocation: { type: String, default: '合同文件' },
|
attachmentLocation: { type: String, default: CONTRACT_SEAL_MATERIAL_LOCATION },
|
||||||
attachmentLocationOptions: { type: Array, default: null },
|
attachmentLocationOptions: { type: Array, default: null },
|
||||||
|
/** 附件类型业务字典 code,默认用印材料字典;佐证材料传 contract_supporting_attachment_type */
|
||||||
|
attachmentTypeDictCode: {
|
||||||
|
type: String,
|
||||||
|
default: CONTRACT_ATTACHMENT_TYPE_DICT,
|
||||||
|
},
|
||||||
preview: { type: Function, default: null },
|
preview: { type: Function, default: null },
|
||||||
},
|
},
|
||||||
emits: ['update:rows', 'upload-to-location', 'upload-confirmed'],
|
emits: ['update:rows', 'upload-to-location', 'upload-confirmed'],
|
||||||
@@ -317,8 +373,9 @@ export default {
|
|||||||
selected: [],
|
selected: [],
|
||||||
attachmentFileTypes,
|
attachmentFileTypes,
|
||||||
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
|
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
|
||||||
|
activeAttachmentTypeDictCode: CONTRACT_ATTACHMENT_TYPE_DICT,
|
||||||
uploadDialogVisible: false,
|
uploadDialogVisible: false,
|
||||||
uploadDialogLocation: '合同文件',
|
uploadDialogLocation: CONTRACT_SEAL_MATERIAL_LOCATION,
|
||||||
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
|
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
|
||||||
uploadDialogFiles: [],
|
uploadDialogFiles: [],
|
||||||
uploadDialogFileList: [],
|
uploadDialogFileList: [],
|
||||||
@@ -337,26 +394,68 @@ export default {
|
|||||||
defaultAttachmentType() {
|
defaultAttachmentType() {
|
||||||
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
|
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
|
||||||
},
|
},
|
||||||
|
resolvedAttachmentTypeDictCode() {
|
||||||
|
return this.attachmentTypeDictCode || CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||||
|
},
|
||||||
resolvedLocationOptions() {
|
resolvedLocationOptions() {
|
||||||
const options = (this.attachmentLocationOptions || []).filter(Boolean);
|
const options = (this.attachmentLocationOptions || []).filter(Boolean);
|
||||||
if (options.length) return options;
|
if (options.length) {
|
||||||
return ['合同文件', '其它附件'];
|
return options.map(item => {
|
||||||
|
if (item === '合同文件') return CONTRACT_SEAL_MATERIAL_LOCATION;
|
||||||
|
if (item === '其它附件' || item === '其他附件') {
|
||||||
|
return CONTRACT_SUPPORTING_ATTACHMENT_LOCATION;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [CONTRACT_SEAL_MATERIAL_LOCATION, CONTRACT_SUPPORTING_ATTACHMENT_LOCATION];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
attachmentTypeDictCode: {
|
||||||
|
immediate: false,
|
||||||
|
handler() {
|
||||||
|
this.loadAttachmentTypeOptions(this.resolvedAttachmentTypeDictCode, true);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadAttachmentTypeOptions();
|
this.loadAttachmentTypeOptions(this.resolvedAttachmentTypeDictCode, true);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
attachmentName,
|
attachmentName,
|
||||||
loadAttachmentTypeOptions() {
|
currentUploadUserName() {
|
||||||
return getBizDictionary({ code: 'contract_attachment_type' })
|
const raw = this.$store.getters.userInfo;
|
||||||
|
const userInfo = raw && !Array.isArray(raw) ? raw : {};
|
||||||
|
// 与顶部栏一致优先 real_name;用印材料 / 佐证材料共用
|
||||||
|
return (
|
||||||
|
userInfo.real_name ||
|
||||||
|
userInfo.realName ||
|
||||||
|
userInfo.nick_name ||
|
||||||
|
userInfo.nickName ||
|
||||||
|
userInfo.user_name ||
|
||||||
|
userInfo.userName ||
|
||||||
|
userInfo.account ||
|
||||||
|
''
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loadAttachmentTypeOptions(dictCode = this.resolvedAttachmentTypeDictCode, alignRows = false) {
|
||||||
|
const code = dictCode || CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||||
|
this.activeAttachmentTypeDictCode = code;
|
||||||
|
const fallbackOptions = defaultOptionsByDictCode(code);
|
||||||
|
return getBizDictionary({ code })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const options = mapDictOptions(res);
|
// 切换字典期间若已发起更新请求,忽略过期响应
|
||||||
|
if (this.activeAttachmentTypeDictCode !== code) return;
|
||||||
|
const options = mapDictOptions(res, code);
|
||||||
if (options.length) {
|
if (options.length) {
|
||||||
// 字典缺少「其他文件」时补上,保证未匹配文件名可默认选中
|
// 字典缺少「其他文件」时补上,保证未匹配文件名可默认选中
|
||||||
this.contractAttachmentTypeOptions = options.some(isOtherAttachmentTypeOption)
|
this.contractAttachmentTypeOptions = options.some(isOtherAttachmentTypeOption)
|
||||||
? options
|
? options
|
||||||
: [...options, { label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER }];
|
: [...options, { label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER }];
|
||||||
|
} else {
|
||||||
|
this.contractAttachmentTypeOptions = fallbackOptions;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
!this.contractAttachmentTypeOptions.some(
|
!this.contractAttachmentTypeOptions.some(
|
||||||
item => String(item.value) === String(this.uploadDialogType)
|
item => String(item.value) === String(this.uploadDialogType)
|
||||||
@@ -365,7 +464,7 @@ export default {
|
|||||||
this.uploadDialogType = this.defaultAttachmentType;
|
this.uploadDialogType = this.defaultAttachmentType;
|
||||||
}
|
}
|
||||||
// 字典加载后,把已有行的中文类型名对齐到 dictKey,保证下拉可选中
|
// 字典加载后,把已有行的中文类型名对齐到 dictKey,保证下拉可选中
|
||||||
if (this.attachmentType && (this.rows || []).length) {
|
if (alignRows && this.attachmentType && (this.rows || []).length) {
|
||||||
const aligned = (this.rows || []).map(row => ({
|
const aligned = (this.rows || []).map(row => ({
|
||||||
...row,
|
...row,
|
||||||
fileType: resolveContractAttachmentType(row, this.contractAttachmentTypeOptions),
|
fileType: resolveContractAttachmentType(row, this.contractAttachmentTypeOptions),
|
||||||
@@ -375,10 +474,11 @@ export default {
|
|||||||
);
|
);
|
||||||
if (changed) this.update(aligned);
|
if (changed) this.update(aligned);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// 字典加载失败时保留默认选项
|
if (this.activeAttachmentTypeDictCode !== code) return;
|
||||||
|
this.contractAttachmentTypeOptions = fallbackOptions;
|
||||||
|
this.uploadDialogType = this.defaultAttachmentType;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isRowLocked(row) {
|
isRowLocked(row) {
|
||||||
@@ -388,13 +488,14 @@ export default {
|
|||||||
this.$emit('update:rows', Array.isArray(rows) ? rows.map(item => ({ ...item })) : []);
|
this.$emit('update:rows', Array.isArray(rows) ? rows.map(item => ({ ...item })) : []);
|
||||||
},
|
},
|
||||||
enrichUploadedRow(row, existing) {
|
enrichUploadedRow(row, existing) {
|
||||||
const uploadUserName = this.$store.getters.userInfo?.realName || '';
|
const uploadUserName = this.currentUploadUserName();
|
||||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
const next = {
|
const next = {
|
||||||
...existing,
|
...existing,
|
||||||
...row,
|
...row,
|
||||||
description: row.description || existing?.description || '',
|
description: row.description || existing?.description || '',
|
||||||
uploadUserName: existing?.uploadUserName || row.uploadUserName || uploadUserName,
|
// 新上传行始终写入当前用户;已有行保留原上传人
|
||||||
|
uploadUserName: existing?.uploadUserName || uploadUserName || row.uploadUserName || '',
|
||||||
uploadTime: existing?.uploadTime || row.uploadTime || uploadTime,
|
uploadTime: existing?.uploadTime || row.uploadTime || uploadTime,
|
||||||
approved: existing?.approved || row.approved || false,
|
approved: existing?.approved || row.approved || false,
|
||||||
};
|
};
|
||||||
@@ -434,20 +535,38 @@ export default {
|
|||||||
resolveTypeByLocation(location) {
|
resolveTypeByLocation(location) {
|
||||||
const options = this.contractAttachmentTypeOptions || [];
|
const options = this.contractAttachmentTypeOptions || [];
|
||||||
const locationText = String(location || '');
|
const locationText = String(location || '');
|
||||||
if (locationText.includes('其它') || locationText.includes('其他')) {
|
if (
|
||||||
|
isContractSupportingAttachmentLocation(locationText) ||
|
||||||
|
locationText.includes('佐证') ||
|
||||||
|
locationText.includes('其它') ||
|
||||||
|
locationText.includes('其他')
|
||||||
|
) {
|
||||||
return this.defaultAttachmentType;
|
return this.defaultAttachmentType;
|
||||||
}
|
}
|
||||||
const matched = options.find(
|
const matched = options.find(
|
||||||
item =>
|
item =>
|
||||||
String(item.value) === locationText ||
|
String(item.value) === locationText ||
|
||||||
String(item.label) === locationText ||
|
String(item.label) === locationText ||
|
||||||
|
String(item.value).includes('用印') ||
|
||||||
|
String(item.label).includes('用印') ||
|
||||||
String(item.value).includes('合同') ||
|
String(item.value).includes('合同') ||
|
||||||
String(item.label).includes('合同')
|
String(item.label).includes('合同')
|
||||||
);
|
);
|
||||||
return matched?.value || options[0]?.value || this.defaultAttachmentType;
|
return matched?.value || options[0]?.value || this.defaultAttachmentType;
|
||||||
},
|
},
|
||||||
syncUploadDialogTypeByLocation() {
|
syncUploadDialogTypeByLocation() {
|
||||||
|
const dictCode = resolveAttachmentTypeDictCode(
|
||||||
|
this.uploadDialogLocation,
|
||||||
|
this.resolvedAttachmentTypeDictCode
|
||||||
|
);
|
||||||
|
const applyType = () => {
|
||||||
this.uploadDialogType = this.resolveTypeByLocation(this.uploadDialogLocation);
|
this.uploadDialogType = this.resolveTypeByLocation(this.uploadDialogLocation);
|
||||||
|
};
|
||||||
|
if (dictCode !== this.activeAttachmentTypeDictCode) {
|
||||||
|
this.loadAttachmentTypeOptions(dictCode, false).finally(applyType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyType();
|
||||||
},
|
},
|
||||||
openUploadDialog() {
|
openUploadDialog() {
|
||||||
const options = this.resolvedLocationOptions;
|
const options = this.resolvedLocationOptions;
|
||||||
@@ -544,13 +663,13 @@ export default {
|
|||||||
this.$message.warning('请选择附件类型');
|
this.$message.warning('请选择附件类型');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uploadUserName = this.$store.getters.userInfo?.realName || '';
|
const uploadUserName = this.currentUploadUserName();
|
||||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
const appended = this.uploadDialogFiles.map(row => ({
|
const appended = this.uploadDialogFiles.map(row => ({
|
||||||
...row,
|
...row,
|
||||||
fileType: this.uploadDialogType,
|
fileType: this.uploadDialogType,
|
||||||
description: row.description || '',
|
description: row.description || '',
|
||||||
uploadUserName: row.uploadUserName || uploadUserName,
|
uploadUserName: uploadUserName || row.uploadUserName || '',
|
||||||
uploadTime: row.uploadTime || uploadTime,
|
uploadTime: row.uploadTime || uploadTime,
|
||||||
...(this.markApprovedOnUpload ? { approved: true } : {}),
|
...(this.markApprovedOnUpload ? { approved: true } : {}),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -266,7 +266,7 @@
|
|||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="单价">
|
<el-form-item label="单价">
|
||||||
<el-input :model-value="freightGroupUnitPrice(group)" inputmode="decimal" placeholder="请输入" @input="value => handleFreightGroupUnitPriceInput(group, value)" @clear="() => handleFreightGroupUnitPriceInput(group, '')">
|
<el-input :model-value="freightGroupUnitPrice(group)" inputmode="decimal" placeholder="请输入" @input="value => handleFreightGroupUnitPriceInput(group, value)" @clear="() => handleFreightGroupUnitPriceInput(group, '')">
|
||||||
<template #append><el-select :model-value="freightGroupPriceUnit(group)" class="freight-unit-select" @change="value => handleFreightGroupPriceUnitChange(group, value)"><el-option label="元/吨" value="元/吨" /><el-option label="元/件" value="元/件" /><el-option label="元/方" value="元/方" /></el-select></template>
|
<template #append><el-select :model-value="freightGroupPriceUnit(group)" class="freight-unit-select" @change="value => handleFreightGroupPriceUnitChange(group, value)"><el-option v-for="item in priceUnitOptions(route)" :key="item.value" :label="item.label" :value="item.value" /></el-select></template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -318,6 +318,11 @@
|
|||||||
import * as api from '@/api/business/master-order';
|
import * as api from '@/api/business/master-order';
|
||||||
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
|
import {
|
||||||
|
currencyUnit,
|
||||||
|
remapPriceUnitCurrency,
|
||||||
|
resolvePriceUnitByQuantityUnit,
|
||||||
|
} from '@/utils/currency';
|
||||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||||
import { isMobile } from '@/utils/validate';
|
import { isMobile } from '@/utils/validate';
|
||||||
|
|
||||||
@@ -429,8 +434,10 @@ export default {
|
|||||||
},
|
},
|
||||||
async loadContractCurrency() {
|
async loadContractCurrency() {
|
||||||
const directCurrency = this.master?.settlementCurrency || this.master?.settlementCurrencyCode || this.master?.currency;
|
const directCurrency = this.master?.settlementCurrency || this.master?.settlementCurrencyCode || this.master?.currency;
|
||||||
|
const previousCurrency = this.master?.settlementCurrency;
|
||||||
if (!this.master?.projectId || !this.master?.contractId) {
|
if (!this.master?.projectId || !this.master?.contractId) {
|
||||||
this.master.settlementCurrency = this.normalizeCurrencyValue(directCurrency || 'RMB');
|
this.master.settlementCurrency = this.normalizeCurrencyValue(directCurrency || 'RMB');
|
||||||
|
this.syncRouteCurrency(previousCurrency, this.master.settlementCurrency);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -444,6 +451,20 @@ export default {
|
|||||||
this.master.customerContractPartyB = '';
|
this.master.customerContractPartyB = '';
|
||||||
this.master.settlementCurrency = 'RMB';
|
this.master.settlementCurrency = 'RMB';
|
||||||
}
|
}
|
||||||
|
this.syncRouteCurrency(previousCurrency, this.master.settlementCurrency);
|
||||||
|
},
|
||||||
|
syncRouteCurrency(_previousCurrency, nextCurrency) {
|
||||||
|
const currency = this.normalizeCurrencyValue(nextCurrency || 'RMB');
|
||||||
|
(this.routes || []).forEach(route => {
|
||||||
|
route.currency = currency;
|
||||||
|
(route.freightItems || []).forEach(item => {
|
||||||
|
if (item.priceUnit) {
|
||||||
|
item.priceUnit = remapPriceUnitCurrency(item.priceUnit, currency);
|
||||||
|
} else if (item.quantityUnit) {
|
||||||
|
item.priceUnit = resolvePriceUnitByQuantityUnit(item.quantityUnit, currency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
dispatchRouteNodes() {
|
dispatchRouteNodes() {
|
||||||
const routes = Array.isArray(this.master?.routes) ? this.master.routes : [];
|
const routes = Array.isArray(this.master?.routes) ? this.master.routes : [];
|
||||||
@@ -618,8 +639,23 @@ export default {
|
|||||||
const prices = (group.rows || []).map(item => String(item.unitPrice ?? '').trim()).filter(Boolean);
|
const prices = (group.rows || []).map(item => String(item.unitPrice ?? '').trim()).filter(Boolean);
|
||||||
return !prices.length || prices.some(price => price !== prices[0]) ? '' : prices[0];
|
return !prices.length || prices.some(price => price !== prices[0]) ? '' : prices[0];
|
||||||
},
|
},
|
||||||
freightGroupPriceUnit(group = {}) { return group.rows?.[0]?.priceUnit || (group.quantityUnit ? `元/${group.quantityUnit}` : ''); },
|
freightGroupPriceUnit(group = {}) {
|
||||||
|
if (group.rows?.[0]?.priceUnit) return group.rows[0].priceUnit;
|
||||||
|
if (!group.quantityUnit) return '';
|
||||||
|
return (
|
||||||
|
resolvePriceUnitByQuantityUnit(group.quantityUnit, this.master?.settlementCurrency || 'RMB') ||
|
||||||
|
`${currencyUnit(this.master?.settlementCurrency || 'RMB')}/${group.quantityUnit}`
|
||||||
|
);
|
||||||
|
},
|
||||||
freightGroupAmount(group = {}) { return (group.rows || []).reduce((sum, item) => sum + this.freightAmount(item), 0); },
|
freightGroupAmount(group = {}) { return (group.rows || []).reduce((sum, item) => sum + this.freightAmount(item), 0); },
|
||||||
|
priceUnitOptions(route = {}) {
|
||||||
|
const unit = currencyUnit(route.currency || this.master?.settlementCurrency || 'RMB');
|
||||||
|
return [
|
||||||
|
{ label: `${unit}/吨`, value: `${unit}/吨` },
|
||||||
|
{ label: `${unit}/件`, value: `${unit}/件` },
|
||||||
|
{ label: `${unit}/方`, value: `${unit}/方` },
|
||||||
|
];
|
||||||
|
},
|
||||||
handleFreightGroupUnitPriceInput(group, value) {
|
handleFreightGroupUnitPriceInput(group, value) {
|
||||||
const [integer = '', decimal = ''] = String(value || '').replace(/[^\d.]/g, '').split('.');
|
const [integer = '', decimal = ''] = String(value || '').replace(/[^\d.]/g, '').split('.');
|
||||||
const normalized = decimal || String(value || '').includes('.') ? `${integer}.${decimal.slice(0, 2)}` : integer;
|
const normalized = decimal || String(value || '').includes('.') ? `${integer}.${decimal.slice(0, 2)}` : integer;
|
||||||
@@ -629,6 +665,10 @@ export default {
|
|||||||
handleFreightGroupQuantityUnitChange(route, group, value) {
|
handleFreightGroupQuantityUnitChange(route, group, value) {
|
||||||
(group.rows || []).forEach(item => {
|
(group.rows || []).forEach(item => {
|
||||||
item.quantityUnit = value || '';
|
item.quantityUnit = value || '';
|
||||||
|
item.priceUnit = resolvePriceUnitByQuantityUnit(
|
||||||
|
value,
|
||||||
|
route.currency || this.master?.settlementCurrency || 'RMB'
|
||||||
|
) || item.priceUnit;
|
||||||
const goods = (route.goods || []).find(row => String(row.sourceIndex) === String(item.sourceIndex));
|
const goods = (route.goods || []).find(row => String(row.sourceIndex) === String(item.sourceIndex));
|
||||||
if (goods) goods.quantityUnit = value || '';
|
if (goods) goods.quantityUnit = value || '';
|
||||||
});
|
});
|
||||||
@@ -651,7 +691,7 @@ export default {
|
|||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
currencyLabel(value) { return ({ CNY: '元', RMB: '元', USD: '美元', EUR: '欧元' })[value] || value || '元'; },
|
currencyLabel(value) { return currencyUnit(value); },
|
||||||
quantityNumber(value) { return Number(value || 0); },
|
quantityNumber(value) { return Number(value || 0); },
|
||||||
goodsKey(goods = {}, includeUnit = true) {
|
goodsKey(goods = {}, includeUnit = true) {
|
||||||
return [
|
return [
|
||||||
@@ -841,6 +881,7 @@ export default {
|
|||||||
removeGoods(route, index) { route.goods.splice(index, 1); this.syncFreightItems(route); },
|
removeGoods(route, index) { route.goods.splice(index, 1); this.syncFreightItems(route); },
|
||||||
syncFreightItems(route) {
|
syncFreightItems(route) {
|
||||||
const oldItems = route.freightItems || [];
|
const oldItems = route.freightItems || [];
|
||||||
|
const currency = route.currency || this.master?.settlementCurrency || 'RMB';
|
||||||
route.freightItems = (route.goods || [])
|
route.freightItems = (route.goods || [])
|
||||||
.filter(item => item.sourceIndex !== undefined && item.sourceIndex !== '')
|
.filter(item => item.sourceIndex !== undefined && item.sourceIndex !== '')
|
||||||
.map(item => {
|
.map(item => {
|
||||||
@@ -852,7 +893,10 @@ export default {
|
|||||||
quantity: item.dispatchQuantity || '',
|
quantity: item.dispatchQuantity || '',
|
||||||
quantityUnit: item.quantityUnit || '',
|
quantityUnit: item.quantityUnit || '',
|
||||||
unitPrice: old.unitPrice || '',
|
unitPrice: old.unitPrice || '',
|
||||||
priceUnit: old.priceUnit || `元/${item.quantityUnit || '吨'}`,
|
priceUnit:
|
||||||
|
old.priceUnit ||
|
||||||
|
resolvePriceUnitByQuantityUnit(item.quantityUnit || '吨', currency) ||
|
||||||
|
`${currencyUnit(currency)}/${item.quantityUnit || '吨'}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1744,11 +1744,11 @@
|
|||||||
><el-select
|
><el-select
|
||||||
:model-value="dispatchFreightGroupPriceUnit(group)"
|
:model-value="dispatchFreightGroupPriceUnit(group)"
|
||||||
class="transport-plan-page__dispatch-unit-select"
|
class="transport-plan-page__dispatch-unit-select"
|
||||||
placeholder="元/吨"
|
:placeholder="defaultPriceUnit"
|
||||||
@change="value => handleDispatchFreightGroupPriceUnitChange(group, value)"
|
@change="value => handleDispatchFreightGroupPriceUnitChange(group, value)"
|
||||||
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
||||||
><el-option
|
><el-option
|
||||||
v-for="item in taskFeeUnitOptions"
|
v-for="item in taskFeeUnitSelectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value" /></el-select
|
:value="item.value" /></el-select
|
||||||
@@ -1969,17 +1969,17 @@
|
|||||||
<el-select
|
<el-select
|
||||||
v-model="dispatchItemForm.priceUnit"
|
v-model="dispatchItemForm.priceUnit"
|
||||||
class="transport-plan-page__dispatch-unit-select"
|
class="transport-plan-page__dispatch-unit-select"
|
||||||
placeholder="元/吨"
|
:placeholder="defaultPriceUnit"
|
||||||
:loading="taskFeeUnitLoading"
|
:loading="taskFeeUnitLoading"
|
||||||
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-if="!taskFeeUnitOptions.some(item => item.value === '元/吨')"
|
v-if="!taskFeeUnitSelectOptions.some(item => item.value === defaultPriceUnit)"
|
||||||
label="元/吨"
|
:label="defaultPriceUnit"
|
||||||
value="元/吨"
|
:value="defaultPriceUnit"
|
||||||
/>
|
/>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in taskFeeUnitOptions"
|
v-for="item in taskFeeUnitSelectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -2741,6 +2741,12 @@ import { addressTypeOptions } from '@/option/base/common-address';
|
|||||||
import { packageOptions } from '@/option/business/common';
|
import { packageOptions } from '@/option/business/common';
|
||||||
import { formatUpdateUserName } from '@/utils/audit';
|
import { formatUpdateUserName } from '@/utils/audit';
|
||||||
import { getToken, isAdministrator } from '@/utils/auth';
|
import { getToken, isAdministrator } from '@/utils/auth';
|
||||||
|
import {
|
||||||
|
currencyUnit,
|
||||||
|
mapFeeUnitOptions,
|
||||||
|
remapPriceUnitCurrency,
|
||||||
|
resolvePriceUnitByQuantityUnit,
|
||||||
|
} from '@/utils/currency';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search';
|
||||||
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
import { loadMeasurementUnitOptions } from '@/utils/measurement-unit';
|
||||||
@@ -3612,6 +3618,22 @@ export default {
|
|||||||
const value = this.dispatchItemForm.freightCurrency || 'RMB';
|
const value = this.dispatchItemForm.freightCurrency || 'RMB';
|
||||||
return this.currencyRemark(value);
|
return this.currencyRemark(value);
|
||||||
},
|
},
|
||||||
|
taskFeeUnitSelectOptions() {
|
||||||
|
const currency =
|
||||||
|
this.dispatchItemForm.freightCurrency ||
|
||||||
|
this.dispatchContractCurrency ||
|
||||||
|
this.form.settlementCurrency ||
|
||||||
|
'RMB';
|
||||||
|
return mapFeeUnitOptions(this.taskFeeUnitOptions, currency);
|
||||||
|
},
|
||||||
|
defaultPriceUnit() {
|
||||||
|
return `${currencyUnit(
|
||||||
|
this.dispatchItemForm.freightCurrency ||
|
||||||
|
this.dispatchContractCurrency ||
|
||||||
|
this.form.settlementCurrency ||
|
||||||
|
'RMB'
|
||||||
|
)}/吨`;
|
||||||
|
},
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return isAdministrator(this.userInfo);
|
return isAdministrator(this.userInfo);
|
||||||
},
|
},
|
||||||
@@ -4806,19 +4828,49 @@ export default {
|
|||||||
contract.customerNames ||
|
contract.customerNames ||
|
||||||
this.form.customerName ||
|
this.form.customerName ||
|
||||||
'';
|
'';
|
||||||
|
const nextCurrency = this.getContractCurrency(contract);
|
||||||
|
this.remapTransportPlanPriceUnits(nextCurrency);
|
||||||
this.$refs.crud?.validateField?.('contractName');
|
this.$refs.crud?.validateField?.('contractName');
|
||||||
},
|
},
|
||||||
|
remapTransportPlanPriceUnits(currency) {
|
||||||
|
if (this.form.priceUnit) {
|
||||||
|
this.form.priceUnit = remapPriceUnitCurrency(this.form.priceUnit, currency);
|
||||||
|
}
|
||||||
|
if (this.dispatchItemForm.priceUnit) {
|
||||||
|
this.dispatchItemForm.priceUnit = remapPriceUnitCurrency(
|
||||||
|
this.dispatchItemForm.priceUnit,
|
||||||
|
currency
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.dispatchItemForm.freightCurrency = currency;
|
||||||
|
(this.dispatchItemCargoRows || []).forEach(cargo => {
|
||||||
|
if (cargo.priceUnit) {
|
||||||
|
cargo.priceUnit = remapPriceUnitCurrency(cargo.priceUnit, currency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(this.transportCargoRows || []).forEach(cargo => {
|
||||||
|
if (cargo.priceUnit) {
|
||||||
|
cargo.priceUnit = remapPriceUnitCurrency(cargo.priceUnit, currency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
loadDispatchContractCurrency(plan = {}) {
|
loadDispatchContractCurrency(plan = {}) {
|
||||||
const findContract = () =>
|
const findContract = () =>
|
||||||
this.contractOptions.find(item => String(item.id) === String(plan.contractId)) ||
|
this.contractOptions.find(item => String(item.id) === String(plan.contractId)) ||
|
||||||
this.contractOptions.find(item => item.contractName === plan.contractName);
|
this.contractOptions.find(item => item.contractName === plan.contractName);
|
||||||
const applyCurrency = contract => {
|
const applyCurrency = contract => {
|
||||||
|
const previousCurrency = this.dispatchContractCurrency;
|
||||||
this.dispatchContractCurrency = this.getContractCurrency(
|
this.dispatchContractCurrency = this.getContractCurrency(
|
||||||
contract &&
|
contract &&
|
||||||
(contract.settlementCurrency || contract.settlementCurrencyCode || contract.currency)
|
(contract.settlementCurrency || contract.settlementCurrencyCode || contract.currency)
|
||||||
? contract
|
? contract
|
||||||
: { settlementCurrency: plan.settlementCurrency }
|
: { settlementCurrency: plan.settlementCurrency }
|
||||||
);
|
);
|
||||||
|
if (previousCurrency && previousCurrency !== this.dispatchContractCurrency) {
|
||||||
|
this.remapTransportPlanPriceUnits(this.dispatchContractCurrency);
|
||||||
|
} else {
|
||||||
|
this.dispatchItemForm.freightCurrency = this.dispatchContractCurrency;
|
||||||
|
}
|
||||||
return this.dispatchContractCurrency;
|
return this.dispatchContractCurrency;
|
||||||
};
|
};
|
||||||
const current = findContract();
|
const current = findContract();
|
||||||
@@ -5217,7 +5269,7 @@ export default {
|
|||||||
},
|
},
|
||||||
addTransportCargoRow(index = -1, row = {}) {
|
addTransportCargoRow(index = -1, row = {}) {
|
||||||
const nextRow = this.normalizeTransportCargoRow({
|
const nextRow = this.normalizeTransportCargoRow({
|
||||||
priceUnit: this.form.priceUnit || '元/吨',
|
priceUnit: this.form.priceUnit || this.defaultPriceUnit,
|
||||||
...row,
|
...row,
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
@@ -7680,7 +7732,10 @@ export default {
|
|||||||
this.dispatchItemCargoRows = this.dispatchItemCargoRows.map((cargo, itemIndex) => ({
|
this.dispatchItemCargoRows = this.dispatchItemCargoRows.map((cargo, itemIndex) => ({
|
||||||
...cargo,
|
...cargo,
|
||||||
unitPrice: cargo.unitPrice || freightInfo.freightItems[itemIndex]?.unitPrice || '',
|
unitPrice: cargo.unitPrice || freightInfo.freightItems[itemIndex]?.unitPrice || '',
|
||||||
priceUnit: cargo.priceUnit || freightInfo.freightItems[itemIndex]?.priceUnit || '元/吨',
|
priceUnit:
|
||||||
|
cargo.priceUnit ||
|
||||||
|
freightInfo.freightItems[itemIndex]?.priceUnit ||
|
||||||
|
this.defaultPriceUnit,
|
||||||
freightAmount:
|
freightAmount:
|
||||||
cargo.freightAmount || freightInfo.freightItems[itemIndex]?.freightAmount || '',
|
cargo.freightAmount || freightInfo.freightItems[itemIndex]?.freightAmount || '',
|
||||||
}));
|
}));
|
||||||
@@ -7929,7 +7984,7 @@ export default {
|
|||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
addDispatchCargoRow(index = -1) {
|
addDispatchCargoRow(index = -1) {
|
||||||
const nextRow = this.normalizeTransportCargoRow({ priceUnit: '元/吨' });
|
const nextRow = this.normalizeTransportCargoRow({ priceUnit: this.defaultPriceUnit });
|
||||||
if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow);
|
if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow);
|
||||||
else this.dispatchItemCargoRows.push(nextRow);
|
else this.dispatchItemCargoRows.push(nextRow);
|
||||||
},
|
},
|
||||||
@@ -7973,7 +8028,14 @@ export default {
|
|||||||
return prices[0];
|
return prices[0];
|
||||||
},
|
},
|
||||||
dispatchFreightGroupPriceUnit(group = {}) {
|
dispatchFreightGroupPriceUnit(group = {}) {
|
||||||
return group.rows?.[0]?.priceUnit || (group.quantityUnit ? `元/${group.quantityUnit}` : '');
|
if (group.rows?.[0]?.priceUnit) return group.rows[0].priceUnit;
|
||||||
|
if (!group.quantityUnit) return '';
|
||||||
|
return (
|
||||||
|
resolvePriceUnitByQuantityUnit(
|
||||||
|
group.quantityUnit,
|
||||||
|
this.dispatchItemForm.freightCurrency || this.dispatchContractCurrency || 'RMB'
|
||||||
|
) || `${currencyUnit(this.dispatchContractCurrency || 'RMB')}/${group.quantityUnit}`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
dispatchFreightGroupAmount(group = {}) {
|
dispatchFreightGroupAmount(group = {}) {
|
||||||
const amount = (group.rows || []).reduce(
|
const amount = (group.rows || []).reduce(
|
||||||
|
|||||||
@@ -270,15 +270,22 @@
|
|||||||
prop="mileage"
|
prop="mileage"
|
||||||
label="里程(km)"
|
label="里程(km)"
|
||||||
width="100"
|
width="100"
|
||||||
/><el-table-column prop="unitPrice" label="单价" width="90" /><el-table-column
|
>
|
||||||
prop="freight"
|
<template #default="{ row }">{{ formatOptionalNumber(row.mileage) }}</template>
|
||||||
label="运费"
|
</el-table-column>
|
||||||
width="100"
|
<el-table-column prop="unitPrice" label="单价" width="90">
|
||||||
/><el-table-column prop="otherFeeTotal" label="其他费用合计" width="120" /><el-table-column
|
<template #default="{ row }">{{ formatOptionalNumber(row.unitPrice) }}</template>
|
||||||
prop="freightTotal"
|
</el-table-column>
|
||||||
label="运费合计"
|
<el-table-column prop="freight" label="运费" width="100">
|
||||||
width="100"
|
<template #default="{ row }">{{ formatOptionalNumber(row.freight) }}</template>
|
||||||
/><el-table-column
|
</el-table-column>
|
||||||
|
<el-table-column prop="otherFeeTotal" label="其他费用合计" width="120">
|
||||||
|
<template #default="{ row }">{{ formatOptionalNumber(row.otherFeeTotal) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="freightTotal" label="运费合计" width="100">
|
||||||
|
<template #default="{ row }">{{ formatOptionalNumber(row.freightTotal) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
prop="actualStartDate"
|
prop="actualStartDate"
|
||||||
label="实际发货时间"
|
label="实际发货时间"
|
||||||
width="160"
|
width="160"
|
||||||
@@ -1107,7 +1114,7 @@ const loadDetails = async () => {
|
|||||||
current: detailPage.current,
|
current: detailPage.current,
|
||||||
size: detailPage.size,
|
size: detailPage.size,
|
||||||
});
|
});
|
||||||
details.value = res.data?.data?.records || [];
|
details.value = (res.data?.data?.records || []).map(normalizeDetailRow);
|
||||||
detailPage.total = res.data?.data?.total || 0;
|
detailPage.total = res.data?.data?.total || 0;
|
||||||
};
|
};
|
||||||
const openCreate = () => prepareCreateForm(true);
|
const openCreate = () => prepareCreateForm(true);
|
||||||
@@ -1156,7 +1163,7 @@ const loadBatchRows = async batchId => {
|
|||||||
if (!batchId) return;
|
if (!batchId) return;
|
||||||
const res = await api.getImportDetails({ batchId, current: 1, size: 9999 });
|
const res = await api.getImportDetails({ batchId, current: 1, size: 9999 });
|
||||||
rows.value = extractRecords(res).map(({ id, ...item }, index) => ({
|
rows.value = extractRecords(res).map(({ id, ...item }, index) => ({
|
||||||
...item,
|
...normalizeDetailRow(item),
|
||||||
_key: `${id || 'row'}-${index}`,
|
_key: `${id || 'row'}-${index}`,
|
||||||
batchNo: item.batchNo || form.batchNo,
|
batchNo: item.batchNo || form.batchNo,
|
||||||
}));
|
}));
|
||||||
@@ -1300,10 +1307,22 @@ const carrierChange = value => {
|
|||||||
};
|
};
|
||||||
const firstNotEmpty = (...values) => values.find(value => String(value ?? '').trim()) ?? '';
|
const firstNotEmpty = (...values) => values.find(value => String(value ?? '').trim()) ?? '';
|
||||||
const OPTIONAL_NUMBER_EMPTY_SENTINELS = [-1];
|
const OPTIONAL_NUMBER_EMPTY_SENTINELS = [-1];
|
||||||
|
const OPTIONAL_NUMBER_PROPS = ['mileage', 'unitPrice', 'freight', 'otherFeeTotal', 'freightTotal'];
|
||||||
const normalizeOptionalImportNumber = (value, emptySentinels = OPTIONAL_NUMBER_EMPTY_SENTINELS) => {
|
const normalizeOptionalImportNumber = (value, emptySentinels = OPTIONAL_NUMBER_EMPTY_SENTINELS) => {
|
||||||
if (!String(value ?? '').trim()) return null;
|
if (!String(value ?? '').trim()) return null;
|
||||||
return emptySentinels.some(sentinel => Number(value) === sentinel) ? null : value;
|
return emptySentinels.some(sentinel => Number(value) === sentinel) ? null : value;
|
||||||
};
|
};
|
||||||
|
const formatOptionalNumber = value => {
|
||||||
|
const normalized = normalizeOptionalImportNumber(value);
|
||||||
|
return normalized === null || normalized === undefined ? '' : normalized;
|
||||||
|
};
|
||||||
|
const normalizeDetailRow = row => {
|
||||||
|
const next = { ...(row || {}) };
|
||||||
|
OPTIONAL_NUMBER_PROPS.forEach(prop => {
|
||||||
|
next[prop] = formatOptionalNumber(next[prop]);
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
};
|
||||||
const normalizeImportRow = row => ({
|
const normalizeImportRow = row => ({
|
||||||
...row,
|
...row,
|
||||||
mileage: normalizeOptionalImportNumber(
|
mileage: normalizeOptionalImportNumber(
|
||||||
|
|||||||
@@ -1083,7 +1083,7 @@
|
|||||||
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in taskFeeUnitOptions"
|
v-for="item in taskFeeUnitSelectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -1416,7 +1416,7 @@
|
|||||||
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
@visible-change="visible => visible && loadTaskFeeUnitOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in taskFeeUnitOptions"
|
v-for="item in taskFeeUnitSelectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -3642,6 +3642,12 @@ import { addressTypeOptions } from '@/option/base/common-address';
|
|||||||
import { packageOptions } from '@/option/business/common';
|
import { packageOptions } from '@/option/business/common';
|
||||||
import { formatUpdateUserName } from '@/utils/audit';
|
import { formatUpdateUserName } from '@/utils/audit';
|
||||||
import { getToken, isAdministrator } from '@/utils/auth';
|
import { getToken, isAdministrator } from '@/utils/auth';
|
||||||
|
import {
|
||||||
|
currencyUnit,
|
||||||
|
mapFeeUnitOptions,
|
||||||
|
remapPriceUnitCurrency,
|
||||||
|
resolvePriceUnitByQuantityUnit,
|
||||||
|
} from '@/utils/currency';
|
||||||
import { formatContractOptionLabel } from '@/utils/contract';
|
import { formatContractOptionLabel } from '@/utils/contract';
|
||||||
import { wgs84ToGcj02 } from '@/utils/coordinate';
|
import { wgs84ToGcj02 } from '@/utils/coordinate';
|
||||||
import { openImportDialog } from '@/utils/import-excel';
|
import { openImportDialog } from '@/utils/import-excel';
|
||||||
@@ -4185,6 +4191,8 @@ export default {
|
|||||||
},
|
},
|
||||||
selectionList: [],
|
selectionList: [],
|
||||||
attachmentUploadMode: false,
|
attachmentUploadMode: false,
|
||||||
|
/** 复制来源运单业务状态(表单字段 addDisplay:false,提交时需显式带回) */
|
||||||
|
copySourceBusinessStatus: '',
|
||||||
standaloneFormKey: '',
|
standaloneFormKey: '',
|
||||||
// 实例所属路由的路径快照:用于判断当前 $route 是否还是「本实例自己的路由」。
|
// 实例所属路由的路径快照:用于判断当前 $route 是否还是「本实例自己的路由」。
|
||||||
// 必须在 data 里初始化,不能放 created —— Vue 的 Options API 执行顺序是
|
// 必须在 data 里初始化,不能放 created —— Vue 的 Options API 执行顺序是
|
||||||
@@ -4581,6 +4589,12 @@ export default {
|
|||||||
if (!currency) return '';
|
if (!currency) return '';
|
||||||
return this.currencyRemark(currency);
|
return this.currencyRemark(currency);
|
||||||
},
|
},
|
||||||
|
taskFeeUnitSelectOptions() {
|
||||||
|
return mapFeeUnitOptions(this.taskFeeUnitOptions, this.taskFreightCurrency || 'RMB');
|
||||||
|
},
|
||||||
|
defaultPriceUnit() {
|
||||||
|
return `${currencyUnit(this.taskFreightCurrency || 'RMB')}/吨`;
|
||||||
|
},
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return isAdministrator(this.userInfo);
|
return isAdministrator(this.userInfo);
|
||||||
},
|
},
|
||||||
@@ -5175,7 +5189,7 @@ export default {
|
|||||||
quantity: goods.quantity ?? goods.cargoQuantity ?? goods.goodsQuantity ?? '',
|
quantity: goods.quantity ?? goods.cargoQuantity ?? goods.goodsQuantity ?? '',
|
||||||
quantityUnit: goods.quantityUnit || goods.goodsQuantityUnit || goods.unit || '吨',
|
quantityUnit: goods.quantityUnit || goods.goodsQuantityUnit || goods.unit || '吨',
|
||||||
unitPrice: goods.unitPrice,
|
unitPrice: goods.unitPrice,
|
||||||
priceUnit: goods.priceUnit || '元/吨',
|
priceUnit: goods.priceUnit || this.defaultPriceUnit,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
canOperation(operation, row) {
|
canOperation(operation, row) {
|
||||||
@@ -6477,6 +6491,18 @@ export default {
|
|||||||
if (isAddMode) {
|
if (isAddMode) {
|
||||||
delete submitRow.id;
|
delete submitRow.id;
|
||||||
delete submitRow.waybillNo;
|
delete submitRow.waybillNo;
|
||||||
|
// 复制提交:状态列未进表单,Avue 可能不带回;显式保留原运单业务状态(含已完成)
|
||||||
|
const copyId = this.$route.query.copyId || '';
|
||||||
|
if (copyId && !saveAsDraft) {
|
||||||
|
const copiedStatus =
|
||||||
|
this.form?.businessStatus ||
|
||||||
|
this.copySourceBusinessStatus ||
|
||||||
|
submitRow.businessStatus ||
|
||||||
|
'';
|
||||||
|
if (copiedStatus) {
|
||||||
|
submitRow.businessStatus = copiedStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const skipValidation = saveAsDraft && this.config.skipDraftValidation === true;
|
const skipValidation = saveAsDraft && this.config.skipDraftValidation === true;
|
||||||
if (
|
if (
|
||||||
@@ -6596,7 +6622,29 @@ export default {
|
|||||||
selected?.currency ||
|
selected?.currency ||
|
||||||
this.form.settlementCurrency ||
|
this.form.settlementCurrency ||
|
||||||
'RMB';
|
'RMB';
|
||||||
this.taskFreightCurrency = this.normalizeFreightCurrency(value);
|
const nextCurrency = this.normalizeFreightCurrency(value);
|
||||||
|
const previousCurrency = this.taskFreightCurrency;
|
||||||
|
this.taskFreightCurrency = nextCurrency;
|
||||||
|
if (previousCurrency && previousCurrency !== nextCurrency) {
|
||||||
|
this.remapWaybillPriceUnits(nextCurrency);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remapWaybillPriceUnits(currency) {
|
||||||
|
if (this.form.priceUnit) {
|
||||||
|
this.form.priceUnit = remapPriceUnitCurrency(this.form.priceUnit, currency);
|
||||||
|
}
|
||||||
|
(this.transportCargoRows || []).forEach(cargo => {
|
||||||
|
if (cargo.priceUnit) {
|
||||||
|
cargo.priceUnit = remapPriceUnitCurrency(cargo.priceUnit, currency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(this.taskFreightGroups || []).forEach(group => {
|
||||||
|
(group.rows || []).forEach(row => {
|
||||||
|
if (row.priceUnit) {
|
||||||
|
row.priceUnit = remapPriceUnitCurrency(row.priceUnit, currency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
loadFreightCurrencyOptions() {
|
loadFreightCurrencyOptions() {
|
||||||
if (this.freightCurrencyOptions.length || this.freightCurrencyLoading) {
|
if (this.freightCurrencyOptions.length || this.freightCurrencyLoading) {
|
||||||
@@ -6880,6 +6928,8 @@ export default {
|
|||||||
if (type === 'add' && copyId) {
|
if (type === 'add' && copyId) {
|
||||||
this.api.getDetail(copyId).then(res => {
|
this.api.getDetail(copyId).then(res => {
|
||||||
const sourceData = res.data.data || {};
|
const sourceData = res.data.data || {};
|
||||||
|
// 原运单业务状态(如 completed)提交时需原样带回并生成应收应付
|
||||||
|
this.copySourceBusinessStatus = sourceData.businessStatus || '';
|
||||||
// 清除不应该复制的字段(业务状态与原运单保持一致)
|
// 清除不应该复制的字段(业务状态与原运单保持一致)
|
||||||
delete sourceData.id;
|
delete sourceData.id;
|
||||||
delete sourceData.code;
|
delete sourceData.code;
|
||||||
@@ -6901,14 +6951,18 @@ export default {
|
|||||||
delete sourceData.updateUserName;
|
delete sourceData.updateUserName;
|
||||||
|
|
||||||
this.applyFormDetail(sourceData);
|
this.applyFormDetail(sourceData);
|
||||||
// 标记为手工创建;业务状态沿用原运单
|
// 标记为手工创建;业务状态沿用原运单(含已完成)
|
||||||
this.form.dataSource = '手工创建';
|
this.form.dataSource = '手工创建';
|
||||||
|
if (this.copySourceBusinessStatus) {
|
||||||
|
this.form.businessStatus = this.copySourceBusinessStatus;
|
||||||
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
|
this.copySourceBusinessStatus = '';
|
||||||
this.form = {
|
this.form = {
|
||||||
...(this.config.defaultForm || {}),
|
...(this.config.defaultForm || {}),
|
||||||
};
|
};
|
||||||
@@ -7382,7 +7436,7 @@ export default {
|
|||||||
if (this.isSentinelMinusOne(this.form[prop])) this.form[prop] = '';
|
if (this.isSentinelMinusOne(this.form[prop])) this.form[prop] = '';
|
||||||
});
|
});
|
||||||
this.form.priceUnit =
|
this.form.priceUnit =
|
||||||
this.form.priceUnit || taskInfo.priceUnit || goodsInfo.priceUnit || '元/吨';
|
this.form.priceUnit || taskInfo.priceUnit || goodsInfo.priceUnit || this.defaultPriceUnit;
|
||||||
this.form.otherFeeTotal = this.normalizeOptionalAmount(
|
this.form.otherFeeTotal = this.normalizeOptionalAmount(
|
||||||
this.form.otherFeeTotal || taskInfo.otherFeeTotal || goodsInfo.otherFeeTotal || ''
|
this.form.otherFeeTotal || taskInfo.otherFeeTotal || goodsInfo.otherFeeTotal || ''
|
||||||
);
|
);
|
||||||
@@ -8095,7 +8149,7 @@ export default {
|
|||||||
if (index === 0 && !cargo.unitPrice && this.form.unitPrice) {
|
if (index === 0 && !cargo.unitPrice && this.form.unitPrice) {
|
||||||
cargo.unitPrice = this.form.unitPrice;
|
cargo.unitPrice = this.form.unitPrice;
|
||||||
}
|
}
|
||||||
cargo.priceUnit = cargo.priceUnit || this.form.priceUnit || '元/吨';
|
cargo.priceUnit = cargo.priceUnit || this.form.priceUnit || this.defaultPriceUnit;
|
||||||
if (this.isBillingFieldEmpty(cargo.freightAmount)) {
|
if (this.isBillingFieldEmpty(cargo.freightAmount)) {
|
||||||
cargo.freightAmount = this.normalizeOptionalAmount(
|
cargo.freightAmount = this.normalizeOptionalAmount(
|
||||||
freightItems[index]?.freightAmount ?? ''
|
freightItems[index]?.freightAmount ?? ''
|
||||||
@@ -8477,7 +8531,7 @@ export default {
|
|||||||
},
|
},
|
||||||
addTransportCargoRow(index = -1, row = {}) {
|
addTransportCargoRow(index = -1, row = {}) {
|
||||||
const nextRow = this.normalizeTransportCargoRow({
|
const nextRow = this.normalizeTransportCargoRow({
|
||||||
priceUnit: this.form.priceUnit || '元/吨',
|
priceUnit: this.form.priceUnit || this.defaultPriceUnit,
|
||||||
...row,
|
...row,
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
@@ -8524,12 +8578,9 @@ export default {
|
|||||||
this.syncTransportCargoJson();
|
this.syncTransportCargoJson();
|
||||||
},
|
},
|
||||||
resolvePriceUnitByQuantityUnit(quantityUnit) {
|
resolvePriceUnitByQuantityUnit(quantityUnit) {
|
||||||
return (
|
return resolvePriceUnitByQuantityUnit(
|
||||||
{
|
quantityUnit,
|
||||||
吨: '元/吨',
|
this.taskFreightCurrency || this.form.settlementCurrency || 'RMB'
|
||||||
方: '元/立方',
|
|
||||||
件: '元/件',
|
|
||||||
}[String(quantityUnit || '').trim()] || ''
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
handleTransportCargoQuantityUnitChange(quantityUnit) {
|
handleTransportCargoQuantityUnitChange(quantityUnit) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<el-form-item label="项目">
|
<el-form-item label="项目">
|
||||||
<el-input v-model="form.projectName" disabled />
|
<el-input v-model="form.projectName" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属组织">
|
<el-form-item label="合同负责部门">
|
||||||
<el-input v-model="form.organizationName" disabled />
|
<el-input v-model="form.organizationName" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="甲方">
|
<el-form-item label="甲方">
|
||||||
@@ -63,6 +63,15 @@
|
|||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="申请日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.applyDate"
|
||||||
|
type="date"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="合同期限">
|
<el-form-item label="合同期限">
|
||||||
<div class="contract-basic-section__date-range">
|
<div class="contract-basic-section__date-range">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -91,7 +100,7 @@
|
|||||||
<template #suffix>天</template>
|
<template #suffix>天</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同金额">
|
<el-form-item label="合同金额" prop="contractAmount">
|
||||||
<el-input
|
<el-input
|
||||||
:model-value="contractAmountDisplay"
|
:model-value="contractAmountDisplay"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@@ -154,7 +163,7 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否需要加盖法人章">
|
<el-form-item label="是否需要加盖法人章">
|
||||||
<el-select v-model="form.legalSealFlag" placeholder="请选择">
|
<el-select v-model="form.legalSealFlag" placeholder="请选择" clearable>
|
||||||
<el-option label="否" :value="0" />
|
<el-option label="否" :value="0" />
|
||||||
<el-option label="是" :value="1" />
|
<el-option label="是" :value="1" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -173,20 +182,30 @@
|
|||||||
<el-input v-model="form.handlerUserName" disabled />
|
<el-input v-model="form.handlerUserName" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注" class="contract-basic-section__remark">
|
<el-form-item label="合同主要内容" class="contract-basic-section__remark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.mainContent"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
maxlength="1000"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入合同主要内容"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="其他事项说明" class="contract-basic-section__remark">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.remark"
|
v-model="form.remark"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="2"
|
:rows="2"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入备注"
|
placeholder="请输入其他事项说明"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="合同文件"
|
title="用印材料"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
:attachment-location-options="changeAttachmentLocationOptions"
|
:attachment-location-options="changeAttachmentLocationOptions"
|
||||||
@@ -251,13 +270,20 @@
|
|||||||
<el-button type="primary" plain @click="addPaymentRatioRow">添加</el-button>
|
<el-button type="primary" plain @click="addPaymentRatioRow">添加</el-button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<billing-plan-editor v-model="planDialogVisible" :value="planEditor" :index="planEditorIndex" @save="savePlan" />
|
<billing-plan-editor
|
||||||
|
v-model="planDialogVisible"
|
||||||
|
:value="planEditor"
|
||||||
|
:index="planEditorIndex"
|
||||||
|
:settlement-currency="form.settlementCurrency"
|
||||||
|
@save="savePlan"
|
||||||
|
/>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="其它附件"
|
title="附件(佐证材料等)"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
attachment-location="其它附件"
|
attachment-location="附件(佐证材料等)"
|
||||||
|
attachment-type-dict-code="contract_supporting_attachment_type"
|
||||||
:attachment-location-options="changeAttachmentLocationOptions"
|
:attachment-location-options="changeAttachmentLocationOptions"
|
||||||
:rows="attachments"
|
:rows="attachments"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@@ -308,6 +334,8 @@ import * as api from '@/api/business/contract-manage';
|
|||||||
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||||
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
||||||
import ContractAttachmentSection, {
|
import ContractAttachmentSection, {
|
||||||
|
isContractSealMaterialLocation,
|
||||||
|
isContractSupportingAttachmentLocation,
|
||||||
normalizeContractFileRows,
|
normalizeContractFileRows,
|
||||||
} from './components/contract-attachment-section.vue';
|
} from './components/contract-attachment-section.vue';
|
||||||
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
|
||||||
@@ -424,6 +452,21 @@ export default {
|
|||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
|
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
|
||||||
|
contractAmount: [
|
||||||
|
{ required: true, message: '请输入合同金额', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
return callback(new Error('请输入合同金额'));
|
||||||
|
}
|
||||||
|
if (Number(value) < 0) {
|
||||||
|
return callback(new Error('合同金额不能小于0'));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
changeReason: [{ max: 500, message: '变更原因不能超过500个字符', trigger: 'blur' }],
|
changeReason: [{ max: 500, message: '变更原因不能超过500个字符', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -433,7 +476,7 @@ export default {
|
|||||||
return this.$route.query.name || '合同变更';
|
return this.$route.query.name || '合同变更';
|
||||||
},
|
},
|
||||||
changeAttachmentLocationOptions() {
|
changeAttachmentLocationOptions() {
|
||||||
return ['合同文件', '其它附件', '变更材料'];
|
return ['用印材料', '附件(佐证材料等)', '变更材料'];
|
||||||
},
|
},
|
||||||
contractAmountDisplay() {
|
contractAmountDisplay() {
|
||||||
const value = this.form.contractAmount;
|
const value = this.form.contractAmount;
|
||||||
@@ -601,7 +644,7 @@ export default {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 审核通过后上一次的变更材料归集到「其它附件」,按文件地址/文件名去重,避免重复展示
|
// 审核通过后上一次的变更材料归集到「附件(佐证材料等)」,按文件地址/文件名去重,避免重复展示
|
||||||
mergeChangeAttachments(attachments = [], changeAttachments = []) {
|
mergeChangeAttachments(attachments = [], changeAttachments = []) {
|
||||||
const key = item => this.attachmentUrl(item) || this.attachmentName(item);
|
const key = item => this.attachmentUrl(item) || this.attachmentName(item);
|
||||||
const exists = new Set(attachments.map(key));
|
const exists = new Set(attachments.map(key));
|
||||||
@@ -655,11 +698,11 @@ export default {
|
|||||||
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
|
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
|
||||||
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (location === '合同文件') {
|
if (isContractSealMaterialLocation(location)) {
|
||||||
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location === '其它附件') {
|
if (isContractSupportingAttachmentLocation(location)) {
|
||||||
this.attachments = [...(this.attachments || []), ...rows];
|
this.attachments = [...(this.attachments || []), ...rows];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
<el-descriptions-item label="甲方">{{ detailValue('partyA') }}</el-descriptions-item>
|
<el-descriptions-item label="甲方">{{ detailValue('partyA') }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="乙方">{{ detailValue('partyB') }}</el-descriptions-item>
|
<el-descriptions-item label="乙方">{{ detailValue('partyB') }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="合同期限">{{ detailContractPeriod }}</el-descriptions-item>
|
<el-descriptions-item label="合同期限">{{ detailContractPeriod }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="所属组织">{{
|
<el-descriptions-item label="合同负责部门">{{
|
||||||
detailValue('organizationName')
|
detailValue('organizationName')
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="经办人">{{
|
<el-descriptions-item label="经办人">{{
|
||||||
@@ -140,6 +140,9 @@
|
|||||||
<el-descriptions-item label="签订日期">{{
|
<el-descriptions-item label="签订日期">{{
|
||||||
detailValue('signDate')
|
detailValue('signDate')
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="申请日期">{{
|
||||||
|
detailValue('applyDate')
|
||||||
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="合同格式">{{
|
<el-descriptions-item label="合同格式">{{
|
||||||
detailValue('contractFormat')
|
detailValue('contractFormat')
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
@@ -171,14 +174,17 @@
|
|||||||
<el-descriptions-item label="是否电子章">
|
<el-descriptions-item label="是否电子章">
|
||||||
{{ detailElectronicSealText }}
|
{{ detailElectronicSealText }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="备注" :span="3">{{
|
<el-descriptions-item label="合同主要内容" :span="3">{{
|
||||||
|
detailValue('mainContent')
|
||||||
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="其他事项说明" :span="3">{{
|
||||||
detailValue('remark')
|
detailValue('remark')
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="合同文件"
|
title="用印材料"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
readonly
|
readonly
|
||||||
@@ -294,7 +300,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="其它附件"
|
title="附件(佐证材料等)"
|
||||||
description
|
description
|
||||||
readonly
|
readonly
|
||||||
:rows="detailAttachmentRows"
|
:rows="detailAttachmentRows"
|
||||||
@@ -377,8 +383,14 @@
|
|||||||
<el-form-item label="合同编号">
|
<el-form-item label="合同编号">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.contractNo"
|
v-model="form.contractNo"
|
||||||
:disabled="formMode === 'edit'"
|
disabled
|
||||||
placeholder="不填写,则系统自动生成"
|
:placeholder="
|
||||||
|
formMode === 'add'
|
||||||
|
? form.organizationId
|
||||||
|
? '系统按部门规则自动生成'
|
||||||
|
: '请先选择合同负责部门'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同名称" prop="contractName">
|
<el-form-item label="合同名称" prop="contractName">
|
||||||
@@ -417,7 +429,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属组织" prop="organizationName">
|
<el-form-item label="合同负责部门" prop="organizationName">
|
||||||
<el-cascader
|
<el-cascader
|
||||||
v-model="organizationCascaderValue"
|
v-model="organizationCascaderValue"
|
||||||
:options="organizationOptions"
|
:options="organizationOptions"
|
||||||
@@ -471,6 +483,16 @@
|
|||||||
placeholder="请选择签订日期"
|
placeholder="请选择签订日期"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="申请日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.applyDate"
|
||||||
|
type="date"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="提交正式合同时自动填充"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="合同期限" prop="startDate">
|
<el-form-item label="合同期限" prop="startDate">
|
||||||
<div class="contract-manage-form__date-range">
|
<div class="contract-manage-form__date-range">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -497,7 +519,7 @@
|
|||||||
@input="value => integerInput('paymentDays', value)"
|
@input="value => integerInput('paymentDays', value)"
|
||||||
><template #suffix>天</template></el-input>
|
><template #suffix>天</template></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同金额">
|
<el-form-item label="合同金额" prop="contractAmount">
|
||||||
<el-input
|
<el-input
|
||||||
:model-value="contractAmountDisplay"
|
:model-value="contractAmountDisplay"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@@ -557,7 +579,7 @@
|
|||||||
>
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否需要加盖法人章" prop="legalSealFlag">
|
<el-form-item label="是否需要加盖法人章" prop="legalSealFlag">
|
||||||
<el-select v-model="form.legalSealFlag" placeholder="请选择">
|
<el-select v-model="form.legalSealFlag" placeholder="请选择" clearable>
|
||||||
<el-option label="否" :value="0" />
|
<el-option label="否" :value="0" />
|
||||||
<el-option label="是" :value="1" />
|
<el-option label="是" :value="1" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -576,20 +598,30 @@
|
|||||||
<el-input v-model="form.handlerUserName" disabled />
|
<el-input v-model="form.handlerUserName" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注" prop="remark" class="contract-manage-form__remark">
|
<el-form-item label="合同主要内容" prop="mainContent" class="contract-manage-form__remark">
|
||||||
|
<el-input
|
||||||
|
v-model="form.mainContent"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
maxlength="1000"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入合同主要内容"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="其他事项说明" prop="remark" class="contract-manage-form__remark">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.remark"
|
v-model="form.remark"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="2"
|
:rows="2"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入备注"
|
placeholder="请输入其他事项说明"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="合同文件"
|
title="用印材料"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
:attachment-location-options="contractAttachmentLocationOptions"
|
:attachment-location-options="contractAttachmentLocationOptions"
|
||||||
@@ -809,10 +841,11 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="其它附件"
|
title="附件(佐证材料等)"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
attachment-location="其它附件"
|
attachment-location="附件(佐证材料等)"
|
||||||
|
attachment-type-dict-code="contract_supporting_attachment_type"
|
||||||
:attachment-location-options="contractAttachmentLocationOptions"
|
:attachment-location-options="contractAttachmentLocationOptions"
|
||||||
:readonly="isAttachmentUploadMode"
|
:readonly="isAttachmentUploadMode"
|
||||||
:rows="attachmentRows"
|
:rows="attachmentRows"
|
||||||
@@ -920,18 +953,19 @@
|
|||||||
v-model="billingPlanBox"
|
v-model="billingPlanBox"
|
||||||
:value="billingPlanForm"
|
:value="billingPlanForm"
|
||||||
:index="billingPlanIndex"
|
:index="billingPlanIndex"
|
||||||
|
:settlement-currency="form.settlementCurrency"
|
||||||
@save="saveBillingPlan"
|
@save="saveBillingPlan"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
ref="listAttachmentUploader"
|
ref="listAttachmentUploader"
|
||||||
dialog-only
|
dialog-only
|
||||||
title="合同文件"
|
title="用印材料"
|
||||||
description
|
description
|
||||||
attachment-type
|
attachment-type
|
||||||
use-upload-dialog
|
use-upload-dialog
|
||||||
mark-approved-on-upload
|
mark-approved-on-upload
|
||||||
:attachment-location-options="['合同文件', '其它附件']"
|
:attachment-location-options="['用印材料', '附件(佐证材料等)']"
|
||||||
:rows="listUploadContractFileRows"
|
:rows="listUploadContractFileRows"
|
||||||
@update:rows="listUploadContractFileRows = $event"
|
@update:rows="listUploadContractFileRows = $event"
|
||||||
@upload-to-location="handleListAttachmentUploadToLocation"
|
@upload-to-location="handleListAttachmentUploadToLocation"
|
||||||
@@ -988,6 +1022,7 @@
|
|||||||
v-model="detailBillingPlanBox"
|
v-model="detailBillingPlanBox"
|
||||||
:value="detailBillingPlanForm"
|
:value="detailBillingPlanForm"
|
||||||
:index="detailBillingPlanIndex"
|
:index="detailBillingPlanIndex"
|
||||||
|
:settlement-currency="detail.settlementCurrency || form.settlementCurrency"
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -1026,6 +1061,8 @@ import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/m
|
|||||||
import ContractAttachmentSection, {
|
import ContractAttachmentSection, {
|
||||||
attachmentName as sharedAttachmentName,
|
attachmentName as sharedAttachmentName,
|
||||||
attachmentUrl as sharedAttachmentUrl,
|
attachmentUrl as sharedAttachmentUrl,
|
||||||
|
isContractSealMaterialLocation,
|
||||||
|
isContractSupportingAttachmentLocation,
|
||||||
normalizeContractFileRows,
|
normalizeContractFileRows,
|
||||||
} from './components/contract-attachment-section.vue';
|
} from './components/contract-attachment-section.vue';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
@@ -1093,8 +1130,9 @@ const defaultForm = () => ({
|
|||||||
partyB: '',
|
partyB: '',
|
||||||
handlerUserName: '',
|
handlerUserName: '',
|
||||||
signDate: '',
|
signDate: '',
|
||||||
|
applyDate: '',
|
||||||
contractFormat: '',
|
contractFormat: '',
|
||||||
legalSealFlag: 0,
|
legalSealFlag: null,
|
||||||
copyCount: '',
|
copyCount: '',
|
||||||
settlementCurrency: '',
|
settlementCurrency: '',
|
||||||
invoiceCycle: '',
|
invoiceCycle: '',
|
||||||
@@ -1110,6 +1148,7 @@ const defaultForm = () => ({
|
|||||||
effectiveType: '',
|
effectiveType: '',
|
||||||
feeGenerationMode: 'system',
|
feeGenerationMode: 'system',
|
||||||
archiveStatus: '未归档',
|
archiveStatus: '未归档',
|
||||||
|
mainContent: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
// 复制新增:仅保留可编辑字段与明细,剔除主键、编号、审批与流程信息
|
// 复制新增:仅保留可编辑字段与明细,剔除主键、编号、审批与流程信息
|
||||||
@@ -1121,6 +1160,7 @@ const createCopiedDetail = (detail = {}) => {
|
|||||||
return {
|
return {
|
||||||
...form,
|
...form,
|
||||||
contractNo: '',
|
contractNo: '',
|
||||||
|
applyDate: '',
|
||||||
archiveStatus: '未归档',
|
archiveStatus: '未归档',
|
||||||
feeGenerationMode: detail.feeGenerationMode || '',
|
feeGenerationMode: detail.feeGenerationMode || '',
|
||||||
billingEnabled: detail.billingEnabled,
|
billingEnabled: detail.billingEnabled,
|
||||||
@@ -1215,13 +1255,12 @@ export default {
|
|||||||
signType: [{ required: true, message: '请选择签约类型', trigger: 'change' }],
|
signType: [{ required: true, message: '请选择签约类型', trigger: 'change' }],
|
||||||
settlementCurrency: [{ required: true, message: '请选择结算币种', trigger: 'change' }],
|
settlementCurrency: [{ required: true, message: '请选择结算币种', trigger: 'change' }],
|
||||||
projectName: [{ required: true, message: '请选择所属项目', trigger: 'change' }],
|
projectName: [{ required: true, message: '请选择所属项目', trigger: 'change' }],
|
||||||
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
organizationName: [{ required: true, message: '请选择合同负责部门', trigger: 'change' }],
|
||||||
partyA: [{ required: true, message: '请选择甲方', trigger: 'change' }],
|
partyA: [{ required: true, message: '请选择甲方', trigger: 'change' }],
|
||||||
partyB: [{ required: true, message: '请选择乙方', trigger: 'change' }],
|
partyB: [{ required: true, message: '请选择乙方', trigger: 'change' }],
|
||||||
signDate: [{ required: true, message: '请选择签订日期', trigger: 'change' }],
|
signDate: [{ required: true, message: '请选择签订日期', trigger: 'change' }],
|
||||||
settlementMode: [{ required: true, message: '请选择结算方式', trigger: 'change' }],
|
settlementMode: [{ required: true, message: '请选择结算方式', trigger: 'change' }],
|
||||||
contractFormat: [{ required: true, message: '请选择合同格式', trigger: 'change' }],
|
contractFormat: [{ required: true, message: '请选择合同格式', trigger: 'change' }],
|
||||||
legalSealFlag: [{ required: true, message: '请选择是否需要加盖法人章', trigger: 'change' }],
|
|
||||||
invoiceCycle: [
|
invoiceCycle: [
|
||||||
{
|
{
|
||||||
validator: (rule, value, callback) => {
|
validator: (rule, value, callback) => {
|
||||||
@@ -1236,6 +1275,22 @@ export default {
|
|||||||
],
|
],
|
||||||
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
|
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
|
||||||
endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
|
endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
|
||||||
|
contractAmount: [
|
||||||
|
{ required: true, message: '请输入合同金额', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
return callback(new Error('请输入合同金额'));
|
||||||
|
}
|
||||||
|
if (Number(value) < 0) {
|
||||||
|
return callback(new Error('合同金额不能小于0'));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mainContent: [{ max: 1000, message: '合同主要内容不能超过1000个字符', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
submitAction: '',
|
submitAction: '',
|
||||||
projectOptions: [],
|
projectOptions: [],
|
||||||
@@ -1345,8 +1400,8 @@ export default {
|
|||||||
return this.$route.query.attachmentUpload === '1';
|
return this.$route.query.attachmentUpload === '1';
|
||||||
},
|
},
|
||||||
contractAttachmentLocationOptions() {
|
contractAttachmentLocationOptions() {
|
||||||
if (this.isAttachmentUploadMode) return ['合同文件'];
|
if (this.isAttachmentUploadMode) return ['用印材料'];
|
||||||
return ['合同文件', '其它附件'];
|
return ['用印材料', '附件(佐证材料等)'];
|
||||||
},
|
},
|
||||||
contractAmountDisplay() {
|
contractAmountDisplay() {
|
||||||
const value = this.form.contractAmount;
|
const value = this.form.contractAmount;
|
||||||
@@ -1413,7 +1468,7 @@ export default {
|
|||||||
expandTrigger: 'click',
|
expandTrigger: 'click',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 所属组织级联:selectedOrganizationId 存部门 id,级联控件用根到节点的 id 路径,
|
// 合同负责部门级联:selectedOrganizationId 存部门 id,级联控件用根到节点的 id 路径,
|
||||||
// 两者在此双向转换;依赖 organizationOptions,树加载完成后回显自动刷新
|
// 两者在此双向转换;依赖 organizationOptions,树加载完成后回显自动刷新
|
||||||
organizationCascaderValue: {
|
organizationCascaderValue: {
|
||||||
get() {
|
get() {
|
||||||
@@ -1775,6 +1830,7 @@ export default {
|
|||||||
this.applyDetail(createCopiedDetail(detail));
|
this.applyDetail(createCopiedDetail(detail));
|
||||||
this.form.handlerUserName =
|
this.form.handlerUserName =
|
||||||
this.userInfo?.realName || this.userInfo?.userName || this.userInfo?.account || '';
|
this.userInfo?.realName || this.userInfo?.userName || this.userInfo?.account || '';
|
||||||
|
this.refreshPreviewContractNo();
|
||||||
this.$nextTick(() => this.$refs.contractForm?.clearValidate());
|
this.$nextTick(() => this.$refs.contractForm?.clearValidate());
|
||||||
},
|
},
|
||||||
closeForm() {
|
closeForm() {
|
||||||
@@ -1789,9 +1845,10 @@ export default {
|
|||||||
['contractName', '合同名称'],
|
['contractName', '合同名称'],
|
||||||
['contractCategory', '合同类型'],
|
['contractCategory', '合同类型'],
|
||||||
['projectName', '所属项目'],
|
['projectName', '所属项目'],
|
||||||
['organizationName', '所属组织'],
|
['organizationName', '合同负责部门'],
|
||||||
['partyA', '甲方'],
|
['partyA', '甲方'],
|
||||||
['partyB', '乙方'],
|
['partyB', '乙方'],
|
||||||
|
['contractAmount', '合同金额'],
|
||||||
];
|
];
|
||||||
const empty = required.find(([prop]) => {
|
const empty = required.find(([prop]) => {
|
||||||
const value = this.form[prop];
|
const value = this.form[prop];
|
||||||
@@ -1799,10 +1856,16 @@ export default {
|
|||||||
});
|
});
|
||||||
if (empty) {
|
if (empty) {
|
||||||
this.$message.warning(
|
this.$message.warning(
|
||||||
empty[0] === 'contractName' ? `请输入${empty[1]}` : `请选择${empty[1]}`
|
['contractName', 'contractAmount'].includes(empty[0])
|
||||||
|
? `请输入${empty[1]}`
|
||||||
|
: `请选择${empty[1]}`
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (Number(this.form.contractAmount) < 0) {
|
||||||
|
this.$message.warning('合同金额不能小于0');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!this.form.contractName?.trim()) {
|
if (!this.form.contractName?.trim()) {
|
||||||
@@ -1825,7 +1888,7 @@ export default {
|
|||||||
if (action !== 'formal' && action !== 'edit') return true;
|
if (action !== 'formal' && action !== 'edit') return true;
|
||||||
const required = [
|
const required = [
|
||||||
['projectName', '所属项目'],
|
['projectName', '所属项目'],
|
||||||
['organizationName', '所属组织'],
|
['organizationName', '合同负责部门'],
|
||||||
['partyA', '甲方'],
|
['partyA', '甲方'],
|
||||||
['partyB', '乙方'],
|
['partyB', '乙方'],
|
||||||
['startDate', '合同开始日期'],
|
['startDate', '合同开始日期'],
|
||||||
@@ -1833,10 +1896,20 @@ export default {
|
|||||||
['signDate', '签订日期'],
|
['signDate', '签订日期'],
|
||||||
['settlementMode', '结算方式'],
|
['settlementMode', '结算方式'],
|
||||||
['contractFormat', '合同格式'],
|
['contractFormat', '合同格式'],
|
||||||
|
['contractAmount', '合同金额'],
|
||||||
];
|
];
|
||||||
const empty = required.find(([prop]) => !this.form[prop]);
|
const empty = required.find(([prop]) => {
|
||||||
|
const value = this.form[prop];
|
||||||
|
return value === undefined || value === null || String(value).trim() === '';
|
||||||
|
});
|
||||||
if (empty) {
|
if (empty) {
|
||||||
this.$message.warning(`请选择或输入${empty[1]}`);
|
this.$message.warning(
|
||||||
|
empty[0] === 'contractAmount' ? `请输入${empty[1]}` : `请选择或输入${empty[1]}`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Number(this.form.contractAmount) < 0) {
|
||||||
|
this.$message.warning('合同金额不能小于0');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -2026,11 +2099,11 @@ export default {
|
|||||||
},
|
},
|
||||||
handleListAttachmentUploadToLocation({ location, rows = [] }) {
|
handleListAttachmentUploadToLocation({ location, rows = [] }) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (location === '合同文件') {
|
if (isContractSealMaterialLocation(location)) {
|
||||||
this.listUploadContractFileRows = [...(this.listUploadContractFileRows || []), ...rows];
|
this.listUploadContractFileRows = [...(this.listUploadContractFileRows || []), ...rows];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location === '其它附件') {
|
if (isContractSupportingAttachmentLocation(location)) {
|
||||||
this.listUploadAttachmentRows = [...(this.listUploadAttachmentRows || []), ...rows];
|
this.listUploadAttachmentRows = [...(this.listUploadAttachmentRows || []), ...rows];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2150,6 +2223,9 @@ export default {
|
|||||||
column.renderSearch = scope => this.renderOrganizationSearch(scope);
|
column.renderSearch = scope => this.renderOrganizationSearch(scope);
|
||||||
}
|
}
|
||||||
this.syncCurrentOrganizationOption();
|
this.syncCurrentOrganizationOption();
|
||||||
|
if (this.isFormPage && this.formMode === 'add' && this.form.organizationId) {
|
||||||
|
this.refreshPreviewContractNo();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.organizationLoading = false;
|
this.organizationLoading = false;
|
||||||
@@ -2212,6 +2288,29 @@ export default {
|
|||||||
}
|
}
|
||||||
: { organizationId: '', organizationName: '' }
|
: { organizationId: '', organizationName: '' }
|
||||||
);
|
);
|
||||||
|
if (this.formMode === 'add') {
|
||||||
|
this.refreshPreviewContractNo();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshPreviewContractNo() {
|
||||||
|
if (this.formMode !== 'add') return;
|
||||||
|
const organizationId = this.form.organizationId;
|
||||||
|
if (!organizationId) {
|
||||||
|
this.form.contractNo = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const requestId = (this.contractNoPreviewRequestId =
|
||||||
|
(this.contractNoPreviewRequestId || 0) + 1);
|
||||||
|
api
|
||||||
|
.nextContractNo(organizationId)
|
||||||
|
.then(res => {
|
||||||
|
if (requestId !== this.contractNoPreviewRequestId) return;
|
||||||
|
this.form.contractNo = res.data?.data || '';
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (requestId !== this.contractNoPreviewRequestId) return;
|
||||||
|
this.form.contractNo = '';
|
||||||
|
});
|
||||||
},
|
},
|
||||||
syncCurrentOrganizationOption() {
|
syncCurrentOrganizationOption() {
|
||||||
if (!this.form.organizationId || !this.form.organizationName) return;
|
if (!this.form.organizationId || !this.form.organizationName) return;
|
||||||
@@ -2225,7 +2324,7 @@ export default {
|
|||||||
this.organizationFlatOptions.unshift(item);
|
this.organizationFlatOptions.unshift(item);
|
||||||
},
|
},
|
||||||
renderOrganizationSearch(scope) {
|
renderOrganizationSearch(scope) {
|
||||||
// 搜索区所属组织:单选级联(与新增/编辑表单一致)
|
// 搜索区合同负责部门:单选级联(与新增/编辑表单一致)
|
||||||
// 级联返回 id 路径数组,提交时由 normalizeSearch 取末级 id 转组织名称
|
// 级联返回 id 路径数组,提交时由 normalizeSearch 取末级 id 转组织名称
|
||||||
return h(ElCascader, {
|
return h(ElCascader, {
|
||||||
modelValue: Array.isArray(scope.row?.organizationName) ? scope.row.organizationName : [],
|
modelValue: Array.isArray(scope.row?.organizationName) ? scope.row.organizationName : [],
|
||||||
@@ -2244,7 +2343,7 @@ export default {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
style: 'width: 100%',
|
style: 'width: 100%',
|
||||||
placeholder: '请选择 所属组织',
|
placeholder: '请选择 合同负责部门',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
integerInput(prop, value) {
|
integerInput(prop, value) {
|
||||||
@@ -2289,11 +2388,11 @@ export default {
|
|||||||
},
|
},
|
||||||
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
handleAttachmentUploadToLocation({ location, rows = [] }) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (location === '合同文件') {
|
if (isContractSealMaterialLocation(location)) {
|
||||||
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
this.contractFileRows = [...(this.contractFileRows || []), ...rows];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location === '其它附件') {
|
if (isContractSupportingAttachmentLocation(location)) {
|
||||||
this.attachmentRows = [...(this.attachmentRows || []), ...rows];
|
this.attachmentRows = [...(this.attachmentRows || []), ...rows];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2654,15 +2753,16 @@ export default {
|
|||||||
templateFlag: '是否范本',
|
templateFlag: '是否范本',
|
||||||
originalContractNo: '原件合同编号',
|
originalContractNo: '原件合同编号',
|
||||||
electronicSealFlag: '是否电子章',
|
electronicSealFlag: '是否电子章',
|
||||||
remark: '备注',
|
mainContent: '合同主要内容',
|
||||||
|
remark: '其他事项说明',
|
||||||
feeGenerationMode: '费用生成模式',
|
feeGenerationMode: '费用生成模式',
|
||||||
billingPlanJson: '计费方案',
|
billingPlanJson: '计费方案',
|
||||||
settlementRuleJson: '结算单规则',
|
settlementRuleJson: '结算单规则',
|
||||||
preSettlementConfigJson: '预结算配置',
|
preSettlementConfigJson: '预结算配置',
|
||||||
formalSettlementConfigJson: '正式结算配置',
|
formalSettlementConfigJson: '正式结算配置',
|
||||||
paymentRatioJson: '付款比例设置',
|
paymentRatioJson: '付款比例设置',
|
||||||
contractFileJson: '合同文件',
|
contractFileJson: '用印材料',
|
||||||
attachmentsJson: '其它附件',
|
attachmentsJson: '附件(佐证材料等)',
|
||||||
changeAttachmentsJson: '变更材料',
|
changeAttachmentsJson: '变更材料',
|
||||||
};
|
};
|
||||||
return labels[field] || field;
|
return labels[field] || field;
|
||||||
|
|||||||
@@ -357,6 +357,33 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '合同编码前缀',
|
||||||
|
prop: 'contractCodePrefix',
|
||||||
|
minWidth: 160,
|
||||||
|
order: 62,
|
||||||
|
maxlength: 64,
|
||||||
|
showWordLimit: true,
|
||||||
|
placeholder: '如 GWZH成都',
|
||||||
|
tip: '合同编号规则:前缀(年份)三位流水号,流水按本部门独立编号',
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
const prefix = String(value || '').trim();
|
||||||
|
if (!prefix) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (prefix.length > 64) {
|
||||||
|
callback(new Error('合同编码前缀不能超过64个字符'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '组织简称',
|
label: '组织简称',
|
||||||
prop: 'shortName',
|
prop: 'shortName',
|
||||||
@@ -995,6 +1022,7 @@ export default {
|
|||||||
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
||||||
row.deptCode = String(row.deptCode || '').trim();
|
row.deptCode = String(row.deptCode || '').trim();
|
||||||
row.kingdeeDeptCode = String(row.kingdeeDeptCode || '').trim();
|
row.kingdeeDeptCode = String(row.kingdeeDeptCode || '').trim();
|
||||||
|
row.contractCodePrefix = String(row.contractCodePrefix || '').trim();
|
||||||
row.leaderId = func.join(row.leaderId);
|
row.leaderId = func.join(row.leaderId);
|
||||||
this.normalizeTopParent(row);
|
this.normalizeTopParent(row);
|
||||||
if (!this.prepareDeptSubmitRow(row)) {
|
if (!this.prepareDeptSubmitRow(row)) {
|
||||||
@@ -1030,6 +1058,7 @@ export default {
|
|||||||
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
|
||||||
row.deptCode = String(row.deptCode || '').trim();
|
row.deptCode = String(row.deptCode || '').trim();
|
||||||
row.kingdeeDeptCode = String(row.kingdeeDeptCode || '').trim();
|
row.kingdeeDeptCode = String(row.kingdeeDeptCode || '').trim();
|
||||||
|
row.contractCodePrefix = String(row.contractCodePrefix || '').trim();
|
||||||
row.leaderId = func.join(row.leaderId);
|
row.leaderId = func.join(row.leaderId);
|
||||||
this.normalizeTopParent(row);
|
this.normalizeTopParent(row);
|
||||||
if (!this.prepareDeptSubmitRow(row)) {
|
if (!this.prepareDeptSubmitRow(row)) {
|
||||||
|
|||||||
@@ -428,4 +428,43 @@ export default {
|
|||||||
.vehicle-dispatch-page :deep(.vehicle-dispatch-form-control) {
|
.vehicle-dispatch-page :deep(.vehicle-dispatch-form-control) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
/* 查看弹窗保持与新增/编辑相同的表单样式,仅禁用控件 */
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-form-item) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-input.is-disabled .el-input__wrapper) {
|
||||||
|
box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-input.is-disabled .el-input__inner),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-textarea.is-disabled .el-textarea__inner),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-range-editor.is-disabled),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-range-editor.is-disabled input) {
|
||||||
|
color: var(--el-disabled-text-color);
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
padding-left: 11px;
|
||||||
|
box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
|
||||||
|
-webkit-text-fill-color: var(--el-disabled-text-color);
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-select__wrapper.is-disabled) {
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset !important;
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-select__suffix),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-input__suffix),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-input__prefix) {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-input__inner),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-textarea__inner) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-cascader) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-cascader .el-input.is-disabled .el-input__wrapper),
|
||||||
|
.vehicle-dispatch-page :deep(.avue--detail .el-autocomplete .el-input.is-disabled .el-input__wrapper) {
|
||||||
|
box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -985,7 +985,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.applyDrivingLicenseValidity(inspectionValidity);
|
this.applyDrivingLicenseValidity(inspectionValidity);
|
||||||
if (registrationDate) {
|
if (registrationDate) {
|
||||||
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate);
|
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate, 'first');
|
||||||
}
|
}
|
||||||
if (registrationNo) {
|
if (registrationNo) {
|
||||||
this.vehicleForm.registrationNo = String(registrationNo).replace(/\s/g, '');
|
this.vehicleForm.registrationNo = String(registrationNo).replace(/\s/g, '');
|
||||||
@@ -1000,6 +1000,7 @@ export default {
|
|||||||
'vehicleType',
|
'vehicleType',
|
||||||
'drivingLicenseNo',
|
'drivingLicenseNo',
|
||||||
'drivingLicenseEndDate',
|
'drivingLicenseEndDate',
|
||||||
|
'registrationDate',
|
||||||
'registrationNo',
|
'registrationNo',
|
||||||
'approvedLoadKg',
|
'approvedLoadKg',
|
||||||
'outerLength',
|
'outerLength',
|
||||||
@@ -1057,9 +1058,9 @@ export default {
|
|||||||
this.vehicleForm.roadTransportCertEndDate = '';
|
this.vehicleForm.roadTransportCertEndDate = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dateList = value.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || [];
|
const endDate = this.normalizeDate(value, 'last');
|
||||||
if (dateList.length) {
|
if (endDate) {
|
||||||
this.vehicleForm.roadTransportCertEndDate = this.normalizeDate(dateList[dateList.length - 1]);
|
this.vehicleForm.roadTransportCertEndDate = endDate;
|
||||||
this.vehicleForm.roadTransportCertLongTerm = 0;
|
this.vehicleForm.roadTransportCertLongTerm = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1071,9 +1072,9 @@ export default {
|
|||||||
this.vehicleForm.drivingLicenseEndDate = '';
|
this.vehicleForm.drivingLicenseEndDate = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dateList = value.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || [];
|
const endDate = this.normalizeDate(value, 'last');
|
||||||
if (dateList.length) {
|
if (endDate) {
|
||||||
this.vehicleForm.drivingLicenseEndDate = this.normalizeDate(dateList[dateList.length - 1]);
|
this.vehicleForm.drivingLicenseEndDate = endDate;
|
||||||
this.vehicleForm.drivingLicenseLongTerm = 0;
|
this.vehicleForm.drivingLicenseLongTerm = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1150,11 +1151,11 @@ export default {
|
|||||||
this.vehicleForm.drivingLicenseNo = drivingLicenseNo;
|
this.vehicleForm.drivingLicenseNo = drivingLicenseNo;
|
||||||
}
|
}
|
||||||
if (drivingLicenseEndDate) {
|
if (drivingLicenseEndDate) {
|
||||||
this.vehicleForm.drivingLicenseEndDate = this.normalizeDate(drivingLicenseEndDate);
|
this.vehicleForm.drivingLicenseEndDate = this.normalizeDate(drivingLicenseEndDate, 'last');
|
||||||
this.vehicleForm.drivingLicenseLongTerm = 0;
|
this.vehicleForm.drivingLicenseLongTerm = 0;
|
||||||
}
|
}
|
||||||
if (registrationDate) {
|
if (registrationDate) {
|
||||||
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate);
|
this.vehicleForm.registrationDate = this.normalizeDate(registrationDate, 'first');
|
||||||
}
|
}
|
||||||
if (registrationNo) {
|
if (registrationNo) {
|
||||||
this.vehicleForm.registrationNo = registrationNo;
|
this.vehicleForm.registrationNo = registrationNo;
|
||||||
@@ -1165,18 +1166,60 @@ export default {
|
|||||||
'vehicleType',
|
'vehicleType',
|
||||||
'drivingLicenseNo',
|
'drivingLicenseNo',
|
||||||
'drivingLicenseEndDate',
|
'drivingLicenseEndDate',
|
||||||
|
'registrationDate',
|
||||||
'registrationNo',
|
'registrationNo',
|
||||||
].forEach(prop => {
|
].forEach(prop => {
|
||||||
this.$refs.vehicleForm?.validateField(prop);
|
this.$refs.vehicleForm?.validateField(prop);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
normalizeDate(value = '') {
|
/**
|
||||||
|
* OCR 日期常为区间(如 2022-08-17至2024-01-05)或含无效日(如 01-65)。
|
||||||
|
* 提取可解析的合法日期;prefer=first 取登记类起始日,prefer=last 取有效期截止日。
|
||||||
|
*/
|
||||||
|
normalizeDate(value = '', prefer = 'first') {
|
||||||
const dateValue = String(value || '').trim();
|
const dateValue = String(value || '').trim();
|
||||||
const match = dateValue.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
|
if (!dateValue) return '';
|
||||||
if (!match) return dateValue;
|
const segments = dateValue.match(/\d{4}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/g) || [];
|
||||||
const [, year, month, day] = match;
|
const sourceList = segments.length ? segments : [dateValue];
|
||||||
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
|
const candidates = sourceList
|
||||||
|
.map(item => {
|
||||||
|
const match = String(item)
|
||||||
|
.trim()
|
||||||
|
.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
|
||||||
|
if (!match) return '';
|
||||||
|
const year = Number(match[1]);
|
||||||
|
const month = Number(match[2]);
|
||||||
|
const day = Number(match[3]);
|
||||||
|
const date = new Date(year, month - 1, day);
|
||||||
|
if (
|
||||||
|
date.getFullYear() !== year ||
|
||||||
|
date.getMonth() !== month - 1 ||
|
||||||
|
date.getDate() !== day
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
if (!candidates.length) return '';
|
||||||
|
return prefer === 'last' ? candidates[candidates.length - 1] : candidates[0];
|
||||||
|
},
|
||||||
|
normalizeVehicleDateFields(form = {}) {
|
||||||
|
const dateFields = [
|
||||||
|
['compulsoryScrapDate', 'first'],
|
||||||
|
['drivingLicenseStartDate', 'first'],
|
||||||
|
['drivingLicenseEndDate', 'last'],
|
||||||
|
['roadTransportCertStartDate', 'first'],
|
||||||
|
['roadTransportCertEndDate', 'last'],
|
||||||
|
['annualReviewEndDate', 'last'],
|
||||||
|
['registrationDate', 'first'],
|
||||||
|
];
|
||||||
|
dateFields.forEach(([prop, prefer]) => {
|
||||||
|
if (form[prop] === undefined || form[prop] === null || form[prop] === '') return;
|
||||||
|
form[prop] = this.normalizeDate(form[prop], prefer);
|
||||||
|
});
|
||||||
|
return form;
|
||||||
},
|
},
|
||||||
normalizeVehicleType(value = '') {
|
normalizeVehicleType(value = '') {
|
||||||
const vehicleType = String(value || '').trim();
|
const vehicleType = String(value || '').trim();
|
||||||
@@ -1207,12 +1250,14 @@ export default {
|
|||||||
},
|
},
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
this.syncPlateNo();
|
this.syncPlateNo();
|
||||||
|
this.normalizeVehicleDateFields(this.vehicleForm);
|
||||||
this.$refs.vehicleForm.validate(valid => {
|
this.$refs.vehicleForm.validate(valid => {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.submitLoading = true;
|
this.submitLoading = true;
|
||||||
submit(this.vehicleForm)
|
const payload = this.normalizeVehicleDateFields({ ...this.vehicleForm });
|
||||||
|
submit(payload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success('操作成功');
|
this.$message.success('操作成功');
|
||||||
this.vehicleBox = false;
|
this.vehicleBox = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user