完善保险端的前后端
This commit is contained in:
@@ -216,7 +216,7 @@ const fetchMenus = async () => {
|
||||
key: 'CompletedTask',
|
||||
icon: () => h(FileDoneOutlined),
|
||||
label: '监管任务已结项',
|
||||
path: '/dashboard' // 重定向到仪表板
|
||||
path: '/completed-tasks'
|
||||
},
|
||||
{
|
||||
key: 'InsuredCustomers',
|
||||
@@ -239,6 +239,11 @@ const fetchMenus = async () => {
|
||||
key: 'PolicyManagement',
|
||||
label: '生资保单列表',
|
||||
path: '/policies'
|
||||
},
|
||||
{
|
||||
key: 'LivestockPolicyManagement',
|
||||
label: '生资保单管理',
|
||||
path: '/livestock-policies'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -7,6 +7,27 @@ import 'ant-design-vue/dist/reset.css'
|
||||
// Ant Design Vue的中文语言包
|
||||
import antdZhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||
|
||||
// 抑制ResizeObserver警告
|
||||
const resizeObserverErrorHandler = (e) => {
|
||||
if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
|
||||
e.message.includes('ResizeObserver loop')) {
|
||||
e.preventDefault()
|
||||
e.stopImmediatePropagation()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未捕获的错误
|
||||
window.addEventListener('error', resizeObserverErrorHandler)
|
||||
|
||||
// 处理未捕获的Promise错误
|
||||
window.addEventListener('unhandledrejection', (e) => {
|
||||
if (e.reason && e.reason.message && e.reason.message.includes('ResizeObserver loop')) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
// Ant Design Vue 4.x配置
|
||||
// 使用Ant Design Vue内置的dayjs实例,避免版本冲突
|
||||
const antdConfig = {
|
||||
|
||||
@@ -18,6 +18,7 @@ import AntdConfigDebug from '@/views/AntdConfigDebug.vue'
|
||||
import SimpleDayjsTest from '@/views/SimpleDayjsTest.vue'
|
||||
import RangePickerTest from '@/views/RangePickerTest.vue'
|
||||
import LoginTest from '@/views/LoginTest.vue'
|
||||
import LivestockPolicyManagement from '@/views/LivestockPolicyManagement.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -79,6 +80,12 @@ const routes = [
|
||||
component: SupervisionTaskManagement,
|
||||
meta: { title: '监管任务管理' }
|
||||
},
|
||||
{
|
||||
path: 'completed-tasks',
|
||||
name: 'CompletedTasks',
|
||||
component: () => import('@/views/CompletedTaskManagement.vue'),
|
||||
meta: { requiresAuth: true, title: '监管任务结项' }
|
||||
},
|
||||
{
|
||||
path: 'installation-tasks',
|
||||
alias: ['pending-installation', 'installation-task'], // 添加别名兼容不同形式
|
||||
@@ -86,6 +93,12 @@ const routes = [
|
||||
component: InstallationTaskManagement,
|
||||
meta: { title: '待安装任务管理' }
|
||||
},
|
||||
{
|
||||
path: 'livestock-policies',
|
||||
name: 'LivestockPolicyManagement',
|
||||
component: LivestockPolicyManagement,
|
||||
meta: { title: '生资保单管理' }
|
||||
},
|
||||
{
|
||||
path: 'date-picker-test',
|
||||
name: 'DatePickerTest',
|
||||
|
||||
@@ -66,8 +66,14 @@ export const insuranceTypeAPI = {
|
||||
export const applicationAPI = {
|
||||
getList: (params) => api.get('/insurance/applications', { params }),
|
||||
getDetail: (id) => api.get(`/insurance/applications/${id}`),
|
||||
create: (data) => api.post('/insurance/applications', data),
|
||||
update: (id, data) => api.put(`/insurance/applications/${id}`, data),
|
||||
review: (id, data) => api.patch(`/insurance/applications/${id}/review`, data),
|
||||
updateStatus: (id, data) => api.put(`/insurance/applications/${id}/status`, data),
|
||||
delete: (id) => api.delete(`/insurance/applications/${id}`)
|
||||
delete: (id) => api.delete(`/insurance/applications/${id}`),
|
||||
getStats: () => api.get('/insurance/applications-stats'),
|
||||
getCategories: () => api.get('/insurance/categories'),
|
||||
export: (params) => api.get('/insurance/applications/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const policyAPI = {
|
||||
@@ -113,13 +119,48 @@ export const supervisionTaskApi = {
|
||||
export const installationTaskApi = {
|
||||
getList: (params) => api.get('/installation-tasks', { params }),
|
||||
create: (data) => api.post('/installation-tasks', data),
|
||||
update: (data) => api.put(`/installation-tasks/${data.id}`, data),
|
||||
update: (id, data) => api.put(`/installation-tasks/${id}`, data),
|
||||
delete: (id) => api.delete(`/installation-tasks/${id}`),
|
||||
getDetail: (id) => api.get(`/installation-tasks/${id}`),
|
||||
batchDelete: (ids) => api.post('/installation-tasks/batch-delete', { ids }),
|
||||
batchUpdateStatus: (data) => api.post('/installation-tasks/batch-update-status', data),
|
||||
export: (params) => api.get('/installation-tasks/export/excel', { params, responseType: 'blob' }),
|
||||
getStats: () => api.get('/installation-tasks/statistics/summary')
|
||||
getById: (id) => api.get(`/installation-tasks/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/installation-tasks/${id}/status`, data),
|
||||
getStats: () => api.get('/installation-tasks/stats'),
|
||||
assign: (id, data) => api.post(`/installation-tasks/${id}/assign`, data),
|
||||
complete: (id, data) => api.post(`/installation-tasks/${id}/complete`, data),
|
||||
getHistory: (id) => api.get(`/installation-tasks/${id}/history`)
|
||||
}
|
||||
|
||||
// 生资保险相关API
|
||||
export const livestockTypeApi = {
|
||||
getList: (params) => api.get('/livestock-types', { params }),
|
||||
create: (data) => api.post('/livestock-types', data),
|
||||
update: (id, data) => api.put(`/livestock-types/${id}`, data),
|
||||
delete: (id, data) => api.delete(`/livestock-types/${id}`, data),
|
||||
getById: (id) => api.get(`/livestock-types/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/livestock-types/${id}/status`, data),
|
||||
batchUpdateStatus: (data) => api.patch('/livestock-types/batch-status', data)
|
||||
}
|
||||
|
||||
export const livestockPolicyApi = {
|
||||
getList: (params) => api.get('/livestock-policies', { params }),
|
||||
create: (data) => api.post('/livestock-policies', data),
|
||||
update: (id, data) => api.put(`/livestock-policies/${id}`, data),
|
||||
delete: (id) => api.delete(`/livestock-policies/${id}`),
|
||||
getById: (id) => api.get(`/livestock-policies/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/livestock-policies/${id}/status`, data),
|
||||
getStats: () => api.get('/livestock-policies/stats'),
|
||||
getLivestockTypes: () => api.get('/livestock-types?status=active')
|
||||
}
|
||||
|
||||
export const livestockClaimApi = {
|
||||
getList: (params) => api.get('/livestock-claims', { params }),
|
||||
create: (data) => api.post('/livestock-claims', data),
|
||||
update: (id, data) => api.put(`/livestock-claims/${id}`, data),
|
||||
delete: (id) => api.delete(`/livestock-claims/${id}`),
|
||||
getById: (id) => api.get(`/livestock-claims/${id}`),
|
||||
approve: (id, data) => api.post(`/livestock-claims/${id}/approve`, data),
|
||||
reject: (id, data) => api.post(`/livestock-claims/${id}/reject`, data),
|
||||
updatePaymentStatus: (id, data) => api.patch(`/livestock-claims/${id}/payment`, data),
|
||||
getStats: () => api.get('/livestock-claims/stats')
|
||||
}
|
||||
|
||||
export default api
|
||||
File diff suppressed because it is too large
Load Diff
509
insurance_admin-system/src/views/CompletedTaskManagement.vue
Normal file
509
insurance_admin-system/src/views/CompletedTaskManagement.vue
Normal file
@@ -0,0 +1,509 @@
|
||||
<template>
|
||||
<div class="completed-task-management">
|
||||
<div class="page-header">
|
||||
<h1>监管任务结项管理</h1>
|
||||
<p>查看和管理已完成的监管任务</p>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选区域 -->
|
||||
<div class="search-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
allow-clear
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="archived">已归档</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-range-picker
|
||||
v-model:value="searchForm.dateRange"
|
||||
style="width: 100%"
|
||||
placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="handleReset">
|
||||
<ReloadOutlined />
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="总结项任务"
|
||||
:value="stats.total"
|
||||
:value-style="{ color: '#3f8600' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<CheckCircleOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="本月结项"
|
||||
:value="stats.thisMonth"
|
||||
:value-style="{ color: '#1890ff' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<CalendarOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="已归档任务"
|
||||
:value="stats.archived"
|
||||
:value-style="{ color: '#722ed1' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<FolderOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="平均处理时长"
|
||||
:value="stats.avgDuration"
|
||||
suffix="天"
|
||||
:value-style="{ color: '#fa8c16' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<ClockCircleOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<div class="table-section">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="taskList"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ 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)">
|
||||
<EyeOutlined />
|
||||
查看
|
||||
</a-button>
|
||||
<a-button type="link" size="small" @click="handleDownload(record)">
|
||||
<DownloadOutlined />
|
||||
下载报告
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="archive" @click="handleArchive(record)">
|
||||
<FolderOutlined />
|
||||
归档
|
||||
</a-menu-item>
|
||||
<a-menu-item key="export" @click="handleExport(record)">
|
||||
<ExportOutlined />
|
||||
导出
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="link" size="small">
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 任务详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="detailModalVisible"
|
||||
title="任务详情"
|
||||
width="800px"
|
||||
:footer="null"
|
||||
>
|
||||
<div v-if="selectedTask" class="task-detail">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="任务名称">
|
||||
{{ selectedTask.taskName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务编号">
|
||||
{{ selectedTask.taskCode }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="优先级">
|
||||
<a-tag :color="getPriorityColor(selectedTask.priority)">
|
||||
{{ getPriorityText(selectedTask.priority) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="状态">
|
||||
<a-tag :color="getStatusColor(selectedTask.status)">
|
||||
{{ getStatusText(selectedTask.status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ selectedTask.createdAt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="完成时间">
|
||||
{{ selectedTask.completedAt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="负责人">
|
||||
{{ selectedTask.assignee }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="处理时长">
|
||||
{{ selectedTask.duration }} 天
|
||||
</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>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import {
|
||||
SearchOutlined,
|
||||
ReloadOutlined,
|
||||
CheckCircleOutlined,
|
||||
CalendarOutlined,
|
||||
FolderOutlined,
|
||||
ClockCircleOutlined,
|
||||
EyeOutlined,
|
||||
DownloadOutlined,
|
||||
ExportOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const taskList = ref([])
|
||||
const detailModalVisible = ref(false)
|
||||
const selectedTask = ref(null)
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
taskName: '',
|
||||
status: undefined,
|
||||
dateRange: undefined
|
||||
})
|
||||
|
||||
// 统计数据
|
||||
const stats = reactive({
|
||||
total: 0,
|
||||
thisMonth: 0,
|
||||
archived: 0,
|
||||
avgDuration: 0
|
||||
})
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '任务编号',
|
||||
dataIndex: 'taskCode',
|
||||
key: 'taskCode',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '任务名称',
|
||||
dataIndex: 'taskName',
|
||||
key: 'taskName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
key: 'priority',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'assignee',
|
||||
key: 'assignee',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '完成时间',
|
||||
dataIndex: 'completedAt',
|
||||
key: 'completedAt',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '处理时长',
|
||||
dataIndex: 'duration',
|
||||
key: 'duration',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
completed: 'green',
|
||||
archived: 'purple'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const textMap = {
|
||||
completed: '已完成',
|
||||
archived: '已归档'
|
||||
}
|
||||
return textMap[status] || status
|
||||
}
|
||||
|
||||
// 获取优先级颜色
|
||||
const getPriorityColor = (priority) => {
|
||||
const colorMap = {
|
||||
high: 'red',
|
||||
medium: 'orange',
|
||||
low: 'blue'
|
||||
}
|
||||
return colorMap[priority] || 'default'
|
||||
}
|
||||
|
||||
// 获取优先级文本
|
||||
const getPriorityText = (priority) => {
|
||||
const textMap = {
|
||||
high: '高',
|
||||
medium: '中',
|
||||
low: '低'
|
||||
}
|
||||
return textMap[priority] || priority
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.keys(searchForm).forEach(key => {
|
||||
searchForm[key] = key === 'status' ? undefined : ''
|
||||
})
|
||||
pagination.current = 1
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 表格变化处理
|
||||
const handleTableChange = (pag) => {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleView = (record) => {
|
||||
selectedTask.value = record
|
||||
detailModalVisible.value = true
|
||||
}
|
||||
|
||||
// 下载报告
|
||||
const handleDownload = (record) => {
|
||||
message.info(`正在下载 ${record.taskName} 的报告...`)
|
||||
// 这里实现下载逻辑
|
||||
}
|
||||
|
||||
// 归档任务
|
||||
const handleArchive = (record) => {
|
||||
message.success(`任务 ${record.taskName} 已归档`)
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 导出任务
|
||||
const handleExport = (record) => {
|
||||
message.info(`正在导出 ${record.taskName} 的数据...`)
|
||||
// 这里实现导出逻辑
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// 模拟数据
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
taskCode: 'RT001',
|
||||
taskName: '农场设备安全检查',
|
||||
priority: 'high',
|
||||
status: 'completed',
|
||||
assignee: '张三',
|
||||
createdAt: '2024-01-15',
|
||||
completedAt: '2024-01-20',
|
||||
duration: 5,
|
||||
description: '对农场所有设备进行安全检查,确保设备正常运行',
|
||||
completionNotes: '检查完成,发现3处需要维修的设备,已安排维修'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
taskCode: 'RT002',
|
||||
taskName: '环境监测数据审核',
|
||||
priority: 'medium',
|
||||
status: 'archived',
|
||||
assignee: '李四',
|
||||
createdAt: '2024-01-10',
|
||||
completedAt: '2024-01-18',
|
||||
duration: 8,
|
||||
description: '审核上月环境监测数据,确保数据准确性',
|
||||
completionNotes: '数据审核完成,所有数据符合标准'
|
||||
}
|
||||
]
|
||||
|
||||
taskList.value = mockData
|
||||
pagination.total = mockData.length
|
||||
|
||||
// 更新统计数据
|
||||
stats.total = 156
|
||||
stats.thisMonth = 23
|
||||
stats.archived = 89
|
||||
stats.avgDuration = 6.5
|
||||
|
||||
} catch (error) {
|
||||
message.error('获取任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.completed-task-management {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stats-section .ant-card {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.task-detail .ant-descriptions {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -67,24 +67,19 @@
|
||||
size="middle"
|
||||
:scroll="{ x: 1800 }"
|
||||
>
|
||||
<!-- 安装状态列 -->
|
||||
<template #installationStatus="{ record }">
|
||||
<a-tag :color="getInstallationStatusColor(record.installationStatus)">
|
||||
{{ record.installationStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 时间列格式化 -->
|
||||
<template #taskGeneratedTime="{ record }">
|
||||
<span>{{ formatDateTime(record.taskGeneratedTime) }}</span>
|
||||
</template>
|
||||
|
||||
<template #installationCompletedTime="{ record }">
|
||||
<span>{{ formatDateTime(record.installationCompletedTime) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'installationStatus'">
|
||||
<a-tag :color="getInstallationStatusColor(record.installationStatus)">
|
||||
{{ record.installationStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'taskGeneratedTime'">
|
||||
<span>{{ formatDateTime(record.taskGeneratedTime) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'installationCompletedTime'">
|
||||
<span>{{ formatDateTime(record.installationCompletedTime) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
查看
|
||||
@@ -101,6 +96,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -205,29 +201,25 @@ const columns = [
|
||||
title: '安装状态',
|
||||
dataIndex: 'installationStatus',
|
||||
key: 'installationStatus',
|
||||
width: 100,
|
||||
slots: { customRender: 'installationStatus' }
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '生成安装任务时间',
|
||||
dataIndex: 'taskGeneratedTime',
|
||||
key: 'taskGeneratedTime',
|
||||
width: 160,
|
||||
slots: { customRender: 'taskGeneratedTime' }
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '安装完成生效时间',
|
||||
dataIndex: 'installationCompletedTime',
|
||||
key: 'installationCompletedTime',
|
||||
width: 160,
|
||||
slots: { customRender: 'installationCompletedTime' }
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { customRender: 'action' }
|
||||
fixed: 'right'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="insurance-type-management">
|
||||
<a-page-header
|
||||
title="保险类型管理"
|
||||
sub-title="管理系统支持的保险产品类型"
|
||||
title="险种管理"
|
||||
sub-title="管理系统支持的保险产品险种"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增类型
|
||||
新增险种
|
||||
</a-button>
|
||||
</template>
|
||||
</a-page-header>
|
||||
@@ -15,22 +15,36 @@
|
||||
<!-- 搜索区域 -->
|
||||
<a-card style="margin-top: 16px">
|
||||
<a-form layout="inline" :model="searchForm">
|
||||
<a-form-item label="类型名称">
|
||||
<a-form-item label="险种名称">
|
||||
<a-input
|
||||
v-model:value="searchForm.name"
|
||||
placeholder="请输入类型名称"
|
||||
placeholder="请输入险种名称"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-form-item label="适用范围">
|
||||
<a-input
|
||||
v-model:value="searchForm.applicable_scope"
|
||||
placeholder="请输入适用范围"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="服务区域">
|
||||
<a-input
|
||||
v-model:value="searchForm.service_area"
|
||||
placeholder="请输入服务区域"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="在线状态">
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
v-model:value="searchForm.online_status"
|
||||
placeholder="请选择在线状态"
|
||||
style="width: 120px"
|
||||
>
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="active">启用</a-select-option>
|
||||
<a-select-option value="inactive">禁用</a-select-option>
|
||||
<a-select-option :value="true">在线</a-select-option>
|
||||
<a-select-option :value="false">离线</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@@ -46,7 +60,7 @@
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<!-- 类型表格 -->
|
||||
<!-- 险种表格 -->
|
||||
<a-card style="margin-top: 16px">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
@@ -54,28 +68,35 @@
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ x: 1500 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 'active' ? 'green' : 'red'">
|
||||
{{ record.status === 'active' ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
<template v-if="column.key === 'online_status'">
|
||||
<a-switch
|
||||
:checked="record.online_status"
|
||||
@change="(checked) => handleToggleOnlineStatus(record, checked)"
|
||||
checked-children="在线"
|
||||
un-checked-children="离线"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'coverage_type'">
|
||||
<span>{{ getCoverageTypeText(record.coverage_type) }}</span>
|
||||
<template v-else-if="column.key === 'premium_price'">
|
||||
<span>{{ record.premium_price ? `¥${record.premium_price}` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'experience_period'">
|
||||
<span>{{ record.experience_period ? `${record.experience_period}个月` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'insurance_period'">
|
||||
<span>{{ record.insurance_period ? `${record.insurance_period}个月` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'product_time'">
|
||||
<span>{{ record.product_time ? formatDate(record.product_time) : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button
|
||||
size="small"
|
||||
:type="record.status === 'active' ? 'danger' : 'primary'"
|
||||
@click="handleToggleStatus(record)"
|
||||
>
|
||||
{{ record.status === 'active' ? '禁用' : '启用' }}
|
||||
</a-button>
|
||||
<a-button size="small" @click="handleView(record)">详情</a-button>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个保险类型吗?"
|
||||
title="确定要删除这个险种吗?"
|
||||
@confirm="handleDelete(record.id)"
|
||||
>
|
||||
<a-button size="small" danger>删除</a-button>
|
||||
@@ -88,11 +109,13 @@
|
||||
|
||||
<!-- 新增/编辑模态框 -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="600px"
|
||||
@ok="handleModalOk"
|
||||
@cancel="handleModalCancel"
|
||||
v-model:open="modalVisible"
|
||||
:title="isEdit ? '编辑险种' : '新增险种'"
|
||||
:ok-text="isEdit ? '更新' : '创建'"
|
||||
cancel-text="取消"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
width="1000px"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
@@ -102,76 +125,107 @@
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="类型名称" name="name">
|
||||
<a-input v-model:value="formState.name" placeholder="请输入类型名称" />
|
||||
<a-form-item label="险种名称" name="name">
|
||||
<a-input v-model:value="formState.name" placeholder="请输入险种名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="类型代码" name="code">
|
||||
<a-input v-model:value="formState.code" placeholder="请输入类型代码" />
|
||||
<a-form-item label="适用范围" name="applicable_scope">
|
||||
<a-input v-model:value="formState.applicable_scope" placeholder="请输入适用范围" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="描述" name="description">
|
||||
|
||||
<a-form-item label="险种描述" name="description">
|
||||
<a-textarea
|
||||
v-model:value="formState.description"
|
||||
placeholder="请输入类型描述"
|
||||
placeholder="请输入险种描述"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保障类型" name="coverage_type">
|
||||
<a-select v-model:value="formState.coverage_type" placeholder="请选择保障类型">
|
||||
<a-select-option value="life">人寿保险</a-select-option>
|
||||
<a-select-option value="health">健康保险</a-select-option>
|
||||
<a-select-option value="property">财产保险</a-select-option>
|
||||
<a-select-option value="accident">意外保险</a-select-option>
|
||||
<a-select-option value="travel">旅行保险</a-select-option>
|
||||
</a-select>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="体验期" name="experience_period">
|
||||
<a-input-number
|
||||
v-model:value="formState.experience_period"
|
||||
placeholder="请输入体验期(月)"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
addon-after="个月"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="状态" name="status">
|
||||
<a-select v-model:value="formState.status" placeholder="请选择状态">
|
||||
<a-select-option value="active">启用</a-select-option>
|
||||
<a-select-option value="inactive">禁用</a-select-option>
|
||||
</a-select>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保险期间" name="insurance_period">
|
||||
<a-input-number
|
||||
v-model:value="formState.insurance_period"
|
||||
placeholder="请输入保险期间(月)"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
addon-after="个月"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费价格" name="premium_price">
|
||||
<a-input-number
|
||||
v-model:value="formState.premium_price"
|
||||
placeholder="请输入保费价格"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
addon-before="¥"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="最小保额" name="min_coverage">
|
||||
<a-input-number
|
||||
v-model:value="formState.min_coverage"
|
||||
:min="0"
|
||||
:step="1000"
|
||||
style="width: 100%"
|
||||
placeholder="最小保额"
|
||||
/>
|
||||
<a-form-item label="服务区域" name="service_area">
|
||||
<a-input v-model:value="formState.service_area" placeholder="请输入服务区域" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="最大保额" name="max_coverage">
|
||||
<a-input-number
|
||||
v-model:value="formState.max_coverage"
|
||||
:min="0"
|
||||
:step="1000"
|
||||
<a-form-item label="上架时间" name="product_time">
|
||||
<a-date-picker
|
||||
v-model:value="formState.product_time"
|
||||
placeholder="请选择上架时间"
|
||||
style="width: 100%"
|
||||
placeholder="最大保额"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
show-time
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="保费计算规则" name="premium_rules">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="在线状态" name="online_status">
|
||||
<a-switch
|
||||
v-model:checked="formState.online_status"
|
||||
checked-children="在线"
|
||||
un-checked-children="离线"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="排序" name="sort_order">
|
||||
<a-input-number
|
||||
v-model:value="formState.sort_order"
|
||||
placeholder="请输入排序值"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="备注" name="remarks">
|
||||
<a-textarea
|
||||
v-model:value="formState.premium_rules"
|
||||
placeholder="请输入保费计算规则(JSON格式)"
|
||||
v-model:value="formState.remarks"
|
||||
placeholder="请输入备注信息"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -183,6 +237,8 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { insuranceTypeAPI } from '@/utils/api'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
@@ -191,24 +247,30 @@ import {
|
||||
|
||||
const loading = ref(false)
|
||||
const modalVisible = ref(false)
|
||||
const editingId = ref(null)
|
||||
const isEdit = ref(false)
|
||||
const typeList = ref([])
|
||||
const formRef = ref()
|
||||
|
||||
const searchForm = reactive({
|
||||
name: '',
|
||||
status: ''
|
||||
applicable_scope: '',
|
||||
service_area: '',
|
||||
online_status: ''
|
||||
})
|
||||
|
||||
const formState = reactive({
|
||||
id: null,
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
coverage_type: '',
|
||||
status: 'active',
|
||||
min_coverage: null,
|
||||
max_coverage: null,
|
||||
premium_rules: ''
|
||||
applicable_scope: '',
|
||||
experience_period: null,
|
||||
insurance_period: null,
|
||||
premium_price: null,
|
||||
service_area: '',
|
||||
product_time: null,
|
||||
online_status: true,
|
||||
sort_order: 0,
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
@@ -222,49 +284,54 @@ const pagination = reactive({
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '类型名称',
|
||||
title: '险种名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
key: 'name',
|
||||
width: 150,
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title: '类型代码',
|
||||
dataIndex: 'code',
|
||||
key: 'code'
|
||||
title: '适用范围',
|
||||
dataIndex: 'applicable_scope',
|
||||
key: 'applicable_scope',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '保障类型',
|
||||
key: 'coverage_type',
|
||||
dataIndex: 'coverage_type'
|
||||
title: '体验期',
|
||||
dataIndex: 'experience_period',
|
||||
key: 'experience_period',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '最小保额',
|
||||
dataIndex: 'min_coverage',
|
||||
key: 'min_coverage',
|
||||
render: (text) => text ? `¥${text.toLocaleString()}` : '-'
|
||||
title: '保险期间',
|
||||
dataIndex: 'insurance_period',
|
||||
key: 'insurance_period',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '最大保额',
|
||||
dataIndex: 'max_coverage',
|
||||
key: 'max_coverage',
|
||||
render: (text) => text ? `¥${text.toLocaleString()}` : '-'
|
||||
title: '保费价格',
|
||||
dataIndex: 'premium_price',
|
||||
key: 'premium_price',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status'
|
||||
title: '服务区域',
|
||||
dataIndex: 'service_area',
|
||||
key: 'service_area',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
title: '上架时间',
|
||||
dataIndex: 'product_time',
|
||||
key: 'product_time',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: '在线状态',
|
||||
dataIndex: 'online_status',
|
||||
key: 'online_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
@@ -274,25 +341,18 @@ const columns = [
|
||||
]
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入类型名称' }],
|
||||
code: [{ required: true, message: '请输入类型代码' }],
|
||||
coverage_type: [{ required: true, message: '请选择保障类型' }],
|
||||
status: [{ required: true, message: '请选择状态' }]
|
||||
name: [{ required: true, message: '请输入险种名称' }],
|
||||
applicable_scope: [{ required: true, message: '请输入适用范围' }],
|
||||
experience_period: [{ required: true, message: '请输入体验期' }],
|
||||
insurance_period: [{ required: true, message: '请输入保险期间' }],
|
||||
premium_price: [{ required: true, message: '请输入保费价格' }],
|
||||
service_area: [{ required: true, message: '请输入服务区域' }]
|
||||
}
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return editingId.value ? '编辑保险类型' : '新增保险类型'
|
||||
})
|
||||
|
||||
const getCoverageTypeText = (type) => {
|
||||
const types = {
|
||||
life: '人寿保险',
|
||||
health: '健康保险',
|
||||
property: '财产保险',
|
||||
accident: '意外保险',
|
||||
travel: '旅行保险'
|
||||
}
|
||||
return types[type] || type
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '-'
|
||||
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
const loadInsuranceTypes = async () => {
|
||||
@@ -304,41 +364,17 @@ const loadInsuranceTypes = async () => {
|
||||
...searchForm
|
||||
}
|
||||
|
||||
// 这里应该是实际的API调用
|
||||
// const response = await insuranceTypeAPI.getList(params)
|
||||
// typeList.value = response.data.list
|
||||
// pagination.total = response.data.total
|
||||
const response = await insuranceTypeAPI.getList(params)
|
||||
|
||||
// 模拟数据
|
||||
typeList.value = [
|
||||
{
|
||||
id: 1,
|
||||
name: '综合意外险',
|
||||
code: 'ACCIDENT_001',
|
||||
description: '提供全面的意外伤害保障',
|
||||
coverage_type: 'accident',
|
||||
status: 'active',
|
||||
min_coverage: 100000,
|
||||
max_coverage: 500000,
|
||||
premium_rules: '{\"base_rate\": 0.001, \"age_factor\": 1.2}',
|
||||
created_at: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '终身寿险',
|
||||
code: 'LIFE_001',
|
||||
description: '提供终身的人寿保障',
|
||||
coverage_type: 'life',
|
||||
status: 'active',
|
||||
min_coverage: 500000,
|
||||
max_coverage: 2000000,
|
||||
premium_rules: '{\"base_rate\": 0.0005, \"age_factor\": 1.5}',
|
||||
created_at: '2024-01-02 14:30:00'
|
||||
}
|
||||
]
|
||||
pagination.total = 2
|
||||
if (response.status === 'success') {
|
||||
typeList.value = response.data.list
|
||||
pagination.total = response.data.total
|
||||
} else {
|
||||
message.error(response.message || '加载险种列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('加载保险类型列表失败')
|
||||
message.error('加载险种列表失败')
|
||||
console.error('Error loading insurance types:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -350,8 +386,12 @@ const handleSearch = () => {
|
||||
}
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.name = ''
|
||||
searchForm.status = ''
|
||||
Object.assign(searchForm, {
|
||||
name: '',
|
||||
applicable_scope: '',
|
||||
service_area: '',
|
||||
online_status: ''
|
||||
})
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
@@ -362,76 +402,119 @@ const handleTableChange = (pag) => {
|
||||
}
|
||||
|
||||
const showModal = () => {
|
||||
editingId.value = null
|
||||
isEdit.value = false
|
||||
Object.assign(formState, {
|
||||
id: null,
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
coverage_type: '',
|
||||
status: 'active',
|
||||
min_coverage: null,
|
||||
max_coverage: null,
|
||||
premium_rules: ''
|
||||
applicable_scope: '',
|
||||
experience_period: null,
|
||||
insurance_period: null,
|
||||
premium_price: null,
|
||||
service_area: '',
|
||||
product_time: null,
|
||||
online_status: true,
|
||||
sort_order: 0,
|
||||
remarks: ''
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (record) => {
|
||||
editingId.value = record.id
|
||||
isEdit.value = true
|
||||
Object.assign(formState, {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
code: record.code,
|
||||
description: record.description,
|
||||
coverage_type: record.coverage_type,
|
||||
status: record.status,
|
||||
min_coverage: record.min_coverage,
|
||||
max_coverage: record.max_coverage,
|
||||
premium_rules: record.premium_rules
|
||||
applicable_scope: record.applicable_scope,
|
||||
experience_period: record.experience_period,
|
||||
insurance_period: record.insurance_period,
|
||||
premium_price: record.premium_price,
|
||||
service_area: record.service_area,
|
||||
product_time: record.product_time ? dayjs(record.product_time) : null,
|
||||
online_status: record.online_status,
|
||||
sort_order: record.sort_order,
|
||||
remarks: record.remarks
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleModalOk = async () => {
|
||||
const handleView = (record) => {
|
||||
// 查看详情功能
|
||||
message.info('查看详情功能待实现')
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
if (editingId.value) {
|
||||
// await insuranceTypeAPI.update(editingId.value, formState)
|
||||
message.success('保险类型更新成功')
|
||||
const submitData = {
|
||||
...formState,
|
||||
product_time: formState.product_time ? formState.product_time.format('YYYY-MM-DD HH:mm:ss') : null
|
||||
}
|
||||
|
||||
if (isEdit.value) {
|
||||
const response = await insuranceTypeAPI.update(formState.id, submitData)
|
||||
if (response.status === 'success') {
|
||||
message.success('险种更新成功')
|
||||
} else {
|
||||
message.error(response.message || '险种更新失败')
|
||||
}
|
||||
} else {
|
||||
// await insuranceTypeAPI.create(formState)
|
||||
message.success('保险类型创建成功')
|
||||
const response = await insuranceTypeAPI.create(submitData)
|
||||
if (response.status === 'success') {
|
||||
message.success('险种创建成功')
|
||||
} else {
|
||||
message.error(response.message || '险种创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
modalVisible.value = false
|
||||
loadInsuranceTypes()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败', error)
|
||||
if (error.response?.data?.message) {
|
||||
message.error(error.response.data.message)
|
||||
} else {
|
||||
console.log('表单验证失败', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalCancel = () => {
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
}
|
||||
|
||||
const handleToggleStatus = async (record) => {
|
||||
const handleToggleOnlineStatus = async (record, checked) => {
|
||||
try {
|
||||
const newStatus = record.status === 'active' ? 'inactive' : 'active'
|
||||
// await insuranceTypeAPI.update(record.id, { status: newStatus })
|
||||
message.success('状态更新成功')
|
||||
loadInsuranceTypes()
|
||||
const response = await insuranceTypeAPI.updateStatus(record.id, {
|
||||
online_status: checked
|
||||
})
|
||||
|
||||
if (response.status === 'success') {
|
||||
message.success('在线状态更新成功')
|
||||
loadInsuranceTypes()
|
||||
} else {
|
||||
message.error(response.message || '在线状态更新失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('状态更新失败')
|
||||
message.error('在线状态更新失败')
|
||||
console.error('Error updating online status:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
// await insuranceTypeAPI.delete(id)
|
||||
message.success('保险类型删除成功')
|
||||
loadInsuranceTypes()
|
||||
const response = await insuranceTypeAPI.delete(id)
|
||||
|
||||
if (response.status === 'success') {
|
||||
message.success('险种删除成功')
|
||||
loadInsuranceTypes()
|
||||
} else {
|
||||
message.error(response.message || '险种删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('保险类型删除失败')
|
||||
message.error('险种删除失败')
|
||||
console.error('Error deleting insurance type:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
747
insurance_admin-system/src/views/LivestockPolicyManagement.vue
Normal file
747
insurance_admin-system/src/views/LivestockPolicyManagement.vue
Normal file
@@ -0,0 +1,747 @@
|
||||
<template>
|
||||
<div class="livestock-policy-management">
|
||||
<div class="page-header">
|
||||
<h2>生资保单管理</h2>
|
||||
<a-button type="primary" @click="showCreateModal">
|
||||
<PlusOutlined />
|
||||
新建保单
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索筛选区域 -->
|
||||
<div class="search-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.policy_no"
|
||||
placeholder="保单编号"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.farmer_name"
|
||||
placeholder="农户姓名"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.policy_status"
|
||||
placeholder="保单状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="draft">草稿</a-select-option>
|
||||
<a-select-option value="active">生效</a-select-option>
|
||||
<a-select-option value="expired">过期</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.payment_status"
|
||||
placeholder="支付状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="unpaid">未支付</a-select-option>
|
||||
<a-select-option value="paid">已支付</a-select-option>
|
||||
<a-select-option value="partial">部分支付</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16" style="margin-top: 16px">
|
||||
<a-col :span="8">
|
||||
<a-range-picker
|
||||
v-model:value="searchForm.dateRange"
|
||||
placeholder="['开始日期', '结束日期']"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button style="margin-left: 8px" @click="handleReset">
|
||||
重置
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-section">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'policy_status'">
|
||||
<a-tag :color="getStatusColor(record.policy_status)">
|
||||
{{ getStatusText(record.policy_status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'payment_status'">
|
||||
<a-tag :color="getPaymentStatusColor(record.payment_status)">
|
||||
{{ getPaymentStatusText(record.payment_status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'total_value'">
|
||||
¥{{ record.total_value?.toLocaleString() }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'premium_amount'">
|
||||
¥{{ record.premium_amount?.toLocaleString() }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="viewDetail(record)">
|
||||
查看
|
||||
</a-button>
|
||||
<a-button type="link" size="small" @click="editRecord(record)">
|
||||
编辑
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick($event, record)">
|
||||
<a-menu-item key="activate" v-if="record.policy_status === 'draft'">
|
||||
激活保单
|
||||
</a-menu-item>
|
||||
<a-menu-item key="cancel" v-if="['draft', 'active'].includes(record.policy_status)">
|
||||
取消保单
|
||||
</a-menu-item>
|
||||
<a-menu-item key="payment" v-if="record.payment_status === 'unpaid'">
|
||||
标记已支付
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="link" size="small">
|
||||
更多 <DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 创建/编辑保单弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="800px"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="农户姓名" name="farmer_name">
|
||||
<a-input v-model:value="formData.farmer_name" placeholder="请输入农户姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="农户电话" name="farmer_phone">
|
||||
<a-input v-model:value="formData.farmer_phone" placeholder="请输入农户电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="身份证号" name="farmer_id_card">
|
||||
<a-input v-model:value="formData.farmer_id_card" placeholder="请输入身份证号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="牲畜类型" name="livestock_type_id">
|
||||
<a-select
|
||||
v-model:value="formData.livestock_type_id"
|
||||
placeholder="请选择牲畜类型"
|
||||
@change="handleLivestockTypeChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="type in livestockTypes"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
>
|
||||
{{ type.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="农户地址" name="farmer_address">
|
||||
<a-textarea v-model:value="formData.farmer_address" placeholder="请输入农户地址" :rows="2" />
|
||||
</a-form-item>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="牲畜数量" name="livestock_count">
|
||||
<a-input-number
|
||||
v-model:value="formData.livestock_count"
|
||||
placeholder="请输入数量"
|
||||
:min="1"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="单头价值" name="unit_value">
|
||||
<a-input-number
|
||||
v-model:value="formData.unit_value"
|
||||
placeholder="请输入单头价值"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费费率" name="premium_rate">
|
||||
<a-input-number
|
||||
v-model:value="formData.premium_rate"
|
||||
placeholder="请输入费率"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:precision="4"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="总保额">
|
||||
<a-input
|
||||
:value="`¥${formData.total_value?.toLocaleString() || '0'}`"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费金额">
|
||||
<a-input
|
||||
:value="`¥${formData.premium_amount?.toLocaleString() || '0'}`"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保险开始日期" name="start_date">
|
||||
<a-date-picker
|
||||
v-model:value="formData.start_date"
|
||||
placeholder="请选择开始日期"
|
||||
style="width: 100%"
|
||||
@change="handleStartDateChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保险结束日期" name="end_date">
|
||||
<a-date-picker
|
||||
v-model:value="formData.end_date"
|
||||
placeholder="请选择结束日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="备注" name="notes">
|
||||
<a-textarea v-model:value="formData.notes" placeholder="请输入备注" :rows="3" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 详情查看弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="detailVisible"
|
||||
title="保单详情"
|
||||
width="800px"
|
||||
:footer="null"
|
||||
>
|
||||
<a-descriptions :column="2" bordered v-if="currentRecord">
|
||||
<a-descriptions-item label="保单编号">{{ currentRecord.policy_no }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保单状态">
|
||||
<a-tag :color="getStatusColor(currentRecord.policy_status)">
|
||||
{{ getStatusText(currentRecord.policy_status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="农户姓名">{{ currentRecord.farmer_name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="农户电话">{{ currentRecord.farmer_phone }}</a-descriptions-item>
|
||||
<a-descriptions-item label="身份证号">{{ currentRecord.farmer_id_card }}</a-descriptions-item>
|
||||
<a-descriptions-item label="牲畜类型">{{ currentRecord.livestock_type?.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="农户地址" :span="2">{{ currentRecord.farmer_address }}</a-descriptions-item>
|
||||
<a-descriptions-item label="牲畜数量">{{ currentRecord.livestock_count }}头</a-descriptions-item>
|
||||
<a-descriptions-item label="单头价值">¥{{ currentRecord.unit_value?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="总保额">¥{{ currentRecord.total_value?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保费费率">{{ (currentRecord.premium_rate * 100).toFixed(2) }}%</a-descriptions-item>
|
||||
<a-descriptions-item label="保费金额">¥{{ currentRecord.premium_amount?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="支付状态">
|
||||
<a-tag :color="getPaymentStatusColor(currentRecord.payment_status)">
|
||||
{{ getPaymentStatusText(currentRecord.payment_status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="保险开始日期">{{ currentRecord.start_date }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保险结束日期">{{ currentRecord.end_date }}</a-descriptions-item>
|
||||
<a-descriptions-item label="支付时间">{{ currentRecord.payment_date || '未支付' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">{{ formatDateTime(currentRecord.created_at) }}</a-descriptions-item>
|
||||
<a-descriptions-item label="备注" :span="2">{{ currentRecord.notes || '无' }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { livestockPolicyApi } from '@/utils/api'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const tableData = ref([])
|
||||
const modalVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const currentRecord = ref(null)
|
||||
const formRef = ref()
|
||||
const livestockTypes = ref([])
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
policy_no: '',
|
||||
farmer_name: '',
|
||||
policy_status: undefined,
|
||||
payment_status: undefined,
|
||||
dateRange: []
|
||||
})
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
farmer_name: '',
|
||||
farmer_phone: '',
|
||||
farmer_id_card: '',
|
||||
farmer_address: '',
|
||||
livestock_type_id: undefined,
|
||||
livestock_count: undefined,
|
||||
unit_value: undefined,
|
||||
premium_rate: undefined,
|
||||
total_value: 0,
|
||||
premium_amount: 0,
|
||||
start_date: undefined,
|
||||
end_date: undefined,
|
||||
notes: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
farmer_name: [{ required: true, message: '请输入农户姓名' }],
|
||||
farmer_phone: [
|
||||
{ required: true, message: '请输入农户电话' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
],
|
||||
farmer_id_card: [
|
||||
{ required: true, message: '请输入身份证号' },
|
||||
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, message: '请输入正确的身份证号' }
|
||||
],
|
||||
farmer_address: [{ required: true, message: '请输入农户地址' }],
|
||||
livestock_type_id: [{ required: true, message: '请选择牲畜类型' }],
|
||||
livestock_count: [{ required: true, message: '请输入牲畜数量' }],
|
||||
unit_value: [{ required: true, message: '请输入单头价值' }],
|
||||
premium_rate: [{ required: true, message: '请输入保费费率' }],
|
||||
start_date: [{ required: true, message: '请选择保险开始日期' }],
|
||||
end_date: [{ required: true, message: '请选择保险结束日期' }]
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const modalTitle = computed(() => {
|
||||
return currentRecord.value ? '编辑保单' : '新建保单'
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '保单编号',
|
||||
dataIndex: 'policy_no',
|
||||
key: 'policy_no',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '农户姓名',
|
||||
dataIndex: 'farmer_name',
|
||||
key: 'farmer_name',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '农户电话',
|
||||
dataIndex: 'farmer_phone',
|
||||
key: 'farmer_phone',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '牲畜类型',
|
||||
dataIndex: ['livestock_type', 'name'],
|
||||
key: 'livestock_type',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'livestock_count',
|
||||
key: 'livestock_count',
|
||||
width: 80,
|
||||
customRender: ({ text }) => `${text}头`
|
||||
},
|
||||
{
|
||||
title: '总保额',
|
||||
dataIndex: 'total_value',
|
||||
key: 'total_value',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '保费金额',
|
||||
dataIndex: 'premium_amount',
|
||||
key: 'premium_amount',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '保单状态',
|
||||
dataIndex: 'policy_status',
|
||||
key: 'policy_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '支付状态',
|
||||
dataIndex: 'payment_status',
|
||||
key: 'payment_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '保险期间',
|
||||
key: 'insurance_period',
|
||||
width: 180,
|
||||
customRender: ({ record }) => `${record.start_date} 至 ${record.end_date}`
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
// 方法
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
limit: pagination.pageSize,
|
||||
...searchForm
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||
params.start_date = dayjs(searchForm.dateRange[0]).format('YYYY-MM-DD')
|
||||
params.end_date = dayjs(searchForm.dateRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
delete params.dateRange
|
||||
|
||||
const response = await livestockPolicyApi.getList(params)
|
||||
if (response.code === 200) {
|
||||
tableData.value = response.data.list
|
||||
pagination.total = response.data.total
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('获取数据失败')
|
||||
console.error('获取数据失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchLivestockTypes = async () => {
|
||||
try {
|
||||
const response = await livestockPolicyApi.getLivestockTypes()
|
||||
if (response.code === 200) {
|
||||
livestockTypes.value = response.data.list
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
Object.keys(searchForm).forEach(key => {
|
||||
if (key === 'dateRange') {
|
||||
searchForm[key] = []
|
||||
} else {
|
||||
searchForm[key] = key.includes('status') ? undefined : ''
|
||||
}
|
||||
})
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleTableChange = (pag) => {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const showCreateModal = () => {
|
||||
currentRecord.value = null
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const viewDetail = (record) => {
|
||||
currentRecord.value = record
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const editRecord = (record) => {
|
||||
currentRecord.value = record
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key === 'start_date' || key === 'end_date') {
|
||||
formData[key] = record[key] ? dayjs(record[key]) : undefined
|
||||
} else {
|
||||
formData[key] = record[key]
|
||||
}
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleMenuClick = async ({ key }, record) => {
|
||||
try {
|
||||
switch (key) {
|
||||
case 'activate':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
policy_status: 'active'
|
||||
})
|
||||
message.success('保单已激活')
|
||||
fetchData()
|
||||
break
|
||||
case 'cancel':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
policy_status: 'cancelled'
|
||||
})
|
||||
message.success('保单已取消')
|
||||
fetchData()
|
||||
break
|
||||
case 'payment':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
payment_status: 'paid'
|
||||
})
|
||||
message.success('支付状态已更新')
|
||||
fetchData()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('操作失败')
|
||||
console.error('操作失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
const submitData = { ...formData }
|
||||
if (submitData.start_date) {
|
||||
submitData.start_date = dayjs(submitData.start_date).format('YYYY-MM-DD')
|
||||
}
|
||||
if (submitData.end_date) {
|
||||
submitData.end_date = dayjs(submitData.end_date).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
if (currentRecord.value) {
|
||||
await livestockPolicyApi.update(currentRecord.value.id, submitData)
|
||||
message.success('保单更新成功')
|
||||
} else {
|
||||
await livestockPolicyApi.create(submitData)
|
||||
message.success('保单创建成功')
|
||||
}
|
||||
|
||||
modalVisible.value = false
|
||||
fetchData()
|
||||
} catch (error) {
|
||||
if (error.errorFields) {
|
||||
message.error('请检查表单填写')
|
||||
} else {
|
||||
message.error('操作失败')
|
||||
console.error('操作失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key === 'total_value' || key === 'premium_amount') {
|
||||
formData[key] = 0
|
||||
} else {
|
||||
formData[key] = undefined
|
||||
}
|
||||
})
|
||||
formData.notes = ''
|
||||
}
|
||||
|
||||
const handleLivestockTypeChange = (value) => {
|
||||
const selectedType = livestockTypes.value.find(type => type.id === value)
|
||||
if (selectedType) {
|
||||
formData.unit_value = selectedType.base_value
|
||||
formData.premium_rate = selectedType.premium_rate
|
||||
calculateAmounts()
|
||||
}
|
||||
}
|
||||
|
||||
const calculateAmounts = () => {
|
||||
if (formData.livestock_count && formData.unit_value) {
|
||||
formData.total_value = formData.livestock_count * formData.unit_value
|
||||
}
|
||||
if (formData.total_value && formData.premium_rate) {
|
||||
formData.premium_amount = formData.total_value * formData.premium_rate
|
||||
}
|
||||
}
|
||||
|
||||
const handleStartDateChange = (date) => {
|
||||
if (date) {
|
||||
// 自动设置结束日期为一年后
|
||||
formData.end_date = dayjs(date).add(1, 'year').subtract(1, 'day')
|
||||
}
|
||||
}
|
||||
|
||||
// 状态相关方法
|
||||
const getStatusColor = (status) => {
|
||||
const colors = {
|
||||
draft: 'default',
|
||||
active: 'success',
|
||||
expired: 'warning',
|
||||
cancelled: 'error'
|
||||
}
|
||||
return colors[status] || 'default'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const texts = {
|
||||
draft: '草稿',
|
||||
active: '生效',
|
||||
expired: '过期',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const getPaymentStatusColor = (status) => {
|
||||
const colors = {
|
||||
unpaid: 'error',
|
||||
paid: 'success',
|
||||
partial: 'warning'
|
||||
}
|
||||
return colors[status] || 'default'
|
||||
}
|
||||
|
||||
const getPaymentStatusText = (status) => {
|
||||
const texts = {
|
||||
unpaid: '未支付',
|
||||
paid: '已支付',
|
||||
partial: '部分支付'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const formatDateTime = (dateTime) => {
|
||||
return dateTime ? dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss') : ''
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
fetchLivestockTypes()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.livestock-policy-management {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.table-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:deep(.ant-table) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.ant-table-thead > tr > th) {
|
||||
background: #fafafa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-descriptions-item-label) {
|
||||
font-weight: 600;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
@@ -64,34 +64,18 @@
|
||||
size="middle"
|
||||
:scroll="{ x: 1500 }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 任务类型列 -->
|
||||
<template #taskType="{ record }">
|
||||
<a-tag :color="getTaskTypeColor(record.taskType)">
|
||||
{{ getTaskTypeText(record.taskType) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 优先级列 -->
|
||||
<template #priority="{ record }">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ getPriorityText(record.priority) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 金额列 -->
|
||||
<template #amount="{ record }">
|
||||
<span>{{ formatAmount(record.applicableAmount) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'taskStatus'">
|
||||
<a-tag :color="getStatusColor(record.taskStatus)">
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
查看
|
||||
@@ -108,6 +92,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -365,22 +350,19 @@ export default {
|
||||
title: '任务状态',
|
||||
dataIndex: 'taskStatus',
|
||||
key: 'taskStatus',
|
||||
width: 100,
|
||||
slots: { customRender: 'status' }
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
key: 'priority',
|
||||
width: 80,
|
||||
slots: { customRender: 'priority' }
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
slots: { customRender: 'action' }
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -54,26 +54,21 @@
|
||||
row-key="id"
|
||||
size="middle"
|
||||
>
|
||||
<!-- 任务状态列 -->
|
||||
<template #taskStatus="{ record }">
|
||||
<a-tag
|
||||
:color="getStatusColor(record.taskStatus)"
|
||||
>
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 优先级列 -->
|
||||
<template #priority="{ record }">
|
||||
<a-tag
|
||||
:color="getPriorityColor(record.priority)"
|
||||
>
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'applicableSupplies'">
|
||||
{{ formatApplicableSupplies(record.applicableSupplies) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'taskStatus'">
|
||||
<a-tag :color="getStatusColor(record.taskStatus)">
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="link"
|
||||
@@ -102,6 +97,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -378,15 +374,7 @@ const columns = [
|
||||
title: '适用生资',
|
||||
dataIndex: 'applicableSupplies',
|
||||
key: 'applicableSupplies',
|
||||
width: 120,
|
||||
customRender: ({ record }) => {
|
||||
try {
|
||||
const supplies = JSON.parse(record.applicableSupplies || '[]')
|
||||
return Array.isArray(supplies) ? supplies.join(', ') : record.applicableSupplies
|
||||
} catch {
|
||||
return record.applicableSupplies || '-'
|
||||
}
|
||||
}
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '监管生资数量',
|
||||
@@ -397,7 +385,6 @@ const columns = [
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
width: 200,
|
||||
fixed: 'right'
|
||||
}
|
||||
@@ -646,6 +633,16 @@ const downloadTemplate = () => {
|
||||
message.info('模板下载功能开发中')
|
||||
}
|
||||
|
||||
// 格式化适用生资
|
||||
const formatApplicableSupplies = (supplies) => {
|
||||
try {
|
||||
const parsed = JSON.parse(supplies || '[]')
|
||||
return Array.isArray(parsed) ? parsed.join(', ') : supplies || ''
|
||||
} catch {
|
||||
return supplies || ''
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
|
||||
Reference in New Issue
Block a user