Files
tms-erp-web/src/views/vehicle/insurance-record.vue
T
gxwebsoft 3d6e297147 feat(vehicle): 归一化保险记录导入日期格式,修复设备台账批量删除按钮
- 保险记录批量导入时,前端读取 Excel 并规范日期格式为 YYYY-MM-DD,兼容多种日期写法
- 重写 `handleImport` 支持自定义上传逻辑,调用归一化处理后再上传文件
- 添加日期归一化相关方法,确保不合法或已标准格式保持原值
- 设备台账界面新增批量删除按钮及相关逻辑,支持多选数据批量删除并提示确认
- 补充导出含创建时间和更新时间的后端修复说明,避免导出时间字段为空问题
- 说明后端导出分配逻辑,强调导出字段由后端控制,前端修改导出列无效
2026-09-17 21:21:33 +08:00

964 lines
31 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<basic-container class="insurance-record-page">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
:permission="permissionList"
:before-open="beforeOpen"
v-model="form"
ref="crud"
@row-update="rowUpdate"
@row-save="rowSave"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="primary"
icon="el-icon-upload"
plain
v-if="hasPermission('insurance_record_import')"
@click="handleImport"
>批量导入
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="hasPermission('insurance_record_template')"
@click="handleTemplate"
>下载模板
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="hasPermission('insurance_record_export')"
@click="handleExport"
>批量导出
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission('insurance_record_delete')"
@click="handleDelete"
>批量删除
</el-button>
</template>
<template #vehicleNo="{ row, index }">
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
{{ row.vehicleNo }}
</el-button>
</template>
<template #vehicleNoForm>
<el-select
v-model="form.vehicleNo"
:disabled="boxType === 'view'"
:loading="vehicleOptionsLoading"
:placeholder="vehicleNoPlaceholder"
clearable
filterable
remote
:remote-method="loadVehicleOptions"
@visible-change="handleVehicleSelectVisible"
>
<el-option
v-for="item in vehicleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #vehicleNo-form>
<el-select
v-model="form.vehicleNo"
:disabled="boxType === 'view'"
:loading="vehicleOptionsLoading"
:placeholder="vehicleNoPlaceholder"
clearable
filterable
remote
:remote-method="loadVehicleOptions"
@visible-change="handleVehicleSelectVisible"
>
<el-option
v-for="item in vehicleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #insuredAmountForm>
<el-input
v-model="form.insuredAmount"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('insuredAmount', value)"
>
<template #append></template>
</el-input>
</template>
<template #premiumForm>
<el-input
v-model="form.premium"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('premium', value)"
>
<template #append></template>
</el-input>
</template>
<template #policyFileForm>
<div class="insurance-record-page__upload">
<div class="insurance-record-page__upload-info">
<div class="insurance-record-page__upload-icon" aria-hidden="true">
<span class="insurance-record-page__upload-card">
<span></span>
</span>
</div>
<div class="insurance-record-page__upload-text">
<strong>上传图片,自动识别填写</strong>
<span>支持JPGPNGBMP上传</span>
</div>
</div>
<el-upload
v-if="hasPermission('insurance_record_ocr')"
class="insurance-record-page__upload-action"
action="/api/blade-resource/oss/endpoint/put-file"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handlePolicyUploadSuccess"
:on-error="handlePolicyUploadError"
:before-upload="validateOcrUpload"
accept=".jpg,.jpeg,.png,.bmp"
>
<el-button class="insurance-record-page__upload-button" type="primary" plain>
上传文件
</el-button>
</el-upload>
</div>
</template>
<template #policyFile-form>
<div class="insurance-record-page__upload">
<div class="insurance-record-page__upload-info">
<div class="insurance-record-page__upload-icon" aria-hidden="true">
<span class="insurance-record-page__upload-card">
<span></span>
</span>
</div>
<div class="insurance-record-page__upload-text">
<strong>上传图片,自动识别填写</strong>
<span>支持JPGPNGBMP上传</span>
</div>
</div>
<el-upload
v-if="hasPermission('insurance_record_ocr')"
class="insurance-record-page__upload-action"
action="/api/blade-resource/oss/endpoint/put-file"
name="file"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handlePolicyUploadSuccess"
:on-error="handlePolicyUploadError"
:before-upload="validateOcrUpload"
accept=".jpg,.jpeg,.png,.bmp"
>
<el-button class="insurance-record-page__upload-button" type="primary" plain>
上传文件
</el-button>
</el-upload>
</div>
</template>
</avue-crud>
<empty-pagination
:page="page"
@size-change="sizeChange"
@current-change="currentChange"
@load="onLoad(page, query)"
/>
<el-dialog title="保险记录数据导入" append-to-body v-model="excelBox" width="555px">
<avue-form :option="excelOption" v-model="excelForm">
<template #excelTemplate>
<el-button type="primary" @click="handleTemplate">
点击下载<i class="el-icon-download el-icon--right"></i>
</el-button>
</template>
</avue-form>
</el-dialog>
</basic-container>
</template>
<script>
import {
add,
getDetail,
getList,
recognizeGeneralOcr,
remove,
update,
} from '@/api/vehicle/insurance-record';
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
import { getList as getOcrTemplateList } from '@/api/base/insurance-ocr-template';
import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { handleImportExcel } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import { mapGetters } from 'vuex';
import { ElLoading } from 'element-plus';
import { excelOption, exportColumns, option } from '@/option/vehicle/insurance-record';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
};
export default {
data() {
return {
form: {},
query: {},
loading: true,
excelBox: false,
excelForm: {},
isDetailLoading: false,
boxType: '',
vehicleOptions: [],
vehicleOptionsLoading: false,
vehicleOptionsRequestId: 0,
option,
excelOption,
page: {
pageSize: 10,
pageSizes: [10, 20, 50, 100],
currentPage: 1,
total: 0,
},
selectionList: [],
data: [],
ocrTemplateOptions: [],
};
},
created() {
this.initDeptTree();
this.initOcrTemplateOptions('车辆');
},
computed: {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
},
permissionList() {
return {
addBtn: this.hasPermission('insurance_record_add'),
viewBtn: this.hasPermission('insurance_record_view'),
delBtn: this.hasPermission('insurance_record_delete'),
editBtn: this.hasPermission('insurance_record_edit'),
};
},
vehicleNoPlaceholder() {
return this.form.vehicleType === '船舶' ? '请选择船号' : '请选择车牌号';
},
ids() {
const ids = [];
this.selectionList.forEach(ele => {
ids.push(ele.id);
});
return ids.join(',');
},
uploadHeaders() {
return getUploadHeaders();
},
},
watch: {
'form.vehicleType': {
handler(value, oldValue) {
this.updateVehicleTypeDisplays(value || '车辆', oldValue);
},
immediate: true,
},
},
methods: {
hasPermission(code) {
return this.isAdmin || this.validData(this.permission[code], false);
},
updateVehicleTypeDisplays(vehicleType, oldValue) {
const vehicleNoColumn = this.findColumn(this.option.column, 'vehicleNo');
vehicleNoColumn.placeholder =
vehicleType === '船舶' ? '请选择船号' : '请选择车牌号';
if (!this.isDetailLoading && oldValue && vehicleType !== oldValue) {
this.form.vehicleNo = '';
this.vehicleOptions = [];
this.loadVehicleOptions('');
this.initOcrTemplateOptions(vehicleType, true);
}
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
const column = this.findColumn(this.option.column, 'createDept');
column.dicData = res.data.data;
});
},
initOcrTemplateOptions(vehicleType = '车辆', resetSelected = false) {
const type = vehicleType || '车辆';
return getOcrTemplateList(1, 100, { vehicleType: type })
.then(res => {
const records = res.data.data?.records || [];
this.ocrTemplateOptions = records.map(item => ({
label: item.name,
value: item.name,
mappingConfig: item.mappingConfig || '[]',
}));
const templateColumn = this.findColumn(this.option.column, 'ocrTemplate');
if (templateColumn) templateColumn.dicData = this.ocrTemplateOptions;
if (resetSelected || !this.ocrTemplateOptions.some(item => item.value === this.form.ocrTemplate)) {
this.form.ocrTemplate = this.ocrTemplateOptions[0]?.value || '';
}
})
.catch(() => {
this.ocrTemplateOptions = [];
const templateColumn = this.findColumn(this.option.column, 'ocrTemplate');
if (templateColumn) templateColumn.dicData = [];
if (resetSelected) this.form.ocrTemplate = '';
});
},
handleVehicleSelectVisible(visible) {
if (visible && !this.vehicleOptions.length) this.loadVehicleOptions('');
},
loadVehicleOptions(queryString = '') {
const vehicleType = this.form.vehicleType || '车辆';
const isShip = vehicleType === '船舶';
const request = isShip ? getShipList : getVehicleList;
const params = isShip ? { shipIdentifierNo: queryString } : { plateNo: queryString };
const requestId = ++this.vehicleOptionsRequestId;
this.vehicleOptionsLoading = true;
request(1, 20, params)
.then(res => {
if (requestId !== this.vehicleOptionsRequestId) return;
const values = (res.data.data.records || [])
.map(item => (isShip ? item.shipIdentifierNo || item.shipName : item.plateNo))
.filter(Boolean);
this.vehicleOptions = [...new Set(values)].map(value => ({ label: value, value }));
if (this.form.vehicleNo && !this.vehicleOptions.some(item => item.value === this.form.vehicleNo)) {
this.vehicleOptions.unshift({ label: this.form.vehicleNo, value: this.form.vehicleNo });
}
})
.catch(() => {
if (requestId === this.vehicleOptionsRequestId) this.vehicleOptions = [];
})
.finally(() => {
if (requestId === this.vehicleOptionsRequestId) this.vehicleOptionsLoading = false;
});
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';
if (values.vehicleNo) {
values.vehicleNo =
values.vehicleType === '船舶'
? values.vehicleNo.trim()
: values.vehicleNo.trim().toUpperCase();
}
if (values.policyNo) {
values.policyNo = values.policyNo.trim();
}
return values;
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
buildQuery(params = {}) {
return normalizeSearchRangeParams({ ...params, ...this.query }, createTimeRangeMap);
},
validateRow(row) {
if (row.startDate && row.endDate && row.endDate < row.startDate) {
this.$message.warning('结束日期不能早于开始日期');
return false;
}
if (Number(row.insuredAmount) < 0 || Number(row.premium) < 0) {
this.$message.warning('保额、保费不能小于 0');
return false;
}
if (row.remark && row.remark.length > 200) {
this.$message.warning('备注最多 200 个字');
return false;
}
return true;
},
rowSave(row, done, loading) {
const values = this.normalizeRow(row);
if (!this.validateRow(values)) {
loading();
return;
}
add(values).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowUpdate(row, index, done, loading) {
const values = this.normalizeRow(row);
if (!this.validateRow(values)) {
loading();
return;
}
update(values).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowDel(row) {
this.$confirm('确定删除该条数据,删除后不可恢复?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定删除所选数据,删除后不可恢复?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(this.ids))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
this.$refs.crud.toggleSelection();
});
},
beforeOpen(done, type) {
this.boxType = type;
if (type === 'add') {
this.vehicleOptions = [];
this.loadVehicleOptions('');
this.form.vehicleType = '车辆';
this.initOcrTemplateOptions('车辆', true);
}
if (['edit', 'view'].includes(type)) {
this.isDetailLoading = true;
getDetail(this.form.id)
.then(res => {
this.form = {
...res.data.data,
vehicleType: res.data.data.vehicleType || '车辆',
};
this.vehicleOptions = [];
this.loadVehicleOptions(this.form.vehicleNo || '');
return this.initOcrTemplateOptions(this.form.vehicleType);
})
.finally(() => {
this.isDetailLoading = false;
});
}
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = this.buildQuery(params);
this.page.currentPage = 1;
this.onLoad(this.page);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
this.$refs.crud.toggleSelection();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page, params = {}) {
this.loading = true;
const query = this.buildQuery(params);
getList(page.currentPage, page.pageSize, query)
.then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.selectionClear();
})
.finally(() => {
this.loading = false;
});
},
handleImport() {
const column = this.findColumn(this.excelOption.column, 'excelFile');
if (!column) {
this.$message.error('导入配置异常,请稍后重试');
return;
}
// 上传前先归一化日期列(2026-9-1 → 2026-09-01),兼容两种格式
column.httpRequest = async (uploadOption, uploadColumn) => {
try {
const normalizedFile = await this.normalizeExcelDates(uploadOption.file);
await handleImportExcel(
this,
{ ...uploadOption, file: normalizedFile },
uploadColumn,
'保险记录',
null,
{ timeout: 60000 }
);
} catch (error) {
this.$message.error(error.message || '导入失败');
if (typeof uploadOption.onError === 'function') {
uploadOption.onError(error);
}
}
};
this.excelBox = true;
},
// 读取 Excel,把指定日期列统一归一化为 YYYY-MM-DD,再写回新文件
async normalizeExcelDates(file) {
const rows = await this.readExcelRows(file);
const dateHeaders = ['开始日期', '结束日期', '开票日期'];
const headerIndex = (rows[0] || []).reduce((map, label, index) => {
const key = String(label || '').trim();
if (key) map[key] = index;
return map;
}, {});
const targetIndexes = dateHeaders
.map(label => headerIndex[label])
.filter(index => index !== undefined);
if (!targetIndexes.length) return file;
rows.forEach((row, rowIndex) => {
if (rowIndex === 0) return;
targetIndexes.forEach(index => {
if (row[index] !== undefined && row[index] !== '') {
const normalized = this.normalizeDateString(String(row[index]));
if (normalized) row[index] = normalized;
}
});
});
return this.writeExcelFile(rows, file.name);
},
async readExcelRows(file) {
const XLSX = await import('xlsx');
const buffer = await file.arrayBuffer();
const workbook = XLSX.read(buffer, { type: 'array', cellDates: false });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
return XLSX.utils.sheet_to_json(worksheet, {
header: 1,
defval: '',
raw: false,
blankrows: false,
});
},
async writeExcelFile(rows, fileName) {
const XLSX = await import('xlsx');
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(rows), 'Sheet1');
const binary = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
return new File([binary], fileName || 'insurance-record.xlsx', {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
},
// 兼容 YYYY-M-D / YYYY-MM-DD / YYYY/M/D / YYYY.年.月.日,非法或已标准格式原样返回 null
normalizeDateString(value) {
const text = String(value || '').trim();
if (!text) return null;
const match = text.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
if (!match) return null;
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 null;
}
const pad = n => String(n).padStart(2, '0');
return `${year}-${pad(month)}-${pad(day)}`;
},
handleExport() {
this.$confirm('是否导出保险记录数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob(
'/blade-transport/insurance-record/export-insurance-record',
this.buildExportParams(),
{ feedback: true }
)
.then(res => {
downloadXls(res.data, `保险记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
})
.finally(() => {
NProgress.done();
});
});
},
buildExportParams() {
return {
...this.buildQuery(),
ids: this.ids,
exportColumns: JSON.stringify(exportColumns),
[this.website.tokenHeader]: getToken(),
};
},
handleTemplate() {
exportBlob(
'/blade-transport/insurance-record/export-template',
{ exportColumns: JSON.stringify(exportColumns), [this.website.tokenHeader]: getToken() },
{ feedback: true }
).then(res => {
downloadXls(res.data, '保险记录模板.xlsx');
});
},
handlePolicyUploadSuccess(res) {
if (res?.code !== 200 || !res.data) {
this.$message.error(res?.msg || '上传失败');
return;
}
const imageUrl = res.data.link || res.data.url || res.data.domain || '';
if (!imageUrl) {
this.$message.warning('文件上传成功,未获取到文件地址,无法自动识别');
return;
}
this.form.policyFile = imageUrl;
this.recognizePolicyFile(imageUrl);
},
handlePolicyUploadError() {
this.$message.error('上传失败');
},
recognizePolicyFile(imageUrl) {
const loading = ElLoading.service({
lock: true,
text: '保单识别中',
background: 'rgba(255, 255, 255, 0.7)',
});
recognizeGeneralOcr(imageUrl)
.then(res => {
const result = res.data.data?.result || {};
const filledCount = this.applyTemplateRecognition(result);
if (filledCount > 0) {
this.$message.success(`保单识别完成,已填充${filledCount}项`);
} else {
this.$message.warning('未识别到相关字段,请手动填写保险信息');
}
})
.catch(() => {
this.$message.warning('文件上传成功,自动识别失败,请手动填写保险信息');
})
.finally(() => {
loading.close();
});
},
applyTemplateRecognition(data = {}) {
const template = this.ocrTemplateOptions.find(item => item.value === this.form.ocrTemplate);
const mappings = this.parseTemplateMappings(template?.mappingConfig);
const words = this.getGeneralOcrWords(data);
const fieldPropMap = {
保险类型: 'insuranceType',
保单号: 'policyNo',
开始日期: 'startDate',
结束日期: 'endDate',
保额: 'insuredAmount',
保费: 'premium',
发票号: 'invoiceNo',
开票日期: 'invoiceDate',
备注: 'remark',
};
let filledCount = 0;
mappings.forEach(item => {
const prop = fieldPropMap[item.key];
const value = this.getMappedOcrValue(words, item.value);
if (!prop || !value) return;
this.form[prop] = this.normalizeRecognizedValue(prop, value);
filledCount += 1;
});
return filledCount;
},
parseTemplateMappings(mappingConfig) {
try {
const mappings = JSON.parse(mappingConfig || '[]');
return Array.isArray(mappings)
? mappings.filter(
item => String(item?.key || '').trim() && String(item?.value || '').trim()
)
: [];
} catch (error) {
return [];
}
},
getGeneralOcrWords(data = {}) {
const wordsResult = data.words_result || data.wordsResult || [];
if (Array.isArray(wordsResult)) {
return wordsResult.map(item => item.words || item.value || '').filter(Boolean);
}
return Object.entries(wordsResult)
.flatMap(([key, value]) => [
key,
typeof value === 'object' ? value.words || value.value || '' : value,
])
.filter(Boolean);
},
getMappedOcrValue(words = [], mappingLabel = '') {
const label = String(mappingLabel || '').trim();
if (!label) return '';
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const normalizedWords = words.map(word => String(word || '').trim()).filter(Boolean);
const exactIndex = normalizedWords.findIndex(word =>
new RegExp(`^${escapedLabel}\\s*[:]?$`).test(word)
);
if (exactIndex >= 0) return normalizedWords[exactIndex + 1] || '';
const valueLine = normalizedWords.find(word =>
new RegExp(`${escapedLabel}\\s*[:]?\\s*(.+)$`).test(word)
);
if (!valueLine) return '';
return valueLine.replace(new RegExp(`^.*?${escapedLabel}\\s*[:]?\\s*`), '').trim();
},
normalizeRecognizedValue(prop, value) {
const text = String(value || '').trim();
if (['startDate', 'endDate', 'invoiceDate'].includes(prop)) {
const match = text.match(/(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?/);
return match
? `${match[1]}-${match[2].padStart(2, '0')}-${match[3].padStart(2, '0')}`
: text;
}
if (['insuredAmount', 'premium'].includes(prop)) {
return text.replace(/,/g, '').replace(/[^\d.]/g, '');
}
if (prop === 'insuranceType') {
const typeRules = [
{ value: '交强险', pattern: /(交强险|交通事故责任强制保险)/ },
{ value: '商业险', pattern: /(商业险|商业保险)/ },
{ value: '承运人责任险', pattern: /承运人.*责任/ },
{ value: '货运险', pattern: /(货运险|货物运输保险)/ },
{ value: '船舶险', pattern: /(船舶险|船舶保险)/ },
];
return typeRules.find(item => item.pattern.test(text))?.value || text;
}
if (prop === 'remark') return text.slice(0, 200);
return text;
},
validateOcrUpload(file) {
if (!this.form.ocrTemplate) {
this.$message.warning('请先选择OCR识别模板');
return false;
}
const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type);
const validSize = file.size / 1024 / 1024 < 5;
if (!validType) this.$message.error('仅支持 JPG、PNG、BMP 图片');
if (!validSize) this.$message.error('图片大小不能超过 5MB');
return validType && validSize;
},
},
};
</script>
<style lang="scss" scoped>
.insurance-record-page {
:deep(.el-table th .cell),
:deep(.el-table td .cell) {
white-space: nowrap;
}
&__vehicle-input {
width: 100%;
}
}
:global(.insurance-record-page__upload) {
display: flex;
align-items: center;
justify-content: space-between;
//min-height: 88px;
padding: 16px 28px 16px 22px;
border: 1px dashed #8b8b8b;
border-radius: 8px;
background: #fff;
}
:global(.insurance-record-page__upload-info) {
display: flex;
align-items: center;
min-width: 0;
}
:global(.insurance-record-page__upload-icon) {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
margin-right: 18px;
color: #707070;
}
:global(.insurance-record-page__upload-icon::before),
:global(.insurance-record-page__upload-icon::after) {
position: absolute;
width: 11px;
height: 11px;
content: '';
}
:global(.insurance-record-page__upload-icon::before) {
top: 0;
left: 0;
border-top: 3px solid currentColor;
border-left: 3px solid currentColor;
border-radius: 3px 0 0;
}
:global(.insurance-record-page__upload-icon::after) {
right: 0;
bottom: 0;
border-right: 3px solid currentColor;
border-bottom: 3px solid currentColor;
border-radius: 0 0 3px;
}
:global(.insurance-record-page__upload-card) {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 22px;
border: 3px solid currentColor;
border-radius: 3px;
}
:global(.insurance-record-page__upload-card::before),
:global(.insurance-record-page__upload-card::after) {
position: absolute;
content: '';
}
:global(.insurance-record-page__upload-card::before) {
top: 5px;
left: 7px;
width: 6px;
height: 6px;
border: 2px solid currentColor;
border-radius: 2px;
}
:global(.insurance-record-page__upload-card::after) {
top: 6px;
right: 5px;
width: 6px;
height: 2px;
background: currentColor;
box-shadow: 0 6px 0 currentColor;
}
:global(.insurance-record-page__upload-card span) {
position: absolute;
right: 5px;
bottom: 4px;
width: 8px;
height: 2px;
background: currentColor;
}
:global(.insurance-record-page__upload-text) {
display: flex;
flex-direction: column;
gap: 4px;
color: #303133;
line-height: 1.25;
}
:global(.insurance-record-page__upload-text strong) {
font-weight: 700;
}
:global(.insurance-record-page__upload-text span) {
color: #303133;
font-weight: 500;
}
:global(.insurance-record-page__upload-action) {
flex: 0 0 auto;
margin-left: 18px;
}
:global(.insurance-record-page__upload-action .el-upload) {
display: block;
}
@media (max-width: 768px) {
:global(.insurance-record-page__upload) {
align-items: stretch;
flex-direction: column;
gap: 12px;
padding: 16px;
}
:global(.insurance-record-page__upload-action) {
margin-left: 0;
}
:global(.insurance-record-page__upload-button) {
width: 100%;
}
}
</style>