部署保险端项目和大屏

This commit is contained in:
xuqiuyun
2025-09-30 17:41:21 +08:00
parent 4e8d4dc92d
commit e9be0f9d98
767 changed files with 50062 additions and 24144 deletions

Binary file not shown.

View File

@@ -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',

View File

@@ -4,7 +4,7 @@ import router from '@/router'
// API基础配置
const API_CONFIG = {
baseURL: 'http://localhost:3000/api',
baseURL: '/insurance/api',
timeout: 10000
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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('用户删除失败')
}
}
}

View File

@@ -4,7 +4,7 @@ const testFrontendAPI = async () => {
console.log('开始测试前端API调用...');
// 模拟前端API调用
const response = await fetch('http://localhost:3000/api/policies?page=1&pageSize=10', {
const response = await fetch('http://49.51.70.206:3000/api/policies?page=1&pageSize=10', {
method: 'GET',
headers: {
'Content-Type': 'application/json',

View File

@@ -6,7 +6,7 @@ async function testApiConnection() {
console.log('开始测试前后端通信...');
// 测试后端健康检查接口
const backendResponse = await axios.get('http://localhost:3000/health', {
const backendResponse = await axios.get('http://49.51.70.206:3000/health', {
timeout: 5000
});
console.log('✅ 后端健康检查接口正常:', backendResponse.data);

View File

@@ -5,7 +5,7 @@ async function testCurrentLogs() {
console.log('🔐 先登录获取token...');
// 登录获取token
const loginResponse = await axios.post('http://localhost:3000/api/auth/login', {
const loginResponse = await axios.post('http://49.51.70.206:3000/api/auth/login', {
username: 'admin',
password: '123456'
});
@@ -16,7 +16,7 @@ async function testCurrentLogs() {
console.log('🔍 测试当前日志API...');
// 测试系统日志API
const logsResponse = await axios.get('http://localhost:3000/api/system/logs', {
const logsResponse = await axios.get('http://49.51.70.206:3000/api/system/logs', {
headers: {
'Authorization': `Bearer ${token}`
},
@@ -32,7 +32,7 @@ async function testCurrentLogs() {
console.log('📄 当前页日志数量:', logsResponse.data.data?.logs?.length || 0);
// 测试仪表板统计API
const statsResponse = await axios.get('http://localhost:3000/api/dashboard/stats', {
const statsResponse = await axios.get('http://49.51.70.206:3000/api/dashboard/stats', {
headers: {
'Authorization': `Bearer ${token}`
}
@@ -41,7 +41,7 @@ async function testCurrentLogs() {
console.log('📋 统计数据:', JSON.stringify(statsResponse.data, null, 2));
// 测试最近活动API
const activitiesResponse = await axios.get('http://localhost:3000/api/dashboard/recent-activities', {
const activitiesResponse = await axios.get('http://49.51.70.206:3000/api/dashboard/recent-activities', {
headers: {
'Authorization': `Bearer ${token}`
}

View File

@@ -19,7 +19,7 @@ export default defineConfig(({ mode }) => {
historyApiFallback: true,
proxy: {
'/api': {
target: env.VITE_API_BASE_URL || 'http://localhost:3000',
target: env.VITE_API_BASE_URL || 'https://ad.ningmuyun.com/insurace/',
changeOrigin: true,
rewrite: (path) => path
}