完善小程序

This commit is contained in:
xuqiuyun
2025-10-09 17:59:26 +08:00
parent 74b2edb510
commit f88383425f
46 changed files with 3477 additions and 185 deletions

View File

@@ -0,0 +1,156 @@
/**
* 环境配置文件
* 统一管理所有API URL和环境变量
*/
// 获取环境变量
const env = import.meta.env.MODE || 'development'
// 环境配置
const envConfig = {
development: {
// 开发环境配置
baseURL: '/insurance/api',
timeout: 30000,
// 其他开发环境配置
wsURL: 'ws://localhost:3000',
},
production: {
// 生产环境配置
baseURL: '/insurance/api',
timeout: 10000,
// 其他生产环境配置
wsURL: 'wss://production-domain.com',
},
test: {
// 测试环境配置
baseURL: '/insurance/api',
timeout: 15000,
// 其他测试环境配置
wsURL: 'ws://test-server:3000',
}
}
// 导出当前环境配置
export default envConfig[env] || envConfig.development
// 导出特定配置项,方便按需引入
export const { baseURL, timeout, wsURL } = envConfig[env] || envConfig.development
// API端点配置
export const API_ENDPOINTS = {
// 认证相关
AUTH: {
LOGIN: '/auth/login',
LOGOUT: '/auth/logout',
REFRESH: '/auth/refresh',
PROFILE: '/users/profile'
},
// 用户管理
USERS: {
LIST: '/users',
CREATE: '/users',
UPDATE: (id) => `/users/${id}`,
DELETE: (id) => `/users/${id}`,
EXPORT: '/users/export'
},
// 保险类型管理
INSURANCE_TYPES: {
LIST: '/insurance-types',
CREATE: '/insurance-types',
UPDATE: (id) => `/insurance-types/${id}`,
DELETE: (id) => `/insurance-types/${id}`,
EXPORT: '/insurance-types/export'
},
// 申请管理
APPLICATIONS: {
LIST: '/insurance/applications',
DETAIL: (id) => `/insurance/applications/${id}`,
CREATE: '/insurance/applications',
UPDATE: (id) => `/insurance/applications/${id}`,
REVIEW: (id) => `/insurance/applications/${id}/review`,
EXPORT: '/insurance/applications/export'
},
// 保单管理
POLICIES: {
LIST: '/policies',
DETAIL: (id) => `/policies/${id}`,
CREATE: '/policies',
UPDATE: (id) => `/policies/${id}`,
EXPORT: '/policies/export'
},
// 理赔管理
CLAIMS: {
LIST: '/claims',
DETAIL: (id) => `/claims/${id}`,
UPDATE_STATUS: (id) => `/claims/${id}/status`,
EXPORT: '/claims/export'
},
// 设备预警
DEVICE_ALERTS: {
LIST: '/device-alerts',
DETAIL: (id) => `/device-alerts/${id}`,
STATS: '/device-alerts/stats',
MARK_READ: (id) => `/device-alerts/${id}/read`,
EXPORT: '/device-alerts/export'
},
// 监管任务
SUPERVISION_TASKS: {
LIST: '/supervision-tasks',
DETAIL: (id) => `/supervision-tasks/${id}`,
CREATE: '/supervision-tasks',
UPDATE: (id) => `/supervision-tasks/${id}`,
DELETE: (id) => `/supervision-tasks/${id}`,
STATS: '/supervision-tasks/stats',
EXPORT: '/supervision-tasks/export',
ARCHIVE: (id) => `/supervision-tasks/${id}/archive`,
DOWNLOAD_REPORT: (id) => `/supervision-tasks/${id}/report`
},
// 待安装任务
INSTALLATION_TASKS: {
LIST: '/installation-tasks',
DETAIL: (id) => `/installation-tasks/${id}`,
CREATE: '/installation-tasks',
UPDATE: (id) => `/installation-tasks/${id}`,
DELETE: (id) => `/installation-tasks/${id}`,
STATS: '/installation-tasks/stats',
EXPORT: '/installation-tasks/export'
},
// 生资保险
LIVESTOCK: {
TYPES: '/livestock-types',
POLICIES: '/livestock-policies',
CLAIMS: '/livestock-claims'
},
// 操作日志
OPERATION_LOGS: {
LIST: '/operation-logs',
STATS: '/operation-logs/stats',
EXPORT: '/operation-logs/export'
},
// 权限管理
PERMISSIONS: {
LIST: '/permissions',
TREE: '/permissions/tree'
},
// 角色权限
ROLE_PERMISSIONS: {
ROLES: '/role-permissions/roles',
PERMISSIONS: '/role-permissions/permissions',
ASSIGN: (roleId) => `/role-permissions/${roleId}/assign`,
COPY: '/role-permissions/copy'
}
}

View File

@@ -20,7 +20,6 @@ import RangePickerTest from '@/views/RangePickerTest.vue'
import LoginTest from '@/views/LoginTest.vue'
import LivestockPolicyManagement from '@/views/LivestockPolicyManagement.vue'
import SystemSettings from '@/views/SystemSettings.vue'
import TokenDebug from '@/views/TokenDebug.vue'
const routes = [
{
@@ -167,7 +166,7 @@ const routes = [
{
path: 'token-debug',
name: 'TokenDebug',
component: TokenDebug,
component: () => import('@/views/TokenDebug.vue'),
meta: { title: 'Token调试' }
}
]

View File

@@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import envConfig from '@/config/env'
export const useUserStore = defineStore('user', () => {
// 状态
@@ -102,7 +103,7 @@ export const useUserStore = defineStore('user', () => {
}
try {
const response = await fetch('/insurance/api/auth/refresh', {
const response = await fetch(`${envConfig.baseURL}/auth/refresh`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -20,7 +20,8 @@ export const userAPI = {
changePassword: (data) => api.put('/users/change-password', data),
uploadAvatar: (formData) => api.post('/users/avatar', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}),
export: (params) => api.get('/users/export', { params, responseType: 'blob' })
};
export const menuAPI = {
@@ -39,7 +40,8 @@ export const insuranceTypeAPI = {
create: (data) => api.post('/insurance-types', data),
update: (id, data) => api.put(`/insurance-types/${id}`, data),
delete: (id) => api.delete(`/insurance-types/${id}`),
updateStatus: (id, data) => api.patch(`/insurance-types/${id}/status`, data)
updateStatus: (id, data) => api.patch(`/insurance-types/${id}/status`, data),
export: (params) => api.get('/insurance-types/export', { params, responseType: 'blob' })
}
export const applicationAPI = {
@@ -61,14 +63,16 @@ export const policyAPI = {
create: (data) => api.post('/policies', data),
update: (id, data) => api.put(`/policies/${id}`, data),
updateStatus: (id, data) => api.put(`/policies/${id}/status`, data),
delete: (id) => api.delete(`/policies/${id}`)
delete: (id) => api.delete(`/policies/${id}`),
export: (params) => api.get('/policies/export', { params, responseType: 'blob' })
}
export const claimAPI = {
getList: (params) => api.get('/claims', { params }),
getDetail: (id) => api.get(`/claims/${id}`),
updateStatus: (id, data) => api.put(`/claims/${id}/status`, data),
delete: (id) => api.delete(`/claims/${id}`)
delete: (id) => api.delete(`/claims/${id}`),
export: (params) => api.get('/claims/export', { params, responseType: 'blob' })
}
export const dashboardAPI = {
@@ -84,7 +88,8 @@ export const deviceAlertAPI = {
getDetail: (id) => api.get(`/device-alerts/${id}`),
markAsRead: (id) => api.patch(`/device-alerts/${id}/read`),
markAllAsRead: () => api.patch('/device-alerts/read-all'),
handle: (id, data) => api.patch(`/device-alerts/${id}/handle`, data)
handle: (id, data) => api.patch(`/device-alerts/${id}/handle`, data),
export: (params) => api.get('/device-alerts/export', { params, responseType: 'blob' })
}
// 数据览仓API
@@ -104,7 +109,10 @@ export const supervisionTaskApi = {
delete: (id) => api.delete(`/supervision-tasks/${id}`),
getDetail: (id) => api.get(`/supervision-tasks/${id}`),
batchOperate: (data) => api.post('/supervision-tasks/batch/operate', data),
getStats: () => api.get('/supervision-tasks/stats')
getStats: () => api.get('/supervision-tasks/stats'),
export: (params) => api.get('/supervision-tasks/export', { params, responseType: 'blob' }),
archive: (id) => api.patch(`/supervision-tasks/${id}/archive`),
downloadReport: (id) => api.get(`/supervision-tasks/${id}/report`, { responseType: 'blob' })
}
// 待安装任务API
@@ -118,7 +126,11 @@ export const installationTaskApi = {
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`)
getHistory: (id) => api.get(`/installation-tasks/${id}/history`),
export: (params) => api.get('/installation-tasks/export', { params, responseType: 'blob' }),
batchDelete: (ids) => api.post('/installation-tasks/batch-delete', { ids }),
batchUpdateStatus: (data) => api.post('/installation-tasks/batch-update-status', data),
getDetail: (id) => api.get(`/installation-tasks/${id}`)
}
// 生资保险相关API
@@ -140,7 +152,8 @@ export const livestockPolicyApi = {
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/active')
getLivestockTypes: () => api.get('/livestock-types/active'),
export: (params) => api.get('/livestock-policies/export', { params, responseType: 'blob' })
}
export const livestockClaimApi = {
@@ -152,7 +165,8 @@ export const livestockClaimApi = {
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')
getStats: () => api.get('/livestock-claims/stats'),
export: (params) => api.get('/livestock-claims/export', { params, responseType: 'blob' })
}
// 操作日志API

View File

@@ -14,7 +14,7 @@ export default {
// 更新安装任务
updateInstallationTask: (id, data) => {
return installationTaskApi.update({ ...data, id })
return installationTaskApi.update(id, data)
},
// 删除安装任务

View File

@@ -1,11 +1,12 @@
import { useUserStore } from '@/stores/user'
import { message, Modal } from 'ant-design-vue'
import router from '@/router'
import envConfig from '@/config/env'
// API基础配置
const API_CONFIG = {
baseURL: '/insurance/api',
timeout: 10000
baseURL: envConfig.baseURL,
timeout: envConfig.timeout
}
// 是否正在刷新token的标志
@@ -85,7 +86,11 @@ const handleResponse = async (response) => {
try {
const contentType = response.headers.get('content-type')
if (contentType && contentType.includes('application/json')) {
// 处理Excel文件下载
if (contentType && contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
data = await response.blob()
} else if (contentType && contentType.includes('application/json')) {
data = await response.json()
} else {
data = await response.text()

View File

@@ -640,7 +640,7 @@ const exportData = async () => {
exportLoading.value = true
try {
const response = await applicationAPI.export(searchForm)
const url = window.URL.createObjectURL(new Blob([response.data]))
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `申请数据_${new Date().toISOString().slice(0, 10)}.xlsx`)

View File

@@ -5,10 +5,16 @@
sub-title="管理系统所有理赔申请"
>
<template #extra>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增理赔
</a-button>
<a-space>
<a-button @click="handleExport">
<download-outlined />
导出Excel
</a-button>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增理赔
</a-button>
</a-space>
</template>
</a-page-header>
@@ -445,7 +451,8 @@ import {
PlusOutlined,
SearchOutlined,
RedoOutlined,
FileTextOutlined
FileTextOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
import { livestockClaimApi } from '@/utils/api'
import dayjs from 'dayjs'
@@ -967,6 +974,42 @@ const handleDelete = async (id) => {
}
}
const handleExport = async () => {
try {
loading.value = true
const params = {
search: searchForm.search || undefined,
status: searchForm.status || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await livestockClaimApi.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `理赔列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
onMounted(() => {
loadClaims()
})

View File

@@ -39,6 +39,10 @@
</a-col>
<a-col :span="6">
<a-space>
<a-button @click="handleExport">
<DownloadOutlined />
导出Excel
</a-button>
<a-button type="primary" @click="handleSearch">
<SearchOutlined />
搜索
@@ -233,6 +237,7 @@ import {
ExportOutlined,
DownOutlined
} from '@ant-design/icons-vue'
import { supervisionTaskApi } from '@/utils/api'
// 响应式数据
const loading = ref(false)
@@ -370,6 +375,42 @@ const handleReset = () => {
fetchTaskList()
}
const handleExport = async () => {
try {
loading.value = true
const params = {
taskName: searchForm.taskName || undefined,
status: searchForm.status || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await supervisionTaskApi.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `监管任务结项列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
// 表格变化处理
const handleTableChange = (pag) => {
pagination.current = pag.current
@@ -384,76 +425,113 @@ const handleView = (record) => {
}
// 下载报告
const handleDownload = (record) => {
message.info(`正在下载 ${record.taskName} 的报告...`)
// 这里实现下载逻辑
const handleDownload = async (record) => {
try {
message.loading(`正在下载 ${record.taskName} 的报告...`, 0)
const response = await supervisionTaskApi.downloadReport(record.id)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `${record.taskName}_报告_${new Date().getTime()}.pdf`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.destroy()
message.success('报告下载成功')
} catch (error) {
message.destroy()
console.error('下载报告失败:', error)
message.error('下载报告失败,请稍后重试')
}
}
// 归档任务
const handleArchive = (record) => {
message.success(`任务 ${record.taskName} 已归档`)
fetchTaskList()
const handleArchive = async (record) => {
try {
const response = await supervisionTaskApi.archive(record.id)
if (response.data && response.data.status === 'success') {
message.success(`任务 ${record.taskName} 已归档`)
fetchTaskList()
} else {
message.error(response.data?.message || '归档失败')
}
} catch (error) {
console.error('归档任务失败:', error)
message.error('归档任务失败,请稍后重试')
}
}
// 导出任务
const handleExport = (record) => {
message.info(`正在导出 ${record.taskName} 的数据...`)
// 这里实现导出逻辑
}
// 导出单个任务此功能已合并到handleExport
// const handleExportSingle = (record) => {
// message.info(`正在导出 ${record.taskName} 的数据...`)
// }
// 获取任务列表
const fetchTaskList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000))
const params = {
page: pagination.current,
limit: pagination.pageSize,
taskStatus: 'completed' // 只获取已完成的任务
}
// 模拟数据
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: '数据审核完成,所有数据符合标准'
}
]
// 添加搜索条件
if (searchForm.taskName) {
params.taskName = searchForm.taskName
}
if (searchForm.status) {
params.status = searchForm.status
}
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
params.startDate = searchForm.dateRange[0].format('YYYY-MM-DD')
params.endDate = searchForm.dateRange[1].format('YYYY-MM-DD')
}
taskList.value = mockData
pagination.total = mockData.length
const response = await supervisionTaskApi.getList(params)
console.log('监管任务结项API响应:', response)
// 更新统计数据
stats.total = 156
stats.thisMonth = 23
stats.archived = 89
stats.avgDuration = 6.5
if (response.data && response.data.status === 'success') {
taskList.value = response.data.data.list || []
pagination.total = response.data.data.total || 0
console.log('监管任务结项数据设置成功:', taskList.value.length, '条')
} else {
console.log('监管任务结项响应格式错误:', response)
message.error(response.data?.message || '获取任务列表失败')
}
// 获取统计数据
await fetchStats()
} catch (error) {
console.error('获取任务列表失败:', error)
message.error('获取任务列表失败')
} finally {
loading.value = false
}
}
// 获取统计数据
const fetchStats = async () => {
try {
const response = await supervisionTaskApi.getStats()
if (response.data && response.data.status === 'success') {
const statsData = response.data.data
stats.total = statsData.total || 0
stats.thisMonth = statsData.thisMonth || 0
stats.archived = statsData.archived || 0
stats.avgDuration = statsData.avgDuration || 0
}
} catch (error) {
console.error('获取统计数据失败:', error)
}
}
// 组件挂载时获取数据
onMounted(() => {
fetchTaskList()

View File

@@ -110,11 +110,147 @@
<div class="pagination-info">
<span> {{ pagination.total }} 条记录</span>
</div>
<!-- 查看/编辑安装任务弹窗 -->
<a-modal
:title="modalTitle"
:open="modalVisible"
:width="900"
@ok="handleModalOk"
@cancel="handleModalCancel"
:confirmLoading="modalLoading"
:footer="isViewMode ? null : undefined"
>
<a-form
ref="modalFormRef"
:model="modalForm"
:rules="modalRules"
layout="vertical"
:disabled="isViewMode"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="申请单号" name="applicationNumber">
<a-input v-model:value="modalForm.applicationNumber" placeholder="请输入申请单号" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="保单编号" name="policyNumber">
<a-input v-model:value="modalForm.policyNumber" placeholder="请输入保单编号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="产品名称" name="productName">
<a-input v-model:value="modalForm.productName" placeholder="请输入产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="客户姓名" name="customerName">
<a-input v-model:value="modalForm.customerName" placeholder="请输入客户姓名" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="证件类型" name="idType">
<a-select v-model:value="modalForm.idType" placeholder="请选择证件类型">
<a-select-option value="身份证">身份证</a-select-option>
<a-select-option value="护照">护照</a-select-option>
<a-select-option value="军官证">军官证</a-select-option>
<a-select-option value="其他">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="证件号码" name="idNumber">
<a-input v-model:value="modalForm.idNumber" placeholder="请输入证件号码" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="养殖生资种类" name="livestockSupplyType">
<a-input v-model:value="modalForm.livestockSupplyType" placeholder="请输入养殖生资种类" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="待安装设备" name="pendingDevices">
<a-input v-model:value="modalForm.pendingDevices" placeholder="请输入待安装设备" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="安装状态" name="installationStatus">
<a-select v-model:value="modalForm.installationStatus" placeholder="请选择安装状态">
<a-select-option value="待安装">待安装</a-select-option>
<a-select-option value="安装中">安装中</a-select-option>
<a-select-option value="已安装">已安装</a-select-option>
<a-select-option value="安装失败">安装失败</a-select-option>
<a-select-option value="已取消">已取消</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="生成安装任务时间" name="taskGeneratedTime">
<a-date-picker
v-model:value="modalForm.taskGeneratedTime"
show-time
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择生成任务时间"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="安装完成生效时间" name="installationCompletedTime">
<a-date-picker
v-model:value="modalForm.installationCompletedTime"
show-time
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择完成时间"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="安装人员" name="installer">
<a-input v-model:value="modalForm.installer" placeholder="请输入安装人员" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="备注" name="remarks">
<a-textarea
v-model:value="modalForm.remarks"
placeholder="请输入备注信息"
:rows="3"
/>
</a-form-item>
<!-- 查看模式下显示操作按钮 -->
<div v-if="isViewMode" style="text-align: right; margin-top: 16px;">
<a-space>
<a-button @click="handleModalCancel">关闭</a-button>
<a-button type="primary" @click="switchToEditMode">编辑</a-button>
</a-space>
</div>
</a-form>
</a-modal>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, computed, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { SearchOutlined } from '@ant-design/icons-vue';
import dayjs from 'dayjs';
@@ -123,6 +259,11 @@ import installationTaskApi from '@/utils/installationTaskApi';
// 响应式数据
const loading = ref(false);
const tableData = ref([]);
const modalVisible = ref(false);
const modalLoading = ref(false);
const modalFormRef = ref();
const editingId = ref(null);
const isViewMode = ref(false);
// 搜索表单
const searchForm = reactive({
@@ -141,6 +282,48 @@ const pagination = reactive({
showTotal: (total, range) => `${range[0]}-${range[1]} 条,共 ${total}`
});
// 弹窗表单
const modalForm = reactive({
applicationNumber: '',
policyNumber: '',
productName: '',
customerName: '',
idType: '',
idNumber: '',
livestockSupplyType: '',
pendingDevices: '',
installationStatus: '',
taskGeneratedTime: null,
installationCompletedTime: null,
installer: '',
remarks: ''
});
// 表单验证规则
const modalRules = {
applicationNumber: [
{ required: true, message: '请输入申请单号', trigger: 'blur' }
],
policyNumber: [
{ required: true, message: '请输入保单编号', trigger: 'blur' }
],
productName: [
{ required: true, message: '请输入产品名称', trigger: 'blur' }
],
customerName: [
{ required: true, message: '请输入客户姓名', trigger: 'blur' }
],
installationStatus: [
{ required: true, message: '请选择安装状态', trigger: 'change' }
]
};
// 计算属性
const modalTitle = computed(() => {
if (isViewMode.value) return '查看安装任务';
return editingId.value ? '编辑安装任务' : '新增安装任务';
});
// 表格列定义
const columns = [
{
@@ -279,12 +462,106 @@ const handleTableChange = (paginationInfo) => {
const handleView = (record) => {
console.log('查看记录:', record);
message.info('查看功能开发中');
isViewMode.value = true;
editingId.value = record.id;
fillModalForm(record);
modalVisible.value = true;
};
const handleEdit = (record) => {
console.log('编辑记录:', record);
message.info('编辑功能开发中');
isViewMode.value = false;
editingId.value = record.id;
fillModalForm(record);
modalVisible.value = true;
};
const fillModalForm = (record) => {
Object.assign(modalForm, {
applicationNumber: record.applicationNumber || '',
policyNumber: record.policyNumber || '',
productName: record.productName || '',
customerName: record.customerName || '',
idType: record.idType || '',
idNumber: record.idNumber || '',
livestockSupplyType: record.livestockSupplyType || '',
pendingDevices: record.pendingDevices || '',
installationStatus: record.installationStatus || '',
taskGeneratedTime: record.taskGeneratedTime ? dayjs(record.taskGeneratedTime) : null,
installationCompletedTime: record.installationCompletedTime ? dayjs(record.installationCompletedTime) : null,
installer: record.installer || '',
remarks: record.remarks || ''
});
};
const resetModalForm = () => {
Object.assign(modalForm, {
applicationNumber: '',
policyNumber: '',
productName: '',
customerName: '',
idType: '',
idNumber: '',
livestockSupplyType: '',
pendingDevices: '',
installationStatus: '',
taskGeneratedTime: null,
installationCompletedTime: null,
installer: '',
remarks: ''
});
};
const switchToEditMode = () => {
isViewMode.value = false;
};
const handleModalOk = async () => {
if (isViewMode.value) {
modalVisible.value = false;
return;
}
try {
await modalFormRef.value.validate();
modalLoading.value = true;
const submitData = {
...modalForm,
taskGeneratedTime: modalForm.taskGeneratedTime ?
dayjs(modalForm.taskGeneratedTime).format('YYYY-MM-DD HH:mm:ss') : null,
installationCompletedTime: modalForm.installationCompletedTime ?
dayjs(modalForm.installationCompletedTime).format('YYYY-MM-DD HH:mm:ss') : null
};
const response = editingId.value
? await installationTaskApi.updateInstallationTask(editingId.value, submitData)
: await installationTaskApi.createInstallationTask(submitData);
if (response.code === 200 || response.code === 201) {
message.success(editingId.value ? '更新成功' : '创建成功');
modalVisible.value = false;
fetchInstallationTasks();
} else {
message.error(response.message || '操作失败');
}
} catch (error) {
console.error('提交失败:', error);
if (error.errorFields) {
message.error('请完善表单信息');
} else {
message.error('操作失败');
}
} finally {
modalLoading.value = false;
}
};
const handleModalCancel = () => {
modalVisible.value = false;
isViewMode.value = false;
editingId.value = null;
resetModalForm();
};
const handleDelete = async (id) => {
@@ -306,16 +583,15 @@ const handleExportTasks = async () => {
try {
const response = await installationTaskApi.exportInstallationTasks(searchForm);
if (response) {
// 处理文件下载
const blob = new Blob([response], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = window.URL.createObjectURL(blob);
if (response && response.data) {
// 处理文件下载 - response.data是Blob对象
const url = window.URL.createObjectURL(response.data);
const link = document.createElement('a');
link.href = url;
link.download = `安装任务导出_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
message.success('导出成功');
@@ -324,7 +600,7 @@ const handleExportTasks = async () => {
}
} catch (error) {
console.error('导出失败:', error);
message.error('导出失败');
message.error('导出失败,请稍后重试');
}
};
@@ -446,4 +722,22 @@ const getInstallationStatusColor = (status) => {
color: #666;
font-size: 14px;
}
/* 查看模式下表单样式优化 */
:deep(.ant-form-item-control-input-content) {
.ant-input[disabled],
.ant-select-disabled .ant-select-selector,
.ant-picker-disabled,
.ant-input-number-disabled {
color: rgba(0, 0, 0, 0.85);
background-color: #fafafa;
border-color: #d9d9d9;
cursor: default;
}
.ant-input-disabled,
.ant-select-disabled .ant-select-selection-item {
color: rgba(0, 0, 0, 0.85);
}
}
</style>

View File

@@ -5,10 +5,16 @@
sub-title="管理系统支持的保险产品险种"
>
<template #extra>
<a-button type="primary" @click="showModal">
<PlusOutlined />
新增险种
</a-button>
<a-space>
<a-button @click="handleExport">
<DownloadOutlined />
导出Excel
</a-button>
<a-button type="primary" @click="showModal">
<PlusOutlined />
新增险种
</a-button>
</a-space>
</template>
</a-page-header>
@@ -326,7 +332,8 @@ import { insuranceTypeAPI } from '@/utils/api'
import {
PlusOutlined,
SearchOutlined,
RedoOutlined
RedoOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
const loading = ref(false)
@@ -1274,6 +1281,35 @@ const handleOnSaleStatusChange = (checked) => {
console.log(`险种销售状态${statusText},时间:${new Date().toLocaleString()}`)
}
// 导出Excel
const handleExport = async () => {
try {
loading.value = true
const params = {
...searchForm
}
const response = await insuranceTypeAPI.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `保险类型列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
// 组件卸载状态跟踪
const isComponentMounted = ref(true)

View File

@@ -2,10 +2,16 @@
<div class="livestock-policy-management">
<div class="page-header">
<h2>生资保单管理</h2>
<a-button type="primary" @click="showCreateModal">
<PlusOutlined />
新建保单
</a-button>
<a-space>
<a-button @click="handleExport">
<DownloadOutlined />
导出Excel
</a-button>
<a-button type="primary" @click="showCreateModal">
<PlusOutlined />
新建保单
</a-button>
</a-space>
</div>
<!-- 搜索筛选区域 -->
@@ -332,7 +338,8 @@ import dayjs from 'dayjs'
import {
PlusOutlined,
SearchOutlined,
DownOutlined
DownOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
import { livestockPolicyApi, livestockTypeApi } from '@/utils/api'
@@ -769,6 +776,44 @@ const getCurrentPremiumRate = () => {
return '请先选择牲畜类型'
}
const handleExport = async () => {
try {
loading.value = true
const params = {
policy_no: searchForm.policy_no || undefined,
policyholder_name: searchForm.policyholder_name || undefined,
policy_status: searchForm.policy_status || undefined,
payment_status: searchForm.payment_status || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await livestockPolicyApi.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `生资保单列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
// 生命周期
onMounted(() => {
fetchData()

View File

@@ -117,6 +117,10 @@
</a-col>
<a-col :span="8">
<a-space>
<a-button @click="handleExport">
<download-outlined />
导出Excel
</a-button>
<a-button type="primary" @click="markAllAsRead" :disabled="!hasUnreadAlerts">
<check-outlined />
全部标记已读
@@ -322,7 +326,8 @@ import {
FireOutlined,
CloudOutlined,
WifiOutlined,
ToolOutlined
ToolOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
import { deviceAlertAPI } from '@/utils/api'
import dayjs from 'dayjs'
@@ -605,6 +610,42 @@ const refreshData = async () => {
message.success('数据已刷新')
}
// 导出Excel
const handleExport = async () => {
try {
loading.value = true
const params = {
...filters
}
// 移除undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await deviceAlertAPI.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `消息通知_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
// 组件挂载时获取数据
onMounted(() => {
fetchStats()

View File

@@ -5,10 +5,16 @@
sub-title="管理系统所有保单信息"
>
<template #extra>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增保单
</a-button>
<a-space>
<a-button @click="handleExport">
<download-outlined />
导出Excel
</a-button>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增保单
</a-button>
</a-space>
</template>
</a-page-header>
@@ -322,7 +328,8 @@ import dayjs from 'dayjs'
import {
PlusOutlined,
SearchOutlined,
RedoOutlined
RedoOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
import { policyAPI } from '@/utils/api'
@@ -639,6 +646,44 @@ const handleDelete = async (id) => {
})
}
const handleExport = async () => {
try {
loading.value = true
const params = {
policy_number: searchForm.policy_number || undefined,
policyholder_name: searchForm.policyholder_name || undefined,
insurance_type_id: searchForm.insurance_type_id || undefined,
status: searchForm.status || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await policyAPI.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `保单列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
onMounted(() => {
loadPolicies()
})

View File

@@ -8,6 +8,10 @@
<!-- 操作按钮区 -->
<div class="action-buttons">
<a-space>
<a-button @click="handleExport">
<DownloadOutlined />
导出Excel
</a-button>
<a-button type="primary" @click="handleCreateTask">
新增监管任务
</a-button>
@@ -108,6 +112,69 @@
<span>页共 0 </span>
</div>
<!-- 查看详情弹窗 -->
<a-modal
title="查看监管任务详情"
:open="viewModalVisible"
:width="900"
@cancel="handleViewModalCancel"
:footer="null"
>
<a-descriptions bordered :column="2" size="middle">
<a-descriptions-item label="申请单号">
{{ viewRecord.applicationNumber || '-' }}
</a-descriptions-item>
<a-descriptions-item label="保单编号">
{{ viewRecord.policyNumber || '-' }}
</a-descriptions-item>
<a-descriptions-item label="产品名称">
{{ viewRecord.productName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="保险期间">
{{ viewRecord.insurancePeriod || '-' }}
</a-descriptions-item>
<a-descriptions-item label="客户姓名">
{{ viewRecord.customerName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="证件类型">
{{ viewRecord.idType || '-' }}
</a-descriptions-item>
<a-descriptions-item label="证件号码">
{{ viewRecord.idNumber || '-' }}
</a-descriptions-item>
<a-descriptions-item label="适用金额">
{{ viewRecord.applicableAmount ? formatAmount(viewRecord.applicableAmount) : '-' }}
</a-descriptions-item>
<a-descriptions-item label="监管生资数量">
{{ viewRecord.supervisorySuppliesQuantity || '-' }}
</a-descriptions-item>
<a-descriptions-item label="任务类型">
<a-tag :color="getTaskTypeColor(viewRecord.taskType)">
{{ getTaskTypeText(viewRecord.taskType) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="任务状态">
<a-tag :color="getStatusColor(viewRecord.taskStatus)">
{{ getStatusText(viewRecord.taskStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="优先级">
<a-tag :color="getPriorityColor(viewRecord.priority)">
{{ getPriorityText(viewRecord.priority) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="创建时间" :span="2">
{{ viewRecord.createdAt || '-' }}
</a-descriptions-item>
<a-descriptions-item label="更新时间" :span="2">
{{ viewRecord.updatedAt || '-' }}
</a-descriptions-item>
<a-descriptions-item label="备注" :span="2">
{{ viewRecord.remarks || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-modal>
<!-- 创建/编辑监管任务弹窗 -->
<a-modal
:title="modalTitle"
@@ -223,13 +290,14 @@
<script>
import { ref, reactive, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import { SearchOutlined, DownloadOutlined } from '@ant-design/icons-vue'
import { supervisionTaskApi } from '@/utils/api'
export default {
name: 'SupervisionTaskManagement',
components: {
SearchOutlined
SearchOutlined,
DownloadOutlined
},
setup() {
const loading = ref(false)
@@ -239,6 +307,8 @@ export default {
const modalLoading = ref(false)
const modalFormRef = ref()
const editingId = ref(null)
const viewModalVisible = ref(false)
const viewRecord = ref({})
// 搜索表单
const searchForm = reactive({
@@ -493,7 +563,13 @@ export default {
const handleView = (record) => {
console.log('查看详情:', record)
message.info('查看功能开发中...')
viewRecord.value = { ...record }
viewModalVisible.value = true
}
const handleViewModalCancel = () => {
viewModalVisible.value = false
viewRecord.value = {}
}
const handleEdit = (record) => {
@@ -564,6 +640,42 @@ export default {
resetModalForm()
}
const handleExport = async () => {
try {
loading.value = true
const params = {
policyNumber: searchForm.policyNumber || undefined,
customerName: searchForm.customerName || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await supervisionTaskApi.export(params)
// 创建下载链接 - response.data已经是Blob对象不需要再包装
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `监管任务列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
// 生命周期
onMounted(() => {
loadData()
@@ -583,6 +695,8 @@ export default {
modalFormRef,
modalRules,
modalTitle,
viewModalVisible,
viewRecord,
getStatusColor,
getStatusText,
getTaskTypeColor,
@@ -597,10 +711,12 @@ export default {
handleTaskGuidance,
handleBatchCreate,
handleView,
handleViewModalCancel,
handleEdit,
handleDelete,
handleModalOk,
handleModalCancel
handleModalCancel,
handleExport
}
}
}

View File

@@ -92,13 +92,19 @@
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="searchLogs">
<SearchOutlined />
搜索
</a-button>
<a-button style="margin-left: 8px" @click="resetLogFilters">
重置
</a-button>
<a-space>
<a-button @click="handleExportLogs">
<DownloadOutlined />
导出Excel
</a-button>
<a-button type="primary" @click="searchLogs">
<SearchOutlined />
搜索
</a-button>
<a-button @click="resetLogFilters">
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
</a-card>
@@ -170,7 +176,7 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import { SearchOutlined, DownloadOutlined } from '@ant-design/icons-vue'
import { operationLogAPI } from '@/utils/api'
import dayjs from 'dayjs'
import PermissionManagement from '@/components/PermissionManagement.vue'
@@ -329,6 +335,48 @@ const resetLogFilters = () => {
searchLogs()
}
const handleExportLogs = async () => {
try {
logLoading.value = true
const params = {
username: logFilters.username || undefined,
action: logFilters.action || undefined
}
// 处理日期范围
if (logFilters.dateRange && logFilters.dateRange.length === 2) {
params.startDate = dayjs(logFilters.dateRange[0]).format('YYYY-MM-DD')
params.endDate = dayjs(logFilters.dateRange[1]).format('YYYY-MM-DD')
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await operationLogAPI.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `操作日志_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
logLoading.value = false
}
}
// 表格变化处理
const handleLogTableChange = (pagination) => {
logPagination.current = pagination.current

View File

@@ -5,10 +5,16 @@
sub-title="管理系统用户和权限"
>
<template #extra>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增用户
</a-button>
<a-space>
<a-button @click="handleExport">
<download-outlined />
导出Excel
</a-button>
<a-button type="primary" @click="showModal">
<plus-outlined />
新增用户
</a-button>
</a-space>
</template>
</a-page-header>
@@ -137,7 +143,8 @@ import { message } from 'ant-design-vue'
import {
PlusOutlined,
SearchOutlined,
RedoOutlined
RedoOutlined,
DownloadOutlined
} from '@ant-design/icons-vue'
import { userAPI } from '@/utils/api'
@@ -406,6 +413,42 @@ const handleDelete = async (id) => {
}
}
const handleExport = async () => {
try {
loading.value = true
const params = {
search: searchForm.username || undefined,
status: searchForm.status || undefined
}
// 过滤掉undefined值
Object.keys(params).forEach(key => {
if (params[key] === undefined) {
delete params[key]
}
})
const response = await userAPI.export(params)
// 创建下载链接
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `用户列表_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
message.success('导出成功')
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败,请稍后重试')
} finally {
loading.value = false
}
}
onMounted(() => {
loadUsers()
})

View File

@@ -18,10 +18,10 @@ export default defineConfig(({ mode }) => {
port: parseInt(env.VITE_PORT) || 3001,
historyApiFallback: true,
proxy: {
'/api': {
target: env.VITE_API_BASE_URL || 'https://ad.ningmuyun.com/insurace/',
'/insurance/api': {
target: env.VITE_API_BASE_URL || 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path
rewrite: (path) => path.replace(/^\/insurance/, '')
}
}
}