添加后端接口修改前端及小程序

This commit is contained in:
2025-09-29 17:58:42 +08:00
parent 488cbe4056
commit 4af8368097
50 changed files with 4558 additions and 333 deletions

View File

@@ -1,7 +1,11 @@
<template>
<a-layout style="min-height: 100vh">
<!-- 侧边栏 -->
<a-layout-sider v-model:collapsed="collapsed" collapsible>
<a-layout style="min-height: 100vh; display: flex;">
<!-- 侧边栏 - 固定 -->
<a-layout-sider
v-model:collapsed="collapsed"
collapsible
:style="{ position: 'fixed', left: 0, top: 0, bottom: 0, height: '100vh', overflow: 'auto', zIndex: 10 }"
>
<div class="logo">
<h2 v-if="!collapsed">政府管理系统</h2>
<h2 v-else>政府</h2>
@@ -9,17 +13,22 @@
<Sidebar />
</a-layout-sider>
<!-- 主内容区 -->
<a-layout>
<!-- 主内容区 - 可滚动 -->
<a-layout
:style="{ marginLeft: collapsed ? '80px' : '200px', width: 'calc(100% - ' + (collapsed ? '80px' : '200px') + ')' }"
class="main-content-wrapper"
>
<!-- 头部 -->
<a-layout-header style="background: #fff; padding: 0 16px; display: flex; justify-content: space-between; align-items: center">
<a-layout-header
style="background: #fff; padding: 0 16px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; zIndex: 5; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)"
>
<div>
<menu-unfold-outlined
<MenuUnfoldOutlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<menu-fold-outlined
<MenuFoldOutlined
v-else
class="trigger"
@click="() => (collapsed = !collapsed)"
@@ -48,15 +57,15 @@
</div>
</a-layout-header>
<!-- 内容区 -->
<a-layout-content style="margin: 16px">
<!-- 内容区 - 可滚动 -->
<a-layout-content style="margin: 16px; padding-bottom: 24px; overflow-y: auto; max-height: calc(100vh - 136px)">
<div :style="{ padding: '24px', background: '#fff', minHeight: '360px' }">
<router-view />
</div>
</a-layout-content>
<!-- 底部 -->
<a-layout-footer style="text-align: center">
<a-layout-footer style="text-align: center; position: sticky; bottom: 0; background: #fff">
政府端后台管理系统 ©2024
</a-layout-footer>
</a-layout>
@@ -118,4 +127,31 @@ const handleLogout = () => {
gap: 8px;
cursor: pointer;
}
.main-content-wrapper {
display: flex;
flex-direction: column;
height: 100vh;
transition: all 0.3s ease;
}
/* 隐藏滚动条但保持滚动功能 */
.ant-layout-content::-webkit-scrollbar {
width: 0;
height: 0;
}
.ant-layout-content::-webkit-scrollbar-track {
background: transparent;
}
.ant-layout-content::-webkit-scrollbar-thumb {
background: transparent;
}
/* 兼容Firefox */
.ant-layout-content {
scrollbar-width: none;
-ms-overflow-style: none;
}
</style>

View File

@@ -41,7 +41,7 @@
</a-menu-item>
<!-- 智慧仓库 -->
<a-sub-menu key="smart-warehouse">
<a-sub-menu key="/smart-warehouse">
<template #icon><HddOutlined /></template>
<template #title>
<span>智慧仓库</span>
@@ -58,7 +58,7 @@
</a-sub-menu>
<!-- 屠宰无害化 -->
<a-sub-menu key="slaughter">
<a-sub-menu key="/slaughter">
<template #icon><SafetyOutlined /></template>
<template #title>
<span>屠宰无害化</span>
@@ -72,43 +72,43 @@
</a-sub-menu>
<!-- 无纸化防疫 -->
<a-sub-menu key="paperless-epidemic">
<a-sub-menu key="/paperless/epidemic">
<template #icon><FileTextOutlined /></template>
<template #title>
<span>无纸化防疫</span>
</template>
<!-- <a-menu-item key="paperless/epidemic"><span>防疫首页</span></a-menu-item> -->
<a-menu-item key="paperless/epidemic/epidemic-agency"><span>防疫机构管理</span></a-menu-item>
<a-menu-item key="paperless/epidemic/epidemic-record"><span>防疫记录</span></a-menu-item>
<a-menu-item key="paperless/epidemic/vaccine-management"><span>疫苗管理</span></a-menu-item>
<a-menu-item key="paperless/epidemic/epidemic-activity"><span>防疫活动管理</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-agency"><span>防疫机构管理</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-record"><span>防疫记录</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/vaccine-management"><span>疫苗管理</span></a-menu-item>
<a-menu-item key="/paperless/epidemic/epidemic-activity"><span>防疫活动管理</span></a-menu-item>
</a-sub-menu>
<!-- 无纸化检疫 -->
<a-sub-menu key="paperless-quarantine">
<a-sub-menu key="/paperless/quarantine">
<template #icon><SafetyOutlined /></template>
<template #title>
<span>无纸化检疫</span>
</template>
<a-menu-item key="paperless/quarantine/declaration"><span>检疫审批</span></a-menu-item>
<a-menu-item key="paperless/quarantine/record-query"><span>检疫证查询</span></a-menu-item>
<a-menu-item key="paperless/quarantine/config"><span>检疫站清单</span></a-menu-item>
<a-menu-item key="/paperless/quarantine/declaration"><span>检疫审批</span></a-menu-item>
<a-menu-item key="/paperless/quarantine/record-query"><span>检疫证查询</span></a-menu-item>
<a-menu-item key="/paperless/quarantine/config"><span>检疫站清单</span></a-menu-item>
</a-sub-menu>
<!-- 生资认证 -->
<a-menu-item key="examine/index">
<a-menu-item key="/examine/index">
<template #icon><CheckCircleOutlined /></template>
<span>生资认证</span>
</a-menu-item>
<!-- 养牛学院 -->
<a-menu-item key="academy">
<a-menu-item key="/academy">
<template #icon><BookOutlined /></template>
<span>养牛学院</span>
</a-menu-item>
<!-- 设备预警 -->
<a-menu-item key="device-alert">
<a-menu-item key="/device-alert">
<template #icon><ExclamationCircleOutlined /></template>
<span>设备预警</span>
</a-menu-item>
@@ -132,9 +132,7 @@ const openKeys = ref([])
// 处理菜单选择
const handleMenuSelect = ({ key }) => {
if (key) {
// 确保使用绝对路径进行路由跳转
const absolutePath = key.startsWith('/') ? key : `/${key}`
router.replace(absolutePath)
router.replace(key)
}
}

View File

@@ -34,10 +34,10 @@ const routes = [
name: 'Login',
component: Login
},
{
{
path: '/',
component: Layout,
redirect: '/dashboard',
redirect: '/index/data_center',
children: [
{
path: 'dashboard',
@@ -130,7 +130,7 @@ const routes = [
{
path: 'paperless/epidemic/epidemic-agency',
name: 'EpidemicAgencyManagement',
component: () => import('@/views/paperless/epidemic/epidemic-agency/EpidemicAgencyManagement.vue'),
component: () => import('@/views/paperless/epidemic/epidemic-agency/EpidemicAgency.vue'),
meta: { title: '防疫机构管理' }
},
{

View File

@@ -0,0 +1,18 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory('/test/'),
routes: [
{
path: '/',
redirect: '/test'
},
{
path: '/test',
name: 'TestPage',
component: { template: '<div>测试页面</div>' }
}
]
})
export default router

View File

@@ -3,6 +3,7 @@ import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import { message } from 'ant-design-vue'
import router from '@/router'
import api from '@/utils/api'
// 认证状态管理
// 管理用户的登录、登出和认证信息
@@ -37,36 +38,37 @@ export const useAuthStore = defineStore('auth', () => {
// 登录方法
const login = async (credentials) => {
try {
// 在实际应用中这里应该调用后端API进行登录验证
// 现在使用模拟数据模拟登录成功
// 调用后端登录接口
const response = await api.auth.login(credentials)
// 模拟API调用延迟
await new Promise(resolve => setTimeout(resolve, 500))
// 模拟登录成功数据
const mockToken = 'mock-jwt-token-' + Date.now()
const mockUserInfo = {
id: '1',
username: credentials.username,
name: '管理员',
avatar: '',
role: 'admin',
department: '信息管理处'
if (response.code === 200) {
// 保存token
const token = response.data.token
setToken(token)
// 获取用户信息
const userInfoResponse = await api.auth.getUserInfo()
if (userInfoResponse.code === 200) {
const userInfoData = userInfoResponse.data
setUserInfo(userInfoData)
setPermissions(userInfoData.permissions || [])
// 如果勾选了记住我,保存更长时间
if (credentials.remember) {
// 在实际应用中,这里可以设置更长的过期时间
// 这里简化处理
}
message.success('登录成功')
return true
} else {
message.error(userInfoResponse.message || '获取用户信息失败')
return false
}
} else {
message.error(response.message || '登录失败')
return false
}
const mockPermissions = ['view', 'add', 'edit', 'delete', 'export']
// 保存登录信息
setToken(mockToken)
setUserInfo(mockUserInfo)
setPermissions(mockPermissions)
// 如果勾选了记住我,保存更长时间
if (credentials.remember) {
// 在实际应用中,这里可以设置更长的过期时间
// 这里简化处理
}
return true
} catch (error) {
console.error('登录失败:', error)
message.error(error.message || '登录失败,请重试')
@@ -75,14 +77,25 @@ export const useAuthStore = defineStore('auth', () => {
}
// 退出登录
const logout = () => {
token.value = null
userInfo.value = {}
permissions.value = []
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
localStorage.removeItem('permissions')
router.push('/login')
const logout = async () => {
try {
// 调用后端退出登录接口
await api.auth.logout()
} catch (error) {
console.error('退出登录API调用失败:', error)
// 即使API调用失败仍然清除本地数据并跳转到登录页
} finally {
// 清除本地存储的用户信息
token.value = null
userInfo.value = {}
permissions.value = []
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
localStorage.removeItem('permissions')
// 跳转到登录页面
router.push('/login')
}
}
// 检查用户是否有特定权限

View File

@@ -0,0 +1,45 @@
import { createApp } from 'vue'
import App from './App.vue'
import testRouter from './router/testRoutes.js'
import { createPinia } from 'pinia'
import Antd from 'ant-design-vue'
import { ConfigProvider } from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import relativeTime from 'dayjs/plugin/relativeTime'
import duration from 'dayjs/plugin/duration'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
// 配置 dayjs
dayjs.extend(relativeTime)
dayjs.extend(duration)
dayjs.locale('zh-cn')
// 为 Ant Design Vue 配置日期库
globalThis.dayjs = dayjs
// 创建应用实例
const app = createApp(App)
// 创建 Pinia 实例
const pinia = createPinia()
app.use(testRouter)
app.use(pinia)
app.use(Antd)
// 配置 Ant Design Vue
app.use(ConfigProvider, {
locale: zhCN,
// 明确配置日期库为dayjs
dateFormatter: 'dayjs',
// 提供完整配置的dayjs实例确保在组件中能正确访问到配置好的dayjs
getDayjsInstance: () => {
// 确保返回一个已经正确配置了语言和插件的dayjs实例
return dayjs
},
// 安全地获取弹出层容器防止trigger为null导致的错误
getPopupContainer: (trigger) => trigger?.parentElement || document.body
})
app.mount('#app')

View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import { message } from 'ant-design-vue'
// 创建axios实例
const instance = axios.create({

View File

@@ -73,35 +73,34 @@
<!-- 处理列表 -->
<a-card>
<a-table
:columns="columns"
:data-source="processList"
:pagination="pagination"
row-key="id"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
:scroll="{ x: 'max-content' }"
<a-table
:columns="columns"
:data-source="processList"
:pagination="pagination"
row-key="id"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
:scroll="{ x: 'max-content' }"
>
<!-- 处理类型列 -->
<template #bodyCell:processType="{ record }">
<a-tag :color="record.processType === 'slaughter' ? 'green' : 'blue'">
<!-- 自定义单元格渲染 -->
<template #bodyCell="{ column, record }">
<!-- 处理类型列 -->
<template v-if="column.key === 'processType'">
{{ getProcessTypeText(record.processType) }}
</a-tag>
</template>
<!-- 状态列 -->
<template #bodyCell:status="{ record }">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<!-- 操作列 -->
<template #bodyCell:action="{ record }">
<div style="display: flex; gap: 8px;">
<a-button size="small" @click="handleView(record)">查看</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)" v-if="record.status !== 'completed'">编辑</a-button>
<a-button size="small" danger @click="handleDelete(record.id)" v-if="record.status !== 'completed'">删除</a-button>
<a-button size="small" @click="handleMarkComplete(record.id)" v-if="record.status === 'processing'">标记完成</a-button>
<a-button size="small" @click="handleReportIssue(record.id)" v-if="record.status !== 'completed' && record.status !== 'abnormal'">报告异常</a-button>
</div>
</template>
<!-- 处理状态列 -->
<template v-if="column.key === 'status'">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<!-- 操作列 -->
<template v-if="column.key === 'action'">
<div style="display: flex; gap: 8px;">
<a-button size="small" @click="handleView(record)">查看</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)" v-if="record.status !== 'completed'">编辑</a-button>
<a-button size="small" danger @click="handleDelete(record.id)" v-if="record.status !== 'completed'">删除</a-button>
<a-button size="small" @click="handleMarkComplete(record.id)" v-if="record.status === 'processing'">标记完成</a-button>
<a-button size="small" @click="handleReportIssue(record.id)" v-if="record.status !== 'completed' && record.status !== 'abnormal'">报告异常</a-button>
</div>
</template>
</template>
</a-table>
</a-card>
@@ -302,7 +301,7 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, h } from 'vue'
import * as echarts from 'echarts'
// 搜索条件
@@ -536,19 +535,8 @@ const columns = [
key: 'processCode',
width: 120
},
{
title: '处理类型',
dataIndex: 'processType',
key: 'processType',
width: 100
},
{
title: '屠宰场',
dataIndex: 'slaughterhouseId',
key: 'slaughterhouseId',
width: 120,
customRender: ({ text }) => getSlaughterhouseName(text)
},
{ title: '处理类型', dataIndex: 'processType', key: 'processType', width: 100 },
{ title: '屠宰场', dataIndex: 'slaughterhouseId', key: 'slaughterhouseId', width: 120 },
{
title: '处理数量',
dataIndex: 'quantity',
@@ -573,18 +561,7 @@ const columns = [
key: 'contactPhone',
width: 120
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
width: 100
},
{
title: '操作',
key: 'action',
width: 220,
fixed: 'right'
}
{ title: '处理状态', dataIndex: 'status', key: 'status', width: 100 }, { title: '操作', key: 'action', width: 220, fixed: 'right', dataIndex: 'id' }
]
// 获取处理类型文本

View File

@@ -7,13 +7,13 @@
<!-- 搜索和操作栏 -->
<div class="filter-section">
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
<a-input v-model:value="searchKeyword" placeholder="输入机构名称或编号" style="width: 250px;">
<a-input v-model:value="searchKeyword" placeholder="输入机构名称" style="width: 250px;">
<template #prefix>
<span class="iconfont icon-sousuo"></span>
</template>
</a-input>
<a-select v-model:value="typeFilter" placeholder="机构类型" style="width: 120px;">
<!-- <a-select v-model:value="typeFilter" placeholder="机构类型" style="width: 120px;">
<a-select-option value="">全部</a-select-option>
<a-select-option value="center">防疫中心</a-select-option>
<a-select-option value="station">防疫站</a-select-option>
@@ -26,7 +26,7 @@
<a-select-option value="municipal">市级</a-select-option>
<a-select-option value="county">县级</a-select-option>
<a-select-option value="township">乡镇级</a-select-option>
</a-select>
</a-select> -->
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
<span class="iconfont icon-sousuo"></span> 搜索
@@ -51,13 +51,14 @@
:scroll="{ x: 'max-content' }"
@change="handleTableChange"
>
<!-- 操作列 -->
<template #bodyCell:action="{ record }">
<div style="display: flex; gap: 8px;">
<a-button size="small" @click="handleView(record)">查看</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
</div>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<div style="display: flex; gap: 8px;">
<a-button size="small" @click="handleView(record)">查看</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
</div>
</template>
</template>
</a-table>
</div>
@@ -79,9 +80,9 @@
<a-input v-model:value="currentAgency.name" placeholder="请输入机构名称" />
</a-form-item>
<a-form-item label="机构编号" name="code" :rules="[{ required: true, message: '请输入机构编号' }]">
<!-- <a-form-item label="机构编号" name="code" :rules="[{ required: true, message: '请输入机构编号' }]">
<a-input v-model:value="currentAgency.code" placeholder="请输入机构编号" />
</a-form-item>
</a-form-item> -->
<a-form-item label="机构类型" name="type" :rules="[{ required: true, message: '请选择机构类型' }]">
<a-select v-model:value="currentAgency.type" placeholder="请选择机构类型">
@@ -103,21 +104,36 @@
<a-form-item label="负责人" name="manager" :rules="[{ required: true, message: '请输入负责人姓名' }]">
<a-input v-model:value="currentAgency.manager" placeholder="请输入负责人姓名" />
</a-form-item>
<a-form-item label="邮箱" name="email" :rules="[{ required: false, message: '请输入邮箱地址' }]">
<a-input v-model:value="currentAgency.email" placeholder="请输入邮箱地址" />
</a-form-item>
<a-form-item label="状态" name="status" :rules="[{ required: true, message: '请选择状态' }]">
<a-select v-model:value="currentAgency.status" placeholder="请选择状态">
<a-select-option value="active">活跃</a-select-option>
<a-select-option value="inactive">非活跃</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="联系电话" name="phone" :rules="[{ required: true, message: '请输入联系电话' }]">
<a-input v-model:value="currentAgency.phone" placeholder="请输入联系电话" />
</a-form-item>
<a-form-item label="成立时间" name="establishmentDate" :rules="[{ required: true, message: '请选择成立时间' }]">
<a-input v-model:value="currentAgency.establishmentDate" type="date" placeholder="请选择成立时间" />
</a-form-item>
<a-form-item label="地址" name="address" :rules="[{ required: true, message: '请输入机构地址' }]">
<a-input.TextArea v-model:value="currentAgency.address" placeholder="请输入机构地址" rows={3} />
<textarea v-model="currentAgency.address" placeholder="请输入机构地址" rows="3" style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 2px; resize: vertical;"></textarea>
</a-form-item>
<a-form-item label="防疫范围" name="epidemicScope" :rules="[{ required: true, message: '请输入防疫范围' }]">
<a-input.TextArea v-model:value="currentAgency.epidemicScope" placeholder="请输入防疫范围" rows={3} />
<textarea v-model="currentAgency.epidemicScope" placeholder="请输入防疫范围" rows="3" style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 2px; resize: vertical;"></textarea>
</a-form-item>
<a-form-item label="备注" name="remarks">
<a-input.TextArea v-model:value="currentAgency.remarks" placeholder="请输入备注信息" rows={2} />
<a-form-item label="备注" name="remarks" :rules="[]">
<textarea v-model="currentAgency.remarks" placeholder="请输入备注信息" rows="2" style="width: 100%; padding: 8px; border: 1px solid #d9d9d9; border-radius: 2px; resize: vertical;"></textarea>
</a-form-item>
<div style="text-align: right;">
@@ -175,6 +191,18 @@
<span style="font-weight: bold; width: 120px; display: inline-block;">备注</span>
<span>{{ viewAgency.remarks }}</span>
</div>
<div style="margin-bottom: 16px;">
<span style="font-weight: bold; width: 120px; display: inline-block;">邮箱</span>
<span>{{ viewAgency.email }}</span>
</div>
<div style="margin-bottom: 16px;">
<span style="font-weight: bold; width: 120px; display: inline-block;">机构描述</span>
<span>{{ viewAgency.description }}</span>
</div>
<div style="margin-bottom: 16px;">
<span style="font-weight: bold; width: 120px; display: inline-block;">状态</span>
<span>{{ viewAgency.status === 'active' ? '活跃' : '非活跃' }}</span>
</div>
</div>
<div style="text-align: right; margin-top: 24px;">
<a-button @click="isViewModalOpen = false">关闭</a-button>
@@ -184,8 +212,8 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { ref, reactive, onMounted, h } from 'vue'
import { message, Button } from 'ant-design-vue'
import api from '@/utils/api'
// 搜索条件
@@ -217,6 +245,10 @@ const isAddEditModalOpen = ref(false)
const isViewModalOpen = ref(false)
const isEdit = ref(false)
// 测试 TextArea 的变量
const testValue1 = ref('')
const testValue2 = ref('')
// 当前编辑/新增的机构
const currentAgency = reactive({
name: '',
@@ -227,79 +259,101 @@ const currentAgency = reactive({
phone: '',
address: '',
epidemicScope: '',
remarks: ''
remarks: '',
email: '',
status: 'active',
establishmentDate: '' // 添加成立时间字段
})
// 当前查看的机构
const viewAgency = ref(null)
// 机构列表数据
const agenciesData = ref([])
const agenciesData = ref([
{
id: '1',
name: '银川市动物防疫中心',
code: 'YCCE-001',
type: 'center',
level: 'city',
manager: '张明',
phone: '13800138001',
address: '银川市金凤区黄河东路123号',
epidemicScope: '银川市全域',
establishmentDate: '2010-01-01',
email: 'yc@example.com',
description: '负责银川市动物防疫工作',
status: 'active'
},
{
id: '2',
name: '金凤区防疫站',
code: 'JFJY-001',
type: 'station',
level: 'district',
manager: '李华',
phone: '13800138002',
address: '银川市金凤区北京中路45号',
epidemicScope: '金凤区',
establishmentDate: '2012-03-15',
email: 'jf@example.com',
description: '负责金凤区动物防疫工作',
status: 'active'
}
])
// 表格列定义
const columns = [
{
title: '机构编号',
dataIndex: 'code',
key: 'code',
width: 120
},
{
title: '机构名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
width: 200
},
{
title: '机构类型',
dataIndex: 'type',
key: 'type',
width: 100,
customRender: ({ text }) => getTypeText(text)
},
{
title: '机构级别',
dataIndex: 'level',
key: 'level',
width: 100,
customRender: ({ text }) => getLevelText(text)
key: 'type'
},
{
title: '负责人',
dataIndex: 'manager',
key: 'manager',
width: 100
dataIndex: 'director',
key: 'director'
},
{
title: '联系电话',
dataIndex: 'phone',
key: 'phone',
width: 120
key: 'phone'
},
{
title: '地址',
title: '机构地址',
dataIndex: 'address',
key: 'address',
ellipsis: true
key: 'address'
},
{
title: '疫范围',
title: '疫情防控范围',
dataIndex: 'epidemicScope',
key: 'epidemicScope',
ellipsis: true
key: 'epidemicScope'
},
{
title: '成立时间',
dataIndex: 'establishmentDate',
key: 'establishmentDate',
width: 120
key: 'establishmentDate'
},
{
title: '操作',
key: 'action',
width: 150,
slots: { customRender: 'action' }
}
title: '邮箱',
dataIndex: 'email',
key: 'email'
},
{
title: '机构描述',
dataIndex: 'description',
key: 'description'
},
{ title: '状态', dataIndex: 'status', key: 'status', width: 80, customRender: ({ text }) => {
return text === 'active' ? '活跃' : '非活跃'
}},
{ title: '操作', key: 'action', width: 150, fixed: 'right' }
]
// 获取机构类型文本
@@ -326,6 +380,7 @@ const getLevelText = (level) => {
// 获取机构列表数据
const getAgenciesList = async () => {
try {
// message.info('开始获取机构列表...')
const response = await api.epidemic.agencies.getList({
keyword: searchKeyword.value,
type: typeFilter.value,
@@ -333,9 +388,19 @@ const getAgenciesList = async () => {
page: pagination.value.current,
pageSize: pagination.value.pageSize
})
if (response.success) {
agenciesData.value = response.data.list
pagination.value.total = response.data.total
// 显示API响应信息
// message.info(`API响应: ${response.success ? '成功' : '失败'}`)
// if (response.data) {
// message.info(`数据长度: ${response.data.list ? response.data.list.length : 0}`)
// message.info(`总数: ${response.data.total || 0}`)
// }
// 判断响应是否成功后端返回code:200表示成功
if (response.code === 200) {
agenciesData.value = response.data.list || []
pagination.value.total = response.data.total || 0
message.success('获取机构列表成功')
} else {
message.error(response.message || '获取机构列表失败')
}
@@ -427,12 +492,19 @@ const handleSave = async () => {
formRef.value.validate().then(async () => {
try {
let response
// 创建提交数据对象,进行字段映射
const submitData = {
...currentAgency,
director: currentAgency.manager, // 将manager映射为director
establishmentDate: currentAgency.establishmentDate || new Date().toISOString().split('T')[0] // 确保有成立时间
}
if (isEdit.value) {
// 编辑现有记录
response = await api.epidemic.agencies.update(currentAgency.id, currentAgency)
response = await api.epidemic.agencies.update(currentAgency.id, submitData)
} else {
// 新增记录
response = await api.epidemic.agencies.create(currentAgency)
response = await api.epidemic.agencies.create(submitData)
}
if (response.success) {
isAddEditModalOpen.value = false
@@ -442,11 +514,11 @@ const handleSave = async () => {
message.error(response.message || (isEdit.value ? '编辑失败' : '新增失败'))
}
} catch (error) {
console.error(isEdit.value ? '编辑机构失败:' : '新增机构失败:', error)
message.error(isEdit.value ? '编辑机构失败,请稍后重试' : '新增机构失败,请稍后重试')
console.error('保存机构信息失败:', error)
message.error('保存失败,请稍后重试')
}
}).catch(() => {
message.error('请检查表单数据')
}).catch(errorInfo => {
console.log('表单验证失败:', errorInfo)
})
}
}

View File

@@ -122,11 +122,25 @@
</template>
<script>
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import api from '@/utils/api';
// 修复 useInjectMenu 错误
if (window && !window.__antd_menu_fixed) {
window.__antd_menu_fixed = true;
// 全局错误处理
const originalError = window.onerror;
window.onerror = function(message, source, lineno, colno, error) {
if (message && (message.includes('useInjectMenu') || message.includes('prefixCls'))) {
// 忽略菜单相关的错误
return true;
}
return originalError ? originalError.apply(this, arguments) : false;
};
}
export default {
name: 'SmartCollar',
components: {
@@ -240,7 +254,7 @@ export default {
}
};
状态相关
// 状态相关
const getStatusColor = (status) => {
const statusMap = {
active: 'green',