Files
zhyc-sheep-ui/src/views/biosafety/diagnosis/index.vue

730 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<!-- 查询区域 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="羊只耳号" prop="allEarNumbers">
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
<!-- 主选择器不显示已选标签 -->
<el-select v-model="queryParams.allEarNumbers" multiple filterable remote reserve-keyword
placeholder="输入耳号搜索" :remote-method="searchEarNumber" :loading="earNumberLoading" allow-create
default-first-option collapse-tags :max-collapse-tags="0" style="width: 300px"
@change="handleEarNumberChange">
<el-option v-for="item in earNumberOptions" :key="item" :label="item" :value="item" />
</el-select>
<!-- 辅助粘贴输入框 -->
<el-input v-model="pasteInput" placeholder="或粘贴多个耳号(空格/换行/逗号分隔)" style="width: 300px" @paste="handlePaste"
@keyup.enter="handlePasteSubmit" clearable>
<template #append>
<el-button @click="handlePasteSubmit" :icon="Plus">添加</el-button>
</template>
</el-input>
<!-- 耳号计数显示 -->
<el-tag v-if="queryParams.allEarNumbers && queryParams.allEarNumbers.length > 0" type="info" effect="plain"
size="large">
已选: {{ queryParams.allEarNumbers.length }}
</el-tag>
<!-- 清空按钮 -->
<el-button type="danger" plain @click="clearAllEarNumbers"
v-if="queryParams.allEarNumbers && queryParams.allEarNumbers.length > 0" :icon="Delete">
清空全部
</el-button>
</div>
<!-- 已选耳号展示区域默认显示2个可展开 -->
<div v-if="queryParams.allEarNumbers && queryParams.allEarNumbers.length > 0"
class="selected-ear-numbers-display" style="margin-top: 10px;">
<!-- 显示前2个或全部耳号 -->
<el-tag v-for="(tag, index) in displayedEarNumbers" :key="tag" closable @close="handleRemoveEarNumber(tag)"
style="margin: 4px;" type="success">
{{ tag }}
</el-tag>
<!-- 展开/收起按钮 -->
<el-button v-if="queryParams.allEarNumbers.length > defaultShowCount" type="primary" link
@click="toggleExpand" style="margin-left: 8px;">
{{ isExpanded ? '收起' : `展开剩余 ${queryParams.allEarNumbers.length - defaultShowCount}` }}
<el-icon class="el-icon--right">
<component :is="isExpanded ? ArrowUp : ArrowDown" />
</el-icon>
</el-button>
</div>
</el-form-item>
<!-- <el-form-item label="羊只" prop="sheepId">
<el-input v-model="queryParams.sheepNo" placeholder="请输入耳号,多个用空格分隔" clearable />
</el-form-item> -->
<el-form-item label="事件日期" style="width: 308px">
<el-date-picker v-model="daterangeDatetime" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="羊舍" prop="sheepfoldId">
<el-select v-model="queryParams.sheepfoldId" clearable placeholder="请选择羊舍" style="width: 120px;" filterable>
<el-option v-for="s in sheepfoldOptions" :key="s.value" :label="s.label" :value="s.value" />
</el-select>
</el-form-item>
<el-form-item label="疾病类型" prop="diseasePid">
<el-select v-model="queryParams.diseasePid" clearable placeholder="请选择疾病类型" style="width: 120px;" filterable
@change="handleDiseaseTypeChange">
<el-option v-for="d in pDiseaseOptions" :key="d.id" :label="d.name" :value="d.id" />
</el-select>
</el-form-item>
<el-form-item label="子疾病" prop="diseaseId">
<el-select v-model="queryParams.diseaseId" :disabled="!queryParams.diseasePid" clearable placeholder="请选择疾病类型"
style="width: 120px;" filterable>
<el-option v-for="d in cDiseaseOptions" :key="d.id" :label="d.name" :value="d.id" />
</el-select>
</el-form-item>
<el-form-item label="诊疗结果" prop="result">
<el-select v-model="queryParams.result" clearable placeholder="请选择诊疗结果" style="width: 120px;">
<el-option v-for="r in diag_result" :key="r.value" :label="r.label" :value="r.value" />
</el-select>
</el-form-item>
<el-form-item label="治疗天数" prop="treatDay">
<el-input v-model="queryParams.treatDay" placeholder="请输入治疗天数" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 按钮区域 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
v-hasPermi="['diagnosis:diagnosis:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPermi="['diagnosis:diagnosis:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['diagnosis:diagnosis:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="diagnosisList" @selection-change="handleSelectionChange" max-height="650px"
@sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="耳号" align="center" prop="sheepNo" />
<el-table-column label="事件日期" align="center" prop="datetime" width="150">
<template #default="scope">
<span>{{ parseTime(scope.row.datetime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="羊只类别" align="center" prop="sheepType" />
<el-table-column label="性别" align="center" prop="gender">
<template #default="scope"><dict-tag :options="sheep_gender" :value="scope.row.gender" /></template>
</el-table-column>
<el-table-column label="胎次" align="center" prop="parity" sortable="custom" />
<el-table-column label="疾病类型" align="center" prop="diseasePName" />
<el-table-column label="子疾病" align="center" prop="diseaseName" />
<el-table-column label="诊疗结果" align="center" prop="result">
<template #default="scope"><dict-tag :options="diag_result" :value="scope.row.result" /></template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="begindate" width="150" sortable="custom">
<template #default="scope">
<span>{{ parseTime(scope.row.begindate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="enddate" width="150" sortable="custom">
<template #default="scope">
<span>{{ parseTime(scope.row.enddate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="治疗天数" align="center" prop="treatDay" sortable="custom" />
<el-table-column label="羊舍" align="center" prop="sheepfold" width="150px" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="140px">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
v-hasPermi="['diagnosis:diagnosis:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['diagnosis:diagnosis:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" :page-sizes="[20, 50, 100, 200, 500, 1000, 2000]" @pagination="getList" />
<el-dialog :title="title" v-model="open" width="45%" append-to-body>
<el-form ref="diagnosisRef" :model="form" :rules="rules" label-width="90px">
<!-- 只读信息 -->
<el-row :gutter="20" class="info-block">
<el-col :span="12">
<el-form-item label="羊只耳号">
<span class="readonly-text">{{ form.sheepNo }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="时间日期">
<span class="readonly-text">{{ form.datetime }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="性别">
<span class="readonly-text"> <dict-tag :options="sheep_gender" :value="form.gender" /></span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="胎次">
<span class="readonly-text">{{ form.parity }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="月龄">
<span class="readonly-text">{{ form.monthAge }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="疾病类型">
<span class="readonly-text">{{ form.diseasePName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="子疾病">
<span class="readonly-text">{{ form.diseaseName }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开始时间">
<span class="readonly-text">{{ form.begindate }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" prop="enddate">
<el-date-picker v-model="form.enddate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="治疗天数">
<span class="readonly-text">{{ form.treatDay }} </span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="诊疗结果">
<el-select v-model="form.result" clearable placeholder="请选择诊疗结果">
<el-option v-for="r in diag_result" :key="r.value" :label="r.label" :value="r.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="羊舍">
<el-select v-model="form.sheepfoldId" clearable placeholder="请选择羊舍" filterable>
<el-option v-for="s in sheepfoldOptions" :key="s.value" :label="s.label" :value="s.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template><script setup name="Diagnosis">
import {
listDiagnosis,
getDiagnosis,
delDiagnosis,
updateDiagnosis,
} from '@/api/biosafety/diagnosis'
import { listSheepfold_management } from '@/api/fileManagement/sheepfold_management'
import { listDisease } from '@/api/biosafety/disease'
import { getCurrentInstance, onMounted, ref, watch } from 'vue'
import { searchEarNumbers } from '@/api/common/sheep'
import { getUserByPost } from '@/api/common/user'
const technicalOptions = ref([]) //自定义技术员数组变量
// 获取技术员列表
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
// value: item.userId,
value: item.nickName,
label: item.nickName,
// postName: item.postName, // 保留用于下拉显示
// postCode: item.postCode // 保留用于逻辑判断
}))
console.log(technicalOptions.value);
} else {
technicalOptions.value = []
ElMessage.warning(res.msg || '获取技术员列表失败')
}
})
}
/* ---------- 基础依赖 ---------- */
const { proxy } = getCurrentInstance()
const { sheep_gender, diag_result } = proxy.useDict('sheep_gender', 'diag_result')
/* ---------- 表格 & 分页 ---------- */
const diagnosisList = ref([])
const loading = ref(true)
const total = ref(0)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
/* ---------- 查询参数 ---------- */
const daterangeDatetime = ref([]) // 日期区间
const queryParams = ref({
pageNum: 1,
pageSize: 20,
sheepNo: null,
allEarNumbers: null,
datetime: null,
diseasePid: null,
diseaseId: null,
result: null,
treatDay: null,
sheepfoldId: null,
orderByColumn: null,
isAsc: null,
})
/* ---------- 表单 & 校验 ---------- */
const open = ref(false)
const title = ref('')
const diagnosisRef = ref(null)
const form = ref({
id: undefined,
sheepNo: undefined,
datetime: undefined,
diseasePid: undefined,
diseaseId: undefined,
result: undefined,
begindate: undefined,
enddate: undefined,
treatDay: undefined,
sheepfoldId: undefined,
remark: undefined,
})
const rules = {
enddate: [
{
validator: (rule, value, callback) => {
if (!value) return callback(new Error('请选择结束时间'))
if (new Date(value) < new Date(form.value.begindate)) {
return callback(new Error('结束时间不能早于开始时间'))
}
callback()
},
trigger: 'change',
},
],
}
// 响应式数据
const pasteInput = ref('') // 批量粘贴输入框
const earNumberOptions = ref([]) // 耳号下拉选项
const earNumberLoading = ref(false) // 耳号加载状态
const isExpanded = ref(false) // 控制耳号展开/折叠状态
const defaultShowCount = 2 // 默认显示的耳号数量
// 计算属性:控制显示的耳号列表
const displayedEarNumbers = computed(() => {
if (!queryParams.allEarNumbers || queryParams.allEarNumbers.length === 0) {
return []
}
// 如果展开或总数<=2显示全部否则只显示前2个
if (isExpanded.value || queryParams.allEarNumbers.length <= defaultShowCount) {
return queryParams.allEarNumbers
} else {
return queryParams.allEarNumbers.slice(0, defaultShowCount)
}
})
/** 处理粘贴事件 */
function handlePaste(event) {
nextTick(() => {
handlePasteSubmit()
})
}
/** 处理粘贴内容提交 */
function handlePasteSubmit() {
if (!pasteInput.value || pasteInput.value.trim() === '') {
return
}
// 支持多种分隔符: 空格、换行、逗号、制表符
const separators = /[\s,\n\r\t]+/
const earNumbers = pasteInput.value
.trim()
.split(separators)
.filter(item => item.trim() !== '')
.map(item => item.trim())
if (earNumbers.length === 0) {
return
}
// 去重并添加到已选列表
const existingSet = new Set(queryParams.value.allEarNumbers || [])
const newEarNumbers = []
const duplicates = []
earNumbers.forEach(earNumber => {
if (!existingSet.has(earNumber)) {
newEarNumbers.push(earNumber)
existingSet.add(earNumber)
} else {
duplicates.push(earNumber)
}
})
// 添加新耳号
if (newEarNumbers.length > 0) {
queryParams.value.allEarNumbers = [
...(queryParams.value.allEarNumbers || []),
...newEarNumbers
]
const message = `成功添加 ${newEarNumbers.length} 个耳号,当前共 ${queryParams.value.allEarNumbers.length}` +
(duplicates.length > 0 ? `,已忽略 ${duplicates.length} 个重复耳号` : '')
proxy.$modal.msgSuccess(message)
} else if (duplicates.length > 0) {
proxy.$modal.msgWarning(`所有耳号均已存在,当前共 ${queryParams.value.allEarNumbers.length}`)
}
// 清空输入框
pasteInput.value = ''
}
/** 远程搜索耳号 */
function searchEarNumber(query) {
if (query !== '') {
earNumberLoading.value = true
const queries = query.trim().split(/[\s,]+/).filter(q => q)
if (queries.length === 1) {
// 单个耳号模糊搜索
searchEarNumbers(query).then(response => {
earNumberOptions.value = response.data || []
earNumberLoading.value = false
}).catch(() => {
earNumberOptions.value = []
earNumberLoading.value = false
})
} else {
// 多个耳号直接作为选项
earNumberOptions.value = queries
earNumberLoading.value = false
}
} else {
earNumberOptions.value = []
}
}
/** 耳号选择变化处理 */
function handleEarNumberChange(value) {
queryParams.value.allEarNumbers = [...new Set(value)]
console.log(`已选择 ${queryParams.value.allEarNumbers.length} 个耳号:`, queryParams.value.allEarNumbers)
}
/** 切换展开/收起状态 */
function toggleExpand() {
isExpanded.value = !isExpanded.value
}
/** 移除单个耳号 */
function handleRemoveEarNumber(tag) {
const index = queryParams.value.allEarNumbers.indexOf(tag)
if (index > -1) {
queryParams.value.allEarNumbers.splice(index, 1)
proxy.$modal.msgSuccess('已移除该耳号')
}
// 如果删除后剩余<=2个自动收起
if (queryParams.value.allEarNumbers.length <= defaultShowCount) {
isExpanded.value = false
}
}
/** 清空所有耳号 */
function clearAllEarNumbers() {
proxy.$modal.confirm('确定要清空所有已选择的耳号吗?').then(() => {
queryParams.value.allEarNumbers = []
pasteInput.value = ''
earNumberOptions.value = []
isExpanded.value = false // 重置展开状态
proxy.$modal.msgSuccess('已清空所有耳号')
}).catch(() => { })
}
/* ---------- 下拉数据源 ---------- */
const pDiseaseOptions = ref([])
const cDiseaseOptions = ref([])
const sheepfoldOptions = ref([])
/* ---------- 疾病下拉 ---------- */
function getDiseases() {
listDisease().then((res) => {
const tree = proxy.handleTree(res.data, 'id', 'pid')
pDiseaseOptions.value = tree.filter((o) => o.pid === 0)
})
}
function handleDiseaseTypeChange(pid) {
if (!pid) {
cDiseaseOptions.value = []
queryParams.value.diseaseId = null
return
}
listDisease({ pid }).then((res) => {
cDiseaseOptions.value = res.data
})
}
/* ---------- 获取列表 ---------- */
function getList() {
loading.value = true
// 组装日期区间
queryParams.value.params = {}
if (daterangeDatetime.value?.length === 2) {
queryParams.value.params.beginDatetime = daterangeDatetime.value[0]
queryParams.value.params.endDatetime = daterangeDatetime.value[1]
}
listDiagnosis(queryParams.value).then((res) => {
diagnosisList.value = res.rows
total.value = res.total
loading.value = false
})
}
/* ---------- 搜索 / 重置 ---------- */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
function resetQuery() {
proxy.resetForm('queryRef')
daterangeDatetime.value = []
resetQueryParams()
handleQuery()
}
function resetQueryParams() {
queryParams.value = {
pageNum: 1,
pageSize: 20,
sheepNo: null,
datetime: null,
diseasePid: null,
diseaseId: null,
result: null,
treatDay: null,
sheepfoldId: null,
orderByColumn: null,
isAsc: null,
}
}
/* ---------- 多选 ---------- */
function handleSelectionChange(selection) {
ids.value = selection.map((i) => i.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
/* ---------- 排序 ---------- */
function handleSortChange({ prop, order }) {
if (!order) {
queryParams.value.orderByColumn = null
queryParams.value.isAsc = null
} else {
queryParams.value.orderByColumn = prop
queryParams.value.isAsc = order === 'ascending' ? 'asc' : 'desc'
}
getList()
}
/* ---------- 新增 / 修改 ---------- */
function handleUpdate(row) {
reset()
const id = row.id || ids.value
getDiagnosis(id).then((res) => {
form.value = res.data
open.value = true
title.value = '修改诊疗结果'
})
}
/* ---------- 提交 ---------- */
function submitForm() {
proxy.$refs.diagnosisRef.validate((valid) => {
if (!valid) return
updateDiagnosis(form.value).then(() => {
proxy.$modal.msgSuccess('修改成功')
open.value = false
getList()
})
})
}
/* ---------- 删除 ---------- */
function handleDelete(row) {
const _ids = row.id || ids.value
proxy
.$modal.confirm('是否确认删除诊疗结果编号为"' + _ids + '"的数据项?')
.then(() => delDiagnosis(_ids))
.then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
})
}
/* ---------- 导出 ---------- */
function handleExport() {
proxy.download(
'diagnosis/diagnosis/export',
{ ...queryParams.value },
`diagnosis_${new Date().getTime()}.xlsx`
)
}
/* ---------- 弹窗取消 / 重置 ---------- */
function cancel() {
open.value = false
reset()
}
function reset() {
form.value = {
id: undefined,
sheepNo: undefined,
datetime: undefined,
diseasePid: undefined,
diseaseId: undefined,
result: undefined,
begindate: undefined,
enddate: undefined,
treatDay: undefined,
sheepfoldId: undefined,
remark: undefined,
}
proxy.resetForm('diagnosisRef')
}
/* ---------- 自动计算治疗天数 ---------- */
const calcTreatDay = () => {
const { begindate, enddate } = form.value
if (!begindate || !enddate) {
form.value.treatDay = 0
return
}
const diff = Math.ceil(
(new Date(enddate) - new Date(begindate)) / (1000 * 60 * 60 * 24)
)
form.value.treatDay = diff >= 0 ? diff : 0
}
watch(() => form.value.begindate, calcTreatDay)
watch(() => form.value.enddate, calcTreatDay)
/* ---------- 初始化 ---------- */
onMounted(() => {
getList()
getDiseases()
listSheepfold_management().then((res) => {
sheepfoldOptions.value = res.rows.map((i) => ({
value: i.id,
label: i.sheepfoldName,
}))
})
fetchTechnicalList() // 获取技术员列表
})
</script>
<style scoped>
/* 已选耳号展示区样式 */
.selected-ear-numbers-display {
max-height: 150px;
overflow-y: auto;
padding: 8px;
background-color: #f5f7fa;
border-radius: 4px;
border: 1px dashed #dcdfe6;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.selected-ear-numbers-display::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.selected-ear-numbers-display::-webkit-scrollbar-thumb {
background-color: #dcdfe6;
border-radius: 3px;
}
.selected-ear-numbers-display::-webkit-scrollbar-track {
background-color: #f5f7fa;
}
.selected-ear-numbers-display .el-tag {
margin: 4px;
cursor: pointer;
}
.selected-ear-numbers-display .el-tag:hover {
opacity: 0.8;
}
/* 隐藏 el-select 的下拉箭头(可选) */
:deep(.el-select__caret) {
display: none;
}
</style>