部署保险端项目和大屏
This commit is contained in:
@@ -102,7 +102,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/auth/refresh', {
|
||||
const response = await fetch('/insurance/api/auth/refresh', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -4,7 +4,7 @@ import router from '@/router'
|
||||
|
||||
// API基础配置
|
||||
const API_CONFIG = {
|
||||
baseURL: 'http://localhost:3000/api',
|
||||
baseURL: '/insurance/api',
|
||||
timeout: 10000
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
allow-clear
|
||||
style="width: 120px"
|
||||
>
|
||||
<a-select-option value="individual">个人参保</a-select-option>
|
||||
<a-select-option value="enterprise">企业参保</a-select-option>
|
||||
<a-select-option value="养殖">养殖</a-select-option>
|
||||
<a-select-option value="畜牧养殖">畜牧养殖</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="申请状态">
|
||||
@@ -100,7 +100,7 @@
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'insurance_category'">
|
||||
{{ record.insurance_category === 'individual' ? '个人' : '企业' }}
|
||||
{{ record.insurance_category }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'insurance_amount'">
|
||||
¥{{ Number(record.insurance_amount).toLocaleString() }}
|
||||
@@ -173,7 +173,7 @@
|
||||
{{ selectedApplication.application_no }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="参保类型">
|
||||
{{ selectedApplication.insurance_category === 'individual' ? '个人参保' : '企业参保' }}
|
||||
{{ selectedApplication.insurance_category }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="参保险种">
|
||||
{{ selectedApplication.insurance_type?.name || selectedApplication.insurance_type_id }}
|
||||
@@ -273,8 +273,8 @@
|
||||
>
|
||||
<a-form-item label="参保类型" name="insurance_category" :rules="[{ required: true, message: '请选择参保类型' }]">
|
||||
<a-radio-group v-model:value="createForm.insurance_category">
|
||||
<a-radio value="individual">个人参保</a-radio>
|
||||
<a-radio value="enterprise">企业参保</a-radio>
|
||||
<a-radio value="养殖">养殖</a-radio>
|
||||
<a-radio value="畜牧养殖">畜牧养殖</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="参保险种" name="insurance_type_id" :rules="[{ required: true, message: '请选择参保险种' }]">
|
||||
@@ -466,11 +466,26 @@ const columns = [
|
||||
const loadApplications = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 构建搜索参数,映射前端字段名到后端期望的字段名
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
limit: pagination.pageSize,
|
||||
...searchForm
|
||||
// 字段名映射
|
||||
applicationNumber: searchForm.application_no || undefined,
|
||||
applicantName: searchForm.customer_name || undefined,
|
||||
insuranceType: searchForm.insuranceType || undefined,
|
||||
insuranceCategory: searchForm.insuranceCategory || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
console.log('搜索参数:', params)
|
||||
const response = await applicationAPI.getList(params)
|
||||
console.log('申请列表API响应:', response)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<!-- 图表区域 -->
|
||||
<a-row :gutter="16" style="margin-top: 24px">
|
||||
<a-col :span="12">
|
||||
<a-card title="保险申请趋势" :bordered="false">
|
||||
<a-card title="申请状态分布" :bordered="false">
|
||||
<div style="height: 300px">
|
||||
<v-chart
|
||||
:option="applicationTrendOption"
|
||||
@@ -71,7 +71,7 @@
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-card title="保单状态分布" :bordered="false">
|
||||
<a-card title="险种分布" :bordered="false">
|
||||
<div style="height: 300px">
|
||||
<v-chart
|
||||
:option="policyDistributionOption"
|
||||
@@ -122,7 +122,7 @@ import {
|
||||
BarChartOutlined,
|
||||
PieChartOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { dashboardAPI } from '@/utils/api'
|
||||
import { dashboardAPI, policyAPI, insuranceTypeAPI, applicationAPI } from '@/utils/api'
|
||||
import { message } from 'ant-design-vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { use } from 'echarts/core'
|
||||
@@ -279,169 +279,66 @@ const loadDashboardData = async () => {
|
||||
created_at: new Date(Date.now() - 10800000).toISOString()
|
||||
}
|
||||
]
|
||||
|
||||
// 加载图表数据
|
||||
await loadChartData()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 无论统计数据和活动数据是否成功,都要加载图表数据
|
||||
await loadChartData()
|
||||
}
|
||||
|
||||
// 加载图表数据
|
||||
const loadChartData = async () => {
|
||||
console.log('开始加载图表数据...')
|
||||
chartLoading.value = true
|
||||
|
||||
// 独立加载申请状态分布数据
|
||||
try {
|
||||
// 获取申请趋势数据
|
||||
console.log('正在获取申请趋势数据...')
|
||||
const applicationTrendResponse = await dashboardAPI.getChartData({
|
||||
type: 'applications',
|
||||
period: '7d'
|
||||
console.log('正在获取申请状态分布数据...')
|
||||
const applicationsResponse = await applicationAPI.getList({
|
||||
page: 1,
|
||||
limit: 100 // 获取所有申请用于状态统计
|
||||
})
|
||||
|
||||
console.log('申请趋势响应:', applicationTrendResponse)
|
||||
if (applicationTrendResponse.data && applicationTrendResponse.data.status === 'success') {
|
||||
console.log('设置申请趋势图表,数据:', applicationTrendResponse.data.data)
|
||||
setupApplicationTrendChart(applicationTrendResponse.data.data)
|
||||
console.log('申请状态分布响应:', applicationsResponse)
|
||||
if (applicationsResponse.data && applicationsResponse.data.status === 'success') {
|
||||
console.log('设置申请状态分布图表,数据:', applicationsResponse.data.data)
|
||||
setupApplicationStatusChart(applicationsResponse.data.data)
|
||||
} else {
|
||||
console.log('申请趋势响应格式错误:', applicationTrendResponse)
|
||||
}
|
||||
|
||||
// 获取保单状态分布数据
|
||||
console.log('正在获取保单状态分布数据...')
|
||||
const policyDistributionResponse = await dashboardAPI.getChartData({
|
||||
type: 'policy_status'
|
||||
})
|
||||
|
||||
console.log('保单状态分布响应:', policyDistributionResponse)
|
||||
if (policyDistributionResponse.data && policyDistributionResponse.data.status === 'success') {
|
||||
console.log('设置保单状态分布图表,数据:', policyDistributionResponse.data.data)
|
||||
setupPolicyDistributionChart(policyDistributionResponse.data.data)
|
||||
} else {
|
||||
console.log('保单状态分布响应格式错误:', policyDistributionResponse)
|
||||
console.log('申请状态分布响应格式错误:', applicationsResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载图表数据失败:', error)
|
||||
console.error('错误详情:', error.response?.data || error.message)
|
||||
message.error('加载图表数据失败')
|
||||
} finally {
|
||||
chartLoading.value = false
|
||||
console.log('图表数据加载完成')
|
||||
console.error('加载申请状态分布数据失败:', error)
|
||||
}
|
||||
|
||||
// 独立加载险种分布数据
|
||||
try {
|
||||
console.log('正在获取险种分布数据...')
|
||||
const insuranceTypesResponse = await insuranceTypeAPI.getList({
|
||||
page: 1,
|
||||
pageSize: 100, // 获取所有险种用于分布统计
|
||||
name: ''
|
||||
})
|
||||
|
||||
console.log('险种分布响应:', insuranceTypesResponse)
|
||||
if (insuranceTypesResponse.data && insuranceTypesResponse.data.status === 'success') {
|
||||
console.log('设置险种分布图表,数据:', insuranceTypesResponse.data.data)
|
||||
setupInsuranceTypeDistributionChart(insuranceTypesResponse.data.data)
|
||||
} else {
|
||||
console.log('险种分布响应格式错误:', insuranceTypesResponse)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载险种分布数据失败:', error)
|
||||
message.error('加载险种分布数据失败')
|
||||
}
|
||||
|
||||
chartLoading.value = false
|
||||
console.log('图表数据加载完成')
|
||||
}
|
||||
|
||||
// 设置申请趋势图表
|
||||
const setupApplicationTrendChart = (data) => {
|
||||
console.log('设置申请趋势图表,接收数据:', data)
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('申请趋势数据为空或格式错误')
|
||||
return
|
||||
}
|
||||
|
||||
const dates = data.map(item => item.date)
|
||||
const counts = data.map(item => item.value || item.count)
|
||||
console.log('处理后的数据 - 日期:', dates, '数量:', counts)
|
||||
|
||||
applicationTrendOption.value = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
borderColor: '#ccc',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#d9d9d9'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#d9d9d9'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666'
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '申请数量',
|
||||
type: 'bar',
|
||||
data: counts,
|
||||
barWidth: '60%',
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: '#1890ff'
|
||||
}, {
|
||||
offset: 1, color: '#40a9ff'
|
||||
}]
|
||||
},
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#0050b3'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 设置保单状态分布图表
|
||||
const setupPolicyDistributionChart = (data) => {
|
||||
console.log('设置保单状态分布图表,接收数据:', data)
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('保单状态分布数据为空或格式错误')
|
||||
return
|
||||
}
|
||||
|
||||
const chartData = data.map(item => ({
|
||||
name: getPolicyStatusLabel(item.status),
|
||||
value: item.count
|
||||
}))
|
||||
console.log('处理后的图表数据:', chartData)
|
||||
|
||||
policyDistributionOption.value = {
|
||||
// 创建饼图配置的通用函数
|
||||
const createPieChartOption = (seriesName, data) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)',
|
||||
@@ -462,9 +359,10 @@ const setupPolicyDistributionChart = (data) => {
|
||||
},
|
||||
itemGap: 15
|
||||
},
|
||||
color: ['#1890ff', '#52c41a', '#faad14', '#f5222d', '#722ed1', '#13c2c2', '#eb2f96', '#fa8c16'],
|
||||
series: [
|
||||
{
|
||||
name: '保单数量',
|
||||
name: seriesName,
|
||||
type: 'pie',
|
||||
radius: ['45%', '75%'],
|
||||
center: ['65%', '50%'],
|
||||
@@ -489,7 +387,7 @@ const setupPolicyDistributionChart = (data) => {
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: chartData,
|
||||
data: data,
|
||||
itemStyle: {
|
||||
borderRadius: 8,
|
||||
borderColor: '#fff',
|
||||
@@ -500,6 +398,78 @@ const setupPolicyDistributionChart = (data) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置申请状态分布图表
|
||||
const setupApplicationStatusChart = (data) => {
|
||||
console.log('设置申请状态分布图表,接收数据:', data)
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('申请状态分布数据为空或格式错误,使用默认数据')
|
||||
// 使用默认数据
|
||||
const defaultData = [
|
||||
{ name: '待审核', value: 0 },
|
||||
{ name: '已通过', value: 0 },
|
||||
{ name: '已拒绝', value: 0 }
|
||||
]
|
||||
applicationTrendOption.value = createPieChartOption('申请数量', defaultData)
|
||||
return
|
||||
}
|
||||
|
||||
// 统计各状态的申请数量
|
||||
const statusCounts = {}
|
||||
data.forEach(app => {
|
||||
const status = app.status || 'unknown'
|
||||
statusCounts[status] = (statusCounts[status] || 0) + 1
|
||||
})
|
||||
|
||||
// 转换为图表数据格式
|
||||
const chartData = Object.entries(statusCounts).map(([status, count]) => ({
|
||||
name: getApplicationStatusLabel(status),
|
||||
value: count
|
||||
}))
|
||||
|
||||
console.log('处理后的申请状态分布图表数据:', chartData)
|
||||
|
||||
applicationTrendOption.value = createPieChartOption('申请数量', chartData)
|
||||
}
|
||||
|
||||
// 设置险种分布图表
|
||||
const setupInsuranceTypeDistributionChart = (data) => {
|
||||
console.log('设置险种分布图表,接收数据:', data)
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('险种分布数据为空或格式错误,使用默认数据')
|
||||
// 使用默认数据
|
||||
const defaultData = [
|
||||
{ name: '人寿保险', value: 0 },
|
||||
{ name: '健康保险', value: 0 },
|
||||
{ name: '财产保险', value: 0 }
|
||||
]
|
||||
policyDistributionOption.value = createPieChartOption('险种数量', defaultData)
|
||||
return
|
||||
}
|
||||
|
||||
// 统计每个险种的数量(这里假设每个险种都有相同的权重,实际项目中可能需要根据保单数量统计)
|
||||
const chartData = data.map((item, index) => ({
|
||||
name: item.name || `险种${index + 1}`,
|
||||
value: 1 // 每个险种计数为1,实际项目中可以根据保单数量统计
|
||||
}))
|
||||
|
||||
console.log('处理后的险种分布图表数据:', chartData)
|
||||
|
||||
policyDistributionOption.value = createPieChartOption('险种数量', chartData)
|
||||
}
|
||||
|
||||
// 获取申请状态标签
|
||||
const getApplicationStatusLabel = (status) => {
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'initial_approved': '初审通过',
|
||||
'under_review': '复审中',
|
||||
'approved': '已通过',
|
||||
'rejected': '已拒绝',
|
||||
'unknown': '未知状态'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 获取保单状态标签
|
||||
const getPolicyStatusLabel = (status) => {
|
||||
const statusMap = {
|
||||
@@ -518,29 +488,8 @@ onMounted(() => {
|
||||
policyDistributionOption: policyDistributionOption.value
|
||||
})
|
||||
|
||||
// 设置测试图表数据
|
||||
console.log('设置测试图表数据...')
|
||||
applicationTrendOption.value = {
|
||||
title: { text: '测试图表' },
|
||||
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
|
||||
yAxis: { type: 'value' },
|
||||
series: [{ data: [120, 200, 150], type: 'bar' }]
|
||||
}
|
||||
|
||||
policyDistributionOption.value = {
|
||||
title: { text: '测试饼图' },
|
||||
series: [{
|
||||
type: 'pie',
|
||||
data: [
|
||||
{ value: 1048, name: '搜索引擎' },
|
||||
{ value: 735, name: '直接访问' },
|
||||
{ value: 580, name: '邮件营销' }
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
console.log('测试图表设置完成')
|
||||
|
||||
// 直接加载动态数据,不再设置测试数据
|
||||
console.log('开始加载动态图表数据...')
|
||||
loadDashboardData()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -259,12 +259,22 @@ const getStatusText = (status) => {
|
||||
const loadUsers = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 构建搜索参数,映射前端字段名到后端期望的字段名
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchForm
|
||||
limit: pagination.pageSize,
|
||||
// 字段名映射
|
||||
search: searchForm.username || undefined,
|
||||
status: searchForm.status || undefined
|
||||
}
|
||||
|
||||
// 过滤掉undefined值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
console.log('用户管理API请求参数:', params)
|
||||
const response = await userAPI.getList(params)
|
||||
console.log('用户管理API响应:', response)
|
||||
@@ -280,6 +290,7 @@ const loadUsers = async () => {
|
||||
userList.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error)
|
||||
message.error('加载用户列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -336,17 +347,27 @@ const handleModalOk = async () => {
|
||||
await formRef.value.validate()
|
||||
|
||||
if (editingId.value) {
|
||||
// await userAPI.update(editingId.value, formState)
|
||||
// 编辑用户 - 不包含密码字段
|
||||
const updateData = { ...formState }
|
||||
delete updateData.password // 编辑时不更新密码
|
||||
|
||||
await userAPI.update(editingId.value, updateData)
|
||||
message.success('用户更新成功')
|
||||
} else {
|
||||
// await userAPI.create(formState)
|
||||
// 新增用户
|
||||
await userAPI.create(formState)
|
||||
message.success('用户创建成功')
|
||||
}
|
||||
|
||||
modalVisible.value = false
|
||||
loadUsers()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败', error)
|
||||
console.error('操作失败:', error)
|
||||
if (error.response?.data?.message) {
|
||||
message.error(error.response.data.message)
|
||||
} else {
|
||||
message.error(editingId.value ? '用户更新失败' : '用户创建失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,21 +378,31 @@ const handleModalCancel = () => {
|
||||
const handleToggleStatus = async (record) => {
|
||||
try {
|
||||
const newStatus = record.status === 'active' ? 'inactive' : 'active'
|
||||
// await userAPI.update(record.id, { status: newStatus })
|
||||
message.success('状态更新成功')
|
||||
await userAPI.update(record.id, { status: newStatus })
|
||||
message.success(`用户已${newStatus === 'active' ? '启用' : '禁用'}`)
|
||||
loadUsers()
|
||||
} catch (error) {
|
||||
message.error('状态更新失败')
|
||||
console.error('状态更新失败:', error)
|
||||
if (error.response?.data?.message) {
|
||||
message.error(error.response.data.message)
|
||||
} else {
|
||||
message.error('状态更新失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
// await userAPI.delete(id)
|
||||
await userAPI.delete(id)
|
||||
message.success('用户删除成功')
|
||||
loadUsers()
|
||||
} catch (error) {
|
||||
message.error('用户删除失败')
|
||||
console.error('删除用户失败:', error)
|
||||
if (error.response?.data?.message) {
|
||||
message.error(error.response.data.message)
|
||||
} else {
|
||||
message.error('用户删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user