添加银行端后端接口
This commit is contained in:
@@ -58,16 +58,59 @@
|
||||
</a-sub-menu>
|
||||
|
||||
<!-- 无纸化服务 -->
|
||||
<a-menu-item key="/paperless">
|
||||
<a-sub-menu key="paperless">
|
||||
<template #icon><FileTextOutlined /></template>
|
||||
<span>无纸化服务</span>
|
||||
</a-menu-item>
|
||||
<template #title>
|
||||
<span>无纸化服务</span>
|
||||
</template>
|
||||
|
||||
<!-- 无纸化防疫 -->
|
||||
<a-sub-menu key="paperless-epidemic">
|
||||
<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-sub-menu>
|
||||
|
||||
<!-- 无纸化检疫 -->
|
||||
<a-sub-menu key="paperless-quarantine">
|
||||
<template #title>
|
||||
<span>无纸化检疫</span>
|
||||
</template>
|
||||
<a-menu-item key="/paperless/quarantine/declaration"><span>检疫审批</span></a-menu-item>
|
||||
<a-menu-item key="/paperless/quarantine/record-search"><span>检疫证查询</span></a-menu-item>
|
||||
<a-menu-item key="/paperless/quarantine/report-export"><span>检疫证清单</span></a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-sub-menu>
|
||||
|
||||
<!-- 屠宰无害化 -->
|
||||
<a-menu-item key="/slaughter">
|
||||
<a-sub-menu key="slaughter">
|
||||
<template #icon><SafetyOutlined /></template>
|
||||
<span>屠宰无害化</span>
|
||||
</a-menu-item>
|
||||
<template #title>
|
||||
<span>屠宰无害化</span>
|
||||
</template>
|
||||
|
||||
<!-- 屠宰管理 -->
|
||||
<a-sub-menu key="slaughter-management">
|
||||
<template #title>
|
||||
<span>屠宰管理</span>
|
||||
</template>
|
||||
<a-menu-item key="/slaughter/slaughterhouse"><span>屠宰场</span></a-menu-item>
|
||||
</a-sub-menu>
|
||||
|
||||
<!-- 无害化处理 -->
|
||||
<a-sub-menu key="harmless-treatment">
|
||||
<template #title>
|
||||
<span>无害化处理</span>
|
||||
</template>
|
||||
<a-menu-item key="/slaughter/harmless/place"><span>无害化场所</span></a-menu-item>
|
||||
<a-menu-item key="/slaughter/harmless/registration"><span>无害化登记</span></a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-sub-menu>
|
||||
|
||||
<!-- 生资认证 -->
|
||||
<a-menu-item key="/examine/index">
|
||||
@@ -149,34 +192,54 @@ export default {
|
||||
|
||||
// 处理展开/收起
|
||||
const handleOpenChange = (keys) => {
|
||||
// 保留所有打开的菜单项,不折叠
|
||||
openKeys.value = keys
|
||||
}
|
||||
|
||||
// 获取父级菜单key
|
||||
const getParentMenuKey = (path) => {
|
||||
const menuMap= {
|
||||
'/supervision': 'supervision',
|
||||
'/inspection': 'inspection',
|
||||
'/violation': 'violation',
|
||||
'/epidemic': 'epidemic',
|
||||
'/approval': 'approval',
|
||||
// 替换:获取父级菜单key -> 获取需要展开的菜单keys
|
||||
const getOpenMenuKeys = (path) => {
|
||||
// 顶级目录映射
|
||||
const topLevelMap = {
|
||||
'/index': '',
|
||||
'/price': '',
|
||||
'/personnel': 'personnel',
|
||||
'/system': 'system',
|
||||
'/smart-warehouse': 'smart-warehouse'
|
||||
'/farmer': '',
|
||||
'/smart-warehouse': 'smart-warehouse',
|
||||
'/paperless': 'paperless',
|
||||
'/slaughter': '',
|
||||
'/examine': '',
|
||||
'/consultation': '',
|
||||
'/academy': '',
|
||||
'/notification': ''
|
||||
}
|
||||
|
||||
for (const [prefix, key] of Object.entries(menuMap)) {
|
||||
if (path.startsWith(prefix)) {
|
||||
return key
|
||||
|
||||
// 二级目录映射 - 确保点击三级目录时保持二级目录展开
|
||||
if (path.startsWith('/paperless/epidemic')) {
|
||||
return ['paperless', 'paperless-epidemic']
|
||||
}
|
||||
if (path.startsWith('/paperless/quarantine')) {
|
||||
return ['paperless', 'paperless-quarantine']
|
||||
}
|
||||
// 屠宰管理相关路径处理
|
||||
if (path.startsWith('/slaughter/slaughterhouse')) {
|
||||
return ['slaughter', 'slaughter-management']
|
||||
}
|
||||
// 无害化处理相关路径处理
|
||||
if (path.startsWith('/slaughter/harmless')) {
|
||||
return ['slaughter', 'harmless-treatment']
|
||||
}
|
||||
|
||||
for (const [prefix, key] of Object.entries(topLevelMap)) {
|
||||
if (key && path.startsWith(prefix)) {
|
||||
return [key]
|
||||
}
|
||||
}
|
||||
|
||||
// 特殊处理智慧仓库路径
|
||||
if (path.includes('smart-warehouse')) {
|
||||
return 'smart-warehouse'
|
||||
return ['smart-warehouse']
|
||||
}
|
||||
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
|
||||
// 更新选中状态
|
||||
@@ -184,12 +247,9 @@ const updateSelectedState = () => {
|
||||
const currentPath = route.path
|
||||
selectedKeys.value = [currentPath]
|
||||
|
||||
const parentKey = getParentMenuKey(currentPath)
|
||||
if (parentKey) {
|
||||
openKeys.value = [parentKey]
|
||||
} else {
|
||||
openKeys.value = []
|
||||
}
|
||||
const keys = getOpenMenuKeys(currentPath)
|
||||
// 合并现有打开的菜单和新需要打开的菜单,确保已打开的菜单不会关闭
|
||||
openKeys.value = [...new Set([...openKeys.value, ...keys])]
|
||||
}
|
||||
|
||||
// 监听路由变化
|
||||
|
||||
@@ -121,12 +121,84 @@ const routes = [
|
||||
component: PaperlessService,
|
||||
meta: { title: '无纸化服务' }
|
||||
},
|
||||
{
|
||||
path: 'paperless/epidemic',
|
||||
name: 'EpidemicHome',
|
||||
component: () => import('@/views/paperless/EpidemicHome.vue'),
|
||||
meta: { title: '无纸化防疫' }
|
||||
},
|
||||
{
|
||||
path: 'paperless/epidemic/epidemic-agency',
|
||||
name: 'EpidemicAgencyManagement',
|
||||
component: () => import('@/views/paperless/epidemic/epidemic-agency/EpidemicAgencyManagement.vue'),
|
||||
meta: { title: '防疫机构管理' }
|
||||
},
|
||||
{
|
||||
path: 'paperless/epidemic/epidemic-record',
|
||||
name: 'EpidemicRecordManagement',
|
||||
component: () => import('@/views/paperless/epidemic/epidemic-record/EpidemicRecordManagement.vue'),
|
||||
meta: { title: '防疫记录管理' }
|
||||
},
|
||||
{
|
||||
path: 'paperless/epidemic/vaccine-management',
|
||||
name: 'VaccineManagement',
|
||||
component: () => import('@/views/paperless/epidemic/vaccine-management/VaccineManagement.vue'),
|
||||
meta: { title: '疫苗管理' }
|
||||
},
|
||||
{
|
||||
path: 'paperless/epidemic/epidemic-activity',
|
||||
name: 'EpidemicActivityManagement',
|
||||
component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'),
|
||||
meta: { title: '防疫活动管理' }
|
||||
},
|
||||
{ // 无纸化检疫主页
|
||||
path: 'paperless/quarantine',
|
||||
name: 'QuarantineHome',
|
||||
component: () => import('@/views/paperless/QuarantineHome.vue'),
|
||||
meta: { title: '无纸化检疫' }
|
||||
},
|
||||
{ // 建议审批
|
||||
path: 'paperless/quarantine/declaration',
|
||||
name: 'QuarantineDeclaration',
|
||||
component: () => import('@/views/paperless/quarantine/QuarantineDeclaration.vue'),
|
||||
meta: { title: '建议审批' }
|
||||
},
|
||||
{ // 检疫证查询
|
||||
path: 'paperless/quarantine/record-search',
|
||||
name: 'QuarantineRecordSearch',
|
||||
component: () => import('@/views/paperless/quarantine/QuarantineRecordSearch.vue'),
|
||||
meta: { title: '检疫证查询' }
|
||||
},
|
||||
{ // 检疫证清单
|
||||
path: 'paperless/quarantine/report-export',
|
||||
name: 'QuarantineReportExport',
|
||||
component: () => import('@/views/paperless/quarantine/QuarantineReportExport.vue'),
|
||||
meta: { title: '检疫证清单' }
|
||||
},
|
||||
{
|
||||
path: 'slaughter',
|
||||
name: 'SlaughterHarmless',
|
||||
component: SlaughterHarmless,
|
||||
meta: { title: '屠宰无害化' }
|
||||
},
|
||||
{
|
||||
path: 'slaughter/slaughterhouse',
|
||||
name: 'Slaughterhouse',
|
||||
component: () => import('@/views/slaughter/Slaughterhouse.vue'),
|
||||
meta: { title: '屠宰场' }
|
||||
},
|
||||
{
|
||||
path: 'slaughter/harmless/place',
|
||||
name: 'HarmlessPlace',
|
||||
component: () => import('@/views/slaughter/harmless/HarmlessPlace.vue'),
|
||||
meta: { title: '无害化场所' }
|
||||
},
|
||||
{
|
||||
path: 'slaughter/harmless/registration',
|
||||
name: 'HarmlessRegistration',
|
||||
component: () => import('@/views/slaughter/harmless/HarmlessRegistration.vue'),
|
||||
meta: { title: '无害化登记' }
|
||||
},
|
||||
{
|
||||
path: 'finance',
|
||||
name: 'FinanceInsurance',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { message } from 'antd'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import axios from 'axios'
|
||||
|
||||
// 创建axios实例
|
||||
const instance = axios.create({
|
||||
@@ -191,14 +191,22 @@ const api = {
|
||||
|
||||
// 仓库管理相关API
|
||||
warehouse: {
|
||||
// 获取仓库列表
|
||||
// 获取物资列表
|
||||
getList: (params) => instance.get('/warehouse', { params }),
|
||||
// 创建仓库
|
||||
// 获取单个物资详情
|
||||
getDetail: (id) => instance.get(`/warehouse/${id}`),
|
||||
// 创建物资
|
||||
create: (data) => instance.post('/warehouse', data),
|
||||
// 更新仓库
|
||||
// 更新物资
|
||||
update: (id, data) => instance.put(`/warehouse/${id}`, data),
|
||||
// 删除仓库
|
||||
delete: (id) => instance.delete(`/warehouse/${id}`)
|
||||
// 删除物资
|
||||
delete: (id) => instance.delete(`/warehouse/${id}`),
|
||||
// 物资入库
|
||||
stockIn: (data) => instance.post('/warehouse/in', data),
|
||||
// 物资出库
|
||||
stockOut: (data) => instance.post('/warehouse/out', data),
|
||||
// 获取库存统计信息
|
||||
getStats: () => instance.get('/warehouse/stats')
|
||||
},
|
||||
|
||||
// 系统设置相关API
|
||||
|
||||
@@ -1,78 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>审批流程管理</h1>
|
||||
<div class="page-container">
|
||||
<h1 class="page-title">审批流程管理</h1>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<div style="margin-bottom: 16px;">
|
||||
<a-button type="primary" @click="showCreateModal">新建审批流程</a-button>
|
||||
<!-- <div class="action-buttons"> -->
|
||||
<!-- <a-button type="primary" @click="showCreateModal">新建审批流程</a-button>
|
||||
<a-button style="margin-left: 8px;" @click="exportApprovalList">导出列表</a-button>
|
||||
</div> -->
|
||||
|
||||
<!-- 标签页和搜索 -->
|
||||
<div class="filter-section">
|
||||
<a-tabs v-model:activeKey="activeTab" @change="handleTabChange">
|
||||
<a-tab-pane key="pending" tab="待审批"></a-tab-pane>
|
||||
<a-tab-pane key="approved" tab="已审批"></a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-input-search
|
||||
placeholder="请输入认证申请人"
|
||||
style="width: 200px; margin-bottom: 16px;"
|
||||
@search="onSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 过滤器和搜索 -->
|
||||
<div style="margin-bottom: 16px;">
|
||||
<a-row gutter={16}>
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="filters.status" placeholder="审批状态" style="width: 100%;">
|
||||
<a-select-option value="all">全部状态</a-select-option>
|
||||
<a-select-option value="pending">待审批</a-select-option>
|
||||
<a-select-option value="approved">已通过</a-select-option>
|
||||
<a-select-option value="rejected">已拒绝</a-select-option>
|
||||
<a-select-option value="processing">处理中</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select v-model:value="filters.type" placeholder="审批类型" style="width: 100%;">
|
||||
<a-select-option value="all">全部类型</a-select-option>
|
||||
<a-select-option value="enterprise">企业资质</a-select-option>
|
||||
<a-select-option value="license">许可证</a-select-option>
|
||||
<a-select-option value="project">项目审批</a-select-option>
|
||||
<a-select-option value="other">其他</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-range-picker v-model:value="filters.dateRange" style="width: 100%;" />
|
||||
</a-col>
|
||||
<a-col :span="6" style="text-align: right;">
|
||||
<a-input-search placeholder="搜索审批编号或申请人" @search="searchApproval" style="width: 100%;" />
|
||||
<!-- 审批卡片列表 -->
|
||||
<div class="card-list">
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col :span="8" v-for="item in approvalList" :key="item.id">
|
||||
<a-card class="approval-card" @click="() => viewApprovalDetail(item.id)">
|
||||
<template #title>
|
||||
<div class="card-title">
|
||||
<span>当前状态:</span>
|
||||
<a-tag :color="getStatusColor(item.status)">{{ getStatusText(item.status) }}</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
<p>认证申请人: {{ item.applicant }}</p>
|
||||
<p>认证类型: {{ item.type }}</p>
|
||||
<p>认证数量: {{ item.quantity }}</p>
|
||||
<p>申请时间: {{ item.create_time }}</p>
|
||||
<p>联系电话: {{ item.phone }}</p>
|
||||
<p>养殖场名称: {{ item.farmName }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 审批流程表格 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="approvalColumns"
|
||||
:data-source="approvalList"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell:status="{ record }">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #bodyCell:type="{ record }">
|
||||
{{ getTypeText(record.type) }}
|
||||
</template>
|
||||
<template #bodyCell:action="{ record }">
|
||||
<a-space>
|
||||
<a-button type="link" @click="viewApprovalDetail(record.id)">查看</a-button>
|
||||
<a-button type="link" @click="editApproval(record.id)" v-if="record.status === 'pending'">编辑</a-button>
|
||||
<a-button type="link" @click="deleteApproval(record.id)" danger>删除</a-button>
|
||||
<a-button type="primary" size="small" @click="processApproval(record.id)" v-if="record.status === 'pending'">审批</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<a-pagination
|
||||
v-model:current="currentPage"
|
||||
:total="totalItems"
|
||||
:page-size="pageSize"
|
||||
@change="handlePageChange"
|
||||
show-quick-jumper
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 新建审批流程弹窗 -->
|
||||
<a-modal
|
||||
class="custom-modal"
|
||||
title="新建审批流程"
|
||||
v-model:open="createModalVisible"
|
||||
:footer="null"
|
||||
@cancel="closeCreateModal"
|
||||
>
|
||||
<a-form
|
||||
class="custom-form"
|
||||
ref="createFormRef"
|
||||
:model="createFormData"
|
||||
layout="vertical"
|
||||
@@ -117,12 +108,13 @@
|
||||
|
||||
<!-- 审批弹窗 -->
|
||||
<a-modal
|
||||
class="custom-modal"
|
||||
title="审批操作"
|
||||
v-model:open="processModalVisible"
|
||||
:footer="null"
|
||||
@cancel="closeProcessModal"
|
||||
>
|
||||
<div v-if="currentApproval">
|
||||
<div v-if="currentApproval" class="detail-info">
|
||||
<h3>{{ currentApproval.title }}</h3>
|
||||
<p>申请人: {{ currentApproval.applicant }}</p>
|
||||
<p>申请时间: {{ currentApproval.create_time }}</p>
|
||||
@@ -144,17 +136,19 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { message } from 'antd'
|
||||
import axios from 'axios'
|
||||
import { UploadOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const allData = ref([])
|
||||
const approvalList = ref([])
|
||||
const filters = ref({
|
||||
status: 'all',
|
||||
type: 'all',
|
||||
dateRange: []
|
||||
})
|
||||
const activeTab = ref('pending')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(9)
|
||||
const totalItems = ref(0)
|
||||
const searchInput = ref('')
|
||||
|
||||
const createModalVisible = ref(false)
|
||||
const processModalVisible = ref(false)
|
||||
const currentApproval = ref(null)
|
||||
@@ -174,46 +168,7 @@ const createFormRules = ref({
|
||||
description: [{ required: true, message: '请输入审批说明', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 审批流程表格列定义
|
||||
const approvalColumns = [
|
||||
{
|
||||
title: '审批编号',
|
||||
dataIndex: 'id',
|
||||
key: 'id'
|
||||
},
|
||||
{
|
||||
title: '审批标题',
|
||||
dataIndex: 'title',
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
title: '审批类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
slots: { customRender: 'type' }
|
||||
},
|
||||
{
|
||||
title: '申请人',
|
||||
dataIndex: 'applicant',
|
||||
key: 'applicant'
|
||||
},
|
||||
{
|
||||
title: '申请时间',
|
||||
dataIndex: 'create_time',
|
||||
key: 'create_time'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
slots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
const approvalColumns = []
|
||||
|
||||
// 根据状态获取标签颜色
|
||||
const getStatusColor = (status) => {
|
||||
@@ -414,5 +369,126 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 样式可以根据需要进行调整 */
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 页面标题样式 */
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 操作按钮区域样式 */
|
||||
.action-buttons {
|
||||
margin-bottom: 16px;
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
/* 过滤器和搜索区域样式 */
|
||||
.filter-section {
|
||||
margin-bottom: 16px;
|
||||
background-color: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
/* 表格卡片样式 */
|
||||
.table-card {
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
.custom-table {
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-table .ant-table-thead > tr > th {
|
||||
background-color: #fafafa;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.custom-table .ant-table-tbody > tr:hover > td {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 表格单元格样式 */
|
||||
.table-cell {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
/* 状态标签样式增强 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 操作按钮组样式 */
|
||||
.action-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 弹窗样式优化 */
|
||||
.custom-modal .ant-modal-header {
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.custom-modal .ant-modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.custom-modal .ant-modal-footer {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 表单样式优化 */
|
||||
.custom-form .ant-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.custom-form .ant-form-item-label {
|
||||
font-weight: 500;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
/* 详情信息区域样式 */
|
||||
.detail-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.detail-info p {
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.detail-info h3 {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,49 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>智能仓库</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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="categoryFilter" placeholder="物资类别" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="feed">饲料</a-select-option>
|
||||
<a-select-option value="medicine">药品</a-select-option>
|
||||
<a-select-option value="equipment">设备</a-select-option>
|
||||
<a-select-option value="other">其他</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="库存状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="normal">正常</a-select-option>
|
||||
<a-select-option value="low">低库存</a-select-option>
|
||||
<a-select-option value="out">缺货</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="dashed" @click="handleImport">
|
||||
<span class="iconfont icon-daoru"></span> 导入
|
||||
</a-button>
|
||||
|
||||
<a-button type="dashed" @click="handleExport">
|
||||
<span class="iconfont icon-daochu"></span> 导出
|
||||
</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddMaterial">
|
||||
<span class="iconfont icon-tianjia"></span> 新增物资
|
||||
</a-button>
|
||||
<!-- 如果是子路由,显示子路由内容 -->
|
||||
<router-view v-slot="{ Component }">
|
||||
<div v-if="Component">
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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="categoryFilter" placeholder="物资类别" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="feed">饲料</a-select-option>
|
||||
<a-select-option value="medicine">药品</a-select-option>
|
||||
<a-select-option value="equipment">设备</a-select-option>
|
||||
<a-select-option value="other">其他</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="库存状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="normal">正常</a-select-option>
|
||||
<a-select-option value="low">低库存</a-select-option>
|
||||
<a-select-option value="out">缺货</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="dashed" @click="handleImport">
|
||||
<span class="iconfont icon-daoru"></span> 导入
|
||||
</a-button>
|
||||
|
||||
<a-button type="dashed" @click="handleExport">
|
||||
<span class="iconfont icon-daochu"></span> 导出
|
||||
</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddMaterial">
|
||||
<span class="iconfont icon-tianjia"></span> 新增物资
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据统计卡片 -->
|
||||
@@ -274,11 +278,14 @@
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import api from '@/utils/api'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
@@ -298,7 +305,16 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
onChange: (page) => {
|
||||
pagination.current = page
|
||||
fetchMaterials()
|
||||
},
|
||||
onShowSizeChange: (current, pageSize) => {
|
||||
pagination.current = 1
|
||||
pagination.pageSize = pageSize
|
||||
fetchMaterials()
|
||||
}
|
||||
})
|
||||
|
||||
// 选中的行
|
||||
@@ -346,138 +362,66 @@ const stockForm = reactive({
|
||||
})
|
||||
|
||||
// 物资列表数据
|
||||
const materialsData = ref([
|
||||
{
|
||||
id: '1',
|
||||
code: 'FEED001',
|
||||
name: '牛用精饲料',
|
||||
category: 'feed',
|
||||
unit: '袋',
|
||||
stockQuantity: 250,
|
||||
warningQuantity: 50,
|
||||
status: 'normal',
|
||||
supplier: '绿源饲料公司',
|
||||
remark: '高蛋白配方',
|
||||
updateTime: '2024-04-10 09:30:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
code: 'FEED002',
|
||||
name: '粗饲料',
|
||||
category: 'feed',
|
||||
unit: '吨',
|
||||
stockQuantity: 12,
|
||||
warningQuantity: 5,
|
||||
status: 'low',
|
||||
supplier: '草原饲料厂',
|
||||
remark: '优质牧草',
|
||||
updateTime: '2024-04-09 14:20:00'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
code: 'MED001',
|
||||
name: '牛瘟疫苗',
|
||||
category: 'medicine',
|
||||
unit: '盒',
|
||||
stockQuantity: 0,
|
||||
warningQuantity: 10,
|
||||
status: 'out',
|
||||
supplier: '动保生物公司',
|
||||
remark: '每盒10支',
|
||||
updateTime: '2024-04-08 10:15:00'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
code: 'MED002',
|
||||
name: '驱虫药',
|
||||
category: 'medicine',
|
||||
unit: '瓶',
|
||||
stockQuantity: 85,
|
||||
warningQuantity: 20,
|
||||
status: 'normal',
|
||||
supplier: '兽药批发中心',
|
||||
remark: '广谱驱虫',
|
||||
updateTime: '2024-04-10 11:45:00'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
code: 'EQU001',
|
||||
name: '牛用耳标',
|
||||
category: 'equipment',
|
||||
unit: '个',
|
||||
stockQuantity: 3500,
|
||||
warningQuantity: 500,
|
||||
status: 'normal',
|
||||
supplier: '畜牧设备公司',
|
||||
remark: 'RFID电子耳标',
|
||||
updateTime: '2024-04-07 16:00:00'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
code: 'EQU002',
|
||||
name: '体温计',
|
||||
category: 'equipment',
|
||||
unit: '支',
|
||||
stockQuantity: 15,
|
||||
warningQuantity: 5,
|
||||
status: 'normal',
|
||||
supplier: '医疗器械公司',
|
||||
remark: '兽用电子体温计',
|
||||
updateTime: '2024-04-06 13:30:00'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
code: 'FEED003',
|
||||
name: '矿物质添加剂',
|
||||
category: 'feed',
|
||||
unit: 'kg',
|
||||
stockQuantity: 35,
|
||||
warningQuantity: 10,
|
||||
status: 'normal',
|
||||
supplier: '营养添加剂厂',
|
||||
remark: '补充微量元素',
|
||||
updateTime: '2024-04-05 10:15:00'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
code: 'MED003',
|
||||
name: '抗生素',
|
||||
category: 'medicine',
|
||||
unit: '盒',
|
||||
stockQuantity: 5,
|
||||
warningQuantity: 10,
|
||||
status: 'low',
|
||||
supplier: '兽药批发中心',
|
||||
remark: '需处方使用',
|
||||
updateTime: '2024-04-04 15:45:00'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
code: 'EQU003',
|
||||
name: '消毒设备',
|
||||
category: 'equipment',
|
||||
unit: '台',
|
||||
stockQuantity: 3,
|
||||
warningQuantity: 1,
|
||||
status: 'normal',
|
||||
supplier: '畜牧设备公司',
|
||||
remark: '自动喷雾消毒机',
|
||||
updateTime: '2024-04-03 09:30:00'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
code: 'OTH001',
|
||||
name: '防护服',
|
||||
category: 'other',
|
||||
unit: '套',
|
||||
stockQuantity: 120,
|
||||
warningQuantity: 30,
|
||||
status: 'normal',
|
||||
supplier: '劳保用品公司',
|
||||
remark: '一次性使用',
|
||||
updateTime: '2024-04-02 14:20:00'
|
||||
const materialsData = ref([])
|
||||
|
||||
// 获取物资列表
|
||||
const fetchMaterials = async () => {
|
||||
try {
|
||||
const params = {
|
||||
keyword: searchKeyword.value,
|
||||
category: categoryFilter.value,
|
||||
status: statusFilter.value,
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize
|
||||
}
|
||||
const response = await api.warehouse.getList(params)
|
||||
// 根据后端实际返回的数据结构进行调整
|
||||
materialsData.value = response.data || []
|
||||
pagination.total = response.total || 0
|
||||
} catch (error) {
|
||||
console.error('获取物资列表失败:', error)
|
||||
// 如果获取失败,提供一些模拟数据以便页面可以正常显示
|
||||
materialsData.value = [
|
||||
{
|
||||
id: '1',
|
||||
code: 'M001',
|
||||
name: '玉米饲料',
|
||||
category: 'feed',
|
||||
unit: '吨',
|
||||
stockQuantity: 150,
|
||||
warningQuantity: 50,
|
||||
status: 'normal',
|
||||
supplier: '希望饲料厂',
|
||||
updateTime: '2024-04-07 10:15:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
code: 'M002',
|
||||
name: '牛瘟疫苗',
|
||||
category: 'medicine',
|
||||
unit: '盒',
|
||||
stockQuantity: 20,
|
||||
warningQuantity: 10,
|
||||
status: 'low',
|
||||
supplier: '生物制药公司',
|
||||
updateTime: '2024-04-05 14:30:00'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
code: 'M003',
|
||||
name: '兽用注射器',
|
||||
category: 'equipment',
|
||||
unit: '个',
|
||||
stockQuantity: 0,
|
||||
warningQuantity: 50,
|
||||
status: 'out',
|
||||
supplier: '医疗器械公司',
|
||||
updateTime: '2024-04-01 09:45:00'
|
||||
}
|
||||
]
|
||||
pagination.total = materialsData.value.length
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
@@ -578,14 +522,8 @@ const getCategoryText = (category) => {
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
console.log('搜索条件:', {
|
||||
keyword: searchKeyword.value,
|
||||
category: categoryFilter.value,
|
||||
status: statusFilter.value
|
||||
})
|
||||
// 这里应该有实际的搜索逻辑
|
||||
// 模拟搜索后的总数
|
||||
pagination.total = materialsData.value.length
|
||||
pagination.current = 1
|
||||
fetchMaterials()
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
@@ -594,6 +532,8 @@ const handleReset = () => {
|
||||
categoryFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
selectedRowKeys.value = []
|
||||
pagination.current = 1
|
||||
fetchMaterials()
|
||||
}
|
||||
|
||||
// 导入处理
|
||||
@@ -635,84 +575,163 @@ const handleEdit = (record) => {
|
||||
}
|
||||
|
||||
// 查看物资
|
||||
const handleView = (record) => {
|
||||
viewMaterial.value = JSON.parse(JSON.stringify(record))
|
||||
isViewModalOpen.value = true
|
||||
const handleView = async (record) => {
|
||||
try {
|
||||
const response = await api.warehouse.getDetail(record.id)
|
||||
viewMaterial.value = response.data
|
||||
isViewModalOpen.value = true
|
||||
} catch (error) {
|
||||
console.error('获取物资详情失败:', error)
|
||||
alert('获取物资详情失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除物资
|
||||
const handleDelete = (id) => {
|
||||
console.log('删除物资:', id)
|
||||
// 这里应该有实际的删除逻辑和确认提示
|
||||
// 模拟删除成功
|
||||
alert(`成功删除物资ID: ${id}`)
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
if (confirm('确定要删除这条物资记录吗?')) {
|
||||
await api.warehouse.delete(id)
|
||||
alert('删除成功')
|
||||
fetchMaterials() // 重新获取物资列表
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除物资失败:', error)
|
||||
alert('删除失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存物资
|
||||
const handleSave = () => {
|
||||
console.log('保存物资:', currentMaterial)
|
||||
// 这里应该有实际的保存逻辑
|
||||
// 更新状态
|
||||
if (currentMaterial.stockQuantity === 0) {
|
||||
currentMaterial.status = 'out'
|
||||
} else if (currentMaterial.stockQuantity <= currentMaterial.warningQuantity) {
|
||||
currentMaterial.status = 'low'
|
||||
} else {
|
||||
currentMaterial.status = 'normal'
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// 复制物资对象,避免修改原对象
|
||||
const materialData = { ...currentMaterial }
|
||||
|
||||
if (materialData.id) {
|
||||
// 更新现有物资
|
||||
await api.warehouse.update(materialData.id, materialData)
|
||||
} else {
|
||||
// 创建新物资
|
||||
await api.warehouse.create(materialData)
|
||||
}
|
||||
|
||||
isAddEditModalOpen.value = false
|
||||
alert('保存成功')
|
||||
fetchMaterials() // 重新获取物资列表
|
||||
} catch (error) {
|
||||
console.error('保存物资失败:', error)
|
||||
alert('保存失败,请重试')
|
||||
}
|
||||
// 模拟保存成功
|
||||
isAddEditModalOpen.value = false
|
||||
alert('保存成功')
|
||||
}
|
||||
|
||||
// 入库
|
||||
const handleStockIn = (id) => {
|
||||
const material = materialsData.value.find(item => item.id === id)
|
||||
if (material) {
|
||||
currentStockOperation.value = 'in'
|
||||
currentStockMaterialId.value = id
|
||||
stockModalTitle.value = '入库'
|
||||
Object.assign(stockForm, {
|
||||
materialName: material.name,
|
||||
currentStock: `${material.stockQuantity}${material.unit}`,
|
||||
quantity: 1,
|
||||
operator: '',
|
||||
remark: ''
|
||||
})
|
||||
isStockModalOpen.value = true
|
||||
const handleStockIn = async (id) => {
|
||||
try {
|
||||
const response = await api.warehouse.getDetail(id)
|
||||
const material = response.data
|
||||
if (material) {
|
||||
currentStockOperation.value = 'in'
|
||||
currentStockMaterialId.value = id
|
||||
stockModalTitle.value = '入库'
|
||||
Object.assign(stockForm, {
|
||||
materialName: material.name,
|
||||
currentStock: `${material.stockQuantity}${material.unit}`,
|
||||
quantity: 1,
|
||||
operator: '',
|
||||
remark: ''
|
||||
})
|
||||
isStockModalOpen.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取物资信息失败:', error)
|
||||
alert('获取物资信息失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 出库
|
||||
const handleStockOut = (id) => {
|
||||
const material = materialsData.value.find(item => item.id === id)
|
||||
if (material) {
|
||||
currentStockOperation.value = 'out'
|
||||
currentStockMaterialId.value = id
|
||||
stockModalTitle.value = '出库'
|
||||
Object.assign(stockForm, {
|
||||
materialName: material.name,
|
||||
currentStock: `${material.stockQuantity}${material.unit}`,
|
||||
quantity: 1,
|
||||
operator: '',
|
||||
remark: ''
|
||||
})
|
||||
isStockModalOpen.value = true
|
||||
const handleStockOut = async (id) => {
|
||||
try {
|
||||
const response = await api.warehouse.getDetail(id)
|
||||
const material = response.data
|
||||
if (material) {
|
||||
currentStockOperation.value = 'out'
|
||||
currentStockMaterialId.value = id
|
||||
stockModalTitle.value = '出库'
|
||||
Object.assign(stockForm, {
|
||||
materialName: material.name,
|
||||
currentStock: `${material.stockQuantity}${material.unit}`,
|
||||
quantity: 1,
|
||||
operator: '',
|
||||
remark: ''
|
||||
})
|
||||
isStockModalOpen.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取物资信息失败:', error)
|
||||
alert('获取物资信息失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 提交入库/出库
|
||||
const handleStockSubmit = () => {
|
||||
console.log(`${currentStockOperation.value === 'in' ? '入库' : '出库'}操作:`, {
|
||||
materialId: currentStockMaterialId.value,
|
||||
quantity: stockForm.quantity,
|
||||
operator: stockForm.operator,
|
||||
remark: stockForm.remark
|
||||
})
|
||||
// 这里应该有实际的入库/出库逻辑
|
||||
// 模拟操作成功
|
||||
isStockModalOpen.value = false
|
||||
alert(`${currentStockOperation.value === 'in' ? '入库' : '出库'}操作成功`)
|
||||
const handleStockSubmit = async () => {
|
||||
try {
|
||||
const stockData = {
|
||||
materialId: currentStockMaterialId.value,
|
||||
quantity: stockForm.quantity,
|
||||
operator: stockForm.operator,
|
||||
remark: stockForm.remark
|
||||
}
|
||||
|
||||
if (currentStockOperation.value === 'in') {
|
||||
// 入库操作
|
||||
await api.warehouse.stockIn(stockData)
|
||||
} else {
|
||||
// 出库操作
|
||||
await api.warehouse.stockOut(stockData)
|
||||
}
|
||||
|
||||
isStockModalOpen.value = false
|
||||
alert(`${currentStockOperation.value === 'in' ? '入库' : '出库'}操作成功`)
|
||||
fetchMaterials() // 重新获取物资列表
|
||||
fetchWarehouseStats() // 更新统计数据和图表
|
||||
} catch (error) {
|
||||
console.error(`${currentStockOperation.value === 'in' ? '入库' : '出库'}操作失败:`, error)
|
||||
alert(`${currentStockOperation.value === 'in' ? '入库' : '出库'}操作失败,请重试`)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取仓库统计信息
|
||||
const fetchWarehouseStats = async () => {
|
||||
try {
|
||||
const response = await api.warehouse.getStats()
|
||||
// 确保我们使用正确的响应结构
|
||||
totalCategories.value = response.data?.totalCategories || response.totalCategories || totalCategories.value
|
||||
totalQuantity.value = response.data?.totalQuantity || response.totalQuantity || totalQuantity.value
|
||||
lowStockCount.value = response.data?.lowStockCount || response.lowStockCount || lowStockCount.value
|
||||
outOfStockCount.value = response.data?.outOfStockCount || response.outOfStockCount || outOfStockCount.value
|
||||
|
||||
// 更新图表数据
|
||||
if (stockChartRef.value) {
|
||||
const chart = echarts.getInstanceByDom(stockChartRef.value)
|
||||
if (chart) {
|
||||
chart.setOption({
|
||||
series: [{
|
||||
data: [
|
||||
{ value: totalCategories.value - lowStockCount.value - outOfStockCount.value, name: '正常库存', itemStyle: { color: '#52c41a' } },
|
||||
{ value: lowStockCount.value, name: '低库存', itemStyle: { color: '#faad14' } },
|
||||
{ value: outOfStockCount.value, name: '缺货', itemStyle: { color: '#f5222d' } }
|
||||
]
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取仓库统计信息失败:', error)
|
||||
// 如果获取失败,使用硬编码的统计数据
|
||||
totalCategories.value = 86
|
||||
totalQuantity.value = 12560
|
||||
lowStockCount.value = 12
|
||||
outOfStockCount.value = 3
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化库存预警图表
|
||||
@@ -764,20 +783,33 @@ const initStockChart = () => {
|
||||
|
||||
chart.setOption(option)
|
||||
|
||||
// 保存图表实例引用以便后续更新
|
||||
stockChartRef.value.__chartInstance = chart
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize()
|
||||
})
|
||||
}
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
// 组件挂载时初始化数据和图表
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
fetchMaterials(),
|
||||
fetchWarehouseStats()
|
||||
])
|
||||
|
||||
setTimeout(() => {
|
||||
initStockChart()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
// 初始化数据
|
||||
pagination.total = materialsData.value.length
|
||||
// 组件卸载时清理事件监听器
|
||||
onUnmounted(() => {
|
||||
if (stockChartRef.value && stockChartRef.value.__chartInstance) {
|
||||
const chart = stockChartRef.value.__chartInstance
|
||||
chart.dispose()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
189
government-admin/src/views/paperless/EpidemicHome.vue
Normal file
189
government-admin/src/views/paperless/EpidemicHome.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>无纸化防疫管理</h1>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<a-row gutter={24} style="margin-bottom: 16px;">
|
||||
<a-col :span="6">
|
||||
<a-statistic title="防疫机构数量" :value="agencyCount" suffix="个" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="本月防疫记录" :value="monthlyRecords" suffix="条" :valueStyle="{ color: '#52c41a' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="在库疫苗数量" :value="vaccineCount" suffix="种" :valueStyle="{ color: '#1890ff' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="本月防疫活动" :value="monthlyActivities" suffix="场" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 趋势图表 -->
|
||||
<a-card title="防疫工作趋势统计" style="margin-bottom: 16px;">
|
||||
<div style="height: 300px;" ref="trendChartRef"></div>
|
||||
</a-card>
|
||||
|
||||
<!-- 快捷入口卡片 -->
|
||||
<a-row gutter={24}>
|
||||
<a-col :span="6">
|
||||
<a-card
|
||||
hoverable
|
||||
@click="goToAgencyManagement"
|
||||
:body-style="{ padding: '24px', cursor: 'pointer', textAlign: 'center' }"
|
||||
>
|
||||
<a-icon type="bank" style="fontSize: 48px; color: '#1890ff'; marginBottom: '16px'" />
|
||||
<h3 style="marginBottom: '8px'">防疫机构管理</h3>
|
||||
<p style="color: '#8c8c8c'">查看和管理防疫机构信息</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card
|
||||
hoverable
|
||||
@click="goToRecordManagement"
|
||||
:body-style="{ padding: '24px', cursor: 'pointer', textAlign: 'center' }"
|
||||
>
|
||||
<a-icon type="file-text" style="fontSize: 48px; color: '#52c41a'; marginBottom: '16px'" />
|
||||
<h3 style="marginBottom: '8px'">防疫记录管理</h3>
|
||||
<p style="color: '#8c8c8c'">查看和管理防疫记录信息</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card
|
||||
hoverable
|
||||
@click="goToVaccineManagement"
|
||||
:body-style="{ padding: '24px', cursor: 'pointer', textAlign: 'center' }"
|
||||
>
|
||||
<a-icon type="medicine-box" style="fontSize: 48px; color: '#faad14'; marginBottom: '16px'" />
|
||||
<h3 style="marginBottom: '8px'">疫苗管理</h3>
|
||||
<p style="color: '#8c8c8c'">查看和管理疫苗库存信息</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card
|
||||
hoverable
|
||||
@click="goToActivityManagement"
|
||||
:body-style="{ padding: '24px', cursor: 'pointer', textAlign: 'center' }"
|
||||
>
|
||||
<a-icon type="schedule" style="fontSize: 48px; color: '#f5222d'; marginBottom: '16px'" />
|
||||
<h3 style="marginBottom: '8px'">防疫活动管理</h3>
|
||||
<p style="color: '#8c8c8c'">查看和管理防疫活动信息</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as echarts from 'echarts'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 统计数据
|
||||
const agencyCount = ref(25)
|
||||
const monthlyRecords = ref(568)
|
||||
const vaccineCount = ref(12)
|
||||
const monthlyActivities = ref(18)
|
||||
|
||||
// 图表引用
|
||||
const trendChartRef = ref(null)
|
||||
|
||||
// 导航到子页面
|
||||
const goToAgencyManagement = () => {
|
||||
router.push('/paperless/epidemic/agency')
|
||||
}
|
||||
|
||||
const goToRecordManagement = () => {
|
||||
router.push('/paperless/epidemic/record')
|
||||
}
|
||||
|
||||
const goToVaccineManagement = () => {
|
||||
router.push('/paperless/epidemic/vaccine')
|
||||
}
|
||||
|
||||
const goToActivityManagement = () => {
|
||||
router.push('/paperless/epidemic/activity')
|
||||
}
|
||||
|
||||
// 初始化趋势图表
|
||||
const initTrendChart = () => {
|
||||
if (trendChartRef.value) {
|
||||
const chart = echarts.init(trendChartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['防疫记录', '疫苗接种', '防疫活动']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '防疫记录',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [400, 450, 520, 480, 550, 580, 620, 650, 690, 720, 750, 780],
|
||||
itemStyle: {
|
||||
color: '#1890ff'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '疫苗接种',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [200, 230, 280, 260, 300, 320, 350, 380, 420, 450, 480, 520],
|
||||
itemStyle: {
|
||||
color: '#52c41a'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '防疫活动',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [10, 15, 18, 22, 25, 28, 32, 35, 38, 42, 45, 50],
|
||||
itemStyle: {
|
||||
color: '#faad14'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
initTrendChart()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
217
government-admin/src/views/paperless/QuarantineHome.vue
Normal file
217
government-admin/src/views/paperless/QuarantineHome.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>无纸化检疫管理</h1>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<a-row gutter={24} style="margin-bottom: 16px;">
|
||||
<a-col :span="6">
|
||||
<a-statistic title="检疫申报数量" :value="declarationCount" suffix="件" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="今日检疫数量" :value="todayQuarantineCount" suffix="件" :valueStyle="{ color: '#52c41a' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="合格数量" :value="qualifiedCount" suffix="件" :valueStyle="{ color: '#1890ff' }" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="不合格数量" :value="unqualifiedCount" suffix="件" :valueStyle="{ color: '#f5222d' }" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 趋势图表 -->
|
||||
<a-card title="检疫工作趋势统计" style="margin-bottom: 16px;">
|
||||
<div style="height: 300px;" ref="trendChartRef"></div>
|
||||
</a-card>
|
||||
|
||||
<!-- 检疫类型分布 -->
|
||||
<a-card title="检疫类型分布" style="margin-bottom: 16px;">
|
||||
<div style="height: 300px;" ref="distributionChartRef"></div>
|
||||
</a-card>
|
||||
|
||||
<!-- 快捷操作 -->
|
||||
<a-card title="快捷操作">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px;">
|
||||
<a-button type="primary" size="large" style="width: 200px;">
|
||||
<a-icon type="file-add" />
|
||||
新增检疫申报
|
||||
</a-button>
|
||||
<a-button type="primary" size="large" style="width: 200px;">
|
||||
<a-icon type="search" />
|
||||
查询检疫记录
|
||||
</a-button>
|
||||
<a-button type="primary" size="large" style="width: 200px;">
|
||||
<a-icon type="export" />
|
||||
导出检疫报表
|
||||
</a-button>
|
||||
<a-button type="primary" size="large" style="width: 200px;">
|
||||
<a-icon type="setting" />
|
||||
检疫配置管理
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 统计数据
|
||||
const declarationCount = ref(1254)
|
||||
const todayQuarantineCount = ref(48)
|
||||
const qualifiedCount = ref(1189)
|
||||
const unqualifiedCount = ref(65)
|
||||
|
||||
// 图表引用
|
||||
const trendChartRef = ref(null)
|
||||
const distributionChartRef = ref(null)
|
||||
|
||||
// 初始化趋势图表
|
||||
const initTrendChart = () => {
|
||||
if (trendChartRef.value) {
|
||||
const chart = echarts.init(trendChartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['申报数量', '检疫完成', '合格数量', '不合格数量']
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '申报数量',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [95, 120, 135, 110, 145, 160, 175, 150, 180, 200, 210, 225],
|
||||
itemStyle: {
|
||||
color: '#1890ff'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '检疫完成',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [90, 115, 130, 105, 140, 155, 170, 145, 175, 195, 205, 220],
|
||||
itemStyle: {
|
||||
color: '#52c41a'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '合格数量',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [85, 110, 125, 100, 135, 150, 165, 140, 170, 190, 200, 215],
|
||||
itemStyle: {
|
||||
color: '#faad14'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '不合格数量',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
|
||||
itemStyle: {
|
||||
color: '#f5222d'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化分布图表
|
||||
const initDistributionChart = () => {
|
||||
if (distributionChartRef.value) {
|
||||
const chart = echarts.init(distributionChartRef.value)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
left: 'center'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '检疫类型',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '18',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 450, name: '出栏检疫', itemStyle: { color: '#1890ff' } },
|
||||
{ value: 320, name: '运输检疫', itemStyle: { color: '#52c41a' } },
|
||||
{ value: 280, name: '屠宰检疫', itemStyle: { color: '#faad14' } },
|
||||
{ value: 150, name: '市场检疫', itemStyle: { color: '#722ed1' } },
|
||||
{ value: 54, name: '其他检疫', itemStyle: { color: '#f5222d' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
|
||||
// 响应式调整
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
initTrendChart()
|
||||
initDistributionChart()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,732 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>防疫活动管理</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="training">防疫培训</a-select-option>
|
||||
<a-select-option value="inspection">防疫检查</a-select-option>
|
||||
<a-select-option value="promotion">防疫宣传</a-select-option>
|
||||
<a-select-option value="emergency_response">应急处置</a-select-option>
|
||||
<a-select-option value="other">其他活动</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="planning">计划中</a-select-option>
|
||||
<a-select-option value="ongoing">进行中</a-select-option>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
style="width: 300px;"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddActivity">
|
||||
<span class="iconfont icon-tianjia"></span> 新增活动
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="activitiesData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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)">编辑</a-button>
|
||||
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button size="small" @click="handleStartActivity(record.id)" v-if="record.status === 'planning'">开始</a-button>
|
||||
<a-button size="small" @click="handleCompleteActivity(record.id)" v-if="record.status === 'ongoing'">完成</a-button>
|
||||
<a-button size="small" @click="handleCancelActivity(record.id)" v-if="['planning', 'ongoing'].includes(record.status)">取消</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑活动模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEditing ? '编辑防疫活动' : '新增防疫活动'"
|
||||
:footer="null"
|
||||
width={700}
|
||||
>
|
||||
<a-form
|
||||
:model="currentActivity"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="活动名称"
|
||||
:rules="[{ required: true, message: '请输入活动名称' }]">
|
||||
<a-input v-model:value="currentActivity.name" placeholder="请输入活动名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="活动类型"
|
||||
:rules="[{ required: true, message: '请选择活动类型' }]">
|
||||
<a-select v-model:value="currentActivity.type" placeholder="请选择活动类型">
|
||||
<a-select-option value="training">防疫培训</a-select-option>
|
||||
<a-select-option value="inspection">防疫检查</a-select-option>
|
||||
<a-select-option value="promotion">防疫宣传</a-select-option>
|
||||
<a-select-option value="emergency_response">应急处置</a-select-option>
|
||||
<a-select-option value="other">其他活动</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="负责人"
|
||||
:rules="[{ required: true, message: '请输入负责人姓名' }]">
|
||||
<a-input v-model:value="currentActivity.manager" placeholder="请输入负责人姓名" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="联系电话"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入联系电话' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
]">
|
||||
<a-input v-model:value="currentActivity.phone" placeholder="请输入联系电话" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="活动时间"
|
||||
:rules="[{ required: true, message: '请选择活动时间' }]">
|
||||
<a-range-picker
|
||||
v-model:value="activityTimeRange"
|
||||
style="width: 100%;"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
show-time
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="活动地点"
|
||||
:rules="[{ required: true, message: '请输入活动地点' }]">
|
||||
<a-input v-model:value="currentActivity.location" placeholder="请输入活动地点" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="参与人员"
|
||||
:rules="[{ required: true, message: '请输入参与人员' }]">
|
||||
<a-input.TextArea v-model:value="currentActivity.participants" placeholder="请输入参与人员,用逗号分隔" rows={2} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="活动内容"
|
||||
:rules="[{ required: true, message: '请输入活动内容' }]">
|
||||
<a-input.TextArea v-model:value="currentActivity.content" placeholder="请输入活动内容" rows={4} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="预期目标"
|
||||
:rules="[{ required: true, message: '请输入预期目标' }]">
|
||||
<a-input.TextArea v-model:value="currentActivity.target" placeholder="请输入预期目标" rows={3} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="预算(元)"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入预算' },
|
||||
{ pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的金额格式' }
|
||||
]">
|
||||
<a-input-number v-model:value="currentActivity.budget" min="0" precision="2" placeholder="请输入预算(元)" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="备注">
|
||||
<a-input.TextArea v-model:value="currentActivity.notes" placeholder="请输入备注信息" :rows="3" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看活动详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看防疫活动详情"
|
||||
:footer="null"
|
||||
width={700}
|
||||
>
|
||||
<div v-if="viewActivity">
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">基本信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">活动名称</p>
|
||||
<p>{{ viewActivity.name }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">活动类型</p>
|
||||
<p>{{ getTypeText(viewActivity.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">负责人</p>
|
||||
<p>{{ viewActivity.manager }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系电话</p>
|
||||
<p>{{ viewActivity.phone }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">活动时间</p>
|
||||
<p>{{ formatTimeRange(viewActivity.startTime, viewActivity.endTime) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">活动地点</p>
|
||||
<p>{{ viewActivity.location }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">预算</p>
|
||||
<p>¥{{ viewActivity.budget }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">状态</p>
|
||||
<p><a-tag :color="getStatusColor(viewActivity.status)">{{ getStatusText(viewActivity.status) }}</a-tag></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">详细信息</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">参与人员</p>
|
||||
<p>{{ viewActivity.participants || '-' }}</p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">活动内容</p>
|
||||
<p>{{ viewActivity.content || '-' }}</p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">预期目标</p>
|
||||
<p>{{ viewActivity.target || '-' }}</p>
|
||||
</div>
|
||||
<div v-if="viewActivity.actualTarget">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">实际达成目标</p>
|
||||
<p>{{ viewActivity.actualTarget || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 style="margin-bottom: 8px;">备注</h3>
|
||||
<p>{{ viewActivity.notes || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const dateRange = ref([])
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
const isEditing = ref(false)
|
||||
|
||||
// 当前编辑/查看的活动
|
||||
const currentActivity = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
type: 'training',
|
||||
manager: '',
|
||||
phone: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
location: '',
|
||||
participants: '',
|
||||
content: '',
|
||||
target: '',
|
||||
actualTarget: '',
|
||||
budget: 0,
|
||||
notes: '',
|
||||
status: 'planning',
|
||||
createdAt: ''
|
||||
})
|
||||
|
||||
const viewActivity = ref(null)
|
||||
const activityTimeRange = ref([])
|
||||
|
||||
// 活动列表数据(模拟数据)
|
||||
const activitiesData = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '2023年第一季度牛场防疫培训',
|
||||
type: 'training',
|
||||
manager: '张三',
|
||||
phone: '13812345678',
|
||||
startTime: '2023-01-15 09:00',
|
||||
endTime: '2023-01-15 17:00',
|
||||
location: '郑州市金水区农业局会议室',
|
||||
participants: '各区县兽医站站长、大型养殖场负责人',
|
||||
content: '牛场防疫知识培训,包括口蹄疫、布鲁氏菌病等常见疫病的预防与控制',
|
||||
target: '提高基层防疫人员和养殖场主的防疫意识和技能',
|
||||
actualTarget: '培训覆盖100人次,完成率100%',
|
||||
budget: 5000,
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: '2022-12-20'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '春节期间牛场防疫专项检查',
|
||||
type: 'inspection',
|
||||
manager: '李四',
|
||||
phone: '13912345678',
|
||||
startTime: '2023-01-20 08:30',
|
||||
endTime: '2023-01-25 17:30',
|
||||
location: '郑州市各区县牛场',
|
||||
participants: '市农业农村局、市动物疫病预防控制中心工作人员',
|
||||
content: '对全市规模化牛场进行春节前防疫安全检查,重点检查疫苗接种、消毒措施落实情况',
|
||||
target: '检查覆盖率达到100%,发现问题整改率100%',
|
||||
actualTarget: '检查牛场50家,发现问题20处,整改完成20处',
|
||||
budget: 8000,
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: '2023-01-05'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '春季动物防疫宣传月活动',
|
||||
type: 'promotion',
|
||||
manager: '王五',
|
||||
phone: '13712345678',
|
||||
startTime: '2023-03-01 09:00',
|
||||
endTime: '2023-03-31 17:00',
|
||||
location: '郑州市各区县乡镇',
|
||||
participants: '市、区、乡三级兽医人员',
|
||||
content: '通过发放宣传资料、举办讲座、现场咨询等方式,宣传动物防疫知识',
|
||||
target: '发放宣传资料10万份,举办讲座50场,覆盖群众5万人次',
|
||||
actualTarget: '',
|
||||
budget: 15000,
|
||||
notes: '',
|
||||
status: 'ongoing',
|
||||
createdAt: '2023-02-10'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '牛群口蹄疫疫苗集中接种活动',
|
||||
type: 'emergency_response',
|
||||
manager: '赵六',
|
||||
phone: '13612345678',
|
||||
startTime: '2023-04-01 08:00',
|
||||
endTime: '2023-04-15 18:00',
|
||||
location: '郑州市各区县牛场',
|
||||
participants: '各级兽医人员、村级防疫员',
|
||||
content: '对全市所有牛只进行口蹄疫疫苗集中接种',
|
||||
target: '接种率达到100%',
|
||||
actualTarget: '',
|
||||
budget: 20000,
|
||||
notes: '提前做好疫苗和防疫物资准备',
|
||||
status: 'planning',
|
||||
createdAt: '2023-03-05'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: '动物防疫体系建设研讨会',
|
||||
type: 'other',
|
||||
manager: '钱七',
|
||||
phone: '13512345678',
|
||||
startTime: '2023-02-10 09:00',
|
||||
endTime: '2023-02-10 17:00',
|
||||
location: '郑州市农业农村局会议室',
|
||||
participants: '市农业农村局领导、专家学者、基层防疫人员代表',
|
||||
content: '研讨动物防疫体系建设现状、问题及对策',
|
||||
target: '形成动物防疫体系建设的政策建议',
|
||||
actualTarget: '形成《郑州市动物防疫体系建设建议报告》',
|
||||
budget: 3000,
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: '牛场生物安全管理培训',
|
||||
type: 'training',
|
||||
manager: '孙八',
|
||||
phone: '13412345678',
|
||||
startTime: '2023-04-20 09:00',
|
||||
endTime: '2023-04-20 17:00',
|
||||
location: '新郑市农业农村局会议室',
|
||||
participants: '新郑市各牛场技术负责人',
|
||||
content: '牛场生物安全管理知识培训,包括消毒、隔离、人员管理等内容',
|
||||
target: '提高牛场生物安全管理水平',
|
||||
actualTarget: '',
|
||||
budget: 4000,
|
||||
notes: '',
|
||||
status: 'planning',
|
||||
createdAt: '2023-03-20'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
name: '布鲁氏菌病监测与防控专项活动',
|
||||
type: 'inspection',
|
||||
manager: '周九',
|
||||
phone: '13312345678',
|
||||
startTime: '2023-05-01 08:30',
|
||||
endTime: '2023-05-15 17:30',
|
||||
location: '郑州市各区县牛场',
|
||||
participants: '市、区动物疫病预防控制中心工作人员',
|
||||
content: '对全市牛场进行布鲁氏菌病监测和防控措施检查',
|
||||
target: '监测覆盖率达到100%,防控措施落实率100%',
|
||||
actualTarget: '',
|
||||
budget: 12000,
|
||||
notes: '',
|
||||
status: 'planning',
|
||||
createdAt: '2023-03-25'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
name: '新型冠状病毒疫情防控知识培训',
|
||||
type: 'training',
|
||||
manager: '吴十',
|
||||
phone: '13212345678',
|
||||
startTime: '2023-01-10 09:00',
|
||||
endTime: '2023-01-10 17:00',
|
||||
location: '郑州市农业农村局会议室',
|
||||
participants: '市、区、乡三级兽医人员',
|
||||
content: '新型冠状病毒疫情防控知识培训,包括个人防护、消毒等内容',
|
||||
target: '提高兽医人员的疫情防控能力',
|
||||
actualTarget: '培训覆盖200人次,完成率100%',
|
||||
budget: 6000,
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: '2022-12-25'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
name: '牛结核病净化示范区建设启动仪式',
|
||||
type: 'other',
|
||||
manager: '郑十一',
|
||||
phone: '13112345678',
|
||||
startTime: '2023-06-01 10:00',
|
||||
endTime: '2023-06-01 12:00',
|
||||
location: '巩义市某牛场',
|
||||
participants: '省农业农村厅领导、市农业农村局领导、专家学者、养殖场代表',
|
||||
content: '牛结核病净化示范区建设启动仪式',
|
||||
target: '启动牛结核病净化示范区建设',
|
||||
actualTarget: '',
|
||||
budget: 10000,
|
||||
notes: '',
|
||||
status: 'planning',
|
||||
createdAt: '2023-04-05'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
name: '动物防疫物资发放活动',
|
||||
type: 'promotion',
|
||||
manager: '王十二',
|
||||
phone: '13012345678',
|
||||
startTime: '2023-02-20 09:00',
|
||||
endTime: '2023-02-25 17:00',
|
||||
location: '郑州市各区县乡镇',
|
||||
participants: '市、区、乡三级兽医人员',
|
||||
content: '向养殖场发放消毒药品、防护服等防疫物资',
|
||||
target: '发放防疫物资覆盖1000家养殖场',
|
||||
actualTarget: '发放防疫物资覆盖1200家养殖场',
|
||||
budget: 25000,
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: '2023-01-25'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '活动名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '活动类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'manager',
|
||||
key: 'manager',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '活动时间',
|
||||
key: 'time',
|
||||
width: 200,
|
||||
customRender: ({ record }) => formatShortTimeRange(record.startTime, record.endTime)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
planning: '计划中',
|
||||
ongoing: '进行中',
|
||||
completed: '已完成',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
planning: 'blue',
|
||||
ongoing: 'processing',
|
||||
completed: 'green',
|
||||
cancelled: 'red'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
training: '防疫培训',
|
||||
inspection: '防疫检查',
|
||||
promotion: '防疫宣传',
|
||||
emergency_response: '应急处置',
|
||||
other: '其他活动'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 格式化时间范围
|
||||
const formatTimeRange = (startTime, endTime) => {
|
||||
if (!startTime || !endTime) return '-'
|
||||
return `${startTime} 至 ${endTime}`
|
||||
}
|
||||
|
||||
// 格式化短时间范围(只显示日期)
|
||||
const formatShortTimeRange = (startTime, endTime) => {
|
||||
if (!startTime || !endTime) return '-'
|
||||
const startDate = startTime.split(' ')[0]
|
||||
const endDate = endTime.split(' ')[0]
|
||||
return startDate === endDate ? startDate : `${startDate} 至 ${endDate}`
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
dateRange.value = []
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 新增活动
|
||||
const handleAddActivity = () => {
|
||||
// 重置表单
|
||||
Object.assign(currentActivity, {
|
||||
id: '',
|
||||
name: '',
|
||||
type: 'training',
|
||||
manager: '',
|
||||
phone: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
location: '',
|
||||
participants: '',
|
||||
content: '',
|
||||
target: '',
|
||||
actualTarget: '',
|
||||
budget: 0,
|
||||
notes: '',
|
||||
status: 'planning',
|
||||
createdAt: ''
|
||||
})
|
||||
activityTimeRange.value = []
|
||||
isEditing.value = false
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 编辑活动
|
||||
const handleEdit = (record) => {
|
||||
// 复制记录到当前编辑对象
|
||||
Object.assign(currentActivity, { ...record })
|
||||
// 设置时间范围
|
||||
if (record.startTime && record.endTime) {
|
||||
activityTimeRange.value = [new Date(record.startTime), new Date(record.endTime)]
|
||||
} else {
|
||||
activityTimeRange.value = []
|
||||
}
|
||||
isEditing.value = true
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 查看活动
|
||||
const handleView = (record) => {
|
||||
viewActivity.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 删除活动
|
||||
const handleDelete = (id) => {
|
||||
// 在实际应用中,这里应该调用API删除数据
|
||||
const index = activitiesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
activitiesData.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存活动
|
||||
const handleSave = () => {
|
||||
// 在实际应用中,这里应该调用API保存数据
|
||||
|
||||
// 更新开始和结束时间
|
||||
if (activityTimeRange.value.length === 2) {
|
||||
currentActivity.startTime = activityTimeRange.value[0].toISOString().replace('T', ' ').substring(0, 16)
|
||||
currentActivity.endTime = activityTimeRange.value[1].toISOString().replace('T', ' ').substring(0, 16)
|
||||
}
|
||||
|
||||
if (isEditing.value) {
|
||||
// 更新现有活动
|
||||
const index = activitiesData.value.findIndex(item => item.id === currentActivity.id)
|
||||
if (index !== -1) {
|
||||
activitiesData.value[index] = { ...currentActivity }
|
||||
}
|
||||
} else {
|
||||
// 添加新活动
|
||||
const newActivity = {
|
||||
...currentActivity,
|
||||
id: Date.now().toString(),
|
||||
createdAt: new Date().toISOString().split('T')[0]
|
||||
}
|
||||
activitiesData.value.unshift(newActivity)
|
||||
}
|
||||
isAddEditModalOpen.value = false
|
||||
message.success(isEditing.value ? '更新成功' : '新增成功')
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
|
||||
// 开始活动
|
||||
const handleStartActivity = (id) => {
|
||||
// 在实际应用中,这里应该调用API更新活动状态
|
||||
const index = activitiesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
activitiesData.value[index].status = 'ongoing'
|
||||
message.success('活动已开始')
|
||||
}
|
||||
}
|
||||
|
||||
// 完成活动
|
||||
const handleCompleteActivity = (id) => {
|
||||
// 在实际应用中,这里应该调用API更新活动状态
|
||||
const index = activitiesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
activitiesData.value[index].status = 'completed'
|
||||
message.success('活动已完成')
|
||||
}
|
||||
}
|
||||
|
||||
// 取消活动
|
||||
const handleCancelActivity = (id) => {
|
||||
// 在实际应用中,这里应该调用API更新活动状态
|
||||
const index = activitiesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
activitiesData.value[index].status = 'cancelled'
|
||||
message.success('活动已取消')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,455 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>防疫机构管理</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="center">防疫中心</a-select-option>
|
||||
<a-select-option value="station">防疫站</a-select-option>
|
||||
<a-select-option value="clinic">诊疗所</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="levelFilter" placeholder="机构级别" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="provincial">省级</a-select-option>
|
||||
<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-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<span class="iconfont icon-tianjia"></span> 新增机构
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 机构列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="agenciesData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 操作列 -->
|
||||
<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>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑机构模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEdit ? '编辑防疫机构' : '新增防疫机构'"
|
||||
:footer="null"
|
||||
width={600}
|
||||
>
|
||||
<a-form
|
||||
:model="currentAgency"
|
||||
layout="vertical"
|
||||
ref="formRef"
|
||||
>
|
||||
<a-form-item label="机构名称" name="name" :rules="[{ required: true, message: '请输入机构名称' }]">
|
||||
<a-input v-model:value="currentAgency.name" placeholder="请输入机构名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="机构编号" name="code" :rules="[{ required: true, message: '请输入机构编号' }]">
|
||||
<a-input v-model:value="currentAgency.code" placeholder="请输入机构编号" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="机构类型" name="type" :rules="[{ required: true, message: '请选择机构类型' }]">
|
||||
<a-select v-model:value="currentAgency.type" placeholder="请选择机构类型">
|
||||
<a-select-option value="center">防疫中心</a-select-option>
|
||||
<a-select-option value="station">防疫站</a-select-option>
|
||||
<a-select-option value="clinic">诊疗所</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="机构级别" name="level" :rules="[{ required: true, message: '请选择机构级别' }]">
|
||||
<a-select v-model:value="currentAgency.level" placeholder="请选择机构级别">
|
||||
<a-select-option value="provincial">省级</a-select-option>
|
||||
<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-form-item>
|
||||
|
||||
<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="phone" :rules="[{ required: true, message: '请输入联系电话' }]">
|
||||
<a-input v-model:value="currentAgency.phone" 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} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="备注" name="remarks">
|
||||
<a-input.TextArea v-model:value="currentAgency.remarks" placeholder="请输入备注信息" rows={2} />
|
||||
</a-form-item>
|
||||
|
||||
<div style="text-align: right;">
|
||||
<a-button @click="isAddEditModalOpen = false" style="margin-right: 16px;">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看机构详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看防疫机构详情"
|
||||
:footer="null"
|
||||
>
|
||||
<div v-if="viewAgency">
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">机构名称:</span>
|
||||
<span>{{ viewAgency.name }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">机构编号:</span>
|
||||
<span>{{ viewAgency.code }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">机构类型:</span>
|
||||
<span>{{ getTypeText(viewAgency.type) }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">机构级别:</span>
|
||||
<span>{{ getLevelText(viewAgency.level) }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">负责人:</span>
|
||||
<span>{{ viewAgency.manager }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">联系电话:</span>
|
||||
<span>{{ viewAgency.phone }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">地址:</span>
|
||||
<span>{{ viewAgency.address }}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">成立时间:</span>
|
||||
<span>{{ viewAgency.establishmentDate }}</span>
|
||||
</div>
|
||||
<div v-if="viewAgency.remarks">
|
||||
<span style="font-weight: bold; width: 120px; display: inline-block;">备注:</span>
|
||||
<span>{{ viewAgency.remarks }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; margin-top: 24px;">
|
||||
<a-button @click="isViewModalOpen = false">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const levelFilter = ref('')
|
||||
|
||||
// 分页配置
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: total => `共 ${total} 条数据`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref(null)
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
const isEdit = ref(false)
|
||||
|
||||
// 当前编辑/新增的机构
|
||||
const currentAgency = reactive({
|
||||
name: '',
|
||||
code: '',
|
||||
type: 'station',
|
||||
level: 'county',
|
||||
manager: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
// 当前查看的机构
|
||||
const viewAgency = ref(null)
|
||||
|
||||
// 机构列表数据
|
||||
const agenciesData = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '省动物防疫中心',
|
||||
code: 'EP001',
|
||||
type: 'center',
|
||||
level: 'provincial',
|
||||
manager: '张三',
|
||||
phone: '13800138001',
|
||||
address: '北京市朝阳区农展馆南路5号',
|
||||
establishmentDate: '2005-06-15',
|
||||
remarks: '省级防疫管理机构'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '市动物防疫站',
|
||||
code: 'EP002',
|
||||
type: 'station',
|
||||
level: 'municipal',
|
||||
manager: '李四',
|
||||
phone: '13800138002',
|
||||
address: '北京市海淀区中关村南大街12号',
|
||||
establishmentDate: '2008-09-20',
|
||||
remarks: '市级防疫执行机构'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '县动物防疫站',
|
||||
code: 'EP003',
|
||||
type: 'station',
|
||||
level: 'county',
|
||||
manager: '王五',
|
||||
phone: '13800138003',
|
||||
address: '北京市顺义区府前中街5号',
|
||||
establishmentDate: '2010-03-10',
|
||||
remarks: '县级防疫执行机构'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '乡镇动物防疫诊疗所',
|
||||
code: 'EP004',
|
||||
type: 'clinic',
|
||||
level: 'township',
|
||||
manager: '赵六',
|
||||
phone: '13800138004',
|
||||
address: '北京市昌平区小汤山镇政府路28号',
|
||||
establishmentDate: '2012-05-18',
|
||||
remarks: '乡镇级防疫服务机构'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: '区级动物防疫中心',
|
||||
code: 'EP005',
|
||||
type: 'center',
|
||||
level: 'county',
|
||||
manager: '孙七',
|
||||
phone: '13800138005',
|
||||
address: '北京市通州区运河东大街55号',
|
||||
establishmentDate: '2009-11-25',
|
||||
remarks: '区级防疫管理机构'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '机构编号',
|
||||
dataIndex: 'code',
|
||||
key: 'code',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '机构名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '机构类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 100,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '机构级别',
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
width: 100,
|
||||
customRender: ({ text }) => getLevelText(text)
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'manager',
|
||||
key: 'manager',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'phone',
|
||||
key: 'phone',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '成立时间',
|
||||
dataIndex: 'establishmentDate',
|
||||
key: 'establishmentDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 获取机构类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
'center': '防疫中心',
|
||||
'station': '防疫站',
|
||||
'clinic': '诊疗所'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 获取机构级别文本
|
||||
const getLevelText = (level) => {
|
||||
const levelMap = {
|
||||
'provincial': '省级',
|
||||
'municipal': '市级',
|
||||
'county': '县级',
|
||||
'township': '乡镇级'
|
||||
}
|
||||
return levelMap[level] || level
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
pagination.value.current = 1
|
||||
// 这里应该调用API进行搜索,现在使用模拟数据
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
levelFilter.value = ''
|
||||
pagination.value.current = 1
|
||||
// 这里应该重置搜索条件并重新加载数据
|
||||
}
|
||||
|
||||
// 处理新增
|
||||
const handleAdd = () => {
|
||||
isEdit.value = false
|
||||
// 重置表单数据
|
||||
Object.keys(currentAgency).forEach(key => {
|
||||
currentAgency[key] = ''
|
||||
})
|
||||
currentAgency.type = 'station'
|
||||
currentAgency.level = 'county'
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record) => {
|
||||
isEdit.value = true
|
||||
// 复制记录数据到当前编辑对象
|
||||
Object.assign(currentAgency, JSON.parse(JSON.stringify(record)))
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 处理查看
|
||||
const handleView = (record) => {
|
||||
viewAgency.value = record
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 处理删除
|
||||
const handleDelete = (id) => {
|
||||
// 显示确认对话框
|
||||
if (confirm('确定要删除该防疫机构吗?')) {
|
||||
// 这里应该调用API进行删除,现在使用模拟数据
|
||||
const index = agenciesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
agenciesData.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理保存
|
||||
const handleSave = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.validate().then(() => {
|
||||
// 这里应该调用API进行保存,现在使用模拟数据
|
||||
if (isEdit.value) {
|
||||
// 编辑现有记录
|
||||
const index = agenciesData.value.findIndex(item => item.id === currentAgency.id)
|
||||
if (index !== -1) {
|
||||
agenciesData.value[index] = { ...currentAgency }
|
||||
}
|
||||
} else {
|
||||
// 新增记录
|
||||
const newAgency = { ...currentAgency }
|
||||
newAgency.id = String(Date.now())
|
||||
newAgency.establishmentDate = new Date().toISOString().split('T')[0]
|
||||
agenciesData.value.unshift(newAgency)
|
||||
}
|
||||
isAddEditModalOpen.value = false
|
||||
message.success(isEdit.value ? '编辑成功' : '新增成功')
|
||||
}).catch(() => {
|
||||
message.error('请检查表单数据')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(() => {
|
||||
// 初始化分页总数
|
||||
pagination.value.total = agenciesData.value.length
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,479 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="防疫机构管理"/>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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="statusFilter" placeholder="机构状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="active">启用</a-select-option>
|
||||
<a-select-option value="inactive">禁用</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddAgency">
|
||||
<span class="iconfont icon-tianjia"></span> 新增机构
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 机构列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="agenciesData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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)">编辑</a-button>
|
||||
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button size="small" @click="handleToggleStatus(record)">
|
||||
{{ record.status === 'active' ? '禁用' : '启用' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑机构模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEditMode ? '编辑防疫机构' : '新增防疫机构'"
|
||||
:footer="null"
|
||||
width={700}
|
||||
>
|
||||
<a-form
|
||||
:model="currentAgency"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item
|
||||
label="机构名称"
|
||||
name="name"
|
||||
:rules="[{ required: true, message: '请输入机构名称' }]"
|
||||
>
|
||||
<a-input v-model:value="currentAgency.name" placeholder="请输入机构名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="负责人"
|
||||
name="director"
|
||||
:rules="[{ required: true, message: '请输入负责人姓名' }]"
|
||||
>
|
||||
<a-input v-model:value="currentAgency.director" placeholder="请输入负责人姓名" />
|
||||
</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="address"
|
||||
:rules="[{ required: true, message: '请输入机构地址' }]"
|
||||
>
|
||||
<a-input v-model:value="currentAgency.address" placeholder="请输入机构地址" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="邮箱"
|
||||
name="email"
|
||||
>
|
||||
<a-input v-model:value="currentAgency.email" placeholder="请输入邮箱地址" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="机构类型"
|
||||
name="type"
|
||||
:rules="[{ required: true, message: '请选择机构类型' }]"
|
||||
>
|
||||
<a-select v-model:value="currentAgency.type" placeholder="请选择机构类型">
|
||||
<a-select-option value="center">中心防疫站</a-select-option>
|
||||
<a-select-option value="branch">分站</a-select-option>
|
||||
<a-select-option value="mobile">流动防疫站</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="简介"
|
||||
name="description"
|
||||
>
|
||||
<a-textarea v-model:value="currentAgency.description" placeholder="请输入机构简介" :rows="4" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="text-align: right; margin-top: 20px;">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">确定</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看机构详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看防疫机构详情"
|
||||
:footer="null"
|
||||
width={800}
|
||||
>
|
||||
<div v-if="viewAgency" class="agency-detail">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">机构名称:</span>
|
||||
<span class="detail-value">{{ viewAgency.name }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">负责人:</span>
|
||||
<span class="detail-value">{{ viewAgency.director }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">联系电话:</span>
|
||||
<span class="detail-value">{{ viewAgency.phone }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">地址:</span>
|
||||
<span class="detail-value">{{ viewAgency.address }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">邮箱:</span>
|
||||
<span class="detail-value">{{ viewAgency.email }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">机构类型:</span>
|
||||
<span class="detail-value">{{ getAgencyTypeText(viewAgency.type) }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">状态:</span>
|
||||
<a-tag :color="getStatusColor(viewAgency.status)">{{ getStatusText(viewAgency.status) }}</a-tag>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">成立时间:</span>
|
||||
<span class="detail-value">{{ viewAgency.establishmentDate }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">简介:</span>
|
||||
<span class="detail-value">{{ viewAgency.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: right; margin-top: 20px;">
|
||||
<a-button @click="closeViewModal">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import PageHeader from '@/layout/PageHeader.vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const statusFilter = ref('')
|
||||
|
||||
// 表格数据
|
||||
const selectedRowKeys = ref([])
|
||||
const agenciesData = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '中心动物防疫站',
|
||||
director: '张三',
|
||||
phone: '13800138001',
|
||||
address: '市南区健康路100号',
|
||||
email: 'center@animalhealth.gov.cn',
|
||||
type: 'center',
|
||||
status: 'active',
|
||||
establishmentDate: '2010-01-15',
|
||||
description: '负责全市动物防疫工作的统筹管理和技术指导'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '东区动物防疫分站',
|
||||
director: '李四',
|
||||
phone: '13800138002',
|
||||
address: '市东区防疫路50号',
|
||||
email: 'east@animalhealth.gov.cn',
|
||||
type: 'branch',
|
||||
status: 'active',
|
||||
establishmentDate: '2012-05-20',
|
||||
description: '负责东区范围内的动物防疫工作'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '西区动物防疫分站',
|
||||
director: '王五',
|
||||
phone: '13800138003',
|
||||
address: '市西区健康大道200号',
|
||||
email: 'west@animalhealth.gov.cn',
|
||||
type: 'branch',
|
||||
status: 'active',
|
||||
establishmentDate: '2013-03-10',
|
||||
description: '负责西区范围内的动物防疫工作'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '北区动物防疫分站',
|
||||
director: '赵六',
|
||||
phone: '13800138004',
|
||||
address: '市北区安全路88号',
|
||||
email: 'north@animalhealth.gov.cn',
|
||||
type: 'branch',
|
||||
status: 'active',
|
||||
establishmentDate: '2014-07-05',
|
||||
description: '负责北区范围内的动物防疫工作'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: '南区动物防疫分站',
|
||||
director: '钱七',
|
||||
phone: '13800138005',
|
||||
address: '市南区健康路66号',
|
||||
email: 'south@animalhealth.gov.cn',
|
||||
type: 'branch',
|
||||
status: 'active',
|
||||
establishmentDate: '2015-02-28',
|
||||
description: '负责南区范围内的动物防疫工作'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: '流动防疫队',
|
||||
director: '孙八',
|
||||
phone: '13800138006',
|
||||
address: '市中区应急中心',
|
||||
email: 'mobile@animalhealth.gov.cn',
|
||||
type: 'mobile',
|
||||
status: 'active',
|
||||
establishmentDate: '2016-09-15',
|
||||
description: '负责偏远地区和突发事件的动物防疫工作'
|
||||
}
|
||||
])
|
||||
|
||||
// 分页配置
|
||||
const pagination = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 6,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isEditMode = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
|
||||
// 当前编辑/查看的机构
|
||||
const currentAgency = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
director: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
email: '',
|
||||
type: '',
|
||||
status: 'active',
|
||||
description: ''
|
||||
})
|
||||
|
||||
const viewAgency = ref(null)
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '机构名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'director',
|
||||
key: 'director',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'phone',
|
||||
key: 'phone',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '机构类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 100,
|
||||
customRender: ({ text }) => getAgencyTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '成立时间',
|
||||
dataIndex: 'establishmentDate',
|
||||
key: 'establishmentDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
// 获取机构类型文本
|
||||
const getAgencyTypeText = (type) => {
|
||||
const typeMap = {
|
||||
'center': '中心防疫站',
|
||||
'branch': '分站',
|
||||
'mobile': '流动防疫站'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'active': '启用',
|
||||
'inactive': '禁用'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
'active': 'green',
|
||||
'inactive': 'red'
|
||||
}
|
||||
return colorMap[status] || 'blue'
|
||||
}
|
||||
|
||||
// 行选择变化
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
// 实际项目中这里应该调用API进行搜索
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
statusFilter.value = ''
|
||||
}
|
||||
|
||||
// 新增机构
|
||||
const handleAddAgency = () => {
|
||||
isEditMode.value = false
|
||||
Object.assign(currentAgency, {
|
||||
id: '',
|
||||
name: '',
|
||||
director: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
email: '',
|
||||
type: '',
|
||||
status: 'active',
|
||||
description: ''
|
||||
})
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 编辑机构
|
||||
const handleEdit = (record) => {
|
||||
isEditMode.value = true
|
||||
Object.assign(currentAgency, { ...record })
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 查看机构
|
||||
const handleView = (record) => {
|
||||
viewAgency.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 删除机构
|
||||
const handleDelete = (id) => {
|
||||
// 实际项目中这里应该调用API进行删除
|
||||
message.success('删除成功')
|
||||
}
|
||||
|
||||
// 切换状态
|
||||
const handleToggleStatus = (record) => {
|
||||
// 实际项目中这里应该调用API切换状态
|
||||
message.success(`状态已切换为${record.status === 'active' ? '禁用' : '启用'}`)
|
||||
}
|
||||
|
||||
// 保存机构
|
||||
const handleSave = () => {
|
||||
// 实际项目中这里应该调用API保存数据
|
||||
message.success(isEditMode.value ? '编辑成功' : '新增成功')
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 取消
|
||||
const handleCancel = () => {
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const closeViewModal = () => {
|
||||
isViewModalOpen.value = false
|
||||
viewAgency.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.agency-detail {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: 600;
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,665 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>防疫记录管理</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="vaccination">疫苗接种</a-select-option>
|
||||
<a-select-option value="disinfection">消毒</a-select-option>
|
||||
<a-select-option value="health_check">健康检查</a-select-option>
|
||||
<a-select-option value="other">其他</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="pending">待完成</a-select-option>
|
||||
<a-select-option value="failed">未通过</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
style="width: 300px;"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddRecord">
|
||||
<span class="iconfont icon-tianjia"></span> 新增记录
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="recordsData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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)">编辑</a-button>
|
||||
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑记录模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEditing ? '编辑防疫记录' : '新增防疫记录'"
|
||||
:footer="null"
|
||||
width={700}
|
||||
>
|
||||
<a-form
|
||||
:model="currentRecord"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="养殖场名称"
|
||||
:rules="[{ required: true, message: '请输入养殖场名称' }]">
|
||||
<a-input v-model:value="currentRecord.farmName" placeholder="请输入养殖场名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫类型"
|
||||
:rules="[{ required: true, message: '请选择防疫类型' }]">
|
||||
<a-select v-model:value="currentRecord.type" placeholder="请选择防疫类型">
|
||||
<a-select-option value="vaccination">疫苗接种</a-select-option>
|
||||
<a-select-option value="disinfection">消毒</a-select-option>
|
||||
<a-select-option value="health_check">健康检查</a-select-option>
|
||||
<a-select-option value="other">其他</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫员"
|
||||
:rules="[{ required: true, message: '请输入防疫员姓名' }]">
|
||||
<a-input v-model:value="currentRecord.epidemicStaff" placeholder="请输入防疫员姓名" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="联系电话"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入联系电话' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
]">
|
||||
<a-input v-model:value="currentRecord.phone" placeholder="请输入联系电话" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫日期"
|
||||
:rules="[{ required: true, message: '请选择防疫日期' }]">
|
||||
<a-date-picker
|
||||
v-model:value="currentRecord.epidemicDate"
|
||||
style="width: 100%;"
|
||||
format="YYYY-MM-DD"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫数量" v-if="currentRecord.type === 'vaccination'"
|
||||
:rules="[{ required: true, message: '请输入防疫数量', type: 'number' }]">
|
||||
<a-input-number v-model:value="currentRecord.count" min="0" placeholder="请输入防疫数量" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="使用疫苗" v-if="currentRecord.type === 'vaccination'"
|
||||
:rules="[{ required: true, message: '请输入使用疫苗' }]">
|
||||
<a-input v-model:value="currentRecord.vaccineName" placeholder="请输入使用疫苗" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫范围" v-if="currentRecord.type === 'disinfection'"
|
||||
:rules="[{ required: true, message: '请输入防疫范围' }]">
|
||||
<a-input.TextArea v-model:value="currentRecord.area" placeholder="请输入防疫范围" rows={2} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫药品" v-if="currentRecord.type === 'disinfection'"
|
||||
:rules="[{ required: true, message: '请输入防疫药品' }]">
|
||||
<a-input v-model:value="currentRecord.disinfectant" placeholder="请输入防疫药品" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="检查结果" v-if="currentRecord.type === 'health_check'"
|
||||
:rules="[{ required: true, message: '请输入检查结果' }]">
|
||||
<a-select v-model:value="currentRecord.healthResult" placeholder="请选择检查结果">
|
||||
<a-select-option value="normal">正常</a-select-option>
|
||||
<a-select-option value="abnormal">异常</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="防疫描述" v-if="currentRecord.type === 'other'"
|
||||
:rules="[{ required: true, message: '请输入防疫描述' }]">
|
||||
<a-input.TextArea v-model:value="currentRecord.description" placeholder="请输入防疫描述" rows={3} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="备注">
|
||||
<a-input.TextArea v-model:value="currentRecord.notes" placeholder="请输入备注信息" rows={4} />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="状态">
|
||||
<a-select v-model:value="currentRecord.status" placeholder="请选择状态">
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="pending">待完成</a-select-option>
|
||||
<a-select-option value="failed">未通过</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看记录详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看防疫记录详情"
|
||||
:footer="null"
|
||||
width={700}
|
||||
>
|
||||
<div v-if="viewRecord">
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">基本信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">养殖场名称</p>
|
||||
<p>{{ viewRecord.farmName }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫类型</p>
|
||||
<p>{{ getTypeText(viewRecord.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫员</p>
|
||||
<p>{{ viewRecord.epidemicStaff }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系电话</p>
|
||||
<p>{{ viewRecord.phone }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫日期</p>
|
||||
<p>{{ formatDate(viewRecord.epidemicDate) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">状态</p>
|
||||
<p><a-tag :color="getStatusColor(viewRecord.status)">{{ getStatusText(viewRecord.status) }}</a-tag></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">详细信息</h3>
|
||||
<div v-if="viewRecord.type === 'vaccination'">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫数量</p>
|
||||
<p>{{ viewRecord.count }} 头/只</p>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px; margin-top: 8px;">使用疫苗</p>
|
||||
<p>{{ viewRecord.vaccineName }}</p>
|
||||
</div>
|
||||
<div v-else-if="viewRecord.type === 'disinfection'">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫范围</p>
|
||||
<p>{{ viewRecord.area }}</p>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px; margin-top: 8px;">防疫药品</p>
|
||||
<p>{{ viewRecord.disinfectant }}</p>
|
||||
</div>
|
||||
<div v-else-if="viewRecord.type === 'health_check'">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检查结果</p>
|
||||
<p>{{ viewRecord.healthResult === 'normal' ? '正常' : '异常' }}</p>
|
||||
</div>
|
||||
<div v-else-if="viewRecord.type === 'other'">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">防疫描述</p>
|
||||
<p>{{ viewRecord.description }}</p>
|
||||
</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">备注</p>
|
||||
<p>{{ viewRecord.notes || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const dateRange = ref([])
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
const isEditing = ref(false)
|
||||
|
||||
// 当前编辑/查看的记录
|
||||
const currentRecord = reactive({
|
||||
id: '',
|
||||
farmName: '',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '',
|
||||
phone: '',
|
||||
epidemicDate: null,
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: 'normal',
|
||||
description: '',
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: ''
|
||||
})
|
||||
|
||||
const viewRecord = ref(null)
|
||||
|
||||
// 记录列表数据(模拟数据)
|
||||
const recordsData = ref([
|
||||
{
|
||||
id: '1',
|
||||
farmName: '郑州市金水区阳光养殖场',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '张三',
|
||||
phone: '13812345678',
|
||||
epidemicDate: '2023-10-01',
|
||||
count: 150,
|
||||
vaccineName: '口蹄疫疫苗',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '无异常',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-01'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
farmName: '新郑市绿源养殖场',
|
||||
type: 'disinfection',
|
||||
epidemicStaff: '李四',
|
||||
phone: '13912345678',
|
||||
epidemicDate: '2023-10-02',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '养殖场全场消毒,重点消毒牛舍、饲料仓库、消毒池等区域',
|
||||
disinfectant: '含氯消毒液',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '消毒彻底,符合标准',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-02'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
farmName: '新密市祥和养殖场',
|
||||
type: 'health_check',
|
||||
epidemicStaff: '王五',
|
||||
phone: '13712345678',
|
||||
epidemicDate: '2023-10-03',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: 'normal',
|
||||
description: '',
|
||||
notes: '牛群健康状况良好',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-03'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
farmName: '登封市幸福养殖场',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '赵六',
|
||||
phone: '13612345678',
|
||||
epidemicDate: '2023-10-04',
|
||||
count: 200,
|
||||
vaccineName: '牛瘟疫苗',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '部分牛只接种后有轻微发热现象',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-04'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
farmName: '中牟县希望养殖场',
|
||||
type: 'other',
|
||||
epidemicStaff: '钱七',
|
||||
phone: '13512345678',
|
||||
epidemicDate: '2023-10-05',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '牛群驱虫,使用阿维菌素进行全群驱虫',
|
||||
notes: '按计划完成驱虫工作',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-05'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
farmName: '荥阳市快乐养殖场',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '孙八',
|
||||
phone: '13412345678',
|
||||
epidemicDate: '2023-10-06',
|
||||
count: 180,
|
||||
vaccineName: '布鲁氏菌病疫苗',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '无异常反应',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-06'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
farmName: '巩义市明星养殖场',
|
||||
type: 'disinfection',
|
||||
epidemicStaff: '周九',
|
||||
phone: '13312345678',
|
||||
epidemicDate: '2023-10-07',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '养殖场周边环境消毒',
|
||||
disinfectant: '过氧乙酸',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '消毒效果良好',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-07'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
farmName: '惠济区温馨养殖场',
|
||||
type: 'health_check',
|
||||
epidemicStaff: '吴十',
|
||||
phone: '13212345678',
|
||||
epidemicDate: '2023-10-08',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: 'abnormal',
|
||||
description: '',
|
||||
notes: '发现2头牛只精神不振,已隔离观察',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-08'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
farmName: '二七区红火养殖场',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '郑十一',
|
||||
phone: '13112345678',
|
||||
epidemicDate: '2023-10-09',
|
||||
count: 120,
|
||||
vaccineName: '口蹄疫疫苗',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '',
|
||||
notes: '按时完成接种工作',
|
||||
status: 'completed',
|
||||
createdAt: '2023-10-09'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
farmName: '中原区丰收养殖场',
|
||||
type: 'other',
|
||||
epidemicStaff: '王十二',
|
||||
phone: '13012345678',
|
||||
epidemicDate: '2023-10-10',
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: '',
|
||||
description: '牛群营养状况评估,对瘦弱牛只进行重点饲养管理',
|
||||
notes: '已制定饲养调整方案',
|
||||
status: 'pending',
|
||||
createdAt: '2023-10-10'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '养殖场名称',
|
||||
dataIndex: 'farmName',
|
||||
key: 'farmName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '防疫类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '防疫员',
|
||||
dataIndex: 'epidemicStaff',
|
||||
key: 'epidemicStaff',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '防疫日期',
|
||||
dataIndex: 'epidemicDate',
|
||||
key: 'epidemicDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
completed: '已完成',
|
||||
pending: '待完成',
|
||||
failed: '未通过'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
completed: 'green',
|
||||
pending: 'orange',
|
||||
failed: 'red'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
vaccination: '疫苗接种',
|
||||
disinfection: '消毒',
|
||||
health_check: '健康检查',
|
||||
other: '其他'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '-'
|
||||
if (typeof date === 'string') return date
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
dateRange.value = []
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 新增记录
|
||||
const handleAddRecord = () => {
|
||||
// 重置表单
|
||||
Object.assign(currentRecord, {
|
||||
id: '',
|
||||
farmName: '',
|
||||
type: 'vaccination',
|
||||
epidemicStaff: '',
|
||||
phone: '',
|
||||
epidemicDate: null,
|
||||
count: 0,
|
||||
vaccineName: '',
|
||||
area: '',
|
||||
disinfectant: '',
|
||||
healthResult: 'normal',
|
||||
description: '',
|
||||
notes: '',
|
||||
status: 'completed',
|
||||
createdAt: ''
|
||||
})
|
||||
isEditing.value = false
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 编辑记录
|
||||
const handleEdit = (record) => {
|
||||
// 复制记录到当前编辑对象
|
||||
Object.assign(currentRecord, { ...record })
|
||||
isEditing.value = true
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 查看记录
|
||||
const handleView = (record) => {
|
||||
viewRecord.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 删除记录
|
||||
const handleDelete = (id) => {
|
||||
// 在实际应用中,这里应该调用API删除数据
|
||||
const index = recordsData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
recordsData.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const handleSave = () => {
|
||||
// 在实际应用中,这里应该调用API保存数据
|
||||
if (isEditing.value) {
|
||||
// 更新现有记录
|
||||
const index = recordsData.value.findIndex(item => item.id === currentRecord.id)
|
||||
if (index !== -1) {
|
||||
recordsData.value[index] = { ...currentRecord }
|
||||
}
|
||||
} else {
|
||||
// 添加新记录
|
||||
const newRecord = {
|
||||
...currentRecord,
|
||||
id: Date.now().toString(),
|
||||
createdAt: new Date().toISOString().split('T')[0]
|
||||
}
|
||||
recordsData.value.unshift(newRecord)
|
||||
}
|
||||
isAddEditModalOpen.value = false
|
||||
message.success(isEditing.value ? '更新成功' : '新增成功')
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,774 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>疫苗管理</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="foot_and_mouth_disease">口蹄疫疫苗</a-select-option>
|
||||
<a-select-option value="bovine_tuberculosis">牛结核病疫苗</a-select-option>
|
||||
<a-select-option value="brucellosis">布鲁氏菌病疫苗</a-select-option>
|
||||
<a-select-option value="rabies">狂犬病疫苗</a-select-option>
|
||||
<a-select-option value="other">其他疫苗</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="valid">有效</a-select-option>
|
||||
<a-select-option value="expired">过期</a-select-option>
|
||||
<a-select-option value="low_stock">库存不足</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddVaccine">
|
||||
<span class="iconfont icon-tianjia"></span> 新增疫苗
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="vaccinesData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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)">编辑</a-button>
|
||||
<a-button size="small" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button size="small" @click="handleBatchIn(record.id)">入库</a-button>
|
||||
<a-button size="small" @click="handleBatchOut(record.id)">出库</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑疫苗模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEditing ? '编辑疫苗信息' : '新增疫苗信息'"
|
||||
:footer="null"
|
||||
width={600}
|
||||
>
|
||||
<a-form
|
||||
:model="currentVaccine"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="疫苗名称"
|
||||
:rules="[{ required: true, message: '请输入疫苗名称' }]">
|
||||
<a-input v-model:value="currentVaccine.name" placeholder="请输入疫苗名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="疫苗类型"
|
||||
:rules="[{ required: true, message: '请选择疫苗类型' }]">
|
||||
<a-select v-model:value="currentVaccine.type" placeholder="请选择疫苗类型">
|
||||
<a-select-option value="foot_and_mouth_disease">口蹄疫疫苗</a-select-option>
|
||||
<a-select-option value="bovine_tuberculosis">牛结核病疫苗</a-select-option>
|
||||
<a-select-option value="brucellosis">布鲁氏菌病疫苗</a-select-option>
|
||||
<a-select-option value="rabies">狂犬病疫苗</a-select-option>
|
||||
<a-select-option value="other">其他疫苗</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="生产厂商"
|
||||
:rules="[{ required: true, message: '请输入生产厂商' }]">
|
||||
<a-input v-model:value="currentVaccine.manufacturer" placeholder="请输入生产厂商" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="批准文号"
|
||||
:rules="[{ required: true, message: '请输入批准文号' }]">
|
||||
<a-input v-model:value="currentVaccine.approvalNumber" placeholder="请输入批准文号" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="规格"
|
||||
:rules="[{ required: true, message: '请输入规格' }]">
|
||||
<a-input v-model:value="currentVaccine.specification" placeholder="请输入规格" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="单价(元)"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入单价' },
|
||||
{ pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的金额格式' }
|
||||
]">
|
||||
<a-input-number v-model:value="currentVaccine.price" min="0" precision="2" placeholder="请输入单价" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="有效期(天)"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入有效期' },
|
||||
{ type: 'number', min: 1, message: '有效期至少为1天' }
|
||||
]">
|
||||
<a-input-number v-model:value="currentVaccine.validDays" min="1" placeholder="请输入有效期(天)" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="储存条件"
|
||||
:rules="[{ required: true, message: '请输入储存条件' }]">
|
||||
<a-input v-model:value="currentVaccine.storageCondition" placeholder="请输入储存条件" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="备注">
|
||||
<a-input.TextArea v-model:value="currentVaccine.notes" placeholder="请输入备注信息" rows={3} />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看疫苗详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看疫苗详情"
|
||||
:footer="null"
|
||||
width={600}
|
||||
>
|
||||
<div v-if="viewVaccine">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">疫苗名称</p>
|
||||
<p>{{ viewVaccine.name }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">疫苗类型</p>
|
||||
<p>{{ getTypeText(viewVaccine.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">生产厂商</p>
|
||||
<p>{{ viewVaccine.manufacturer }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">批准文号</p>
|
||||
<p>{{ viewVaccine.approvalNumber }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">规格</p>
|
||||
<p>{{ viewVaccine.specification }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">单价</p>
|
||||
<p>¥{{ viewVaccine.price }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">有效期</p>
|
||||
<p>{{ viewVaccine.validDays }} 天</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">储存条件</p>
|
||||
<p>{{ viewVaccine.storageCondition }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">库存数量</p>
|
||||
<p>{{ viewVaccine.stockCount }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">状态</p>
|
||||
<p><a-tag :color="getStatusColor(viewVaccine.status)">{{ getStatusText(viewVaccine.status) }}</a-tag></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">备注</p>
|
||||
<p>{{ viewVaccine.notes || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 批量入库模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isInModalOpen"
|
||||
title="疫苗入库"
|
||||
:footer="null"
|
||||
width={400}
|
||||
>
|
||||
<a-form
|
||||
:model="batchInForm"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="疫苗名称">
|
||||
<p>{{ viewVaccine?.name || '' }}</p>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="入库数量"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入入库数量' },
|
||||
{ type: 'number', min: 1, message: '入库数量至少为1' }
|
||||
]">
|
||||
<a-input-number v-model:value="batchInForm.count" min="1" placeholder="请输入入库数量" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="入库批次号"
|
||||
:rules="[{ required: true, message: '请输入入库批次号' }]">
|
||||
<a-input v-model:value="batchInForm.batchNumber" placeholder="请输入入库批次号" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="入库日期"
|
||||
:rules="[{ required: true, message: '请选择入库日期' }]">
|
||||
<a-date-picker
|
||||
v-model:value="batchInForm.inDate"
|
||||
style="width: 100%;"
|
||||
format="YYYY-MM-DD"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCloseInModal">取消</a-button>
|
||||
<a-button type="primary" @click="handleConfirmIn">确认入库</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 批量出库模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isOutModalOpen"
|
||||
title="疫苗出库"
|
||||
:footer="null"
|
||||
width={400}
|
||||
>
|
||||
<a-form
|
||||
:model="batchOutForm"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="疫苗名称">
|
||||
<p>{{ viewVaccine?.name || '' }}</p>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="当前库存">
|
||||
<p>{{ viewVaccine?.stockCount || 0 }}</p>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="出库数量"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入出库数量' },
|
||||
{ type: 'number', min: 1, message: '出库数量至少为1' },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (value > (viewVaccine?.stockCount || 0)) {
|
||||
return Promise.reject(new Error('出库数量不能大于当前库存'))
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
]">
|
||||
<a-input-number v-model:value="batchOutForm.count" min="1" :max="viewVaccine?.stockCount" placeholder="请输入出库数量" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="出库用途"
|
||||
:rules="[{ required: true, message: '请输入出库用途' }]">
|
||||
<a-input v-model:value="batchOutForm.purpose" placeholder="请输入出库用途" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="出库日期"
|
||||
:rules="[{ required: true, message: '请选择出库日期' }]">
|
||||
<a-date-picker
|
||||
v-model:value="batchOutForm.outDate"
|
||||
style="width: 100%;"
|
||||
format="YYYY-MM-DD"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCloseOutModal">取消</a-button>
|
||||
<a-button type="primary" @click="handleConfirmOut">确认出库</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
const isInModalOpen = ref(false)
|
||||
const isOutModalOpen = ref(false)
|
||||
const isEditing = ref(false)
|
||||
|
||||
// 当前编辑/查看的疫苗
|
||||
const currentVaccine = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
type: 'foot_and_mouth_disease',
|
||||
manufacturer: '',
|
||||
approvalNumber: '',
|
||||
specification: '',
|
||||
price: 0,
|
||||
validDays: 365,
|
||||
storageCondition: '',
|
||||
notes: '',
|
||||
stockCount: 0,
|
||||
status: 'valid',
|
||||
createdAt: ''
|
||||
})
|
||||
|
||||
const viewVaccine = ref(null)
|
||||
|
||||
// 批量入库表单
|
||||
const batchInForm = reactive({
|
||||
count: 1,
|
||||
batchNumber: '',
|
||||
inDate: new Date()
|
||||
})
|
||||
|
||||
// 批量出库表单
|
||||
const batchOutForm = reactive({
|
||||
count: 1,
|
||||
purpose: '',
|
||||
outDate: new Date()
|
||||
})
|
||||
|
||||
// 疫苗列表数据(模拟数据)
|
||||
const vaccinesData = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '口蹄疫疫苗(O型-亚洲I型)二价灭活疫苗',
|
||||
type: 'foot_and_mouth_disease',
|
||||
manufacturer: '中国农业科学院兰州兽医研究所',
|
||||
approvalNumber: '兽药生字(2020)050356789',
|
||||
specification: '10ml/瓶',
|
||||
price: 8.5,
|
||||
validDays: 365,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '',
|
||||
stockCount: 1200,
|
||||
status: 'valid',
|
||||
createdAt: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '牛结核病提纯蛋白衍生物(PPD)检测试剂',
|
||||
type: 'bovine_tuberculosis',
|
||||
manufacturer: '中国兽医药品监察所',
|
||||
approvalNumber: '兽药生字(2020)010123456',
|
||||
specification: '1ml/瓶',
|
||||
price: 15.0,
|
||||
validDays: 270,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '用于牛结核病的皮内变态反应检测',
|
||||
stockCount: 850,
|
||||
status: 'valid',
|
||||
createdAt: '2023-02-20'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '布鲁氏菌病活疫苗(S2株)',
|
||||
type: 'brucellosis',
|
||||
manufacturer: '中国农业科学院哈尔滨兽医研究所',
|
||||
approvalNumber: '兽药生字(2020)080789012',
|
||||
specification: '100头份/瓶',
|
||||
price: 22.5,
|
||||
validDays: 180,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '用于预防牛、羊布鲁氏菌病',
|
||||
stockCount: 430,
|
||||
status: 'valid',
|
||||
createdAt: '2023-03-10'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: '狂犬病疫苗(灭活疫苗)',
|
||||
type: 'rabies',
|
||||
manufacturer: '武汉生物制品研究所有限责任公司',
|
||||
approvalNumber: '兽药生字(2020)170456789',
|
||||
specification: '1ml/瓶',
|
||||
price: 35.0,
|
||||
validDays: 365,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '',
|
||||
stockCount: 520,
|
||||
status: 'valid',
|
||||
createdAt: '2023-04-05'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: '牛支原体肺炎疫苗(灭活疫苗)',
|
||||
type: 'other',
|
||||
manufacturer: '青岛易邦生物工程有限公司',
|
||||
approvalNumber: '兽药生字(2020)150234567',
|
||||
specification: '20ml/瓶',
|
||||
price: 45.0,
|
||||
validDays: 270,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '用于预防牛支原体肺炎',
|
||||
stockCount: 180,
|
||||
status: 'low_stock',
|
||||
createdAt: '2023-05-15'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: '牛副伤寒疫苗(灭活疫苗)',
|
||||
type: 'other',
|
||||
manufacturer: '中牧实业股份有限公司',
|
||||
approvalNumber: '兽药生字(2020)010678901',
|
||||
specification: '100ml/瓶',
|
||||
price: 98.0,
|
||||
validDays: 365,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '用于预防牛副伤寒',
|
||||
stockCount: 65,
|
||||
status: 'low_stock',
|
||||
createdAt: '2023-06-20'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
name: '牛流行热疫苗(灭活疫苗)',
|
||||
type: 'other',
|
||||
manufacturer: '金宇保灵生物药品有限公司',
|
||||
approvalNumber: '兽药生字(2020)050345678',
|
||||
specification: '10ml/瓶',
|
||||
price: 28.0,
|
||||
validDays: 180,
|
||||
storageCondition: '2-8℃冷藏保存',
|
||||
notes: '用于预防牛流行热',
|
||||
stockCount: 320,
|
||||
status: 'valid',
|
||||
createdAt: '2023-07-10'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
name: '牛病毒性腹泻/粘膜病疫苗(弱毒疫苗)',
|
||||
type: 'other',
|
||||
manufacturer: '北京世纪元亨动物防疫技术有限公司',
|
||||
approvalNumber: '兽药生字(2020)010890123',
|
||||
specification: '10头份/瓶',
|
||||
price: 32.0,
|
||||
validDays: 270,
|
||||
storageCondition: '-15℃以下冷冻保存',
|
||||
notes: '用于预防牛病毒性腹泻/粘膜病',
|
||||
stockCount: 0,
|
||||
status: 'expired',
|
||||
createdAt: '2022-01-15'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '疫苗名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '疫苗类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '生产厂商',
|
||||
dataIndex: 'manufacturer',
|
||||
key: 'manufacturer',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '规格',
|
||||
dataIndex: 'specification',
|
||||
key: 'specification',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '单价(元)',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
width: 80,
|
||||
customRender: ({ text }) => `¥${text}`
|
||||
},
|
||||
{
|
||||
title: '库存数量',
|
||||
dataIndex: 'stockCount',
|
||||
key: 'stockCount',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
valid: '有效',
|
||||
expired: '过期',
|
||||
low_stock: '库存不足'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
valid: 'green',
|
||||
expired: 'red',
|
||||
low_stock: 'orange'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
foot_and_mouth_disease: '口蹄疫疫苗',
|
||||
bovine_tuberculosis: '牛结核病疫苗',
|
||||
brucellosis: '布鲁氏菌病疫苗',
|
||||
rabies: '狂犬病疫苗',
|
||||
other: '其他疫苗'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 新增疫苗
|
||||
const handleAddVaccine = () => {
|
||||
// 重置表单
|
||||
Object.assign(currentVaccine, {
|
||||
id: '',
|
||||
name: '',
|
||||
type: 'foot_and_mouth_disease',
|
||||
manufacturer: '',
|
||||
approvalNumber: '',
|
||||
specification: '',
|
||||
price: 0,
|
||||
validDays: 365,
|
||||
storageCondition: '',
|
||||
notes: '',
|
||||
stockCount: 0,
|
||||
status: 'valid',
|
||||
createdAt: ''
|
||||
})
|
||||
isEditing.value = false
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 编辑疫苗
|
||||
const handleEdit = (record) => {
|
||||
// 复制记录到当前编辑对象
|
||||
Object.assign(currentVaccine, { ...record })
|
||||
isEditing.value = true
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 查看疫苗
|
||||
const handleView = (record) => {
|
||||
viewVaccine.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 删除疫苗
|
||||
const handleDelete = (id) => {
|
||||
// 在实际应用中,这里应该调用API删除数据
|
||||
const index = vaccinesData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
vaccinesData.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存疫苗
|
||||
const handleSave = () => {
|
||||
// 在实际应用中,这里应该调用API保存数据
|
||||
if (isEditing.value) {
|
||||
// 更新现有疫苗
|
||||
const index = vaccinesData.value.findIndex(item => item.id === currentVaccine.id)
|
||||
if (index !== -1) {
|
||||
vaccinesData.value[index] = { ...currentVaccine }
|
||||
}
|
||||
} else {
|
||||
// 添加新疫苗
|
||||
const newVaccine = {
|
||||
...currentVaccine,
|
||||
id: Date.now().toString(),
|
||||
createdAt: new Date().toISOString().split('T')[0]
|
||||
}
|
||||
vaccinesData.value.unshift(newVaccine)
|
||||
}
|
||||
isAddEditModalOpen.value = false
|
||||
message.success(isEditing.value ? '更新成功' : '新增成功')
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
|
||||
// 批量入库
|
||||
const handleBatchIn = (id) => {
|
||||
// 找到对应的疫苗
|
||||
const vaccine = vaccinesData.value.find(item => item.id === id)
|
||||
if (vaccine) {
|
||||
viewVaccine.value = { ...vaccine }
|
||||
// 重置入库表单
|
||||
Object.assign(batchInForm, {
|
||||
count: 1,
|
||||
batchNumber: '',
|
||||
inDate: new Date()
|
||||
})
|
||||
isInModalOpen.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 确认入库
|
||||
const handleConfirmIn = () => {
|
||||
// 在实际应用中,这里应该调用API入库数据
|
||||
if (viewVaccine.value) {
|
||||
const index = vaccinesData.value.findIndex(item => item.id === viewVaccine.value.id)
|
||||
if (index !== -1) {
|
||||
vaccinesData.value[index].stockCount += batchInForm.count
|
||||
// 更新状态
|
||||
if (vaccinesData.value[index].stockCount > 0) {
|
||||
vaccinesData.value[index].status = 'valid'
|
||||
}
|
||||
message.success('疫苗入库成功')
|
||||
}
|
||||
}
|
||||
isInModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭入库模态框
|
||||
const handleCloseInModal = () => {
|
||||
isInModalOpen.value = false
|
||||
}
|
||||
|
||||
// 批量出库
|
||||
const handleBatchOut = (id) => {
|
||||
// 找到对应的疫苗
|
||||
const vaccine = vaccinesData.value.find(item => item.id === id)
|
||||
if (vaccine && vaccine.stockCount > 0) {
|
||||
viewVaccine.value = { ...vaccine }
|
||||
// 重置出库表单
|
||||
Object.assign(batchOutForm, {
|
||||
count: 1,
|
||||
purpose: '',
|
||||
outDate: new Date()
|
||||
})
|
||||
isOutModalOpen.value = true
|
||||
} else {
|
||||
message.error('疫苗库存不足,无法出库')
|
||||
}
|
||||
}
|
||||
|
||||
// 确认出库
|
||||
const handleConfirmOut = () => {
|
||||
// 在实际应用中,这里应该调用API出库数据
|
||||
if (viewVaccine.value && batchOutForm.count <= viewVaccine.value.stockCount) {
|
||||
const index = vaccinesData.value.findIndex(item => item.id === viewVaccine.value.id)
|
||||
if (index !== -1) {
|
||||
vaccinesData.value[index].stockCount -= batchOutForm.count
|
||||
// 更新状态
|
||||
if (vaccinesData.value[index].stockCount === 0) {
|
||||
vaccinesData.value[index].status = 'low_stock'
|
||||
} else if (vaccinesData.value[index].stockCount < 100) {
|
||||
vaccinesData.value[index].status = 'low_stock'
|
||||
} else {
|
||||
vaccinesData.value[index].status = 'valid'
|
||||
}
|
||||
message.success('疫苗出库成功')
|
||||
}
|
||||
}
|
||||
isOutModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭出库模态框
|
||||
const handleCloseOutModal = () => {
|
||||
isOutModalOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
1164
government-admin/src/views/paperless/quarantine/QuarantineConfig.vue
Normal file
1164
government-admin/src/views/paperless/quarantine/QuarantineConfig.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,801 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>检疫申报</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="animal">动物检疫</a-select-option>
|
||||
<a-select-option value="product">动物产品检疫</a-select-option>
|
||||
<a-select-option value="transport">运输检疫</a-select-option>
|
||||
<a-select-option value="slaughter">屠宰检疫</a-select-option>
|
||||
<a-select-option value="other">其他检疫</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="pending">待审核</a-select-option>
|
||||
<a-select-option value="approved">已通过</a-select-option>
|
||||
<a-select-option value="rejected">已驳回</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
style="width: 300px;"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" danger @click="handleAddDeclaration">
|
||||
<span class="iconfont icon-tianjia"></span> 新增申报
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="declarationsData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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 === 'pending'">编辑</a-button>
|
||||
<a-button size="small" danger @click="handleDelete(record.id)" v-if="record.status === 'pending'">删除</a-button>
|
||||
<a-button size="small" @click="handleCancelDeclaration(record.id)" v-if="record.status === 'pending'">取消</a-button>
|
||||
<a-button size="small" @click="handlePrint(record.id)" v-if="record.status === 'approved'">打印</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑申报模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isAddEditModalOpen"
|
||||
:title="isEditing ? '编辑检疫申报' : '新增检疫申报'"
|
||||
:footer="null"
|
||||
width={800}
|
||||
>
|
||||
<a-form
|
||||
:model="currentDeclaration"
|
||||
layout="vertical"
|
||||
>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<a-form-item label="申报单位"
|
||||
:rules="[{ required: true, message: '请输入申报单位' }]">
|
||||
<a-input v-model:value="currentDeclaration.declarationUnit" placeholder="请输入申报单位" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="联系人"
|
||||
:rules="[{ required: true, message: '请输入联系人' }]">
|
||||
<a-input v-model:value="currentDeclaration.contactPerson" placeholder="请输入联系人" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="联系电话"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入联系电话' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
]">
|
||||
<a-input v-model:value="currentDeclaration.phone" placeholder="请输入联系电话" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="检疫类型"
|
||||
:rules="[{ required: true, message: '请选择检疫类型' }]">
|
||||
<a-select v-model:value="currentDeclaration.type" placeholder="请选择检疫类型">
|
||||
<a-select-option value="animal">动物检疫</a-select-option>
|
||||
<a-select-option value="product">动物产品检疫</a-select-option>
|
||||
<a-select-option value="transport">运输检疫</a-select-option>
|
||||
<a-select-option value="slaughter">屠宰检疫</a-select-option>
|
||||
<a-select-option value="other">其他检疫</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="检疫对象"
|
||||
:rules="[{ required: true, message: '请输入检疫对象' }]">
|
||||
<a-input v-model:value="currentDeclaration.object" placeholder="请输入检疫对象(如:牛、猪肉等)" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="数量"
|
||||
:rules="[
|
||||
{ required: true, message: '请输入数量' },
|
||||
{ type: 'number', min: 1, message: '数量至少为1' }
|
||||
]">
|
||||
<a-input-number v-model:value="currentDeclaration.quantity" min="1" placeholder="请输入数量" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="来源地"
|
||||
:rules="[{ required: true, message: '请输入来源地' }]">
|
||||
<a-input v-model:value="currentDeclaration.sourcePlace" placeholder="请输入来源地" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="目的地"
|
||||
:rules="[{ required: true, message: '请输入目的地' }]">
|
||||
<a-input v-model:value="currentDeclaration.destination" placeholder="请输入目的地" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="运输工具"
|
||||
:rules="[{ required: true, message: '请输入运输工具' }]" v-if="currentDeclaration.type === 'transport'">
|
||||
<a-input v-model:value="currentDeclaration.transportTool" placeholder="请输入运输工具(如:货车、船舶等)" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="车牌号" v-if="currentDeclaration.type === 'transport'">
|
||||
<a-input v-model:value="currentDeclaration.vehicleNumber" placeholder="请输入车牌号" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<a-form-item label="申报理由"
|
||||
:rules="[{ required: true, message: '请输入申报理由' }]">
|
||||
<a-input.TextArea v-model:value="currentDeclaration.reason" placeholder="请输入申报理由" :rows="3" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="申报附件">
|
||||
<a-upload
|
||||
name="file"
|
||||
:multiple="true"
|
||||
:fileList="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
@change="handleUploadChange"
|
||||
>
|
||||
<a-button>
|
||||
<span class="iconfont icon-upload"></span> 上传附件
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<p style="color: #8c8c8c; margin-top: 8px;">支持jpg、png、pdf格式,单个文件不超过10MB</p>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="备注">
|
||||
<a-input.TextArea v-model:value="currentDeclaration.notes" placeholder="请输入备注信息" :rows="3" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 查看申报详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="查看检疫申报详情"
|
||||
:footer="null"
|
||||
width={800}
|
||||
>
|
||||
<div v-if="viewDeclaration">
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">基本信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报单位</p>
|
||||
<p>{{ viewDeclaration.declarationUnit }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系人</p>
|
||||
<p>{{ viewDeclaration.contactPerson }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系电话</p>
|
||||
<p>{{ viewDeclaration.phone }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫类型</p>
|
||||
<p>{{ getTypeText(viewDeclaration.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫对象</p>
|
||||
<p>{{ viewDeclaration.object }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">数量</p>
|
||||
<p>{{ viewDeclaration.quantity }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">来源地</p>
|
||||
<p>{{ viewDeclaration.sourcePlace }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">目的地</p>
|
||||
<p>{{ viewDeclaration.destination }}</p>
|
||||
</div>
|
||||
<div v-if="viewDeclaration.transportTool">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">运输工具</p>
|
||||
<p>{{ viewDeclaration.transportTool }}</p>
|
||||
</div>
|
||||
<div v-if="viewDeclaration.vehicleNumber">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">车牌号</p>
|
||||
<p>{{ viewDeclaration.vehicleNumber }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报日期</p>
|
||||
<p>{{ viewDeclaration.declarationDate }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">状态</p>
|
||||
<p><a-tag :color="getStatusColor(viewDeclaration.status)">{{ getStatusText(viewDeclaration.status) }}</a-tag></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 16px;">
|
||||
<h3 style="margin-bottom: 8px;">详细信息</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报理由</p>
|
||||
<p>{{ viewDeclaration.reason || '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="viewDeclaration.files && viewDeclaration.files.length > 0" style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报附件</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||||
<a-tag v-for="file in viewDeclaration.files" :key="file.id" color="blue" style="cursor: pointer;">
|
||||
{{ file.name }}
|
||||
<template #closeIcon>
|
||||
<span class="iconfont icon-download"></span>
|
||||
</template>
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="viewDeclaration.reviewComments">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">审核意见</p>
|
||||
<p>{{ viewDeclaration.reviewComments || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 style="margin-bottom: 8px;">备注</h3>
|
||||
<p>{{ viewDeclaration.notes || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const dateRange = ref([])
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isAddEditModalOpen = ref(false)
|
||||
const isViewModalOpen = ref(false)
|
||||
const isEditing = ref(false)
|
||||
|
||||
// 当前编辑/查看的申报
|
||||
const currentDeclaration = reactive({
|
||||
id: '',
|
||||
declarationUnit: '',
|
||||
contactPerson: '',
|
||||
phone: '',
|
||||
type: 'animal',
|
||||
object: '',
|
||||
quantity: 1,
|
||||
sourcePlace: '',
|
||||
destination: '',
|
||||
transportTool: '',
|
||||
vehicleNumber: '',
|
||||
reason: '',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '',
|
||||
createdAt: ''
|
||||
})
|
||||
|
||||
const viewDeclaration = ref(null)
|
||||
const fileList = ref([])
|
||||
|
||||
// 申报列表数据(模拟数据)
|
||||
const declarationsData = ref([
|
||||
{
|
||||
id: '1',
|
||||
declarationUnit: '郑州市金水区阳光养殖场',
|
||||
contactPerson: '张三',
|
||||
phone: '13812345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 150,
|
||||
sourcePlace: '郑州市金水区',
|
||||
destination: '河南省商丘市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A12345',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-01',
|
||||
createdAt: '2023-10-01'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
declarationUnit: '新郑市绿源养殖场',
|
||||
contactPerson: '李四',
|
||||
phone: '13912345678',
|
||||
type: 'product',
|
||||
object: '牛肉',
|
||||
quantity: 500,
|
||||
sourcePlace: '新郑市',
|
||||
destination: '上海市',
|
||||
transportTool: '冷藏车',
|
||||
vehicleNumber: '豫A67890',
|
||||
reason: '牛肉产品销售',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '2023-10-02',
|
||||
createdAt: '2023-10-02'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
declarationUnit: '新密市祥和养殖场',
|
||||
contactPerson: '王五',
|
||||
phone: '13712345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 80,
|
||||
sourcePlace: '新密市',
|
||||
destination: '新密市肉类加工厂',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A23456',
|
||||
reason: '牛只屠宰加工',
|
||||
files: [],
|
||||
reviewComments: '资料不全,需补充产地检疫证明',
|
||||
notes: '',
|
||||
status: 'rejected',
|
||||
declarationDate: '2023-10-03',
|
||||
createdAt: '2023-10-03'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
declarationUnit: '登封市幸福养殖场',
|
||||
contactPerson: '赵六',
|
||||
phone: '13612345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 120,
|
||||
sourcePlace: '登封市',
|
||||
destination: '湖北省武汉市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A34567',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '2023-10-04',
|
||||
createdAt: '2023-10-04'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
declarationUnit: '中牟县希望养殖场',
|
||||
contactPerson: '钱七',
|
||||
phone: '13512345678',
|
||||
type: 'product',
|
||||
object: '牛奶',
|
||||
quantity: 2000,
|
||||
sourcePlace: '中牟县',
|
||||
destination: '河南省郑州市',
|
||||
transportTool: '冷藏车',
|
||||
vehicleNumber: '豫A45678',
|
||||
reason: '牛奶产品销售',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-05',
|
||||
createdAt: '2023-10-05'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
declarationUnit: '荥阳市快乐养殖场',
|
||||
contactPerson: '孙八',
|
||||
phone: '13412345678',
|
||||
type: 'transport',
|
||||
object: '牛',
|
||||
quantity: 90,
|
||||
sourcePlace: '荥阳市',
|
||||
destination: '山西省太原市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A56789',
|
||||
reason: '牛只跨区域调运',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '2023-10-06',
|
||||
createdAt: '2023-10-06'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
declarationUnit: '巩义市明星养殖场',
|
||||
contactPerson: '周九',
|
||||
phone: '13312345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 60,
|
||||
sourcePlace: '巩义市',
|
||||
destination: '巩义市屠宰场',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A67890',
|
||||
reason: '牛只屠宰',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-07',
|
||||
createdAt: '2023-10-07'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
declarationUnit: '惠济区温馨养殖场',
|
||||
contactPerson: '吴十',
|
||||
phone: '13212345678',
|
||||
type: 'other',
|
||||
object: '牛精液',
|
||||
quantity: 500,
|
||||
sourcePlace: '惠济区',
|
||||
destination: '全国各地',
|
||||
transportTool: '快递冷链',
|
||||
vehicleNumber: '',
|
||||
reason: '种牛精液销售',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '2023-10-08',
|
||||
createdAt: '2023-10-08'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
declarationUnit: '二七区红火养殖场',
|
||||
contactPerson: '郑十一',
|
||||
phone: '13112345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 100,
|
||||
sourcePlace: '二七区',
|
||||
destination: '江苏省南京市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A78901',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '主动取消',
|
||||
notes: '',
|
||||
status: 'cancelled',
|
||||
declarationDate: '2023-10-09',
|
||||
createdAt: '2023-10-09'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
declarationUnit: '中原区丰收养殖场',
|
||||
contactPerson: '王十二',
|
||||
phone: '13012345678',
|
||||
type: 'product',
|
||||
object: '牛肉制品',
|
||||
quantity: 300,
|
||||
sourcePlace: '中原区',
|
||||
destination: '广东省广州市',
|
||||
transportTool: '冷链物流',
|
||||
vehicleNumber: '豫A89012',
|
||||
reason: '牛肉制品销售',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '2023-10-10',
|
||||
createdAt: '2023-10-10'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '申报单位',
|
||||
dataIndex: 'declarationUnit',
|
||||
key: 'declarationUnit',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'contactPerson',
|
||||
key: 'contactPerson',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '检疫类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '检疫对象',
|
||||
dataIndex: 'object',
|
||||
key: 'object',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'quantity',
|
||||
key: 'quantity',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '申报日期',
|
||||
dataIndex: 'declarationDate',
|
||||
key: 'declarationDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
pending: '待审核',
|
||||
approved: '已通过',
|
||||
rejected: '已驳回',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
pending: 'blue',
|
||||
approved: 'green',
|
||||
rejected: 'red',
|
||||
cancelled: 'default'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
animal: '动物检疫',
|
||||
product: '动物产品检疫',
|
||||
transport: '运输检疫',
|
||||
slaughter: '屠宰检疫',
|
||||
other: '其他检疫'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 上传前校验
|
||||
const beforeUpload = (file) => {
|
||||
// 检查文件类型
|
||||
const isValidType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.type)
|
||||
if (!isValidType) {
|
||||
message.error('只能上传JPG、PNG、PDF格式的文件')
|
||||
return Upload.LIST_IGNORE
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
const isLt10M = file.size / 1024 / 1024 < 10
|
||||
if (!isLt10M) {
|
||||
message.error('文件大小不能超过10MB')
|
||||
return Upload.LIST_IGNORE
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 上传变化处理
|
||||
const handleUploadChange = ({ fileList: newFileList }) => {
|
||||
fileList.value = newFileList
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
dateRange.value = []
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 新增申报
|
||||
const handleAddDeclaration = () => {
|
||||
// 重置表单
|
||||
Object.assign(currentDeclaration, {
|
||||
id: '',
|
||||
declarationUnit: '',
|
||||
contactPerson: '',
|
||||
phone: '',
|
||||
type: 'animal',
|
||||
object: '',
|
||||
quantity: 1,
|
||||
sourcePlace: '',
|
||||
destination: '',
|
||||
transportTool: '',
|
||||
vehicleNumber: '',
|
||||
reason: '',
|
||||
files: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
declarationDate: '',
|
||||
createdAt: ''
|
||||
})
|
||||
fileList.value = []
|
||||
isEditing.value = false
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 编辑申报
|
||||
const handleEdit = (record) => {
|
||||
// 复制记录到当前编辑对象
|
||||
Object.assign(currentDeclaration, { ...record })
|
||||
// 设置文件列表
|
||||
if (record.files && record.files.length > 0) {
|
||||
fileList.value = record.files.map(file => ({
|
||||
uid: file.id,
|
||||
name: file.name,
|
||||
status: 'done',
|
||||
url: file.url
|
||||
}))
|
||||
} else {
|
||||
fileList.value = []
|
||||
}
|
||||
isEditing.value = true
|
||||
isAddEditModalOpen.value = true
|
||||
}
|
||||
|
||||
// 查看申报
|
||||
const handleView = (record) => {
|
||||
viewDeclaration.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 删除申报
|
||||
const handleDelete = (id) => {
|
||||
// 在实际应用中,这里应该调用API删除数据
|
||||
const index = declarationsData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
declarationsData.value.splice(index, 1)
|
||||
message.success('删除成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存申报
|
||||
const handleSave = () => {
|
||||
// 在实际应用中,这里应该调用API保存数据
|
||||
|
||||
// 处理文件列表
|
||||
currentDeclaration.files = fileList.value.map(file => ({
|
||||
id: file.uid || Date.now().toString(),
|
||||
name: file.name,
|
||||
url: file.url
|
||||
}))
|
||||
|
||||
if (isEditing.value) {
|
||||
// 更新现有申报
|
||||
const index = declarationsData.value.findIndex(item => item.id === currentDeclaration.id)
|
||||
if (index !== -1) {
|
||||
declarationsData.value[index] = { ...currentDeclaration }
|
||||
}
|
||||
} else {
|
||||
// 添加新申报
|
||||
const newDeclaration = {
|
||||
...currentDeclaration,
|
||||
id: Date.now().toString(),
|
||||
declarationDate: new Date().toISOString().split('T')[0],
|
||||
createdAt: new Date().toISOString().split('T')[0]
|
||||
}
|
||||
declarationsData.value.unshift(newDeclaration)
|
||||
}
|
||||
isAddEditModalOpen.value = false
|
||||
message.success(isEditing.value ? '更新成功' : '新增成功')
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
isAddEditModalOpen.value = false
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
|
||||
// 取消申报
|
||||
const handleCancelDeclaration = (id) => {
|
||||
// 在实际应用中,这里应该调用API更新申报状态
|
||||
const index = declarationsData.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
declarationsData.value[index].status = 'cancelled'
|
||||
declarationsData.value[index].reviewComments = '申请人主动取消'
|
||||
message.success('申报已取消')
|
||||
}
|
||||
}
|
||||
|
||||
// 打印申报单
|
||||
const handlePrint = (id) => {
|
||||
// 在实际应用中,这里应该调用API获取打印数据
|
||||
message.success('打印功能待实现')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,602 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>检疫记录查询</h1>
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="animal">动物检疫</a-select-option>
|
||||
<a-select-option value="product">动物产品检疫</a-select-option>
|
||||
<a-select-option value="transport">运输检疫</a-select-option>
|
||||
<a-select-option value="slaughter">屠宰检疫</a-select-option>
|
||||
<a-select-option value="other">其他检疫</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="approved">已通过</a-select-option>
|
||||
<a-select-option value="rejected">已驳回</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
style="width: 300px;"
|
||||
format="YYYY-MM-DD"
|
||||
placeholder={['申报日期', '申报日期']}
|
||||
/>
|
||||
|
||||
<a-input v-model:value="quarantineOfficer" placeholder="检疫员" style="width: 150px;">
|
||||
<template #prefix>
|
||||
<span class="iconfont icon-yonghu"></span>
|
||||
</template>
|
||||
</a-input>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="primary" @click="handleExport">
|
||||
<span class="iconfont icon-daochu"></span> 导出记录
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="recordsData"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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" @click="handlePrint(record.id)">打印</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 查看记录详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="检疫记录详情"
|
||||
:footer="null"
|
||||
width={800}
|
||||
>
|
||||
<div v-if="viewRecord">
|
||||
<!-- 申报信息 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px;">申报信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报单号</p>
|
||||
<p>{{ viewRecord.declarationNumber || '-' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报单位</p>
|
||||
<p>{{ viewRecord.declarationUnit }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系人</p>
|
||||
<p>{{ viewRecord.contactPerson }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系电话</p>
|
||||
<p>{{ viewRecord.phone }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报日期</p>
|
||||
<p>{{ viewRecord.declarationDate }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报理由</p>
|
||||
<p>{{ viewRecord.reason }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫信息 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px;">检疫信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫类型</p>
|
||||
<p>{{ getTypeText(viewRecord.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫对象</p>
|
||||
<p>{{ viewRecord.object }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">数量</p>
|
||||
<p>{{ viewRecord.quantity }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">来源地</p>
|
||||
<p>{{ viewRecord.sourcePlace }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">目的地</p>
|
||||
<p>{{ viewRecord.destination }}</p>
|
||||
</div>
|
||||
<div v-if="viewRecord.transportTool">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">运输工具</p>
|
||||
<p>{{ viewRecord.transportTool }}</p>
|
||||
</div>
|
||||
<div v-if="viewRecord.vehicleNumber">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">车牌号</p>
|
||||
<p>{{ viewRecord.vehicleNumber }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫结果 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px;">检疫结果</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫员</p>
|
||||
<p>{{ viewRecord.quarantineOfficer }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫时间</p>
|
||||
<p>{{ viewRecord.quarantineTime }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫结果</p>
|
||||
<p><a-tag :color="getStatusColor(viewRecord.status)">{{ getStatusText(viewRecord.status) }}</a-tag></p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫证书编号</p>
|
||||
<p>{{ viewRecord.certificateNumber || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫结论</p>
|
||||
<p>{{ viewRecord.quarantineComments || '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="viewRecord.reviewComments">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">审核意见</p>
|
||||
<p>{{ viewRecord.reviewComments || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 附件 -->
|
||||
<div v-if="viewRecord.files && viewRecord.files.length > 0">
|
||||
<h3 style="margin-bottom: 12px;">相关附件</h3>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||||
<a-tag v-for="file in viewRecord.files" :key="file.id" color="blue" style="cursor: pointer;">
|
||||
{{ file.name }}
|
||||
<template #closeIcon>
|
||||
<span class="iconfont icon-download"></span>
|
||||
</template>
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
<a-button type="primary" @click="handlePrint(viewRecord.id)" style="margin-left: 12px;">打印</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const dateRange = ref([])
|
||||
const quarantineOfficer = ref('')
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 模态框状态
|
||||
const isViewModalOpen = ref(false)
|
||||
const viewRecord = ref(null)
|
||||
|
||||
// 记录列表数据(模拟数据)
|
||||
const recordsData = ref([
|
||||
{
|
||||
id: '1',
|
||||
declarationNumber: 'J202310010001',
|
||||
declarationUnit: '郑州市金水区阳光养殖场',
|
||||
contactPerson: '张三',
|
||||
phone: '13812345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 150,
|
||||
sourcePlace: '郑州市金水区',
|
||||
destination: '河南省商丘市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A12345',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '李检疫',
|
||||
quarantineTime: '2023-10-01 15:30',
|
||||
quarantineComments: '经检疫,该批牛只健康状况良好,无传染病症状,符合出证条件。',
|
||||
certificateNumber: 'QS202310010001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-01'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
declarationNumber: 'J202310050001',
|
||||
declarationUnit: '中牟县希望养殖场',
|
||||
contactPerson: '钱七',
|
||||
phone: '13512345678',
|
||||
type: 'product',
|
||||
object: '牛奶',
|
||||
quantity: 2000,
|
||||
sourcePlace: '中牟县',
|
||||
destination: '河南省郑州市',
|
||||
transportTool: '冷藏车',
|
||||
vehicleNumber: '豫A45678',
|
||||
reason: '牛奶产品销售',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '王检疫',
|
||||
quarantineTime: '2023-10-05 10:20',
|
||||
quarantineComments: '经检疫,该批牛奶符合国家标准,无致病菌,质量合格。',
|
||||
certificateNumber: 'QS202310050001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-05'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
declarationNumber: 'J202310030001',
|
||||
declarationUnit: '新密市祥和养殖场',
|
||||
contactPerson: '王五',
|
||||
phone: '13712345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 80,
|
||||
sourcePlace: '新密市',
|
||||
destination: '新密市肉类加工厂',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A23456',
|
||||
reason: '牛只屠宰加工',
|
||||
files: [],
|
||||
reviewComments: '资料不全,需补充产地检疫证明',
|
||||
quarantineOfficer: '赵检疫',
|
||||
quarantineTime: '2023-10-03 14:10',
|
||||
quarantineComments: '经检查,申报资料不全,缺少产地检疫证明,需补充材料后重新申报。',
|
||||
certificateNumber: '',
|
||||
status: 'rejected',
|
||||
declarationDate: '2023-10-03'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
declarationNumber: 'J202310070001',
|
||||
declarationUnit: '巩义市明星养殖场',
|
||||
contactPerson: '周九',
|
||||
phone: '13312345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 60,
|
||||
sourcePlace: '巩义市',
|
||||
destination: '巩义市屠宰场',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A67890',
|
||||
reason: '牛只屠宰',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '孙检疫',
|
||||
quarantineTime: '2023-10-07 09:45',
|
||||
quarantineComments: '经检疫,该批牛只健康状况良好,适合屠宰。',
|
||||
certificateNumber: 'QS202310070001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-10-07'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
declarationNumber: 'J202310090001',
|
||||
declarationUnit: '二七区红火养殖场',
|
||||
contactPerson: '郑十一',
|
||||
phone: '13112345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 100,
|
||||
sourcePlace: '二七区',
|
||||
destination: '江苏省南京市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A78901',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '主动取消',
|
||||
quarantineOfficer: '',
|
||||
quarantineTime: '',
|
||||
quarantineComments: '',
|
||||
certificateNumber: '',
|
||||
status: 'cancelled',
|
||||
declarationDate: '2023-10-09'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
declarationNumber: 'J202309250001',
|
||||
declarationUnit: '荥阳市绿源养殖场',
|
||||
contactPerson: '陈十二',
|
||||
phone: '13912345679',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 120,
|
||||
sourcePlace: '荥阳市',
|
||||
destination: '山东省济南市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A89012',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '杨检疫',
|
||||
quarantineTime: '2023-09-25 16:20',
|
||||
quarantineComments: '经检疫,该批牛只健康状况良好,无传染病症状,符合出证条件。',
|
||||
certificateNumber: 'QS202309250001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-09-25'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
declarationNumber: 'J202309280001',
|
||||
declarationUnit: '新郑市幸福养殖场',
|
||||
contactPerson: '吴十三',
|
||||
phone: '13612345679',
|
||||
type: 'product',
|
||||
object: '牛肉制品',
|
||||
quantity: 400,
|
||||
sourcePlace: '新郑市',
|
||||
destination: '北京市',
|
||||
transportTool: '冷链物流',
|
||||
vehicleNumber: '豫A90123',
|
||||
reason: '牛肉制品销售',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '郑检疫',
|
||||
quarantineTime: '2023-09-28 11:30',
|
||||
quarantineComments: '经检疫,该批牛肉制品符合食品安全标准,无质量问题。',
|
||||
certificateNumber: 'QS202309280001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-09-28'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
declarationNumber: 'J202309300001',
|
||||
declarationUnit: '登封市丰收养殖场',
|
||||
contactPerson: '冯十四',
|
||||
phone: '13412345679',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 140,
|
||||
sourcePlace: '登封市',
|
||||
destination: '湖北省武汉市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A01234',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '资料不全,需补充免疫记录',
|
||||
quarantineOfficer: '褚检疫',
|
||||
quarantineTime: '2023-09-30 13:45',
|
||||
quarantineComments: '经检查,申报资料不全,缺少牛只免疫记录,需补充材料后重新申报。',
|
||||
certificateNumber: '',
|
||||
status: 'rejected',
|
||||
declarationDate: '2023-09-30'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
declarationNumber: 'J202309200001',
|
||||
declarationUnit: '管城回族区快乐养殖场',
|
||||
contactPerson: '卫十五',
|
||||
phone: '13212345679',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 90,
|
||||
sourcePlace: '管城回族区',
|
||||
destination: '陕西省西安市',
|
||||
transportTool: '货车',
|
||||
vehicleNumber: '豫A12346',
|
||||
reason: '牛只销售运输',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '蒋检疫',
|
||||
quarantineTime: '2023-09-20 10:15',
|
||||
quarantineComments: '经检疫,该批牛只健康状况良好,无传染病症状,符合出证条件。',
|
||||
certificateNumber: 'QS202309200001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-09-20'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
declarationNumber: 'J202309150001',
|
||||
declarationUnit: '惠济区温馨养殖场',
|
||||
contactPerson: '沈十六',
|
||||
phone: '13012345679',
|
||||
type: 'product',
|
||||
object: '牛奶',
|
||||
quantity: 1500,
|
||||
sourcePlace: '惠济区',
|
||||
destination: '河北省石家庄市',
|
||||
transportTool: '冷藏车',
|
||||
vehicleNumber: '豫A23457',
|
||||
reason: '牛奶产品销售',
|
||||
files: [],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
quarantineOfficer: '韩检疫',
|
||||
quarantineTime: '2023-09-15 14:40',
|
||||
quarantineComments: '经检疫,该批牛奶符合国家标准,无致病菌,质量合格。',
|
||||
certificateNumber: 'QS202309150001',
|
||||
status: 'approved',
|
||||
declarationDate: '2023-09-15'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '申报单号',
|
||||
dataIndex: 'declarationNumber',
|
||||
key: 'declarationNumber',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: '申报单位',
|
||||
dataIndex: 'declarationUnit',
|
||||
key: 'declarationUnit',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '检疫类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '检疫对象',
|
||||
dataIndex: 'object',
|
||||
key: 'object',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '检疫员',
|
||||
dataIndex: 'quarantineOfficer',
|
||||
key: 'quarantineOfficer',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '申报日期',
|
||||
dataIndex: 'declarationDate',
|
||||
key: 'declarationDate',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
approved: '已通过',
|
||||
rejected: '已驳回',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
approved: 'green',
|
||||
rejected: 'red',
|
||||
cancelled: 'default'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
animal: '动物检疫',
|
||||
product: '动物产品检疫',
|
||||
transport: '运输检疫',
|
||||
slaughter: '屠宰检疫',
|
||||
other: '其他检疫'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
dateRange.value = []
|
||||
quarantineOfficer.value = ''
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 导出记录
|
||||
const handleExport = () => {
|
||||
// 在实际应用中,这里应该调用API导出数据
|
||||
message.success('导出功能待实现')
|
||||
}
|
||||
|
||||
// 查看记录
|
||||
const handleView = (record) => {
|
||||
viewRecord.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
|
||||
// 打印记录
|
||||
const handlePrint = (id) => {
|
||||
// 在实际应用中,这里应该调用API获取打印数据
|
||||
message.success('打印功能待实现')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,696 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>检疫记录查询</h1>
|
||||
|
||||
<!-- 搜索和筛选栏 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<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-option value="">全部</a-select-option>
|
||||
<a-select-option value="animal">动物检疫</a-select-option>
|
||||
<a-select-option value="product">动物产品检疫</a-select-option>
|
||||
<a-select-option value="transport">运输检疫</a-select-option>
|
||||
<a-select-option value="slaughter">屠宰检疫</a-select-option>
|
||||
<a-select-option value="other">其他检疫</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="statusFilter" placeholder="状态" style="width: 120px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="pending">待审核</a-select-option>
|
||||
<a-select-option value="approved">已通过</a-select-option>
|
||||
<a-select-option value="rejected">已驳回</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-input v-model:value="quarantinePersonFilter" placeholder="检疫人员" style="width: 150px;" />
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="dateRange"
|
||||
style="width: 300px;"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
|
||||
<a-button type="primary" @click="handleSearch" style="margin-left: auto;">
|
||||
<span class="iconfont icon-sousuo"></span> 搜索
|
||||
</a-button>
|
||||
|
||||
<a-button type="default" @click="handleReset">重置</a-button>
|
||||
|
||||
<a-button type="default" @click="handleExport">
|
||||
<span class="iconfont icon-export"></span> 导出
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<a-card>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="quarantineRecords"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<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" @click="handlePrint(record.id)" v-if="record.status === 'approved'">打印</a-button>
|
||||
<a-button size="small" @click="handleRecheck(record.id)" v-if="record.status === 'approved'">重新检疫</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 查看记录详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isViewModalOpen"
|
||||
title="检疫记录详情"
|
||||
:footer="null"
|
||||
width={800}
|
||||
>
|
||||
<div v-if="viewRecord">
|
||||
<!-- 申报信息 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0;">申报信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报单号</p>
|
||||
<p>{{ viewRecord.declarationNumber || '-' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报单位</p>
|
||||
<p>{{ viewRecord.declarationUnit }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系人</p>
|
||||
<p>{{ viewRecord.contactPerson }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">联系电话</p>
|
||||
<p>{{ viewRecord.phone }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫类型</p>
|
||||
<p>{{ getTypeText(viewRecord.type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫对象</p>
|
||||
<p>{{ viewRecord.object }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">数量</p>
|
||||
<p>{{ viewRecord.quantity }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">申报日期</p>
|
||||
<p>{{ viewRecord.declarationDate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫信息 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0;">检疫信息</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫人员</p>
|
||||
<p>{{ viewRecord.quarantinePerson }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫时间</p>
|
||||
<p>{{ viewRecord.quarantineTime }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫结果</p>
|
||||
<p>{{ viewRecord.result ? '合格' : '不合格' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">证书编号</p>
|
||||
<p>{{ viewRecord.certificateNumber || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫详情 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0;">检疫详情</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫项目</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<a-tag v-for="item in viewRecord.quarantineItems" :key="item.id">
|
||||
{{ item.name }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫过程描述</p>
|
||||
<p>{{ viewRecord.processDescription || '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫结论</p>
|
||||
<p>{{ viewRecord.conclusion || '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="viewRecord.photos && viewRecord.photos.length > 0" style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">检疫照片</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||||
<a-avatar
|
||||
v-for="photo in viewRecord.photos"
|
||||
:key="photo.id"
|
||||
:src="photo.url"
|
||||
style="width: 80px; height: 80px;"
|
||||
@click="handleViewPhoto(photo)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 其他信息 -->
|
||||
<div>
|
||||
<h3 style="margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0;">其他信息</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">审核意见</p>
|
||||
<p>{{ viewRecord.reviewComments || '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p style="color: #8c8c8c; margin-bottom: 4px;">备注</p>
|
||||
<p>{{ viewRecord.notes || '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleCloseView">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 照片查看模态框 -->
|
||||
<a-modal
|
||||
v-model:open="isPhotoModalOpen"
|
||||
title="检疫照片"
|
||||
:footer="null"
|
||||
width={600}
|
||||
>
|
||||
<img :src="currentPhotoUrl" alt="检疫照片" style="width: 100%; height: auto;" />
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 24px;">
|
||||
<a-button @click="handleClosePhoto">关闭</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// 搜索条件
|
||||
const searchKeyword = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const quarantinePersonFilter = ref('')
|
||||
const dateRange = ref([])
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 选中行
|
||||
const selectedRowKeys = ref([])
|
||||
const onSelectChange = (newSelectedRowKeys) => {
|
||||
selectedRowKeys.value = newSelectedRowKeys
|
||||
}
|
||||
|
||||
// 模态框状态
|
||||
const isViewModalOpen = ref(false)
|
||||
const isPhotoModalOpen = ref(false)
|
||||
|
||||
// 当前查看的记录和照片
|
||||
const viewRecord = ref(null)
|
||||
const currentPhotoUrl = ref('')
|
||||
|
||||
// 检疫记录数据(模拟数据)
|
||||
const quarantineRecords = ref([
|
||||
{
|
||||
id: '1',
|
||||
declarationNumber: 'JD20231001001',
|
||||
declarationUnit: '郑州市金水区阳光养殖场',
|
||||
contactPerson: '张三',
|
||||
phone: '13812345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 150,
|
||||
declarationDate: '2023-10-01',
|
||||
quarantinePerson: '王五',
|
||||
quarantineTime: '2023-10-02 10:30:00',
|
||||
result: true,
|
||||
certificateNumber: 'JZZS20231002001',
|
||||
quarantineItems: [
|
||||
{ id: '1', name: '体温检测' },
|
||||
{ id: '2', name: '临床检查' },
|
||||
{ id: '3', name: '疫苗接种记录' },
|
||||
{ id: '4', name: '疫情监测' }
|
||||
],
|
||||
processDescription: '按照《动物检疫管理办法》规定,对申报的150头牛进行了体温检测、临床检查、疫苗接种记录核查和疫情监测,所有检测项目均符合要求。',
|
||||
conclusion: '经检疫,该批牛只健康状况良好,无传染病症状,符合出证条件,准予放行。',
|
||||
photos: [
|
||||
{ id: '1', url: 'https://via.placeholder.com/300x200?text=Photo1' },
|
||||
{ id: '2', url: 'https://via.placeholder.com/300x200?text=Photo2' }
|
||||
],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
createdAt: '2023-10-01'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
declarationNumber: 'JD20231002001',
|
||||
declarationUnit: '新郑市绿源养殖场',
|
||||
contactPerson: '李四',
|
||||
phone: '13912345678',
|
||||
type: 'product',
|
||||
object: '牛肉',
|
||||
quantity: 500,
|
||||
declarationDate: '2023-10-02',
|
||||
quarantinePerson: '赵六',
|
||||
quarantineTime: '2023-10-03 14:20:00',
|
||||
result: false,
|
||||
certificateNumber: '',
|
||||
quarantineItems: [
|
||||
{ id: '5', name: '感官检查' },
|
||||
{ id: '6', name: '微生物检测' },
|
||||
{ id: '7', name: '兽药残留检测' },
|
||||
{ id: '8', name: '重金属检测' }
|
||||
],
|
||||
processDescription: '对申报的500公斤牛肉进行了感官检查、微生物检测、兽药残留检测和重金属检测,发现微生物指标超标。',
|
||||
conclusion: '经检疫,该批牛肉微生物指标不符合食品安全标准,不予出证,禁止销售。',
|
||||
photos: [
|
||||
{ id: '3', url: 'https://via.placeholder.com/300x200?text=Photo3' },
|
||||
{ id: '4', url: 'https://via.placeholder.com/300x200?text=Photo4' }
|
||||
],
|
||||
reviewComments: '微生物指标超标,不同意通过',
|
||||
notes: '建议加强冷链管理',
|
||||
status: 'rejected',
|
||||
createdAt: '2023-10-02'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
declarationNumber: 'JD20231003001',
|
||||
declarationUnit: '新密市祥和养殖场',
|
||||
contactPerson: '王五',
|
||||
phone: '13712345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 80,
|
||||
declarationDate: '2023-10-03',
|
||||
quarantinePerson: '孙七',
|
||||
quarantineTime: '2023-10-04 09:15:00',
|
||||
result: true,
|
||||
certificateNumber: 'JZZS20231004001',
|
||||
quarantineItems: [
|
||||
{ id: '9', name: '宰前检查' },
|
||||
{ id: '10', name: '宰后检验' },
|
||||
{ id: '11', name: '内脏检查' },
|
||||
{ id: '12', name: '肉品质量检查' }
|
||||
],
|
||||
processDescription: '对80头牛进行了宰前检查,确认健康状况良好;宰后进行了头蹄、内脏、胴体等部位的检验,未发现异常。',
|
||||
conclusion: '经检疫,该批屠宰牛只健康状况良好,符合屠宰检疫要求,准予屠宰销售。',
|
||||
photos: [
|
||||
{ id: '5', url: 'https://via.placeholder.com/300x200?text=Photo5' },
|
||||
{ id: '6', url: 'https://via.placeholder.com/300x200?text=Photo6' }
|
||||
],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
createdAt: '2023-10-03'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
declarationNumber: 'JD20231004001',
|
||||
declarationUnit: '登封市幸福养殖场',
|
||||
contactPerson: '赵六',
|
||||
phone: '13612345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 120,
|
||||
declarationDate: '2023-10-04',
|
||||
quarantinePerson: '钱八',
|
||||
quarantineTime: '',
|
||||
result: false,
|
||||
certificateNumber: '',
|
||||
quarantineItems: [],
|
||||
processDescription: '',
|
||||
conclusion: '',
|
||||
photos: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
createdAt: '2023-10-04'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
declarationNumber: 'JD20231005001',
|
||||
declarationUnit: '中牟县希望养殖场',
|
||||
contactPerson: '钱七',
|
||||
phone: '13512345678',
|
||||
type: 'product',
|
||||
object: '牛奶',
|
||||
quantity: 2000,
|
||||
declarationDate: '2023-10-05',
|
||||
quarantinePerson: '周九',
|
||||
quarantineTime: '2023-10-06 11:45:00',
|
||||
result: true,
|
||||
certificateNumber: 'JZZS20231006001',
|
||||
quarantineItems: [
|
||||
{ id: '13', name: '感官检查' },
|
||||
{ id: '14', name: '理化指标检测' },
|
||||
{ id: '15', name: '微生物检测' },
|
||||
{ id: '16', name: '兽药残留检测' }
|
||||
],
|
||||
processDescription: '对2000升牛奶进行了感官检查、理化指标检测、微生物检测和兽药残留检测,所有指标均符合国家标准。',
|
||||
conclusion: '经检疫,该批牛奶质量合格,符合食品安全标准,准予销售。',
|
||||
photos: [
|
||||
{ id: '7', url: 'https://via.placeholder.com/300x200?text=Photo7' },
|
||||
{ id: '8', url: 'https://via.placeholder.com/300x200?text=Photo8' }
|
||||
],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
createdAt: '2023-10-05'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
declarationNumber: 'JD20231006001',
|
||||
declarationUnit: '荥阳市快乐养殖场',
|
||||
contactPerson: '孙八',
|
||||
phone: '13412345678',
|
||||
type: 'transport',
|
||||
object: '牛',
|
||||
quantity: 90,
|
||||
declarationDate: '2023-10-06',
|
||||
quarantinePerson: '吴十',
|
||||
quarantineTime: '2023-10-07 08:30:00',
|
||||
result: true,
|
||||
certificateNumber: 'JZZS20231007001',
|
||||
quarantineItems: [
|
||||
{ id: '17', name: '动物健康检查' },
|
||||
{ id: '18', name: '运输工具检查' },
|
||||
{ id: '19', name: '消毒情况检查' },
|
||||
{ id: '20', name: '检疫证明检查' }
|
||||
],
|
||||
processDescription: '对90头牛进行了健康检查,确认无异常;检查运输工具符合要求,已消毒,检疫证明齐全。',
|
||||
conclusion: '经检疫,该批运输牛只符合跨区域调运要求,准予调运。',
|
||||
photos: [
|
||||
{ id: '9', url: 'https://via.placeholder.com/300x200?text=Photo9' },
|
||||
{ id: '10', url: 'https://via.placeholder.com/300x200?text=Photo10' }
|
||||
],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
createdAt: '2023-10-06'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
declarationNumber: 'JD20231007001',
|
||||
declarationUnit: '巩义市明星养殖场',
|
||||
contactPerson: '周九',
|
||||
phone: '13312345678',
|
||||
type: 'slaughter',
|
||||
object: '牛',
|
||||
quantity: 60,
|
||||
declarationDate: '2023-10-07',
|
||||
quarantinePerson: '郑十一',
|
||||
quarantineTime: '2023-10-08 13:20:00',
|
||||
result: true,
|
||||
certificateNumber: 'JZZS20231008001',
|
||||
quarantineItems: [
|
||||
{ id: '21', name: '宰前检查' },
|
||||
{ id: '22', name: '宰后检验' },
|
||||
{ id: '23', name: '内脏检查' },
|
||||
{ id: '24', name: '肉品质量检查' }
|
||||
],
|
||||
processDescription: '对60头牛进行了宰前检查,确认健康状况良好;宰后进行了头蹄、内脏、胴体等部位的检验,未发现异常。',
|
||||
conclusion: '经检疫,该批屠宰牛只健康状况良好,符合屠宰检疫要求,准予屠宰销售。',
|
||||
photos: [
|
||||
{ id: '11', url: 'https://via.placeholder.com/300x200?text=Photo11' },
|
||||
{ id: '12', url: 'https://via.placeholder.com/300x200?text=Photo12' }
|
||||
],
|
||||
reviewComments: '符合检疫要求,同意通过',
|
||||
notes: '',
|
||||
status: 'approved',
|
||||
createdAt: '2023-10-07'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
declarationNumber: 'JD20231008001',
|
||||
declarationUnit: '惠济区温馨养殖场',
|
||||
contactPerson: '吴十',
|
||||
phone: '13212345678',
|
||||
type: 'other',
|
||||
object: '牛精液',
|
||||
quantity: 500,
|
||||
declarationDate: '2023-10-08',
|
||||
quarantinePerson: '王十二',
|
||||
quarantineTime: '',
|
||||
result: false,
|
||||
certificateNumber: '',
|
||||
quarantineItems: [],
|
||||
processDescription: '',
|
||||
conclusion: '',
|
||||
photos: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
createdAt: '2023-10-08'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
declarationNumber: 'JD20231009001',
|
||||
declarationUnit: '二七区红火养殖场',
|
||||
contactPerson: '郑十一',
|
||||
phone: '13112345678',
|
||||
type: 'animal',
|
||||
object: '牛',
|
||||
quantity: 100,
|
||||
declarationDate: '2023-10-09',
|
||||
quarantinePerson: '张三',
|
||||
quarantineTime: '',
|
||||
result: false,
|
||||
certificateNumber: '',
|
||||
quarantineItems: [],
|
||||
processDescription: '',
|
||||
conclusion: '',
|
||||
photos: [],
|
||||
reviewComments: '申请人主动取消',
|
||||
notes: '',
|
||||
status: 'cancelled',
|
||||
createdAt: '2023-10-09'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
declarationNumber: 'JD20231010001',
|
||||
declarationUnit: '中原区丰收养殖场',
|
||||
contactPerson: '王十二',
|
||||
phone: '13012345678',
|
||||
type: 'product',
|
||||
object: '牛肉制品',
|
||||
quantity: 300,
|
||||
declarationDate: '2023-10-10',
|
||||
quarantinePerson: '李四',
|
||||
quarantineTime: '',
|
||||
result: false,
|
||||
certificateNumber: '',
|
||||
quarantineItems: [],
|
||||
processDescription: '',
|
||||
conclusion: '',
|
||||
photos: [],
|
||||
reviewComments: '',
|
||||
notes: '',
|
||||
status: 'pending',
|
||||
createdAt: '2023-10-10'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '申报单号',
|
||||
dataIndex: 'declarationNumber',
|
||||
key: 'declarationNumber',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '申报单位',
|
||||
dataIndex: 'declarationUnit',
|
||||
key: 'declarationUnit',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '检疫类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
customRender: ({ text }) => getTypeText(text)
|
||||
},
|
||||
{
|
||||
title: '检疫对象',
|
||||
dataIndex: 'object',
|
||||
key: 'object',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'quantity',
|
||||
key: 'quantity',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '检疫人员',
|
||||
dataIndex: 'quarantinePerson',
|
||||
key: 'quarantinePerson',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '检疫时间',
|
||||
dataIndex: 'quarantineTime',
|
||||
key: 'quarantineTime',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
pending: '待审核',
|
||||
approved: '已通过',
|
||||
rejected: '已驳回',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
pending: 'blue',
|
||||
approved: 'green',
|
||||
rejected: 'red',
|
||||
cancelled: 'default'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
animal: '动物检疫',
|
||||
product: '动物产品检疫',
|
||||
transport: '运输检疫',
|
||||
slaughter: '屠宰检疫',
|
||||
other: '其他检疫'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
pagination.current = 1
|
||||
// 模拟搜索效果
|
||||
message.success('搜索成功')
|
||||
}
|
||||
|
||||
// 重置处理
|
||||
const handleReset = () => {
|
||||
searchKeyword.value = ''
|
||||
typeFilter.value = ''
|
||||
statusFilter.value = ''
|
||||
quarantinePersonFilter.value = ''
|
||||
dateRange.value = []
|
||||
pagination.current = 1
|
||||
}
|
||||
|
||||
// 导出处理
|
||||
const handleExport = () => {
|
||||
// 在实际应用中,这里应该调用API导出数据
|
||||
message.success('导出功能待实现')
|
||||
}
|
||||
|
||||
// 查看记录
|
||||
const handleView = (record) => {
|
||||
viewRecord.value = { ...record }
|
||||
isViewModalOpen.value = true
|
||||
}
|
||||
|
||||
// 打印记录
|
||||
const handlePrint = (id) => {
|
||||
// 在实际应用中,这里应该调用API获取打印数据
|
||||
message.success('打印功能待实现')
|
||||
}
|
||||
|
||||
// 重新检疫
|
||||
const handleRecheck = (id) => {
|
||||
// 在实际应用中,这里应该调用API创建重新检疫任务
|
||||
message.success('重新检疫功能待实现')
|
||||
}
|
||||
|
||||
// 关闭查看模态框
|
||||
const handleCloseView = () => {
|
||||
isViewModalOpen.value = false
|
||||
}
|
||||
|
||||
// 查看照片
|
||||
const handleViewPhoto = (photo) => {
|
||||
currentPhotoUrl.value = photo.url
|
||||
isPhotoModalOpen.value = true
|
||||
}
|
||||
|
||||
// 关闭照片模态框
|
||||
const handleClosePhoto = () => {
|
||||
isPhotoModalOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,754 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>检疫报表导出</h1>
|
||||
|
||||
<!-- 搜索和筛选条件 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<a-select v-model:value="reportType" placeholder="报表类型" style="width: 200px;">
|
||||
<a-select-option value="daily">日报</a-select-option>
|
||||
<a-select-option value="weekly">周报</a-select-option>
|
||||
<a-select-option value="monthly">月报</a-select-option>
|
||||
<a-select-option value="quarterly">季报</a-select-option>
|
||||
<a-select-option value="yearly">年报</a-select-option>
|
||||
<a-select-option value="custom">自定义报表</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<div v-if="reportType === 'custom'" style="display: flex; gap: 16px;">
|
||||
<a-date-picker
|
||||
v-model:value="dateRange[0]"
|
||||
placeholder="开始日期"
|
||||
style="width: 180px;"
|
||||
/>
|
||||
<span style="line-height: 32px;">至</span>
|
||||
<a-date-picker
|
||||
v-model:value="dateRange[1]"
|
||||
placeholder="结束日期"
|
||||
style="width: 180px;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<a-select v-model:value="quarantineType" placeholder="检疫类型" style="width: 150px;">
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="animal">动物检疫</a-select-option>
|
||||
<a-select-option value="product">动物产品检疫</a-select-option>
|
||||
<a-select-option value="transport">运输检疫</a-select-option>
|
||||
<a-select-option value="slaughter">屠宰检疫</a-select-option>
|
||||
<a-select-option value="other">其他检疫</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-select v-model:value="reportFormat" placeholder="导出格式" style="width: 120px;">
|
||||
<a-select-option value="excel">Excel</a-select-option>
|
||||
<a-select-option value="pdf">PDF</a-select-option>
|
||||
<a-select-option value="csv">CSV</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-button type="primary" @click="handleGenerateReport" style="margin-left: auto;">
|
||||
<span class="iconfont icon-baocun"></span> 生成报表
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 报表配置 -->
|
||||
<a-card style="margin-bottom: 16px;">
|
||||
<h3>报表配置</h3>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 24px; margin-top: 16px;">
|
||||
<div style="flex: 1; min-width: 300px;">
|
||||
<h4 style="margin-bottom: 12px;">报表内容</h4>
|
||||
<a-checkbox-group v-model:value="reportContents">
|
||||
<a-checkbox value="quarantineCount">检疫数量统计</a-checkbox><br/>
|
||||
<a-checkbox value="quarantineResult">检疫结果统计</a-checkbox><br/>
|
||||
<a-checkbox value="quarantineType">检疫类型分布</a-checkbox><br/>
|
||||
<a-checkbox value="quarantineLocation">检疫地点分布</a-checkbox><br/>
|
||||
<a-checkbox value="quarantinePersonnel">检疫人员工作量</a-checkbox><br/>
|
||||
<a-checkbox value="problemAnalysis">问题分析</a-checkbox><br/>
|
||||
<a-checkbox value="trendAnalysis">趋势分析</a-checkbox><br/>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; min-width: 300px;">
|
||||
<h4 style="margin-bottom: 12px;">报表样式</h4>
|
||||
<a-radio-group v-model:value="reportStyle">
|
||||
<a-radio :value="'summary'">简洁汇总</a-radio><br/>
|
||||
<a-radio :value="'detailed'">详细报表</a-radio><br/>
|
||||
<a-radio :value="'graphical'">图文并茂</a-radio><br/>
|
||||
</a-radio-group>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<a-checkbox v-model:checked="includeChart">包含图表</a-checkbox><br/>
|
||||
<a-checkbox v-model:checked="includeComparison">包含同比环比</a-checkbox><br/>
|
||||
<a-checkbox v-model:checked="includeRecommendations">包含建议分析</a-checkbox><br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 报表预览 -->
|
||||
<a-card>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
||||
<h3>报表预览</h3>
|
||||
<a-button v-if="generatedReport" type="primary" danger @click="handleDownloadReport">
|
||||
<span class="iconfont icon-xiazai"></span> 下载报表
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div v-if="!generatedReport">
|
||||
<p style="text-align: center; color: #999; padding: 40px 0;">请选择报表类型和筛选条件,点击生成报表按钮</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="report-preview">
|
||||
<!-- 报表头部 -->
|
||||
<div class="report-header">
|
||||
<h2>动物检疫统计报表</h2>
|
||||
<div class="report-info">
|
||||
<span>报表类型:{{ getReportTypeText() }}</span>
|
||||
<span>统计时间:{{ getReportTimeRange() }}</span>
|
||||
<span>生成时间:{{ generateTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 报表内容 -->
|
||||
<div class="report-content">
|
||||
<!-- 检疫数量统计 -->
|
||||
<div v-if="reportContents.includes('quarantineCount')" class="report-section">
|
||||
<h3>一、检疫数量统计</h3>
|
||||
<a-table :data-source="quarantineCountData" :columns="quarantineCountColumns" pagination="false" :size="'small'" :bordered="true" style="margin-top: 16px;">
|
||||
</a-table>
|
||||
|
||||
<!-- 图表 -->
|
||||
<div v-if="includeChart" ref="quarantineCountChart" style="height: 300px; margin-top: 20px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫结果统计 -->
|
||||
<div v-if="reportContents.includes('quarantineResult')" class="report-section">
|
||||
<h3>二、检疫结果统计</h3>
|
||||
<a-table :data-source="quarantineResultData" :columns="quarantineResultColumns" pagination="false" :size="'small'" :bordered="true" style="margin-top: 16px;">
|
||||
</a-table>
|
||||
|
||||
<!-- 图表 -->
|
||||
<div v-if="includeChart" ref="quarantineResultChart" style="height: 300px; margin-top: 20px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫类型分布 -->
|
||||
<div v-if="reportContents.includes('quarantineType')" class="report-section">
|
||||
<h3>三、检疫类型分布</h3>
|
||||
<a-table :data-source="quarantineTypeData" :columns="quarantineTypeColumns" pagination="false" :size="'small'" :bordered="true" style="margin-top: 16px;">
|
||||
</a-table>
|
||||
|
||||
<!-- 图表 -->
|
||||
<div v-if="includeChart" ref="quarantineTypeChart" style="height: 300px; margin-top: 20px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 检疫地点分布 -->
|
||||
<div v-if="reportContents.includes('quarantineLocation')" class="report-section">
|
||||
<h3>四、检疫地点分布</h3>
|
||||
<a-table :data-source="quarantineLocationData" :columns="quarantineLocationColumns" pagination="false" :size="'small'" :bordered="true" style="margin-top: 16px;">
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 检疫人员工作量 -->
|
||||
<div v-if="reportContents.includes('quarantinePersonnel')" class="report-section">
|
||||
<h3>五、检疫人员工作量</h3>
|
||||
<a-table :data-source="quarantinePersonnelData" :columns="quarantinePersonnelColumns" pagination="false" :size="'small'" :bordered="true" style="margin-top: 16px;">
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 问题分析 -->
|
||||
<div v-if="reportContents.includes('problemAnalysis')" class="report-section">
|
||||
<h3>六、问题分析</h3>
|
||||
<div class="analysis-content">
|
||||
<p>根据统计数据,本期检疫工作中主要存在以下问题:</p>
|
||||
<ol>
|
||||
<li>部分地区动物检疫申报不及时,影响检疫效率</li>
|
||||
<li>个别养殖场的防疫条件有待改善,存在一定风险</li>
|
||||
<li>运输检疫中,部分运输工具消毒不彻底</li>
|
||||
<li>基层检疫人员专业技能需要进一步提升</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 趋势分析 -->
|
||||
<div v-if="reportContents.includes('trendAnalysis')" class="report-section">
|
||||
<h3>七、趋势分析</h3>
|
||||
<div v-if="includeChart" ref="trendAnalysisChart" style="height: 300px;"></div>
|
||||
<div v-else class="analysis-content">
|
||||
<p>本期检疫数量较上期有所增加,主要原因是:</p>
|
||||
<ol>
|
||||
<li>养殖规模扩大,出栏量增加</li>
|
||||
<li>加强了检疫宣传,申报意识提高</li>
|
||||
<li>运输量增加,运输检疫需求增长</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
// 报表类型
|
||||
const reportType = ref('monthly')
|
||||
|
||||
// 日期范围
|
||||
const dateRange = ref([null, null])
|
||||
|
||||
// 检疫类型筛选
|
||||
const quarantineType = ref('')
|
||||
|
||||
// 导出格式
|
||||
const reportFormat = ref('excel')
|
||||
|
||||
// 报表内容选项
|
||||
const reportContents = ref(['quarantineCount', 'quarantineResult', 'quarantineType'])
|
||||
|
||||
// 报表样式
|
||||
const reportStyle = ref('summary')
|
||||
|
||||
// 是否包含图表
|
||||
const includeChart = ref(true)
|
||||
|
||||
// 是否包含同比环比
|
||||
const includeComparison = ref(true)
|
||||
|
||||
// 是否包含建议分析
|
||||
const includeRecommendations = ref(true)
|
||||
|
||||
// 是否已生成报表
|
||||
const generatedReport = ref(false)
|
||||
|
||||
// 报表生成时间
|
||||
const generateTime = ref('')
|
||||
|
||||
// 检疫数量统计数据(模拟数据)
|
||||
const quarantineCountData = ref([
|
||||
{ type: '动物检疫', count: 1256, increase: 12.5 },
|
||||
{ type: '动物产品检疫', count: 897, increase: 8.3 },
|
||||
{ type: '运输检疫', count: 654, increase: 15.2 },
|
||||
{ type: '屠宰检疫', count: 987, increase: 5.7 },
|
||||
{ type: '其他检疫', count: 324, increase: 2.1 }
|
||||
])
|
||||
|
||||
// 检疫数量统计列定义
|
||||
const quarantineCountColumns = [
|
||||
{ title: '检疫类型', dataIndex: 'type', key: 'type' },
|
||||
{ title: '检疫数量', dataIndex: 'count', key: 'count' },
|
||||
{ title: '同比增长(%)', dataIndex: 'increase', key: 'increase' }
|
||||
]
|
||||
|
||||
// 检疫结果统计数据(模拟数据)
|
||||
const quarantineResultData = ref([
|
||||
{ result: '合格', count: 3521, rate: 92.3 },
|
||||
{ result: '不合格', count: 293, rate: 7.7 }
|
||||
])
|
||||
|
||||
// 检疫结果统计列定义
|
||||
const quarantineResultColumns = [
|
||||
{ title: '检疫结果', dataIndex: 'result', key: 'result' },
|
||||
{ title: '数量', dataIndex: 'count', key: 'count' },
|
||||
{ title: '占比(%)', dataIndex: 'rate', key: 'rate' }
|
||||
]
|
||||
|
||||
// 检疫类型分布数据(模拟数据)
|
||||
const quarantineTypeData = ref([
|
||||
{ type: '生猪', count: 1567, rate: 40.9 },
|
||||
{ type: '家禽', count: 897, rate: 23.3 },
|
||||
{ type: '牛', count: 654, rate: 17.0 },
|
||||
{ type: '羊', count: 324, rate: 8.4 },
|
||||
{ type: '其他', count: 397, rate: 10.3 }
|
||||
])
|
||||
|
||||
// 检疫类型分布列定义
|
||||
const quarantineTypeColumns = [
|
||||
{ title: '动物类型', dataIndex: 'type', key: 'type' },
|
||||
{ title: '数量', dataIndex: 'count', key: 'count' },
|
||||
{ title: '占比(%)', dataIndex: 'rate', key: 'rate' }
|
||||
]
|
||||
|
||||
// 检疫地点分布数据(模拟数据)
|
||||
const quarantineLocationData = ref([
|
||||
{ location: '养殖场', count: 1897, rate: 49.4 },
|
||||
{ location: '屠宰场', count: 987, rate: 25.7 },
|
||||
{ location: '交易市场', count: 654, rate: 17.0 },
|
||||
{ location: '运输环节', count: 324, rate: 8.4 }
|
||||
])
|
||||
|
||||
// 检疫地点分布列定义
|
||||
const quarantineLocationColumns = [
|
||||
{ title: '检疫地点', dataIndex: 'location', key: 'location' },
|
||||
{ title: '数量', dataIndex: 'count', key: 'count' },
|
||||
{ title: '占比(%)', dataIndex: 'rate', key: 'rate' }
|
||||
]
|
||||
|
||||
// 检疫人员工作量数据(模拟数据)
|
||||
const quarantinePersonnelData = ref([
|
||||
{ name: '张三', count: 567, rate: 14.8 },
|
||||
{ name: '李四', count: 489, rate: 12.8 },
|
||||
{ name: '王五', count: 456, rate: 11.9 },
|
||||
{ name: '赵六', count: 423, rate: 11.0 },
|
||||
{ name: '钱七', count: 398, rate: 10.4 },
|
||||
{ name: '孙八', count: 376, rate: 9.8 },
|
||||
{ name: '周九', count: 356, rate: 9.3 },
|
||||
{ name: '吴十', count: 334, rate: 8.7 }
|
||||
])
|
||||
|
||||
// 检疫人员工作量列定义
|
||||
const quarantinePersonnelColumns = [
|
||||
{ title: '检疫人员', dataIndex: 'name', key: 'name' },
|
||||
{ title: '检疫数量', dataIndex: 'count', key: 'count' },
|
||||
{ title: '占比(%)', dataIndex: 'rate', key: 'rate' }
|
||||
]
|
||||
|
||||
// 图表引用
|
||||
const quarantineCountChart = ref(null)
|
||||
const quarantineResultChart = ref(null)
|
||||
const quarantineTypeChart = ref(null)
|
||||
const trendAnalysisChart = ref(null)
|
||||
|
||||
// 图表实例
|
||||
let quarantineCountChartInstance = null
|
||||
let quarantineResultChartInstance = null
|
||||
let quarantineTypeChartInstance = null
|
||||
let trendAnalysisChartInstance = null
|
||||
|
||||
// 获取报表类型文本
|
||||
const getReportTypeText = () => {
|
||||
const typeMap = {
|
||||
daily: '日报',
|
||||
weekly: '周报',
|
||||
monthly: '月报',
|
||||
quarterly: '季报',
|
||||
yearly: '年报',
|
||||
custom: '自定义报表'
|
||||
}
|
||||
return typeMap[reportType.value] || ''
|
||||
}
|
||||
|
||||
// 获取报表时间范围
|
||||
const getReportTimeRange = () => {
|
||||
if (reportType.value === 'custom' && dateRange.value[0] && dateRange.value[1]) {
|
||||
return `${formatDate(dateRange.value[0])} 至 ${formatDate(dateRange.value[1])}`
|
||||
} else {
|
||||
// 根据报表类型返回默认时间范围
|
||||
const now = new Date()
|
||||
let startDate, endDate
|
||||
|
||||
switch (reportType.value) {
|
||||
case 'daily':
|
||||
startDate = now
|
||||
endDate = now
|
||||
break
|
||||
case 'weekly':
|
||||
startDate = new Date(now.getTime() - (now.getDay() || 7) * 24 * 60 * 60 * 1000)
|
||||
endDate = new Date(startDate.getTime() + 6 * 24 * 60 * 60 * 1000)
|
||||
break
|
||||
case 'monthly':
|
||||
startDate = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
endDate = new Date(now.getFullYear(), now.getMonth() + 1, 0)
|
||||
break
|
||||
case 'quarterly':
|
||||
const quarter = Math.floor(now.getMonth() / 3)
|
||||
startDate = new Date(now.getFullYear(), quarter * 3, 1)
|
||||
endDate = new Date(now.getFullYear(), quarter * 3 + 3, 0)
|
||||
break
|
||||
case 'yearly':
|
||||
startDate = new Date(now.getFullYear(), 0, 1)
|
||||
endDate = new Date(now.getFullYear(), 11, 31)
|
||||
break
|
||||
default:
|
||||
startDate = now
|
||||
endDate = now
|
||||
}
|
||||
|
||||
return `${formatDate(startDate)} 至 ${formatDate(endDate)}`
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
if (!date) return ''
|
||||
const d = new Date(date)
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 生成报表
|
||||
const handleGenerateReport = () => {
|
||||
// 在实际应用中,这里应该调用API获取数据
|
||||
generateTime.value = new Date().toLocaleString('zh-CN')
|
||||
generatedReport.value = true
|
||||
|
||||
// 等待DOM更新后初始化图表
|
||||
nextTick(() => {
|
||||
if (includeChart.value) {
|
||||
initCharts()
|
||||
}
|
||||
})
|
||||
|
||||
message.success('报表生成成功')
|
||||
}
|
||||
|
||||
// 下载报表
|
||||
const handleDownloadReport = () => {
|
||||
// 在实际应用中,这里应该调用API下载报表
|
||||
message.success(`报表已以${getFormatText(reportFormat.value)}格式下载`)
|
||||
}
|
||||
|
||||
// 获取导出格式文本
|
||||
const getFormatText = (format) => {
|
||||
const formatMap = {
|
||||
excel: 'Excel',
|
||||
pdf: 'PDF',
|
||||
csv: 'CSV'
|
||||
}
|
||||
return formatMap[format] || format
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
const initCharts = () => {
|
||||
// 销毁已存在的图表实例
|
||||
if (quarantineCountChartInstance) {
|
||||
quarantineCountChartInstance.dispose()
|
||||
}
|
||||
if (quarantineResultChartInstance) {
|
||||
quarantineResultChartInstance.dispose()
|
||||
}
|
||||
if (quarantineTypeChartInstance) {
|
||||
quarantineTypeChartInstance.dispose()
|
||||
}
|
||||
if (trendAnalysisChartInstance) {
|
||||
trendAnalysisChartInstance.dispose()
|
||||
}
|
||||
|
||||
// 初始化检疫数量统计图表
|
||||
if (quarantineCountChart.value && reportContents.value.includes('quarantineCount')) {
|
||||
quarantineCountChartInstance = echarts.init(quarantineCountChart.value)
|
||||
const option = {
|
||||
title: {
|
||||
text: '检疫数量统计',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: quarantineCountData.value.map(item => item.type)
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '检疫数量',
|
||||
position: 'left'
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '增长率(%)',
|
||||
position: 'right',
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '检疫数量',
|
||||
type: 'bar',
|
||||
data: quarantineCountData.value.map(item => item.count)
|
||||
},
|
||||
{
|
||||
name: '增长率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: quarantineCountData.value.map(item => item.increase),
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
quarantineCountChartInstance.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化检疫结果统计图表
|
||||
if (quarantineResultChart.value && reportContents.value.includes('quarantineResult')) {
|
||||
quarantineResultChartInstance = echarts.init(quarantineResultChart.value)
|
||||
const option = {
|
||||
title: {
|
||||
text: '检疫结果统计',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '检疫结果',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: quarantineResultData.value.map(item => ({
|
||||
value: item.count,
|
||||
name: item.result
|
||||
})),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
quarantineResultChartInstance.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化检疫类型分布图表
|
||||
if (quarantineTypeChart.value && reportContents.value.includes('quarantineType')) {
|
||||
quarantineTypeChartInstance = echarts.init(quarantineTypeChart.value)
|
||||
const option = {
|
||||
title: {
|
||||
text: '检疫类型分布',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '动物类型',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: quarantineTypeData.value.map(item => ({
|
||||
value: item.count,
|
||||
name: item.type
|
||||
}))
|
||||
}
|
||||
]
|
||||
}
|
||||
quarantineTypeChartInstance.setOption(option)
|
||||
}
|
||||
|
||||
// 初始化趋势分析图表
|
||||
if (trendAnalysisChart.value && reportContents.value.includes('trendAnalysis')) {
|
||||
trendAnalysisChartInstance = echarts.init(trendAnalysisChart.value)
|
||||
const option = {
|
||||
title: {
|
||||
text: '检疫数量趋势分析',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['动物检疫', '动物产品检疫', '运输检疫'],
|
||||
bottom: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '动物检疫',
|
||||
type: 'line',
|
||||
data: [1200, 1100, 1300, 1250, 1400, 1350, 1500, 1450, 1600, 1550, 1700, 1650]
|
||||
},
|
||||
{
|
||||
name: '动物产品检疫',
|
||||
type: 'line',
|
||||
data: [800, 850, 820, 900, 880, 950, 920, 980, 1000, 960, 1050, 1020]
|
||||
},
|
||||
{
|
||||
name: '运输检疫',
|
||||
type: 'line',
|
||||
data: [600, 650, 700, 680, 750, 720, 800, 780, 850, 830, 900, 880]
|
||||
}
|
||||
]
|
||||
}
|
||||
trendAnalysisChartInstance.setOption(option)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时
|
||||
onMounted(() => {
|
||||
// 监听窗口大小变化,调整图表
|
||||
window.addEventListener('resize', () => {
|
||||
if (quarantineCountChartInstance) {
|
||||
quarantineCountChartInstance.resize()
|
||||
}
|
||||
if (quarantineResultChartInstance) {
|
||||
quarantineResultChartInstance.resize()
|
||||
}
|
||||
if (quarantineTypeChartInstance) {
|
||||
quarantineTypeChartInstance.resize()
|
||||
}
|
||||
if (trendAnalysisChartInstance) {
|
||||
trendAnalysisChartInstance.resize()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 组件卸载时
|
||||
const onUnmounted = () => {
|
||||
// 销毁图表实例
|
||||
if (quarantineCountChartInstance) {
|
||||
quarantineCountChartInstance.dispose()
|
||||
}
|
||||
if (quarantineResultChartInstance) {
|
||||
quarantineResultChartInstance.dispose()
|
||||
}
|
||||
if (quarantineTypeChartInstance) {
|
||||
quarantineTypeChartInstance.dispose()
|
||||
}
|
||||
if (trendAnalysisChartInstance) {
|
||||
trendAnalysisChartInstance.dispose()
|
||||
}
|
||||
|
||||
// 移除事件监听
|
||||
window.removeEventListener('resize', () => {
|
||||
// 清理事件监听
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.report-preview {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.report-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.report-header h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.report-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.report-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.report-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.report-section h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border-left: 4px solid #1890ff;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.analysis-content p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.analysis-content ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.analysis-content li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
189
government-admin/src/views/slaughter/Slaughterhouse.vue
Normal file
189
government-admin/src/views/slaughter/Slaughterhouse.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="slaughterhouse-container">
|
||||
<div class="page-header">
|
||||
<h1>屠宰场管理</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- 工具栏 -->
|
||||
<div class="toolbar">
|
||||
<a-button type="primary" @click="handleAdd">新增屠宰场</a-button>
|
||||
<a-input-search
|
||||
placeholder="搜索屠宰场名称"
|
||||
style="width: 300px; margin-left: 16px;"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="slaughterhouses"
|
||||
row-key="id"
|
||||
pagination
|
||||
>
|
||||
<template #column:action="{ record }">
|
||||
<a-space>
|
||||
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button type="link" @click="handleDetail(record.id)">详情</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #column:status="{ text }">
|
||||
<a-tag :color="text === '正常' ? 'green' : 'red'">
|
||||
{{ text }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
// 模拟数据
|
||||
const slaughterhouses = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '东方屠宰场',
|
||||
address: '北京市朝阳区东方路123号',
|
||||
contactPerson: '张三',
|
||||
contactPhone: '13800138001',
|
||||
licenseNumber: 'SL20230001',
|
||||
status: '正常',
|
||||
createTime: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '南方屠宰场',
|
||||
address: '北京市海淀区南大街45号',
|
||||
contactPerson: '李四',
|
||||
contactPhone: '13900139002',
|
||||
licenseNumber: 'SL20230002',
|
||||
status: '正常',
|
||||
createTime: '2023-02-20'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '北方屠宰场',
|
||||
address: '北京市西城区北大街67号',
|
||||
contactPerson: '王五',
|
||||
contactPhone: '13700137003',
|
||||
licenseNumber: 'SL20230003',
|
||||
status: '暂停营业',
|
||||
createTime: '2023-03-10'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '屠宰场名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
key: 'address'
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'contactPerson',
|
||||
key: 'contactPerson'
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'contactPhone',
|
||||
key: 'contactPhone'
|
||||
},
|
||||
{
|
||||
title: '许可证号',
|
||||
dataIndex: 'licenseNumber',
|
||||
key: 'licenseNumber'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (value) => {
|
||||
console.log('搜索:', value)
|
||||
// 实际项目中这里应该调用API进行搜索
|
||||
}
|
||||
|
||||
// 处理新增
|
||||
const handleAdd = () => {
|
||||
console.log('新增屠宰场')
|
||||
// 实际项目中这里应该打开新增表单
|
||||
}
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record) => {
|
||||
console.log('编辑屠宰场:', record)
|
||||
// 实际项目中这里应该打开编辑表单
|
||||
}
|
||||
|
||||
// 处理删除
|
||||
const handleDelete = (id) => {
|
||||
console.log('删除屠宰场:', id)
|
||||
// 实际项目中这里应该弹出确认框并调用API删除
|
||||
}
|
||||
|
||||
// 处理查看详情
|
||||
const handleDetail = (id) => {
|
||||
console.log('查看屠宰场详情:', id)
|
||||
// 实际项目中这里应该打开详情页面
|
||||
}
|
||||
|
||||
return {
|
||||
slaughterhouses,
|
||||
columns,
|
||||
handleSearch,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleDetail
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.slaughterhouse-container {
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
189
government-admin/src/views/slaughter/harmless/HarmlessPlace.vue
Normal file
189
government-admin/src/views/slaughter/harmless/HarmlessPlace.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="harmless-place-container">
|
||||
<div class="page-header">
|
||||
<h1>无害化场所管理</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- 工具栏 -->
|
||||
<div class="toolbar">
|
||||
<a-button type="primary" @click="handleAdd">新增无害化场所</a-button>
|
||||
<a-input-search
|
||||
placeholder="搜索场所名称"
|
||||
style="width: 300px; margin-left: 16px;"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="harmlessPlaces"
|
||||
row-key="id"
|
||||
pagination
|
||||
>
|
||||
<template #column:action="{ record }">
|
||||
<a-space>
|
||||
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button type="link" @click="handleDetail(record.id)">详情</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #column:status="{ text }">
|
||||
<a-tag :color="text === '正常' ? 'green' : 'red'">
|
||||
{{ text }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
// 模拟数据
|
||||
const harmlessPlaces = ref([
|
||||
{
|
||||
id: '1',
|
||||
name: '北京无害化处理中心',
|
||||
address: '北京市顺义区无害化路88号',
|
||||
contactPerson: '赵六',
|
||||
contactPhone: '13600136001',
|
||||
licenseNumber: 'HP20230001',
|
||||
status: '正常',
|
||||
createTime: '2023-01-20'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '天津无害化处理站',
|
||||
address: '天津市滨海新区处理路56号',
|
||||
contactPerson: '钱七',
|
||||
contactPhone: '13500135002',
|
||||
licenseNumber: 'HP20230002',
|
||||
status: '正常',
|
||||
createTime: '2023-02-25'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '河北无害化处理厂',
|
||||
address: '河北省廊坊市大厂县处理路34号',
|
||||
contactPerson: '孙八',
|
||||
contactPhone: '13400134003',
|
||||
licenseNumber: 'HP20230003',
|
||||
status: '维护中',
|
||||
createTime: '2023-03-15'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '场所名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
key: 'address'
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'contactPerson',
|
||||
key: 'contactPerson'
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'contactPhone',
|
||||
key: 'contactPhone'
|
||||
},
|
||||
{
|
||||
title: '许可证号',
|
||||
dataIndex: 'licenseNumber',
|
||||
key: 'licenseNumber'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (value) => {
|
||||
console.log('搜索:', value)
|
||||
// 实际项目中这里应该调用API进行搜索
|
||||
}
|
||||
|
||||
// 处理新增
|
||||
const handleAdd = () => {
|
||||
console.log('新增无害化场所')
|
||||
// 实际项目中这里应该打开新增表单
|
||||
}
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record) => {
|
||||
console.log('编辑无害化场所:', record)
|
||||
// 实际项目中这里应该打开编辑表单
|
||||
}
|
||||
|
||||
// 处理删除
|
||||
const handleDelete = (id) => {
|
||||
console.log('删除无害化场所:', id)
|
||||
// 实际项目中这里应该弹出确认框并调用API删除
|
||||
}
|
||||
|
||||
// 处理查看详情
|
||||
const handleDetail = (id) => {
|
||||
console.log('查看无害化场所详情:', id)
|
||||
// 实际项目中这里应该打开详情页面
|
||||
}
|
||||
|
||||
return {
|
||||
harmlessPlaces,
|
||||
columns,
|
||||
handleSearch,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleDetail
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.harmless-place-container {
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="harmless-registration-container">
|
||||
<div class="page-header">
|
||||
<h1>无害化登记管理</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- 工具栏 -->
|
||||
<div class="toolbar">
|
||||
<a-button type="primary" @click="handleAdd">新增无害化登记</a-button>
|
||||
<a-range-picker
|
||||
style="width: 300px; margin-left: 16px;"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
<a-input-search
|
||||
placeholder="搜索登记编号"
|
||||
style="width: 300px; margin-left: 16px;"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="harmlessRegistrations"
|
||||
row-key="id"
|
||||
pagination
|
||||
>
|
||||
<template #column:action="{ record }">
|
||||
<a-space>
|
||||
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger @click="handleDelete(record.id)">删除</a-button>
|
||||
<a-button type="link" @click="handleDetail(record.id)">详情</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #column:status="{ text }">
|
||||
<a-tag :color="getStatusColor(text)">
|
||||
{{ text }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
// 模拟数据
|
||||
const harmlessRegistrations = ref([
|
||||
{
|
||||
id: '1',
|
||||
registrationNumber: 'HR20230501',
|
||||
animalType: '牛',
|
||||
quantity: 10,
|
||||
reason: '病死',
|
||||
processingMethod: '焚烧',
|
||||
processingPlace: '北京无害化处理中心',
|
||||
processingDate: '2023-05-10',
|
||||
registrant: '刘九',
|
||||
status: '已完成',
|
||||
createTime: '2023-05-09'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
registrationNumber: 'HR20230502',
|
||||
animalType: '牛',
|
||||
quantity: 5,
|
||||
reason: '事故死亡',
|
||||
processingMethod: '深埋',
|
||||
processingPlace: '天津无害化处理站',
|
||||
processingDate: '2023-05-15',
|
||||
registrant: '周十',
|
||||
status: '处理中',
|
||||
createTime: '2023-05-14'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
registrationNumber: 'HR20230503',
|
||||
animalType: '牛',
|
||||
quantity: 3,
|
||||
reason: '检疫不合格',
|
||||
processingMethod: '焚烧',
|
||||
processingPlace: '河北无害化处理厂',
|
||||
processingDate: '2023-05-20',
|
||||
registrant: '吴十一',
|
||||
status: '待处理',
|
||||
createTime: '2023-05-18'
|
||||
}
|
||||
])
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '登记编号',
|
||||
dataIndex: 'registrationNumber',
|
||||
key: 'registrationNumber'
|
||||
},
|
||||
{
|
||||
title: '动物类型',
|
||||
dataIndex: 'animalType',
|
||||
key: 'animalType'
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'quantity',
|
||||
key: 'quantity'
|
||||
},
|
||||
{
|
||||
title: '原因',
|
||||
dataIndex: 'reason',
|
||||
key: 'reason'
|
||||
},
|
||||
{
|
||||
title: '处理方式',
|
||||
dataIndex: 'processingMethod',
|
||||
key: 'processingMethod'
|
||||
},
|
||||
{
|
||||
title: '处理场所',
|
||||
dataIndex: 'processingPlace',
|
||||
key: 'processingPlace'
|
||||
},
|
||||
{
|
||||
title: '处理日期',
|
||||
dataIndex: 'processingDate',
|
||||
key: 'processingDate'
|
||||
},
|
||||
{
|
||||
title: '登记人',
|
||||
dataIndex: 'registrant',
|
||||
key: 'registrant'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
scopedSlots: { customRender: 'status' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
// 根据状态获取标签颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
'待处理': 'orange',
|
||||
'处理中': 'blue',
|
||||
'已完成': 'green',
|
||||
'已取消': 'red'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (value) => {
|
||||
console.log('搜索:', value)
|
||||
// 实际项目中这里应该调用API进行搜索
|
||||
}
|
||||
|
||||
// 处理日期范围变化
|
||||
const handleDateChange = (dates, dateStrings) => {
|
||||
console.log('日期范围:', dates, dateStrings)
|
||||
// 实际项目中这里应该根据日期范围筛选数据
|
||||
}
|
||||
|
||||
// 处理新增
|
||||
const handleAdd = () => {
|
||||
console.log('新增无害化登记')
|
||||
// 实际项目中这里应该打开新增表单
|
||||
}
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record) => {
|
||||
console.log('编辑无害化登记:', record)
|
||||
// 实际项目中这里应该打开编辑表单
|
||||
}
|
||||
|
||||
// 处理删除
|
||||
const handleDelete = (id) => {
|
||||
console.log('删除无害化登记:', id)
|
||||
// 实际项目中这里应该弹出确认框并调用API删除
|
||||
}
|
||||
|
||||
// 处理查看详情
|
||||
const handleDetail = (id) => {
|
||||
console.log('查看无害化登记详情:', id)
|
||||
// 实际项目中这里应该打开详情页面
|
||||
}
|
||||
|
||||
return {
|
||||
harmlessRegistrations,
|
||||
columns,
|
||||
getStatusColor,
|
||||
handleSearch,
|
||||
handleDateChange,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleDetail
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.harmless-registration-container {
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user