Merge branch 'refs/heads/feature/bfi-api-rate' into feature/hcs-api-p0-web
# Conflicts: # src/option/business/contract-manage.js
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
class="contract-manage-form__attachment-table"
|
||||
@selection-change="selected = $event"
|
||||
>
|
||||
<el-table-column v-if="!readonly" type="selection" width="55" align="center" />
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column v-if="attachmentType" label="附件类型" min-width="160" align="center">
|
||||
<template #default="{ row, $index }">
|
||||
@@ -45,11 +45,17 @@
|
||||
align="left"
|
||||
header-align="left"
|
||||
show-overflow-tooltip
|
||||
class-name="is-ellipsis-link"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="preview?.(row, rows)">{{
|
||||
attachmentName(row)
|
||||
}}</el-link>
|
||||
<span
|
||||
class="attachment-file-name"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="preview?.(row, rows)"
|
||||
@keydown.enter="preview?.(row, rows)"
|
||||
@keydown.space.prevent="preview?.(row, rows)"
|
||||
>{{ attachmentName(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="description" label="附件描述" min-width="220">
|
||||
@@ -173,15 +179,43 @@ import { downloadFileByUrl } from '@/utils/util';
|
||||
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
|
||||
|
||||
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 = [
|
||||
{ label: '合同文件', value: '合同文件' },
|
||||
{ label: '用印材料', value: '用印材料' },
|
||||
{ label: '双章归档文件', value: '双章归档文件' },
|
||||
// 高风险/黑名单客商合同必传,后端按 fileType=特批说明 校验与转传特批(与后端 HcsSyncConstant.ATTACHMENT_TYPE_SPEC_EXPLANATION 对齐)
|
||||
{ label: '特批说明', value: '特批说明' },
|
||||
{ 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 isContractSealMaterialLocation = location => {
|
||||
const text = String(location || '');
|
||||
return text === CONTRACT_SEAL_MATERIAL_LOCATION || text === '合同文件';
|
||||
};
|
||||
export const attachmentFileTypes = [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -236,8 +270,11 @@ const isOtherAttachmentTypeOption = item => {
|
||||
const attachmentTypeKeywords = fileType => {
|
||||
const text = String(fileType || '');
|
||||
if (text.includes('双章')) return ['双章归档文件', '双章归档', '双章'];
|
||||
if (text.includes('合同') && !text.includes('其他') && !text.includes('其它')) {
|
||||
return ['合同文件', '合同'];
|
||||
if (
|
||||
text.includes('用印') ||
|
||||
(text.includes('合同') && !text.includes('其他') && !text.includes('其它'))
|
||||
) {
|
||||
return ['用印材料', '合同文件', '用印', '合同'];
|
||||
}
|
||||
if (isOtherAttachmentTypeOption({ label: text, value: text })) return [];
|
||||
return text ? [text] : [];
|
||||
@@ -256,8 +293,13 @@ export const resolveContractAttachmentType = (
|
||||
const list = options.length ? options : defaultContractAttachmentTypeOptions;
|
||||
const otherType = resolveOtherAttachmentType(list);
|
||||
const values = list.map(item => item.value);
|
||||
if (values.includes(row.fileType)) return row.fileType;
|
||||
const matchedByLabel = list.find(item => String(item.label) === String(row.fileType));
|
||||
const rawFileType = 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;
|
||||
const fileName = normalizeAttachmentTypeText(attachmentName(row));
|
||||
if (!fileName) return otherType;
|
||||
@@ -285,17 +327,32 @@ export const normalizeContractFileRows = (rows, options = defaultContractAttachm
|
||||
description: row.description || '',
|
||||
}));
|
||||
|
||||
const mapDictOptions = response => {
|
||||
const mapDictOptions = (response, dictCode = CONTRACT_ATTACHMENT_TYPE_DICT) => {
|
||||
const data = response?.data?.data || response?.data || [];
|
||||
const records = Array.isArray(data) ? data : data.records || [];
|
||||
const applySealAlias = dictCode === CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||
return records
|
||||
.map(item => ({
|
||||
label: item.dictValue,
|
||||
value: item.dictKey,
|
||||
}))
|
||||
.map(item => {
|
||||
const rawLabel = String(item.dictValue || '');
|
||||
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);
|
||||
};
|
||||
|
||||
const defaultOptionsByDictCode = dictCode =>
|
||||
dictCode === CONTRACT_SUPPORTING_ATTACHMENT_TYPE_DICT
|
||||
? [...defaultContractSupportingAttachmentTypeOptions]
|
||||
: [...defaultContractAttachmentTypeOptions];
|
||||
|
||||
export default {
|
||||
name: 'ContractAttachmentSection',
|
||||
components: { UploadFilled },
|
||||
@@ -309,8 +366,13 @@ export default {
|
||||
markApprovedOnUpload: Boolean,
|
||||
useUploadDialog: Boolean,
|
||||
dialogOnly: Boolean,
|
||||
attachmentLocation: { type: String, default: '合同文件' },
|
||||
attachmentLocation: { type: String, default: CONTRACT_SEAL_MATERIAL_LOCATION },
|
||||
attachmentLocationOptions: { type: Array, default: null },
|
||||
/** 附件类型业务字典 code,默认用印材料字典;佐证材料传 contract_supporting_attachment_type */
|
||||
attachmentTypeDictCode: {
|
||||
type: String,
|
||||
default: CONTRACT_ATTACHMENT_TYPE_DICT,
|
||||
},
|
||||
preview: { type: Function, default: null },
|
||||
},
|
||||
emits: ['update:rows', 'upload-to-location', 'upload-confirmed'],
|
||||
@@ -319,8 +381,9 @@ export default {
|
||||
selected: [],
|
||||
attachmentFileTypes,
|
||||
contractAttachmentTypeOptions: [...defaultContractAttachmentTypeOptions],
|
||||
activeAttachmentTypeDictCode: CONTRACT_ATTACHMENT_TYPE_DICT,
|
||||
uploadDialogVisible: false,
|
||||
uploadDialogLocation: '合同文件',
|
||||
uploadDialogLocation: CONTRACT_SEAL_MATERIAL_LOCATION,
|
||||
uploadDialogType: resolveOtherAttachmentType(defaultContractAttachmentTypeOptions),
|
||||
uploadDialogFiles: [],
|
||||
uploadDialogFileList: [],
|
||||
@@ -339,48 +402,91 @@ export default {
|
||||
defaultAttachmentType() {
|
||||
return resolveOtherAttachmentType(this.contractAttachmentTypeOptions);
|
||||
},
|
||||
resolvedAttachmentTypeDictCode() {
|
||||
return this.attachmentTypeDictCode || CONTRACT_ATTACHMENT_TYPE_DICT;
|
||||
},
|
||||
resolvedLocationOptions() {
|
||||
const options = (this.attachmentLocationOptions || []).filter(Boolean);
|
||||
if (options.length) return options;
|
||||
return ['合同文件', '其它附件'];
|
||||
if (options.length) {
|
||||
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() {
|
||||
this.loadAttachmentTypeOptions();
|
||||
this.loadAttachmentTypeOptions(this.resolvedAttachmentTypeDictCode, true);
|
||||
},
|
||||
methods: {
|
||||
attachmentName,
|
||||
loadAttachmentTypeOptions() {
|
||||
return getBizDictionary({ code: 'contract_attachment_type' })
|
||||
currentUploadUserName() {
|
||||
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 => {
|
||||
const options = mapDictOptions(res);
|
||||
// 切换字典期间若已发起更新请求,忽略过期响应
|
||||
if (this.activeAttachmentTypeDictCode !== code) return;
|
||||
const options = mapDictOptions(res, code);
|
||||
if (options.length) {
|
||||
// 字典缺少「其他文件」时补上,保证未匹配文件名可默认选中
|
||||
this.contractAttachmentTypeOptions = options.some(isOtherAttachmentTypeOption)
|
||||
? options
|
||||
: [...options, { label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER }];
|
||||
if (
|
||||
!this.contractAttachmentTypeOptions.some(
|
||||
item => String(item.value) === String(this.uploadDialogType)
|
||||
)
|
||||
) {
|
||||
this.uploadDialogType = this.defaultAttachmentType;
|
||||
}
|
||||
// 字典加载后,把已有行的中文类型名对齐到 dictKey,保证下拉可选中
|
||||
if (this.attachmentType && (this.rows || []).length) {
|
||||
const aligned = (this.rows || []).map(row => ({
|
||||
...row,
|
||||
fileType: resolveContractAttachmentType(row, this.contractAttachmentTypeOptions),
|
||||
}));
|
||||
const changed = aligned.some(
|
||||
(row, index) => row.fileType !== this.rows[index]?.fileType
|
||||
);
|
||||
if (changed) this.update(aligned);
|
||||
}
|
||||
} else {
|
||||
this.contractAttachmentTypeOptions = fallbackOptions;
|
||||
}
|
||||
if (
|
||||
!this.contractAttachmentTypeOptions.some(
|
||||
item => String(item.value) === String(this.uploadDialogType)
|
||||
)
|
||||
) {
|
||||
this.uploadDialogType = this.defaultAttachmentType;
|
||||
}
|
||||
// 字典加载后,把已有行的中文类型名对齐到 dictKey,保证下拉可选中
|
||||
if (alignRows && this.attachmentType && (this.rows || []).length) {
|
||||
const aligned = (this.rows || []).map(row => ({
|
||||
...row,
|
||||
fileType: resolveContractAttachmentType(row, this.contractAttachmentTypeOptions),
|
||||
}));
|
||||
const changed = aligned.some(
|
||||
(row, index) => row.fileType !== this.rows[index]?.fileType
|
||||
);
|
||||
if (changed) this.update(aligned);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 字典加载失败时保留默认选项
|
||||
if (this.activeAttachmentTypeDictCode !== code) return;
|
||||
this.contractAttachmentTypeOptions = fallbackOptions;
|
||||
this.uploadDialogType = this.defaultAttachmentType;
|
||||
});
|
||||
},
|
||||
isRowLocked(row) {
|
||||
@@ -390,13 +496,14 @@ export default {
|
||||
this.$emit('update:rows', Array.isArray(rows) ? rows.map(item => ({ ...item })) : []);
|
||||
},
|
||||
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 next = {
|
||||
...existing,
|
||||
...row,
|
||||
description: row.description || existing?.description || '',
|
||||
uploadUserName: existing?.uploadUserName || row.uploadUserName || uploadUserName,
|
||||
// 新上传行始终写入当前用户;已有行保留原上传人
|
||||
uploadUserName: existing?.uploadUserName || uploadUserName || row.uploadUserName || '',
|
||||
uploadTime: existing?.uploadTime || row.uploadTime || uploadTime,
|
||||
approved: existing?.approved || row.approved || false,
|
||||
};
|
||||
@@ -436,20 +543,38 @@ export default {
|
||||
resolveTypeByLocation(location) {
|
||||
const options = this.contractAttachmentTypeOptions || [];
|
||||
const locationText = String(location || '');
|
||||
if (locationText.includes('其它') || locationText.includes('其他')) {
|
||||
if (
|
||||
isContractSupportingAttachmentLocation(locationText) ||
|
||||
locationText.includes('佐证') ||
|
||||
locationText.includes('其它') ||
|
||||
locationText.includes('其他')
|
||||
) {
|
||||
return this.defaultAttachmentType;
|
||||
}
|
||||
const matched = options.find(
|
||||
item =>
|
||||
String(item.value) === locationText ||
|
||||
String(item.label) === locationText ||
|
||||
String(item.value).includes('用印') ||
|
||||
String(item.label).includes('用印') ||
|
||||
String(item.value).includes('合同') ||
|
||||
String(item.label).includes('合同')
|
||||
);
|
||||
return matched?.value || options[0]?.value || this.defaultAttachmentType;
|
||||
},
|
||||
syncUploadDialogTypeByLocation() {
|
||||
this.uploadDialogType = this.resolveTypeByLocation(this.uploadDialogLocation);
|
||||
const dictCode = resolveAttachmentTypeDictCode(
|
||||
this.uploadDialogLocation,
|
||||
this.resolvedAttachmentTypeDictCode
|
||||
);
|
||||
const applyType = () => {
|
||||
this.uploadDialogType = this.resolveTypeByLocation(this.uploadDialogLocation);
|
||||
};
|
||||
if (dictCode !== this.activeAttachmentTypeDictCode) {
|
||||
this.loadAttachmentTypeOptions(dictCode, false).finally(applyType);
|
||||
return;
|
||||
}
|
||||
applyType();
|
||||
},
|
||||
openUploadDialog() {
|
||||
const options = this.resolvedLocationOptions;
|
||||
@@ -546,13 +671,13 @@ export default {
|
||||
this.$message.warning('请选择附件类型');
|
||||
return;
|
||||
}
|
||||
const uploadUserName = this.$store.getters.userInfo?.realName || '';
|
||||
const uploadUserName = this.currentUploadUserName();
|
||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
const appended = this.uploadDialogFiles.map(row => ({
|
||||
...row,
|
||||
fileType: this.uploadDialogType,
|
||||
description: row.description || '',
|
||||
uploadUserName: row.uploadUserName || uploadUserName,
|
||||
uploadUserName: uploadUserName || row.uploadUserName || '',
|
||||
uploadTime: row.uploadTime || uploadTime,
|
||||
...(this.markApprovedOnUpload ? { approved: true } : {}),
|
||||
}));
|
||||
@@ -609,7 +734,15 @@ export default {
|
||||
downloadFileByUrl(url, attachmentName(row));
|
||||
},
|
||||
batchDownload() {
|
||||
(this.selected.length ? this.selected : this.rows || []).forEach(this.download);
|
||||
const sourceRows = this.selected.length ? this.selected : this.rows || [];
|
||||
const downloadableRows = sourceRows.filter(row => attachmentUrl(row));
|
||||
if (!downloadableRows.length) {
|
||||
this.$message.warning(this.selected.length ? '所选附件暂无可下载地址' : '暂无可下载附件');
|
||||
return;
|
||||
}
|
||||
downloadableRows.forEach((row, index) => {
|
||||
window.setTimeout(() => this.download(row), index * 400);
|
||||
});
|
||||
},
|
||||
formatSize(size) {
|
||||
const value = Number(size);
|
||||
|
||||
Reference in New Issue
Block a user