完善保险端
This commit is contained in:
@@ -87,8 +87,12 @@ const handleResponse = async (response) => {
|
||||
try {
|
||||
const contentType = response.headers.get('content-type')
|
||||
|
||||
// 处理Excel文件下载
|
||||
if (contentType && contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
|
||||
// 处理文件下载(Excel、CSV等)
|
||||
if (contentType && (
|
||||
contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') ||
|
||||
contentType.includes('text/csv') ||
|
||||
contentType.includes('application/octet-stream')
|
||||
)) {
|
||||
data = await response.blob()
|
||||
} else if (contentType && contentType.includes('application/json')) {
|
||||
data = await response.json()
|
||||
|
||||
@@ -643,10 +643,11 @@ const exportData = async () => {
|
||||
const url = window.URL.createObjectURL(response.data)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `申请数据_${new Date().toISOString().slice(0, 10)}.xlsx`)
|
||||
link.setAttribute('download', `保险申请数据_${new Date().toISOString().slice(0, 10)}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url) // 释放内存
|
||||
message.success('导出成功')
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
|
||||
@@ -126,9 +126,9 @@
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
<template v-if="column.key === 'taskStatus'">
|
||||
<a-tag :color="getStatusColor(record.taskStatus)">
|
||||
{{ getStatusText(record.taskStatus) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
@@ -136,9 +136,6 @@
|
||||
{{ getPriorityText(record.priority) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'duration'">
|
||||
{{ record.duration }} 天
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
@@ -182,11 +179,29 @@
|
||||
>
|
||||
<div v-if="selectedTask" class="task-detail">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="任务名称">
|
||||
{{ selectedTask.taskName }}
|
||||
<a-descriptions-item label="申请单号">
|
||||
{{ selectedTask.applicationNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务编号">
|
||||
{{ selectedTask.taskCode }}
|
||||
<a-descriptions-item label="保单编号">
|
||||
{{ selectedTask.policyNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="产品名称">
|
||||
{{ selectedTask.productName || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="保险期间">
|
||||
{{ selectedTask.insurancePeriod || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="客户姓名">
|
||||
{{ selectedTask.customerName || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="证件类型">
|
||||
{{ selectedTask.idType || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="证件号码">
|
||||
{{ selectedTask.idNumber || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="监管生资数量">
|
||||
{{ selectedTask.supervisorySuppliesQuantity || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="优先级">
|
||||
<a-tag :color="getPriorityColor(selectedTask.priority)">
|
||||
@@ -194,27 +209,30 @@
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="状态">
|
||||
<a-tag :color="getStatusColor(selectedTask.status)">
|
||||
{{ getStatusText(selectedTask.status) }}
|
||||
<a-tag :color="getStatusColor(selectedTask.taskStatus)">
|
||||
{{ getStatusText(selectedTask.taskStatus) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="负责人">
|
||||
{{ selectedTask.assignedUser?.real_name || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人">
|
||||
{{ selectedTask.creator?.real_name || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ selectedTask.createdAt }}
|
||||
{{ selectedTask.createdAt || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间">
|
||||
{{ selectedTask.updatedAt || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="完成时间">
|
||||
{{ selectedTask.completedAt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="负责人">
|
||||
{{ selectedTask.assignee }}
|
||||
{{ selectedTask.completedAt || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="处理时长">
|
||||
{{ selectedTask.duration }} 天
|
||||
{{ calculateDuration(selectedTask) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务描述" :span="2">
|
||||
{{ selectedTask.description }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="完成备注" :span="2">
|
||||
{{ selectedTask.completionNotes }}
|
||||
<a-descriptions-item label="备注" :span="2">
|
||||
{{ selectedTask.notes || '-' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
@@ -274,14 +292,14 @@ const pagination = reactive({
|
||||
const columns = [
|
||||
{
|
||||
title: '任务编号',
|
||||
dataIndex: 'taskCode',
|
||||
key: 'taskCode',
|
||||
dataIndex: 'applicationNumber',
|
||||
key: 'applicationNumber',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '任务名称',
|
||||
dataIndex: 'taskName',
|
||||
key: 'taskName',
|
||||
dataIndex: 'productName',
|
||||
key: 'productName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
@@ -292,27 +310,34 @@ const columns = [
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
dataIndex: 'taskStatus',
|
||||
key: 'taskStatus',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'assignee',
|
||||
key: 'assignee',
|
||||
width: 120
|
||||
width: 120,
|
||||
customRender: ({ record }) => record.assignedUser?.real_name || '-'
|
||||
},
|
||||
{
|
||||
title: '完成时间',
|
||||
dataIndex: 'completedAt',
|
||||
key: 'completedAt',
|
||||
width: 150
|
||||
width: 150,
|
||||
customRender: ({ text }) => text || '-'
|
||||
},
|
||||
{
|
||||
title: '处理时长',
|
||||
dataIndex: 'duration',
|
||||
key: 'duration',
|
||||
width: 100
|
||||
width: 100,
|
||||
customRender: ({ record }) => {
|
||||
if (!record.completedAt || !record.createdAt) return '-'
|
||||
const created = new Date(record.createdAt)
|
||||
const completed = new Date(record.completedAt)
|
||||
const days = Math.ceil((completed - created) / (1000 * 60 * 60 * 24))
|
||||
return `${days} 天`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@@ -325,17 +350,31 @@ const columns = [
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
completed: 'green',
|
||||
archived: 'purple'
|
||||
'待处理': 'orange',
|
||||
'处理中': 'blue',
|
||||
'已完成': 'green',
|
||||
'已取消': 'red',
|
||||
'已归档': 'purple',
|
||||
// 英文兼容
|
||||
'completed': 'green',
|
||||
'archived': 'purple'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
// 如果已经是中文,直接返回
|
||||
if (['待处理', '处理中', '已完成', '已取消', '已归档'].includes(status)) {
|
||||
return status
|
||||
}
|
||||
// 英文转中文
|
||||
const textMap = {
|
||||
completed: '已完成',
|
||||
archived: '已归档'
|
||||
'pending': '待处理',
|
||||
'processing': '处理中',
|
||||
'completed': '已完成',
|
||||
'rejected': '已取消',
|
||||
'archived': '已归档'
|
||||
}
|
||||
return textMap[status] || status
|
||||
}
|
||||
@@ -343,23 +382,48 @@ const getStatusText = (status) => {
|
||||
// 获取优先级颜色
|
||||
const getPriorityColor = (priority) => {
|
||||
const colorMap = {
|
||||
high: 'red',
|
||||
medium: 'orange',
|
||||
low: 'blue'
|
||||
'低': 'blue',
|
||||
'中': 'green',
|
||||
'高': 'orange',
|
||||
'紧急': 'red',
|
||||
// 英文兼容
|
||||
'low': 'blue',
|
||||
'medium': 'green',
|
||||
'high': 'orange',
|
||||
'urgent': 'red'
|
||||
}
|
||||
return colorMap[priority] || 'default'
|
||||
}
|
||||
|
||||
// 获取优先级文本
|
||||
const getPriorityText = (priority) => {
|
||||
// 如果已经是中文,直接返回
|
||||
if (['低', '中', '高', '紧急'].includes(priority)) {
|
||||
return priority
|
||||
}
|
||||
// 英文转中文
|
||||
const textMap = {
|
||||
high: '高',
|
||||
medium: '中',
|
||||
low: '低'
|
||||
'low': '低',
|
||||
'medium': '中',
|
||||
'high': '高',
|
||||
'urgent': '紧急'
|
||||
}
|
||||
return textMap[priority] || priority
|
||||
}
|
||||
|
||||
// 计算处理时长
|
||||
const calculateDuration = (task) => {
|
||||
if (!task.completedAt || !task.createdAt) return '-'
|
||||
try {
|
||||
const created = new Date(task.createdAt)
|
||||
const completed = new Date(task.completedAt)
|
||||
const days = Math.ceil((completed - created) / (1000 * 60 * 60 * 24))
|
||||
return `${days} 天`
|
||||
} catch (error) {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
@@ -520,12 +584,20 @@ const fetchTaskList = async () => {
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const response = await supervisionTaskApi.getStats()
|
||||
console.log('统计数据响应:', response)
|
||||
|
||||
if (response.data && response.data.status === 'success') {
|
||||
const statsData = response.data.data
|
||||
stats.total = statsData.total || 0
|
||||
stats.thisMonth = statsData.thisMonth || 0
|
||||
stats.archived = statsData.archived || 0
|
||||
stats.avgDuration = statsData.avgDuration || 0
|
||||
|
||||
// 从statusStats中获取已完成任务数
|
||||
const statusStats = statsData.statusStats || []
|
||||
const completedStat = statusStats.find(s => s.status === '已完成')
|
||||
stats.total = completedStat ? completedStat.count : 0
|
||||
|
||||
// 这些数据暂时从总数获取,后续可以从API补充
|
||||
stats.thisMonth = 0
|
||||
stats.archived = 0
|
||||
stats.avgDuration = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
|
||||
@@ -315,9 +315,10 @@ const getInsuranceCategories = async (req, res) => {
|
||||
// 导出保险申请数据
|
||||
const exportApplications = async (req, res) => {
|
||||
try {
|
||||
const ExcelJS = require('exceljs');
|
||||
const {
|
||||
page = 1,
|
||||
limit = 1000,
|
||||
limit = 10000, // 增加导出数量限制
|
||||
applicantName,
|
||||
insuranceType,
|
||||
insuranceCategory,
|
||||
@@ -344,13 +345,15 @@ const exportApplications = async (req, res) => {
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insuranceTypeInfo',
|
||||
attributes: ['name', 'description']
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description'],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'createdByUser',
|
||||
attributes: ['username', 'real_name']
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'username', 'real_name'],
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
@@ -358,38 +361,101 @@ const exportApplications = async (req, res) => {
|
||||
offset: (parseInt(page) - 1) * parseInt(limit)
|
||||
});
|
||||
|
||||
// 简单的CSV格式导出
|
||||
const csvHeader = '申请编号,申请人姓名,身份证号,联系电话,参保类型,保险类型,保险金额,保险期限,地址,状态,申请时间,备注\n';
|
||||
const csvData = applications.map(app => {
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'initial_approved': '初审通过',
|
||||
'under_review': '复审中',
|
||||
'approved': '已通过',
|
||||
'rejected': '已拒绝'
|
||||
};
|
||||
|
||||
return [
|
||||
app.application_number || '',
|
||||
app.applicant_name || '',
|
||||
app.id_card || '',
|
||||
app.phone || '',
|
||||
app.insurance_category || '',
|
||||
app.insurance_type || '',
|
||||
app.insurance_amount || '',
|
||||
app.insurance_period || '',
|
||||
app.address || '',
|
||||
statusMap[app.status] || app.status,
|
||||
app.created_at ? new Date(app.created_at).toLocaleString('zh-CN') : '',
|
||||
app.remarks || ''
|
||||
].map(field => `"${String(field).replace(/"/g, '""')}"`).join(',');
|
||||
}).join('\n');
|
||||
// 将Sequelize实例转换为纯对象
|
||||
const plainApplications = applications.map(app => app.toJSON());
|
||||
|
||||
const csvContent = csvHeader + csvData;
|
||||
// 创建Excel工作簿
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('保险申请列表');
|
||||
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'initial_approved': '初审通过',
|
||||
'under_review': '复审中',
|
||||
'approved': '已通过',
|
||||
'rejected': '已拒绝'
|
||||
};
|
||||
|
||||
// 定义列(包含所有字段)
|
||||
worksheet.columns = [
|
||||
{ header: 'ID', key: 'id', width: 8 },
|
||||
{ header: '申请编号', key: 'application_no', width: 18 },
|
||||
{ header: '客户姓名', key: 'customer_name', width: 12 },
|
||||
{ header: '身份证号', key: 'customer_id_card', width: 20 },
|
||||
{ header: '联系电话', key: 'customer_phone', width: 15 },
|
||||
{ header: '客户地址', key: 'customer_address', width: 30 },
|
||||
{ header: '参保类型', key: 'insurance_category', width: 12 },
|
||||
{ header: '保险类型ID', key: 'insurance_type_id', width: 12 },
|
||||
{ header: '保险类型名称', key: 'insurance_type_name', width: 15 },
|
||||
{ header: '保险类型说明', key: 'insurance_type_description', width: 25 },
|
||||
{ header: '申请数量', key: 'application_quantity', width: 12 },
|
||||
{ header: '申请金额', key: 'application_amount', width: 15 },
|
||||
{ header: '申请日期', key: 'application_date', width: 20 },
|
||||
{ header: '状态', key: 'status', width: 12 },
|
||||
{ header: '审核人ID', key: 'reviewer_id', width: 10 },
|
||||
{ header: '审核人姓名', key: 'reviewer_name', width: 12 },
|
||||
{ header: '审核日期', key: 'review_date', width: 20 },
|
||||
{ header: '审核意见', key: 'review_notes', width: 30 },
|
||||
{ header: '文档附件', key: 'documents', width: 30 },
|
||||
{ header: '备注', key: 'remarks', width: 30 },
|
||||
{ header: '创建时间', key: 'createdAt', width: 20 },
|
||||
{ header: '更新时间', key: 'updatedAt', width: 20 }
|
||||
];
|
||||
|
||||
// 设置表头样式
|
||||
worksheet.getRow(1).font = { bold: true, size: 12 };
|
||||
worksheet.getRow(1).fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFE0E0E0' }
|
||||
};
|
||||
worksheet.getRow(1).alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
|
||||
// 添加数据行
|
||||
plainApplications.forEach(app => {
|
||||
worksheet.addRow({
|
||||
id: app.id || '',
|
||||
application_no: app.application_no || '',
|
||||
customer_name: app.customer_name || '',
|
||||
customer_id_card: app.customer_id_card || '',
|
||||
customer_phone: app.customer_phone || '',
|
||||
customer_address: app.customer_address || '',
|
||||
insurance_category: app.insurance_category || '',
|
||||
insurance_type_id: app.insurance_type_id || '',
|
||||
insurance_type_name: app.insurance_type?.name || '',
|
||||
insurance_type_description: app.insurance_type?.description || '',
|
||||
application_quantity: app.application_quantity || '',
|
||||
application_amount: app.application_amount || '',
|
||||
application_date: app.application_date || '',
|
||||
status: statusMap[app.status] || app.status || '',
|
||||
reviewer_id: app.reviewer_id || '',
|
||||
reviewer_name: app.reviewer?.real_name || '',
|
||||
review_date: app.review_date || '',
|
||||
review_notes: app.review_notes || '',
|
||||
documents: app.documents || '',
|
||||
remarks: app.remarks || '',
|
||||
createdAt: app.createdAt || '',
|
||||
updatedAt: app.updatedAt || ''
|
||||
});
|
||||
});
|
||||
|
||||
// 设置数据行样式
|
||||
worksheet.eachRow((row, rowNumber) => {
|
||||
if (rowNumber > 1) {
|
||||
row.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||
}
|
||||
});
|
||||
|
||||
// 生成Excel文件
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
|
||||
// 设置响应头
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="insurance_applications_${new Date().toISOString().slice(0, 10)}.xlsx"`);
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="insurance_applications_${new Date().toISOString().slice(0, 10)}.csv"`);
|
||||
res.send('\uFEFF' + csvContent); // 添加BOM以支持中文
|
||||
// 发送文件
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出保险申请数据错误:', error);
|
||||
res.status(500).json(responseFormat.error('导出保险申请数据失败'));
|
||||
|
||||
@@ -544,59 +544,144 @@ const deleteLivestockClaim = async (req, res) => {
|
||||
// 导出理赔列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const { claim_number, claimant_name, status } = req.query;
|
||||
const ExcelJS = require('exceljs');
|
||||
const { claim_no, reporter_name, claim_status } = req.query;
|
||||
|
||||
const where = {};
|
||||
if (claim_number) where.claim_number = { [Op.like]: `%${claim_number}%` };
|
||||
if (claimant_name) where.claimant_name = { [Op.like]: `%${claimant_name}%` };
|
||||
if (status) where.status = status;
|
||||
if (claim_no) where.claim_no = { [Op.like]: `%${claim_no}%` };
|
||||
if (reporter_name) where.reporter_name = { [Op.like]: `%${reporter_name}%` };
|
||||
if (claim_status) where.claim_status = claim_status;
|
||||
|
||||
const claims = await LivestockClaim.findAll({
|
||||
where,
|
||||
include: [{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['policy_number']
|
||||
}],
|
||||
order: [['createdAt', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
include: [
|
||||
{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['id', 'policy_no', 'policyholder_name'],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username'],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'real_name', 'username'],
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
limit: 10000
|
||||
});
|
||||
|
||||
const statusMap = { pending: '待审核', approved: '已批准', rejected: '已拒绝', settled: '已理赔' };
|
||||
// 将Sequelize实例转换为纯对象
|
||||
const plainClaims = claims.map(claim => claim.toJSON());
|
||||
|
||||
const exportData = claims.map(claim => ({
|
||||
claim_number: claim.claim_number || '',
|
||||
policy_number: claim.policy?.policy_number || '',
|
||||
claimant_name: claim.claimant_name || '',
|
||||
claim_amount: claim.claim_amount || 0,
|
||||
approved_amount: claim.approved_amount || 0,
|
||||
claim_reason: claim.claim_reason || '',
|
||||
status: ExcelExport.formatStatus(claim.status, statusMap),
|
||||
claim_date: ExcelExport.formatDate(claim.claim_date),
|
||||
settled_date: ExcelExport.formatDate(claim.settled_date),
|
||||
createdAt: ExcelExport.formatDate(claim.createdAt)
|
||||
}));
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'investigating': '调查中',
|
||||
'approved': '已批准',
|
||||
'rejected': '已拒绝',
|
||||
'paid': '已支付'
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ header: '理赔编号', key: 'claim_number', width: 20 },
|
||||
{ header: '保单编号', key: 'policy_number', width: 20 },
|
||||
{ header: '理赔人', key: 'claimant_name', width: 15 },
|
||||
// 理赔类型映射
|
||||
const claimTypeMap = {
|
||||
'death': '死亡',
|
||||
'disease': '疾病',
|
||||
'accident': '意外事故',
|
||||
'natural_disaster': '自然灾害'
|
||||
};
|
||||
|
||||
// 创建Excel工作簿
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('生资理赔列表');
|
||||
|
||||
// 定义列(包含所有字段)
|
||||
worksheet.columns = [
|
||||
{ header: 'ID', key: 'id', width: 8 },
|
||||
{ header: '理赔编号', key: 'claim_no', width: 18 },
|
||||
{ header: '报案人姓名', key: 'reporter_name', width: 12 },
|
||||
{ header: '联系电话', key: 'contact_phone', width: 15 },
|
||||
{ header: '保单号', key: 'policy_no', width: 18 },
|
||||
{ header: '保单持有人', key: 'policy_holder', width: 12 },
|
||||
{ header: '理赔类型', key: 'claim_type', width: 12 },
|
||||
{ header: '受影响数量', key: 'affected_count', width: 12 },
|
||||
{ header: '理赔金额', key: 'claim_amount', width: 15 },
|
||||
{ header: '批准金额', key: 'approved_amount', width: 15 },
|
||||
{ header: '理赔原因', key: 'claim_reason', width: 30 },
|
||||
{ header: '状态', key: 'status', width: 12 },
|
||||
{ header: '理赔日期', key: 'claim_date', width: 15 },
|
||||
{ header: '结算日期', key: 'settled_date', width: 15 },
|
||||
{ header: '创建时间', key: 'createdAt', width: 20 }
|
||||
{ header: '事故日期', key: 'incident_date', width: 20 },
|
||||
{ header: '报案日期', key: 'report_date', width: 20 },
|
||||
{ header: '事故描述', key: 'incident_description', width: 35 },
|
||||
{ header: '事故地点', key: 'incident_location', width: 25 },
|
||||
{ header: '理赔状态', key: 'claim_status', width: 12 },
|
||||
{ header: '调查报告', key: 'investigation_report', width: 30 },
|
||||
{ header: '审核人', key: 'reviewer_name', width: 12 },
|
||||
{ header: '审核备注', key: 'review_notes', width: 30 },
|
||||
{ header: '审核日期', key: 'review_date', width: 20 },
|
||||
{ header: '赔付日期', key: 'payment_date', width: 20 },
|
||||
{ header: '赔付金额', key: 'payment_amount', width: 15 },
|
||||
{ header: '创建人', key: 'creator_name', width: 12 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 },
|
||||
{ header: '更新时间', key: 'updated_at', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '理赔列表');
|
||||
// 设置表头样式
|
||||
worksheet.getRow(1).font = { bold: true, size: 12 };
|
||||
worksheet.getRow(1).fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFE0E0E0' }
|
||||
};
|
||||
worksheet.getRow(1).alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
|
||||
// 添加数据行
|
||||
plainClaims.forEach(claim => {
|
||||
worksheet.addRow({
|
||||
id: claim.id || '',
|
||||
claim_no: claim.claim_no || '',
|
||||
reporter_name: claim.reporter_name || '',
|
||||
contact_phone: claim.contact_phone || '',
|
||||
policy_no: claim.policy_no || '',
|
||||
policy_holder: claim.policy?.policyholder_name || '',
|
||||
claim_type: claimTypeMap[claim.claim_type] || claim.claim_type || '',
|
||||
affected_count: claim.affected_count || '',
|
||||
claim_amount: claim.claim_amount || '',
|
||||
incident_date: claim.incident_date || '',
|
||||
report_date: claim.report_date || '',
|
||||
incident_description: claim.incident_description || '',
|
||||
incident_location: claim.incident_location || '',
|
||||
claim_status: statusMap[claim.claim_status] || claim.claim_status || '',
|
||||
investigation_report: claim.investigation_report || '',
|
||||
reviewer_name: claim.reviewer?.real_name || '',
|
||||
review_notes: claim.review_notes || '',
|
||||
review_date: claim.review_date || '',
|
||||
payment_date: claim.payment_date || '',
|
||||
payment_amount: claim.payment_amount || '',
|
||||
creator_name: claim.creator?.real_name || '',
|
||||
created_at: claim.created_at || '',
|
||||
updated_at: claim.updated_at || ''
|
||||
});
|
||||
});
|
||||
|
||||
// 设置数据行样式
|
||||
worksheet.eachRow((row, rowNumber) => {
|
||||
if (rowNumber > 1) {
|
||||
row.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||
}
|
||||
});
|
||||
|
||||
// 生成Excel文件
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=livestock_claims_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
} catch (error) {
|
||||
console.error('导出理赔记录失败:', error);
|
||||
console.error('导出生资理赔记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('导出失败'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -367,6 +367,7 @@ const deletePolicy = async (req, res) => {
|
||||
// 导出保单列表到Excel
|
||||
const exportToExcel = async (req, res) => {
|
||||
try {
|
||||
const ExcelJS = require('exceljs');
|
||||
const { policy_number, policyholder_name, status } = req.query;
|
||||
|
||||
const where = {};
|
||||
@@ -378,41 +379,94 @@ const exportToExcel = async (req, res) => {
|
||||
where,
|
||||
include: [{
|
||||
model: InsuranceType,
|
||||
as: 'InsuranceType',
|
||||
attributes: ['name']
|
||||
as: 'insurance_type', // 修正:使用正确的关联别名
|
||||
attributes: ['id', 'name', 'description'],
|
||||
required: false
|
||||
}],
|
||||
order: [['created_at', 'DESC']],
|
||||
raw: true,
|
||||
nest: true
|
||||
limit: 10000
|
||||
});
|
||||
|
||||
const statusMap = { active: '生效中', pending: '待生效', expired: '已过期', cancelled: '已取消' };
|
||||
// 将Sequelize实例转换为纯对象
|
||||
const plainPolicies = policies.map(policy => policy.toJSON());
|
||||
|
||||
const exportData = policies.map(policy => ({
|
||||
policy_number: policy.policy_number || '',
|
||||
policyholder_name: policy.policyholder_name || '',
|
||||
insurance_type_name: policy.InsuranceType?.name || '',
|
||||
coverage_amount: policy.coverage_amount || 0,
|
||||
premium_amount: policy.premium_amount || 0,
|
||||
start_date: ExcelExport.formatDate(policy.start_date),
|
||||
end_date: ExcelExport.formatDate(policy.end_date),
|
||||
status: ExcelExport.formatStatus(policy.status, statusMap),
|
||||
created_at: ExcelExport.formatDate(policy.created_at)
|
||||
}));
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
'active': '生效中',
|
||||
'pending': '待生效',
|
||||
'expired': '已过期',
|
||||
'cancelled': '已取消'
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ header: '保单编号', key: 'policy_number', width: 20 },
|
||||
{ header: '投保人', key: 'policyholder_name', width: 15 },
|
||||
{ header: '险种', key: 'insurance_type_name', width: 20 },
|
||||
{ header: '保额', key: 'coverage_amount', width: 15 },
|
||||
{ header: '保费', key: 'premium_amount', width: 15 },
|
||||
{ header: '开始日期', key: 'start_date', width: 15 },
|
||||
{ header: '结束日期', key: 'end_date', width: 15 },
|
||||
// 创建Excel工作簿
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('保单列表');
|
||||
|
||||
// 定义列(包含所有字段)
|
||||
worksheet.columns = [
|
||||
{ header: 'ID', key: 'id', width: 8 },
|
||||
{ header: '保单编号', key: 'policy_number', width: 18 },
|
||||
{ header: '投保人姓名', key: 'policyholder_name', width: 15 },
|
||||
{ header: '被保人姓名', key: 'insured_name', width: 15 },
|
||||
{ header: '保险类型ID', key: 'insurance_type_id', width: 12 },
|
||||
{ header: '保险类型名称', key: 'insurance_type_name', width: 18 },
|
||||
{ header: '保险类型说明', key: 'insurance_type_description', width: 25 },
|
||||
{ header: '保障金额', key: 'coverage_amount', width: 15 },
|
||||
{ header: '保费金额', key: 'premium_amount', width: 15 },
|
||||
{ header: '开始日期', key: 'start_date', width: 20 },
|
||||
{ header: '结束日期', key: 'end_date', width: 20 },
|
||||
{ header: '状态', key: 'status', width: 12 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 }
|
||||
{ header: '联系电话', key: 'phone', width: 15 },
|
||||
{ header: '电子邮箱', key: 'email', width: 25 },
|
||||
{ header: '地址', key: 'address', width: 30 },
|
||||
{ header: '备注', key: 'remarks', width: 30 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 },
|
||||
{ header: '更新时间', key: 'updated_at', width: 20 }
|
||||
];
|
||||
|
||||
const buffer = await ExcelExport.exportToExcel(exportData, columns, '保单列表');
|
||||
// 设置表头样式
|
||||
worksheet.getRow(1).font = { bold: true, size: 12 };
|
||||
worksheet.getRow(1).fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFE0E0E0' }
|
||||
};
|
||||
worksheet.getRow(1).alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
|
||||
// 添加数据行
|
||||
plainPolicies.forEach(policy => {
|
||||
worksheet.addRow({
|
||||
id: policy.id || '',
|
||||
policy_number: policy.policy_no || '', // 修正:使用 policy_no
|
||||
policyholder_name: policy.policyholder_name || '',
|
||||
insured_name: policy.insured_name || '',
|
||||
insurance_type_id: policy.insurance_type_id || '',
|
||||
insurance_type_name: policy.insurance_type?.name || '',
|
||||
insurance_type_description: policy.insurance_type?.description || '',
|
||||
coverage_amount: policy.coverage_amount || '',
|
||||
premium_amount: policy.premium_amount || '',
|
||||
start_date: policy.start_date || '',
|
||||
end_date: policy.end_date || '',
|
||||
status: statusMap[policy.policy_status] || policy.policy_status || '', // 修正:使用 policy_status
|
||||
phone: policy.phone || '',
|
||||
email: policy.email || '',
|
||||
address: policy.address || '',
|
||||
remarks: policy.remarks || '',
|
||||
created_at: policy.created_at || '',
|
||||
updated_at: policy.updated_at || ''
|
||||
});
|
||||
});
|
||||
|
||||
// 设置数据行样式
|
||||
worksheet.eachRow((row, rowNumber) => {
|
||||
if (rowNumber > 1) {
|
||||
row.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||
}
|
||||
});
|
||||
|
||||
// 生成Excel文件
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=policies_${Date.now()}.xlsx`);
|
||||
res.send(buffer);
|
||||
|
||||
@@ -24,6 +24,22 @@ class SupervisoryTaskController {
|
||||
sortOrder = 'DESC'
|
||||
} = req.query;
|
||||
|
||||
// 状态映射:英文到中文
|
||||
const statusMap = {
|
||||
'pending': '待处理',
|
||||
'processing': '处理中',
|
||||
'completed': '已完成',
|
||||
'rejected': '已取消'
|
||||
};
|
||||
|
||||
// 优先级映射:英文到中文
|
||||
const priorityMap = {
|
||||
'low': '低',
|
||||
'medium': '中',
|
||||
'high': '高',
|
||||
'urgent': '紧急'
|
||||
};
|
||||
|
||||
// 构建查询条件
|
||||
const where = {};
|
||||
|
||||
@@ -36,11 +52,13 @@ class SupervisoryTaskController {
|
||||
}
|
||||
|
||||
if (taskStatus) {
|
||||
where.taskStatus = taskStatus;
|
||||
// 如果传入的是英文状态,转换为中文
|
||||
where.taskStatus = statusMap[taskStatus] || taskStatus;
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
where.priority = priority;
|
||||
// 如果传入的是英文优先级,转换为中文
|
||||
where.priority = priorityMap[priority] || priority;
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
|
||||
@@ -8,6 +8,16 @@ router.get('/applications', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getApplications
|
||||
);
|
||||
|
||||
// 获取保险申请统计(必须在 :id 路由之前)
|
||||
router.get('/applications-stats', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getApplicationStats
|
||||
);
|
||||
|
||||
// 导出保险申请数据(必须在 :id 路由之前)
|
||||
router.get('/applications/export', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.exportApplications
|
||||
);
|
||||
|
||||
// 创建保险申请
|
||||
router.post('/applications', jwtAuth, checkPermission('insurance', 'create'),
|
||||
insuranceController.createApplication
|
||||
@@ -33,16 +43,6 @@ router.delete('/applications/:id', jwtAuth, checkPermission('insurance', 'delete
|
||||
insuranceController.deleteApplication
|
||||
);
|
||||
|
||||
// 获取保险申请统计
|
||||
router.get('/applications-stats', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getApplicationStats
|
||||
);
|
||||
|
||||
// 导出保险申请数据
|
||||
router.get('/applications/export', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.exportApplications
|
||||
);
|
||||
|
||||
// 获取参保类型选项
|
||||
router.get('/categories', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getInsuranceCategories
|
||||
|
||||
Reference in New Issue
Block a user