添加政府,银行大屏,修改政府前后端代码

This commit is contained in:
2025-09-30 17:48:03 +08:00
parent e9be0f9d98
commit deb005b88e
1409 changed files with 69541 additions and 520 deletions

View File

@@ -355,6 +355,28 @@ harmlessRegistration: {
create: (data) => instance.post('/harmless-place/create', data),
update: (id, data) => instance.put(`/harmless-place/update/${id}`, data),
delete: (id) => instance.delete(`/harmless-place/delete/${id}`)
},
// 防疫记录管理相关API
epidemicRecord: {
getList: (params) => instance.get('/epidemic-record/list', { params }),
getDetail: (id) => instance.get(`/epidemic-record/detail/${id}`),
create: (data) => instance.post('/epidemic-record/create', data),
update: (id, data) => instance.put(`/epidemic-record/update/${id}`, data),
delete: (id) => instance.delete(`/epidemic-record/delete/${id}`),
batchDelete: (ids) => instance.delete('/epidemic-record/batch-delete', { data: { ids } })
},
// 疫苗管理相关API
vaccine: {
getList: (params) => instance.get('/vaccine/list', { params }),
getDetail: (id) => instance.get(`/vaccine/detail/${id}`),
create: (data) => instance.post('/vaccine/create', data),
update: (id, data) => instance.put(`/vaccine/update/${id}`, data),
delete: (id) => instance.delete(`/vaccine/delete/${id}`),
batchDelete: (ids) => instance.delete('/vaccine/batch-delete', { data: { ids } }),
stockIn: (id, data) => instance.post(`/vaccine/stock-in/${id}`, data),
stockOut: (id, data) => instance.post(`/vaccine/stock-out/${id}`, data)
}
}

View File

@@ -51,17 +51,19 @@
:columns="columns"
:data-source="recordsData"
:pagination="pagination"
:loading="loading"
row-key="id"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
:scroll="{ x: 'max-content' }"
@change="handleTableChange"
>
<!-- 状态列 -->
<template #bodyCell:status="{ record }">
<template #status="{ record }">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<!-- 操作列 -->
<template #bodyCell:action="{ record }">
<template #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>
@@ -246,8 +248,9 @@
</template>
<script setup>
import { ref, reactive } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import api from '@/utils/api'
// 搜索条件
const searchKeyword = ref('')
@@ -297,179 +300,9 @@ const currentRecord = reactive({
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 recordsData = ref([])
const loading = ref(false)
// 表格列定义
const columns = [
@@ -503,19 +336,19 @@ const columns = [
dataIndex: 'status',
key: 'status',
width: 80,
scopedSlots: { customRender: 'status' }
slots: { customRender: 'status' }
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
dataIndex: 'createTime',
key: 'createTime',
width: 120
},
{
title: '操作',
key: 'action',
width: 150,
scopedSlots: { customRender: 'action' }
slots: { customRender: 'action' }
}
]
@@ -557,12 +390,39 @@ const formatDate = (date) => {
return date.toISOString().split('T')[0]
}
// 获取防疫记录列表
const fetchRecords = async () => {
try {
loading.value = true
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
keyword: searchKeyword.value,
type: typeFilter.value,
status: statusFilter.value,
startDate: dateRange.value?.[0]?.format('YYYY-MM-DD'),
endDate: dateRange.value?.[1]?.format('YYYY-MM-DD')
}
const res = await api.epidemicRecord.getList(params)
if (res.code === 200) {
recordsData.value = res.data.list || []
pagination.total = res.data.total || 0
} else {
message.error(res.message || '获取防疫记录列表失败')
}
} catch (error) {
console.error('获取防疫记录列表失败:', error)
message.error('获取防疫记录列表失败')
} finally {
loading.value = false
}
}
// 搜索处理
const handleSearch = () => {
// 在实际应用中这里应该调用API获取数据
pagination.current = 1
// 模拟搜索效果
message.success('搜索成功')
fetchRecords()
}
// 重置处理
@@ -572,6 +432,7 @@ const handleReset = () => {
statusFilter.value = ''
dateRange.value = []
pagination.current = 1
fetchRecords()
}
// 新增记录
@@ -613,35 +474,61 @@ const handleView = (record) => {
}
// 删除记录
const handleDelete = (id) => {
// 在实际应用中这里应该调用API删除数据
const index = recordsData.value.findIndex(item => item.id === id)
if (index !== -1) {
recordsData.value.splice(index, 1)
message.success('删除成功')
const handleDelete = async (id) => {
try {
const res = await api.epidemicRecord.delete(id)
if (res.code === 200) {
message.success('删除成功')
await fetchRecords()
} else {
message.error(res.message || '删除失败')
}
} catch (error) {
console.error('删除防疫记录失败:', error)
message.error('删除失败')
}
}
// 保存记录
const handleSave = () => {
// 在实际应用中这里应该调用API保存数据
if (isEditing.value) {
// 更新现有记录
const index = recordsData.value.findIndex(item => item.id === currentRecord.id)
if (index !== -1) {
recordsData.value[index] = { ...currentRecord }
const handleSave = async () => {
try {
// 准备提交数据
const submitData = {
farmName: currentRecord.farmName,
type: currentRecord.type,
epidemicStaff: currentRecord.epidemicStaff,
phone: currentRecord.phone,
epidemicDate: currentRecord.epidemicDate?.format('YYYY-MM-DD'),
count: currentRecord.count || 0,
vaccineName: currentRecord.vaccineName || '',
area: currentRecord.area || '',
disinfectant: currentRecord.disinfectant || '',
healthResult: currentRecord.healthResult || '',
description: currentRecord.description || '',
notes: currentRecord.notes || '',
status: currentRecord.status
}
} else {
// 添加新记录
const newRecord = {
...currentRecord,
id: Date.now().toString(),
createdAt: new Date().toISOString().split('T')[0]
let res
if (isEditing.value) {
// 更新现有记录
res = await api.epidemicRecord.update(currentRecord.id, submitData)
} else {
// 添加新记录
res = await api.epidemicRecord.create(submitData)
}
recordsData.value.unshift(newRecord)
if (res.code === 200) {
message.success(isEditing.value ? '更新成功' : '新增成功')
isAddEditModalOpen.value = false
await fetchRecords()
} else {
message.error(res.message || (isEditing.value ? '更新失败' : '新增失败'))
}
} catch (error) {
console.error('保存防疫记录失败:', error)
message.error(isEditing.value ? '更新失败' : '新增失败')
}
isAddEditModalOpen.value = false
message.success(isEditing.value ? '更新成功' : '新增成功')
}
// 取消操作
@@ -653,6 +540,18 @@ const handleCancel = () => {
const handleCloseView = () => {
isViewModalOpen.value = false
}
// 分页变化处理
const handleTableChange = (pag) => {
pagination.current = pag.current
pagination.pageSize = pag.pageSize
fetchRecords()
}
// 组件挂载时获取数据
onMounted(() => {
fetchRecords()
})
</script>
<style scoped>

View File

@@ -45,17 +45,19 @@
:columns="columns"
:data-source="vaccinesData"
:pagination="pagination"
:loading="loading"
row-key="id"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
:scroll="{ x: 'max-content' }"
@change="handleTableChange"
>
<!-- 状态列 -->
<template #bodyCell:status="{ record }">
<template #status="{ record }">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<!-- 操作列 -->
<template #bodyCell:action="{ record }">
<template #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>
@@ -72,7 +74,7 @@
v-model:open="isAddEditModalOpen"
:title="isEditing ? '编辑疫苗信息' : '新增疫苗信息'"
:footer="null"
width={600}
:width="600"
>
<a-form
:model="currentVaccine"
@@ -146,55 +148,54 @@
v-model:open="isViewModalOpen"
title="查看疫苗详情"
:footer="null"
width={600}
:width="300"
>
<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 style="display: flex; flex-direction: column; gap: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">疫苗名称</span>
<span>{{ viewVaccine.name }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">疫苗类型</p>
<p>{{ getTypeText(viewVaccine.type) }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">疫苗类型</span>
<span>{{ getTypeText(viewVaccine.type) }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">生产厂商</p>
<p>{{ viewVaccine.manufacturer }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">生产厂商</span>
<span>{{ viewVaccine.manufacturer }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">批准文号</p>
<p>{{ viewVaccine.approvalNumber }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">批准文号</span>
<span>{{ viewVaccine.approvalNumber }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">规格</p>
<p>{{ viewVaccine.specification }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">规格</span>
<span>{{ viewVaccine.specification }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">单价</p>
<p>{{ viewVaccine.price }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">单价</span>
<span>{{ viewVaccine.price }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">有效期</p>
<p>{{ viewVaccine.validDays }} </p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">有效期</span>
<span>{{ viewVaccine.validDays }} </span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">储存条件</p>
<p>{{ viewVaccine.storageCondition }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">储存条件</span>
<span>{{ viewVaccine.storageCondition }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">库存数量</p>
<p>{{ viewVaccine.stockCount }}</p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">库存数量</span>
<span>{{ viewVaccine.stockCount }}</span>
</div>
<div>
<p style="color: #8c8c8c; margin-bottom: 4px;">状态</p>
<p><a-tag :color="getStatusColor(viewVaccine.status)">{{ getStatusText(viewVaccine.status) }}</a-tag></p>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="color: #8c8c8c; min-width: 80px;">状态</span>
<a-tag :color="getStatusColor(viewVaccine.status)">{{ getStatusText(viewVaccine.status) }}</a-tag>
</div>
<div style="display: flex; flex-direction: column; gap: 4px; margin-top: 8px;">
<span style="color: #8c8c8c;">备注</span>
<span>{{ viewVaccine.notes || '-' }}</span>
</div>
</div>
<div style="margin-top: 16px;">
<p style="color: #8c8c8c; margin-bottom: 4px;">备注</p>
<p>{{ viewVaccine.notes || '-' }}</p>
</div>
</div>
@@ -208,7 +209,7 @@
v-model:open="isInModalOpen"
title="疫苗入库"
:footer="null"
width={400}
width={350}
>
<a-form
:model="batchInForm"
@@ -252,7 +253,7 @@
v-model:open="isOutModalOpen"
title="疫苗出库"
:footer="null"
width={400}
width={350}
>
<a-form
:model="batchOutForm"
@@ -306,8 +307,9 @@
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import api from '@/utils/api'
// 搜索条件
const searchKeyword = ref('')
@@ -370,129 +372,9 @@ const batchOutForm = reactive({
outDate: new Date()
})
// 疫苗列表数据(模拟数据)
const vaccinesData = ref([
{
id: '1',
name: '口蹄疫疫苗O型-亚洲I型二价灭活疫苗',
type: 'foot_and_mouth_disease',
manufacturer: '中国农业科学院兰州兽医研究所',
approvalNumber: '兽药生字2020050356789',
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: '兽药生字2020010123456',
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: '兽药生字2020080789012',
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: '兽药生字2020170456789',
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: '兽药生字2020150234567',
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: '兽药生字2020010678901',
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: '兽药生字2020050345678',
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: '兽药生字2020010890123',
specification: '10头份/瓶',
price: 32.0,
validDays: 270,
storageCondition: '-15℃以下冷冻保存',
notes: '用于预防牛病毒性腹泻/粘膜病',
stockCount: 0,
status: 'expired',
createdAt: '2022-01-15'
}
])
// 疫苗列表数据
const vaccinesData = ref([])
const loading = ref(false)
// 表格列定义
const columns = [
@@ -539,19 +421,19 @@ const columns = [
dataIndex: 'status',
key: 'status',
width: 80,
scopedSlots: { customRender: 'status' }
slots: { customRender: 'status' }
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
dataIndex: 'createTime',
key: 'createTime',
width: 120
},
{
title: '操作',
key: 'action',
width: 180,
scopedSlots: { customRender: 'action' }
slots: { customRender: 'action' }
}
]
@@ -587,12 +469,37 @@ const getTypeText = (type) => {
return typeMap[type] || type
}
// 获取疫苗列表
const fetchVaccines = async () => {
try {
loading.value = true
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
keyword: searchKeyword.value,
type: typeFilter.value,
status: statusFilter.value
}
const res = await api.vaccine.getList(params)
if (res.code === 200) {
vaccinesData.value = res.data.list || []
pagination.total = res.data.total || 0
} else {
message.error(res.message || '获取疫苗列表失败')
}
} catch (error) {
console.error('获取疫苗列表失败:', error)
message.error('获取疫苗列表失败')
} finally {
loading.value = false
}
}
// 搜索处理
const handleSearch = () => {
// 在实际应用中这里应该调用API获取数据
pagination.current = 1
// 模拟搜索效果
message.success('搜索成功')
fetchVaccines()
}
// 重置处理
@@ -601,6 +508,7 @@ const handleReset = () => {
typeFilter.value = ''
statusFilter.value = ''
pagination.current = 1
fetchVaccines()
}
// 新增疫苗
@@ -640,35 +548,42 @@ const handleView = (record) => {
}
// 删除疫苗
const handleDelete = (id) => {
// 在实际应用中这里应该调用API删除数据
const index = vaccinesData.value.findIndex(item => item.id === id)
if (index !== -1) {
vaccinesData.value.splice(index, 1)
message.success('删除成功')
const handleDelete = async (id) => {
try {
const res = await api.vaccine.delete(id)
if (res.code === 200) {
message.success('删除成功')
await fetchVaccines()
} else {
message.error(res.message || '删除失败')
}
} catch (error) {
console.error('删除疫苗失败:', error)
message.error('删除失败')
}
}
// 保存疫苗
const handleSave = () => {
// 在实际应用中这里应该调用API保存数据
if (isEditing.value) {
// 更新现有疫苗
const index = vaccinesData.value.findIndex(item => item.id === currentVaccine.id)
if (index !== -1) {
vaccinesData.value[index] = { ...currentVaccine }
const handleSave = async () => {
try {
let res
if (isEditing.value) {
res = await api.vaccine.update(currentVaccine.id, currentVaccine)
} else {
res = await api.vaccine.create(currentVaccine)
}
} else {
// 添加新疫苗
const newVaccine = {
...currentVaccine,
id: Date.now().toString(),
createdAt: new Date().toISOString().split('T')[0]
if (res.code === 200) {
isAddEditModalOpen.value = false
message.success(isEditing.value ? '更新成功' : '新增成功')
await fetchVaccines()
} else {
message.error(res.message || (isEditing.value ? '更新失败' : '新增失败'))
}
vaccinesData.value.unshift(newVaccine)
} catch (error) {
console.error('保存疫苗失败:', error)
message.error(isEditing.value ? '更新失败' : '新增失败')
}
isAddEditModalOpen.value = false
message.success(isEditing.value ? '更新成功' : '新增成功')
}
// 取消操作
@@ -698,20 +613,28 @@ const handleBatchIn = (id) => {
}
// 确认入库
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'
const handleConfirmIn = async () => {
try {
if (viewVaccine.value) {
const res = await api.vaccine.stockIn(viewVaccine.value.id, {
count: batchInForm.count,
batchNumber: batchInForm.batchNumber,
inDate: batchInForm.inDate
})
if (res.code === 200) {
message.success('疫苗入库成功')
await fetchVaccines()
} else {
message.error(res.message || '入库失败')
}
message.success('疫苗入库成功')
}
} catch (error) {
console.error('疫苗入库失败:', error)
message.error('入库失败')
} finally {
isInModalOpen.value = false
}
isInModalOpen.value = false
}
// 关闭入库模态框
@@ -738,30 +661,46 @@ const handleBatchOut = (id) => {
}
// 确认出库
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'
const handleConfirmOut = async () => {
try {
if (viewVaccine.value && batchOutForm.count <= viewVaccine.value.stockCount) {
const res = await api.vaccine.stockOut(viewVaccine.value.id, {
count: batchOutForm.count,
purpose: batchOutForm.purpose,
outDate: batchOutForm.outDate
})
if (res.code === 200) {
message.success('疫苗出库成功')
await fetchVaccines()
} else {
vaccinesData.value[index].status = 'valid'
message.error(res.message || '出库失败')
}
message.success('疫苗出库成功')
}
} catch (error) {
console.error('疫苗出库失败:', error)
message.error('出库失败')
} finally {
isOutModalOpen.value = false
}
isOutModalOpen.value = false
}
// 关闭出库模态框
const handleCloseOutModal = () => {
isOutModalOpen.value = false
}
// 表格变化处理
const handleTableChange = (paginationInfo) => {
pagination.current = paginationInfo.current
pagination.pageSize = paginationInfo.pageSize
fetchVaccines()
}
// 组件挂载时加载数据
onMounted(() => {
fetchVaccines()
})
</script>
<style scoped>
@@ -771,4 +710,13 @@ const handleCloseOutModal = () => {
margin-bottom: 20px;
color: #333;
}
/* 控制弹窗宽度 */
:deep(.ant-modal) {
max-width: 300px !important;
}
:deep(.ant-modal-content) {
max-width: 300px !important;
}
</style>

View File

@@ -25,7 +25,7 @@
:loading="loading"
@change="handleTableChange"
>
<template #column:action="{ record }">
<template #action="{ record }">
<a-space>
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
<a-popconfirm
@@ -38,9 +38,9 @@
<a-button type="link" @click="handleToggleStatus(record)">{{ record.status === '正常' ? '暂停' : '启用' }}</a-button>
</a-space>
</template>
<template #column:status="{ text }">
<a-tag :color="text === '正常' ? 'green' : 'red'">
{{ text }}
<template #status="{ text }">
<a-tag :color="getStatusColor(text)">
{{ getStatusText(text) }}
</a-tag>
</template>
</a-table>
@@ -49,38 +49,67 @@
<!-- 新增/编辑表单 -->
<a-modal
v-model:open="isModalOpen"
title="屠宰场信息"
:title="formData.id ? '编辑屠宰场' : '新增屠宰场'"
@ok="handleOk"
@cancel="handleCancel"
width="600px"
:confirm-loading="modalLoading"
>
<a-form-model
<a-form
ref="formRef"
:model="formData"
:rules="formRules"
layout="vertical"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
class="slaughterhouse-form"
>
<a-form-model-item label="屠宰场名称" prop="name">
<a-input v-model="formData.name" placeholder="请输入屠宰场名称" />
</a-form-model-item>
<a-form-model-item label="地址" prop="address">
<a-input v-model="formData.address" placeholder="请输入地址" />
</a-form-model-item>
<a-form-model-item label="联系人" prop="contactPerson">
<a-input v-model="formData.contactPerson" placeholder="请输入联系人" />
</a-form-model-item>
<a-form-model-item label="联系电话" prop="contactPhone">
<a-input v-model="formData.contactPhone" placeholder="请输入联系电话" />
</a-form-model-item>
<a-form-model-item label="许可证号" prop="licenseNumber">
<a-input v-model="formData.licenseNumber" placeholder="请输入许可证号" />
</a-form-model-item>
<a-form-model-item label="状态" prop="status">
<a-select v-model="formData.status">
<a-form-item label="屠宰场名称" name="name">
<a-input
v-model:value="formData.name"
placeholder="请输入屠宰场名称"
/>
</a-form-item>
<a-form-item label="许可证号" name="licenseNumber">
<a-input
v-model:value="formData.licenseNumber"
placeholder="请输入许可证号"
/>
</a-form-item>
<a-form-item label="地址" name="address">
<a-textarea
v-model:value="formData.address"
placeholder="请输入详细地址"
:rows="3"
/>
</a-form-item>
<a-form-item label="联系人" name="contactPerson">
<a-input
v-model:value="formData.contactPerson"
placeholder="请输入联系人姓名"
/>
</a-form-item>
<a-form-item label="联系电话" name="contactPhone">
<a-input
v-model:value="formData.contactPhone"
placeholder="请输入联系电话"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-select
v-model:value="formData.status"
placeholder="请选择状态"
style="width: 100%"
>
<a-select-option value="正常">正常</a-select-option>
<a-select-option value="暂停营业">暂停营业</a-select-option>
</a-select>
</a-form-model-item>
</a-form-model>
</a-form-item>
</a-form>
</a-modal>
<!-- 详情弹窗 -->
@@ -112,8 +141,8 @@
</div>
<div class="detail-row">
<span class="detail-label">状态</span>
<span class="detail-value"><a-tag :color="detailData.status === '正常' ? 'green' : 'red'">
{{ detailData.status }}
<span class="detail-value"><a-tag :color="getStatusColor(detailData.status)">
{{ getStatusText(detailData.status) }}
</a-tag></span>
</div>
<div class="detail-row">
@@ -158,6 +187,7 @@ export default {
// 模态框状态
const isModalOpen = ref(false)
const isDetailModalOpen = ref(false)
const modalLoading = ref(false)
// 表单引用
const formRef = ref(null)
@@ -279,19 +309,21 @@ export default {
// 处理新增
const handleAdd = () => {
// 重置表单
Object.assign(formData, {
id: '',
name: '',
address: '',
contactPerson: '',
contactPhone: '',
licenseNumber: '',
status: '正常'
})
// 重置表单数据
formData.id = null
formData.name = ''
formData.address = ''
formData.contactPerson = ''
formData.contactPhone = ''
formData.licenseNumber = ''
formData.status = '正常'
// 重置表单验证状态
if (formRef.value) {
formRef.value.resetFields()
}
// 打开模态框
isModalOpen.value = true
}
@@ -300,7 +332,12 @@ export default {
try {
const response = await api.slaughter.getDetail(record.id)
if (response && response.data) {
Object.assign(formData, response.data)
// 确保状态字段正确映射
const editData = {
...response.data,
status: response.data.status === 'active' ? '正常' : response.data.status === 'inactive' ? '暂停营业' : response.data.status
}
Object.assign(formData, editData)
isModalOpen.value = true
}
} catch (error) {
@@ -337,6 +374,28 @@ export default {
}
}
// 获取状态对应的颜色
const getStatusColor = (status) => {
const colorMap = {
'active': 'green',
'inactive': 'red',
'正常': 'green',
'暂停营业': 'red'
}
return colorMap[status] || 'default'
}
// 获取状态对应的中文文本
const getStatusText = (status) => {
const textMap = {
'active': '正常',
'inactive': '暂停营业',
'正常': '正常',
'暂停营业': '暂停营业'
}
return textMap[status] || status
}
// 处理切换状态
const handleToggleStatus = async (record) => {
try {
@@ -356,18 +415,33 @@ export default {
if (!formRef.value) return
try {
await formRef.value.validate()
modalLoading.value = true
// 准备提交数据,确保状态字段正确
const submitData = {
name: formData.name,
address: formData.address,
contactPerson: formData.contactPerson,
contactPhone: formData.contactPhone,
licenseNumber: formData.licenseNumber,
status: formData.status === '正常' ? 'active' : formData.status === '暂停营业' ? 'inactive' : formData.status
}
let response
if (formData.id) {
// 更新
response = await api.slaughter.update(formData.id, formData)
response = await api.slaughter.update(formData.id, submitData)
} else {
// 新增
response = await api.slaughter.create(formData)
response = await api.slaughter.create(submitData)
}
if (response && response.code === 200) {
if (response && (response.code === 200 || response.code === 201)) {
message.success(formData.id ? '更新成功' : '新增成功')
isModalOpen.value = false
await fetchSlaughterhouses()
} else {
message.error(response?.message || (formData.id ? '更新失败' : '新增失败'))
}
} catch (error) {
if (error && error.errors && error.errors.length > 0) {
@@ -378,6 +452,8 @@ export default {
message.error(formData.id ? '更新失败' : '新增失败')
}
console.error(formData.id ? '更新失败:' : '新增失败:', error)
} finally {
modalLoading.value = false
}
}
@@ -406,11 +482,14 @@ export default {
pagination,
isModalOpen,
isDetailModalOpen,
modalLoading,
formRef,
formData,
detailData,
formRules,
columns,
getStatusColor,
getStatusText,
handleSearch,
handleTableChange,
handleAdd,
@@ -446,4 +525,81 @@ export default {
padding: 24px;
border-radius: 2px;
}
/* 表单样式 */
.slaughterhouse-form {
padding: 16px 0;
}
.slaughterhouse-form .ant-form-item {
margin-bottom: 16px;
}
.slaughterhouse-form .ant-form-item-label > label {
font-weight: 500;
color: #262626;
}
.slaughterhouse-form .ant-input,
.slaughterhouse-form .ant-select,
.slaughterhouse-form .ant-textarea {
border-radius: 4px;
border: 1px solid #d9d9d9;
transition: all 0.3s;
}
.slaughterhouse-form .ant-input:hover,
.slaughterhouse-form .ant-select:hover,
.slaughterhouse-form .ant-textarea:hover {
border-color: #40a9ff;
}
.slaughterhouse-form .ant-input:focus,
.slaughterhouse-form .ant-select:focus,
.slaughterhouse-form .ant-textarea:focus {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.slaughterhouse-form .ant-select .ant-select-selector {
border-radius: 4px;
border: 1px solid #d9d9d9;
}
.slaughterhouse-form .ant-select .ant-select-selector:hover {
border-color: #40a9ff;
}
.slaughterhouse-form .ant-select.ant-select-focused .ant-select-selector {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 详情弹窗样式 */
.detail-container {
padding: 16px 0;
max-height: 400px;
overflow-y: auto;
}
.detail-row {
margin-bottom: 16px;
line-height: 1.8;
display: flex;
align-items: center;
}
.detail-label {
display: inline-block;
width: 100px;
color: rgba(0, 0, 0, 0.65);
font-weight: 500;
flex-shrink: 0;
}
.detail-value {
display: inline-block;
color: rgba(0, 0, 0, 0.85);
flex: 1;
}
</style>

View File

@@ -25,7 +25,7 @@
:loading="loading"
@change="handleTableChange"
>
<template #column:action="{ record }">
<template #action="{ record }">
<a-space>
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
<a-popconfirm
@@ -37,7 +37,7 @@
<a-button type="link" @click="handleDetail(record)">详情</a-button>
</a-space>
</template>
<template #column:status="{ text }">
<template #status="{ text }">
<a-tag :color="getStatusColor(text)">
{{ text }}
</a-tag>
@@ -48,39 +48,68 @@
<!-- 新增/编辑表单 -->
<a-modal
v-model:open="isModalOpen"
title="无害化场所信息"
:title="editingId ? '编辑无害化场所' : '新增无害化场所'"
@ok="handleOk"
@cancel="handleCancel"
width="600px"
:confirm-loading="modalLoading"
>
<a-form-model
<a-form
ref="formRef"
:model="formData"
:rules="formRules"
layout="vertical"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
class="harmless-place-form"
>
<a-form-model-item label="场所名称" prop="name">
<a-input v-model="formData.name" placeholder="请输入场所名称" />
</a-form-model-item>
<a-form-model-item label="地址" prop="address">
<a-input v-model="formData.address" placeholder="请输入地址" />
</a-form-model-item>
<a-form-model-item label="联系人" prop="contactPerson">
<a-input v-model="formData.contactPerson" placeholder="请输入联系人" />
</a-form-model-item>
<a-form-model-item label="联系电话" prop="contactPhone">
<a-input v-model="formData.contactPhone" placeholder="请输入联系电话" />
</a-form-model-item>
<a-form-model-item label="许可证号" prop="licenseNumber">
<a-input v-model="formData.licenseNumber" placeholder="请输入许可证号" />
</a-form-model-item>
<a-form-model-item label="状态" prop="status">
<a-select v-model="formData.status">
<a-form-item label="场所名称" name="name">
<a-input
v-model:value="formData.name"
placeholder="请输入场所名称"
/>
</a-form-item>
<a-form-item label="地址" name="address">
<a-textarea
v-model:value="formData.address"
placeholder="请输入详细地址"
:rows="3"
/>
</a-form-item>
<a-form-item label="联系人" name="contactPerson">
<a-input
v-model:value="formData.contactPerson"
placeholder="请输入联系人姓名"
/>
</a-form-item>
<a-form-item label="联系电话" name="contactPhone">
<a-input
v-model:value="formData.contactPhone"
placeholder="请输入联系电话"
/>
</a-form-item>
<a-form-item label="许可证号" name="licenseNumber">
<a-input
v-model:value="formData.licenseNumber"
placeholder="请输入许可证号"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-select
v-model:value="formData.status"
placeholder="请选择状态"
style="width: 100%"
>
<a-select-option value="正常">正常</a-select-option>
<a-select-option value="维护中">维护中</a-select-option>
<a-select-option value="停用">停用</a-select-option>
</a-select>
</a-form-model-item>
</a-form-model>
</a-form-item>
</a-form>
</a-modal>
<!-- 详情弹窗 -->
@@ -148,6 +177,7 @@ export default {
const isModalOpen = ref(false)
const isDetailModalOpen = ref(false)
const editingId = ref('')
const modalLoading = ref(false)
// 表单引用和数据
const formRef = ref()
@@ -204,7 +234,7 @@ export default {
title: '状态',
dataIndex: 'status',
key: 'status',
scopedSlots: { customRender: 'status' }
slots: { customRender: 'status' }
},
{
title: '创建时间',
@@ -214,7 +244,7 @@ export default {
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' }
slots: { customRender: 'action' }
}
]
@@ -289,21 +319,33 @@ export default {
// 处理编辑
const handleEdit = async (record) => {
resetForm()
editingId.value = record.id
// 填充表单数据
Object.assign(formData, {
name: record.name,
address: record.address,
contactPerson: record.contactPerson,
contactPhone: record.contactPhone,
licenseNumber: record.licenseNumber,
status: record.status
})
await nextTick()
isModalOpen.value = true
try {
// 重置表单
resetForm()
editingId.value = record.id
// 从API获取详细数据
const response = await api.harmlessPlace.getDetail(record.id)
if (response && response.code === 200) {
// 填充表单数据
Object.assign(formData, {
name: response.data.name,
address: response.data.address,
contactPerson: response.data.contactPerson,
contactPhone: response.data.contactPhone,
licenseNumber: response.data.licenseNumber,
status: response.data.status
})
await nextTick()
isModalOpen.value = true
} else {
message.error('获取无害化场所详情失败')
}
} catch (error) {
message.error('获取无害化场所详情失败')
console.error('获取无害化场所详情失败:', error)
}
}
// 处理删除
@@ -347,8 +389,8 @@ export default {
if (!formRef.value) return
await formRef.value.validate()
modalLoading.value = true
loading.value = true
let res
if (editingId.value) {
@@ -370,7 +412,7 @@ export default {
console.error(editingId.value ? '更新无害化场所失败' : '创建无害化场所失败', error)
message.error(editingId.value ? '更新失败' : '创建失败')
} finally {
loading.value = false
modalLoading.value = false
}
}
@@ -397,6 +439,8 @@ export default {
pagination,
isModalOpen,
isDetailModalOpen,
modalLoading,
editingId,
formRef,
formData,
detailData,
@@ -457,4 +501,53 @@ export default {
display: inline-block;
color: rgba(0, 0, 0, 0.85);
}
/* 表单样式 */
.harmless-place-form {
padding: 16px 0;
}
.harmless-place-form .ant-form-item {
margin-bottom: 16px;
}
.harmless-place-form .ant-form-item-label > label {
font-weight: 500;
color: #262626;
}
.harmless-place-form .ant-input,
.harmless-place-form .ant-select,
.harmless-place-form .ant-textarea {
border-radius: 4px;
border: 1px solid #d9d9d9;
transition: all 0.3s;
}
.harmless-place-form .ant-input:hover,
.harmless-place-form .ant-select:hover,
.harmless-place-form .ant-textarea:hover {
border-color: #40a9ff;
}
.harmless-place-form .ant-input:focus,
.harmless-place-form .ant-select:focus,
.harmless-place-form .ant-textarea:focus {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.harmless-place-form .ant-select .ant-select-selector {
border-radius: 4px;
border: 1px solid #d9d9d9;
}
.harmless-place-form .ant-select .ant-select-selector:hover {
border-color: #40a9ff;
}
.harmless-place-form .ant-select.ant-select-focused .ant-select-selector {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
</style>