f6b05ed71d
- 在运输计划调度弹窗待调度列表的el-table组件增加表头背景色样式 - 将表头背景由白色改为浅灰色 #fafafa,与整体风格保持一致 style(insurance): 优化保险OCR模板弹窗表单样式 - 使用section-card组件包裹弹窗表单,实现卡片化白底圆角效果 - 调整字段映射表格列宽和对齐方式,键名列宽改为180居中,自定义映射值列去除宽度限制 - 改善输入框右侧字数统计显示,避免被截断显示不全
319 lines
9.1 KiB
Vue
319 lines
9.1 KiB
Vue
<template>
|
|
<basic-container>
|
|
<div class="insurance-ocr-template-page__toolbar">
|
|
<el-button
|
|
v-if="hasPermission('insurance_ocr_template_add')"
|
|
type="primary"
|
|
icon="el-icon-plus"
|
|
@click="openDialog()"
|
|
>
|
|
新增
|
|
</el-button>
|
|
<el-button
|
|
v-if="hasPermission('insurance_ocr_template_delete')"
|
|
type="danger"
|
|
plain
|
|
icon="el-icon-delete"
|
|
:disabled="!selectionList.length"
|
|
@click="handleRemove(selectionIds)"
|
|
>
|
|
批量删除
|
|
</el-button>
|
|
<div class="insurance-ocr-template-page__search">
|
|
<el-input
|
|
v-model="query.name"
|
|
clearable
|
|
maxlength="100"
|
|
placeholder="模板名称"
|
|
@keyup.enter="search"
|
|
@clear="search"
|
|
/>
|
|
<el-button type="primary" icon="el-icon-search" @click="search">查询</el-button>
|
|
<el-button icon="el-icon-refresh" @click="resetSearch">重置</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="data"
|
|
border
|
|
@selection-change="selectionChange"
|
|
>
|
|
<el-table-column type="selection" width="48" />
|
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
|
<el-table-column prop="name" label="模板名称" min-width="220" show-overflow-tooltip />
|
|
<el-table-column label="已配置字段" min-width="150">
|
|
<template #default="{ row }">{{ getMappingCount(row.mappingConfig) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="updateUserName" label="更新人" min-width="120" />
|
|
<el-table-column prop="updateTime" label="更新时间" min-width="180" />
|
|
<el-table-column label="操作" width="150" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-link
|
|
v-if="hasPermission('insurance_ocr_template_view')"
|
|
type="primary"
|
|
@click="openDialog(row, true)"
|
|
>
|
|
查看
|
|
</el-link>
|
|
<el-link
|
|
v-if="hasPermission('insurance_ocr_template_edit')"
|
|
type="primary"
|
|
@click="openDialog(row)"
|
|
>
|
|
编辑
|
|
</el-link>
|
|
<el-link
|
|
v-if="hasPermission('insurance_ocr_template_delete')"
|
|
type="danger"
|
|
@click="handleRemove(row.id)"
|
|
>
|
|
删除
|
|
</el-link>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<empty-pagination
|
|
:page="page"
|
|
@size-change="sizeChange"
|
|
@current-change="currentChange"
|
|
@load="onLoad"
|
|
/>
|
|
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:title="dialogTitle"
|
|
width="860px"
|
|
append-to-body
|
|
destroy-on-close
|
|
@closed="resetForm"
|
|
>
|
|
<el-form ref="templateForm" :model="form" :rules="rules" label-width="100px" :disabled="readonly">
|
|
<section-card title="模板信息">
|
|
<el-form-item label="模板名称" prop="name">
|
|
<el-input v-model="form.name" maxlength="100" show-word-limit />
|
|
</el-form-item>
|
|
<el-form-item label="字段映射" required>
|
|
<el-table :data="mappingRows" border class="insurance-ocr-template-page__mapping-table">
|
|
<el-table-column prop="key" label="键名" width="180" align="center" />
|
|
<el-table-column label="自定义映射值">
|
|
<template #default="{ row }">
|
|
<el-input
|
|
v-model="row.value"
|
|
maxlength="100"
|
|
show-word-limit
|
|
placeholder="请输入保险单据中的对应字段名称"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-form-item>
|
|
</section-card>
|
|
</el-form>
|
|
|
|
<template #footer>
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button v-if="!readonly" type="primary" :loading="submitLoading" @click="handleSubmit">
|
|
确定
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</basic-container>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import { getDetail, getList, remove, submit } from '@/api/base/insurance-ocr-template';
|
|
import SectionCard from '@/components/section-card/main.vue';
|
|
|
|
const insuranceFieldKeys = [
|
|
'保险类型',
|
|
'保单号',
|
|
'开始日期',
|
|
'结束日期',
|
|
'保额',
|
|
'保费',
|
|
'发票号',
|
|
'开票日期',
|
|
'备注',
|
|
];
|
|
|
|
const createMappingRows = mappingConfig => {
|
|
let valueMap = {};
|
|
try {
|
|
const mappings = JSON.parse(mappingConfig || '[]');
|
|
if (Array.isArray(mappings)) {
|
|
valueMap = mappings.reduce((result, item) => {
|
|
if (insuranceFieldKeys.includes(item?.key)) result[item.key] = item.value || '';
|
|
return result;
|
|
}, {});
|
|
}
|
|
} catch (error) {
|
|
valueMap = {};
|
|
}
|
|
return insuranceFieldKeys.map(key => ({ key, value: valueMap[key] || '' }));
|
|
};
|
|
|
|
export default {
|
|
components: { SectionCard },
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
submitLoading: false,
|
|
dialogVisible: false,
|
|
readonly: false,
|
|
data: [],
|
|
selectionList: [],
|
|
query: { name: '' },
|
|
form: { id: '', name: '' },
|
|
mappingRows: createMappingRows(),
|
|
page: {
|
|
pageSize: 10,
|
|
pageSizes: [10, 20, 50, 100],
|
|
currentPage: 1,
|
|
total: 0,
|
|
},
|
|
rules: {
|
|
name: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['permission', 'userInfo']),
|
|
isAdmin() {
|
|
return String(this.userInfo.authority || '').includes('admin');
|
|
},
|
|
selectionIds() {
|
|
return this.selectionList.map(item => item.id).join(',');
|
|
},
|
|
dialogTitle() {
|
|
if (this.readonly) return '查看保险OCR识别模板';
|
|
return this.form.id ? '编辑保险OCR识别模板' : '新增保险OCR识别模板';
|
|
},
|
|
},
|
|
methods: {
|
|
hasPermission(code) {
|
|
return this.isAdmin || this.permission?.[code] === true;
|
|
},
|
|
onLoad() {
|
|
this.loading = true;
|
|
getList(this.page.currentPage, this.page.pageSize, this.query)
|
|
.then(res => {
|
|
const pageData = res.data.data || {};
|
|
this.data = pageData.records || [];
|
|
this.page.total = pageData.total || 0;
|
|
this.selectionList = [];
|
|
})
|
|
.finally(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
search() {
|
|
this.page.currentPage = 1;
|
|
this.onLoad();
|
|
},
|
|
resetSearch() {
|
|
this.query.name = '';
|
|
this.search();
|
|
},
|
|
currentChange(currentPage) {
|
|
this.page.currentPage = currentPage;
|
|
this.onLoad();
|
|
},
|
|
sizeChange(pageSize) {
|
|
this.page.pageSize = pageSize;
|
|
this.page.currentPage = 1;
|
|
this.onLoad();
|
|
},
|
|
selectionChange(selectionList) {
|
|
this.selectionList = selectionList;
|
|
},
|
|
openDialog(row, readonly = false) {
|
|
this.readonly = readonly;
|
|
if (!row?.id) {
|
|
this.dialogVisible = true;
|
|
return;
|
|
}
|
|
getDetail(row.id).then(res => {
|
|
const detail = res.data.data || {};
|
|
this.form = { id: detail.id, name: detail.name || '' };
|
|
this.mappingRows = createMappingRows(detail.mappingConfig);
|
|
this.dialogVisible = true;
|
|
});
|
|
},
|
|
resetForm() {
|
|
this.form = { id: '', name: '' };
|
|
this.mappingRows = createMappingRows();
|
|
this.readonly = false;
|
|
this.submitLoading = false;
|
|
this.$refs.templateForm?.clearValidate();
|
|
},
|
|
handleSubmit() {
|
|
const hasMappingValue = this.mappingRows.some(item => String(item.value || '').trim());
|
|
if (!hasMappingValue) {
|
|
this.$message.warning('请至少填写一个字段映射值');
|
|
return;
|
|
}
|
|
this.$refs.templateForm.validate(valid => {
|
|
if (!valid) return;
|
|
this.submitLoading = true;
|
|
submit({
|
|
id: this.form.id || undefined,
|
|
name: String(this.form.name || '').trim(),
|
|
mappingConfig: JSON.stringify(
|
|
this.mappingRows.map(item => ({ key: item.key, value: String(item.value || '').trim() }))
|
|
),
|
|
})
|
|
.then(() => {
|
|
this.$message.success('操作成功');
|
|
this.dialogVisible = false;
|
|
this.onLoad();
|
|
})
|
|
.finally(() => {
|
|
this.submitLoading = false;
|
|
});
|
|
});
|
|
},
|
|
handleRemove(ids) {
|
|
this.$confirm('确定删除选中的保险OCR识别模板吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
}).then(() => {
|
|
remove(ids).then(() => {
|
|
this.$message.success('操作成功');
|
|
this.onLoad();
|
|
});
|
|
});
|
|
},
|
|
getMappingCount(mappingConfig) {
|
|
return createMappingRows(mappingConfig).filter(item => String(item.value || '').trim()).length;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.insurance-ocr-template-page {
|
|
&__toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
&__search {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-left: auto;
|
|
width: 380px;
|
|
}
|
|
|
|
&__mapping-table {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|