繁育多耳号查询

This commit is contained in:
zyk
2026-02-10 14:05:27 +08:00
parent 90953eed61
commit 6e58af0fea
5 changed files with 868 additions and 217 deletions

View File

@@ -87,3 +87,11 @@ export function searchHandler(query) {
params: { query } params: { query }
}) })
} }
export function searchEarNumbers(query) {
return request({
url: '/sheep_death/death/search_ear_numbers', // 根据实际路径修改
method: 'get',
params: { query }
})
}

View File

@@ -3,25 +3,100 @@
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="耳号" prop="allEarNumbers"> <el-form-item label="耳号" prop="allEarNumbers">
<el-select <div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
v-model="queryParams.allEarNumbers" <!-- 主选择器不显示已选标签 -->
multiple <el-select
filterable v-model="queryParams.allEarNumbers"
remote multiple
reserve-keyword filterable
placeholder="请输入耳号搜索" remote
:remote-method="searchEarNumber" reserve-keyword
:loading="earNumberLoading" placeholder="输入耳号搜索"
style="width: 240px" :remote-method="searchEarNumber"
clearable :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;"
> >
<el-option <!-- 显示前2个或全部耳号 -->
v-for="item in earNumberOptions" <el-tag
:key="item" v-for="(tag, index) in displayedEarNumbers"
:label="item" :key="tag"
:value="item" closable
/> @close="handleRemoveEarNumber(tag)"
</el-select> 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>
<el-form-item label="是否在群" prop="isInHerd"> <el-form-item label="是否在群" prop="isInHerd">
@@ -231,7 +306,29 @@
<script setup name="Weaning_record"> <script setup name="Weaning_record">
import { listWeaning_record, getWeaning_record, delWeaning_record, addWeaning_record, updateWeaning_record, getSheepIdByEarNumber, searchEarNumbers } from "@/api/Weaning/weaning_record" import { listWeaning_record, getWeaning_record, delWeaning_record, addWeaning_record, updateWeaning_record, getSheepIdByEarNumber, searchEarNumbers } from "@/api/Weaning/weaning_record"
import { ref, computed, nextTick } from 'vue'
import { ArrowUp, ArrowDown, Plus, Delete } from '@element-plus/icons-vue'
// 响应式数据
const pasteInput = ref('') // 批量粘贴输入框
const earNumberOptions = ref([]) // 耳号下拉选项
const earNumberLoading = ref(false) // 耳号加载状态
const isExpanded = ref(false) // 控制耳号展开/折叠状态
const defaultShowCount = 2 // 默认显示的耳号数量
// 计算属性:控制显示的耳号列表
const displayedEarNumbers = computed(() => {
if (!queryParams.value.allEarNumbers || queryParams.value.allEarNumbers.length === 0) {
return []
}
// 如果展开或总数<=2显示全部否则只显示前2个
if (isExpanded.value || queryParams.value.allEarNumbers.length <= defaultShowCount) {
return queryParams.value.allEarNumbers
} else {
return queryParams.value.allEarNumbers.slice(0, defaultShowCount)
}
})
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const weaning_recordList = ref([]) const weaning_recordList = ref([])
@@ -245,9 +342,6 @@ const total = ref(0)
const title = ref("") const title = ref("")
const daterangeTime = ref([]) // 时间范围数组 const daterangeTime = ref([]) // 时间范围数组
// 远程搜索相关
const earNumberOptions = ref([])
const earNumberLoading = ref(false)
const data = reactive({ const data = reactive({
form: {}, form: {},
@@ -299,27 +393,7 @@ function getList() {
}) })
} }
/** 远程搜索耳号 */
function searchEarNumber(query) {
if (query !== '') {
earNumberLoading.value = true
searchEarNumbers(query).then(response => {
earNumberOptions.value = response.data || []
earNumberLoading.value = false
}).catch(() => {
earNumberOptions.value = []
earNumberLoading.value = false
})
} else {
earNumberOptions.value = []
}
}
// 取消按钮
function cancel() {
open.value = false
reset()
}
// 表单重置 // 表单重置
function reset() { function reset() {
@@ -347,11 +421,10 @@ function handleQuery() {
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
daterangeTime.value = [] queryParams.value.allEarNumbers = [] // 🔥 必须添加
earNumberOptions.value = [] earNumberOptions.value = []
isExpanded.value = false
proxy.resetForm("queryRef") proxy.resetForm("queryRef")
// 必须手动清空多选数组
queryParams.value.allEarNumbers = []
handleQuery() handleQuery()
} }
@@ -434,14 +507,170 @@ function handleExport() {
}, `weaning_record_${new Date().getTime()}.xlsx`) }, `weaning_record_${new Date().getTime()}.xlsx`)
} }
/** 处理粘贴事件 */
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(() => {})
}
getList() getList()
</script> </script>
<style scoped> <style scoped>
.app-container { /* 已选耳号展示区样式 */
padding: 20px; .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;
} }
.mb8 {
margin-bottom: 8px; .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> </style>

View File

@@ -369,7 +369,8 @@
<el-row :gutter="20" class="lamb-form-content"> <el-row :gutter="20" class="lamb-form-content">
<el-col :span="8"> <el-col :span="8">
<el-form-item :label="`羔羊耳号`" :prop="`lambForms.${index}.lambEarNumber`"> <el-form-item :label="`羔羊耳号`" :prop="`lambForms.${index}.lambEarNumber`">
<el-input v-model="lamb.lambEarNumber" placeholder="请输入羔羊耳号" /> <el-input v-model="lamb.lambEarNumber" placeholder="请输入羔羊耳号"
@input="handleLambEarNumberInput(index)"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
@@ -567,6 +568,64 @@ const data = reactive({
} }
}) })
// 品种 ID 常量定义 (对应 ScEmbryoFlushServiceImpl)
const VARIETY_IDS = {
HUYANG: 1, // 湖羊
DONGFULISHENG: 2, // 东佛里生
HUIJIAO: 3, // 回交
JIZA_1: 4, // 级杂一代
JIZA_2: 5, // 级杂二代
JIZA_3: 6, // 级杂三代
SHIDAI_1: 7, // 一世代
SHIDAI_2: 8, // 二世代
SHIDAI_3: 9, // 三世代
SHIDAI_4: 10 // 四世代
};
/** * 品种计算核心逻辑 (参考后端 calculateEmbryoVarietyById)
* @param {number} maleId 公羊品种ID
* @param {number} femaleId 母羊品种ID
* @returns {number|null} 羔羊品种ID
*/
function calculateLambBreedId(maleId, femaleId) {
if (!maleId || !femaleId) return null;
// 湖羊 x 湖羊 -> 湖羊
if (maleId === VARIETY_IDS.HUYANG && femaleId === VARIETY_IDS.HUYANG) return VARIETY_IDS.HUYANG;
// 东佛里生 x 东佛里生 -> 东佛里生
if (maleId === VARIETY_IDS.DONGFULISHENG && femaleId === VARIETY_IDS.DONGFULISHENG) return VARIETY_IDS.DONGFULISHENG;
// 东佛里生 x 湖羊 -> 级杂一代
if (maleId === VARIETY_IDS.DONGFULISHENG && femaleId === VARIETY_IDS.HUYANG) return VARIETY_IDS.JIZA_1;
// 东佛里生 x 级杂一代 -> 级杂二代
if (maleId === VARIETY_IDS.DONGFULISHENG && femaleId === VARIETY_IDS.JIZA_1) return VARIETY_IDS.JIZA_2;
// 东佛里生 x 级杂二代 -> 级杂三代
if (maleId === VARIETY_IDS.DONGFULISHENG && femaleId === VARIETY_IDS.JIZA_2) return VARIETY_IDS.JIZA_3;
// 东佛里生 x 级杂三代 -> 东佛里生
if (maleId === VARIETY_IDS.DONGFULISHENG && femaleId === VARIETY_IDS.JIZA_3) return VARIETY_IDS.DONGFULISHENG;
// 湖羊 x 级杂三代 -> 回交
if (maleId === VARIETY_IDS.HUYANG && femaleId === VARIETY_IDS.JIZA_3) return VARIETY_IDS.HUIJIAO;
// 湖羊 x 回交 -> 回交
if (maleId === VARIETY_IDS.HUYANG && femaleId === VARIETY_IDS.HUIJIAO) return VARIETY_IDS.HUIJIAO;
// 世代计算规则
const isMaleCapable = (maleId >= 3 && maleId <= 10);
if (isMaleCapable) {
if (femaleId === VARIETY_IDS.JIZA_2) return VARIETY_IDS.SHIDAI_1;
if (femaleId === VARIETY_IDS.SHIDAI_1) return VARIETY_IDS.SHIDAI_2;
if (femaleId === VARIETY_IDS.SHIDAI_2) return VARIETY_IDS.SHIDAI_3;
if (femaleId === VARIETY_IDS.SHIDAI_3) return VARIETY_IDS.SHIDAI_4;
}
return null;
}
const { queryParams, form, rules } = toRefs(data) const { queryParams, form, rules } = toRefs(data)
// 新增:获取技术员列表 // 新增:获取技术员列表
@@ -767,11 +826,8 @@ function handleEarNumberInput() {
/** 母羊耳号失焦处理 - 自动查询配种信息 */ /** 母羊耳号失焦处理 - 自动查询配种信息 */
function handleEarNumberBlur() { function handleEarNumberBlur() {
const earNumber = form.value.femaleEarNumber const earNumber = form.value.femaleEarNumber;
if (!earNumber || earNumber.trim() === '') { if (!earNumber || earNumber.trim() === '') return;
return
}
// 调用API查询配种信息 // 调用API查询配种信息
getBreedingInfo(earNumber.trim()).then(response => { getBreedingInfo(earNumber.trim()).then(response => {
if (response.code === 200 && response.data) { if (response.code === 200 && response.data) {
@@ -784,7 +840,19 @@ function handleEarNumberBlur() {
form.value.breedingDate = breedingData.breeding_date form.value.breedingDate = breedingData.breeding_date
form.value.pregnancyDays = breedingData.pregnancy_days form.value.pregnancyDays = breedingData.pregnancy_days
form.value.technician = breedingData.technician || '' form.value.technician = breedingData.technician || ''
const mVariety = varietyList.value.find(v => v.variety === breedingData.male_breed);
const fVariety = varietyList.value.find(v => v.variety === breedingData.female_breed);
// 临时存储这些信息,用于初始化羔羊列表
form.value._calculatedLambBreedId = calculateLambBreedId(mVariety?.id, fVariety?.id);
form.value._fatherLineage = breedingData.male_lineage || ''; // 确保后端返回了家系字段
if (lambForms.value && lambForms.value.length > 0) {
lambForms.value.forEach(lamb => {
lamb.lineage = form.value._fatherLineage;
lamb.lambBreed = form.value._calculatedLambBreedId;
});
}
proxy.$modal.msgSuccess("已自动匹配父母品种及家系信息");
proxy.$modal.msgSuccess("已自动填充配种信息") proxy.$modal.msgSuccess("已自动填充配种信息")
} else { } else {
proxy.$modal.msgWarning(response.msg || "未找到该母羊的配种记录") proxy.$modal.msgWarning(response.msg || "未找到该母羊的配种记录")
@@ -801,6 +869,7 @@ function handleEarNumberBlur() {
}) })
} }
/** 查询产羔记录列表 */ /** 查询产羔记录列表 */
function getList() { function getList() {
loading.value = true loading.value = true
@@ -886,21 +955,21 @@ function handleUpdate(row) {
/** 产羔数量变化处理 */ /** 产羔数量变化处理 */
function handleLambsBornChange() { function handleLambsBornChange() {
const count = parseInt(form.value.lambsBorn) || 0 const count = parseInt(form.value.lambsBorn) || 0;
if (count > 0 && count <= 10) { // 限制最大数量 if (count > 0 && count <= 10) {
showLambForms.value = true showLambForms.value = true;
lambForms.value = Array.from({ length: count }, (_, index) => ({ lambForms.value = Array.from({ length: count }, () => ({
lambEarNumber: '', lambEarNumber: '',
gender: '', gender: '',
isRetained: false, isRetained: true, // 默认留养
birthWeight: null, birthWeight: null,
lambBreed: null, lambBreed: form.value._calculatedLambBreedId, // 自动填充计算出的品种
lineage: '', lineage: form.value._fatherLineage, // 随父亲家系
birthday: '' birthday: form.value.createTime || new Date().toISOString().split('T')[0] // 随产羔日期
})) }));
} else { } else {
showLambForms.value = false showLambForms.value = false;
lambForms.value = [] lambForms.value = [];
} }
} }
@@ -967,6 +1036,24 @@ function submitForm() {
} }
}) })
} }
/** 监听羔羊耳号输入,自动识别性别 */
function handleLambEarNumberInput(index) {
const lamb = lambForms.value[index]
const earNumber = lamb.lambEarNumber
// 确保耳号长度至少有2位
if (earNumber && earNumber.length >= 2) {
// 获取第二个字符并转大写
const secondChar = earNumber.charAt(1).toUpperCase()
if (secondChar === 'F') {
lamb.gender = 'female' // 对应 <el-option label="母" value="female">
} else if (secondChar === 'M') {
lamb.gender = 'male' // 对应 <el-option label="公" value="male">
}
}
}
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {

View File

@@ -2,27 +2,101 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="耳号" prop="manageTagsList"> <el-form-item label="母羊耳号" prop="allEarNumbers">
<el-select <div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
v-model="queryParams.manageTagsList" <!-- 主选择器不显示已选标签 -->
multiple <el-select
filterable v-model="queryParams.allEarNumbers"
remote multiple
reserve-keyword filterable
placeholder="请输入耳号搜索" remote
:remote-method="searchEarNumber" reserve-keyword
:loading="earNumberLoading" placeholder="输入耳号搜索"
style="width: 240px" :remote-method="searchEarNumber"
clearable :loading="earNumberLoading"
@change="handleQuery" 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;"
> >
<el-option <!-- 显示前2个或全部耳号 -->
v-for="item in earNumberOptions" <el-tag
:key="item" v-for="(tag, index) in displayedEarNumbers"
:label="item" :key="tag"
:value="item" closable
/> @close="handleRemoveEarNumber(tag)"
</el-select> 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>
<el-form-item label="干奶日期" style="width: 308px"> <el-form-item label="干奶日期" style="width: 308px">
@@ -180,6 +254,29 @@ import { getUserByPost } from '@/api/common/user' // 新增引入
import { listUser } from "@/api/system/user"; import { listUser } from "@/api/system/user";
import axios from 'axios'; import axios from 'axios';
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
import { ref, computed, nextTick } from 'vue'
import { ArrowUp, ArrowDown, Plus, Delete } from '@element-plus/icons-vue'
// 响应式数据
const pasteInput = ref('') // 批量粘贴输入框
const earNumberOptions = ref([]) // 耳号下拉选项
const earNumberLoading = ref(false) // 耳号加载状态
const isExpanded = ref(false) // 控制耳号展开/折叠状态
const defaultShowCount = 2 // 默认显示的耳号数量
// 计算属性:控制显示的耳号列表
const displayedEarNumbers = computed(() => {
if (!queryParams.value.allEarNumbers || queryParams.value.allEarNumbers.length === 0) {
return []
}
// 如果展开或总数<=2显示全部否则只显示前2个
if (isExpanded.value || queryParams.value.allEarNumbers.length <= defaultShowCount) {
return queryParams.value.allEarNumbers
} else {
return queryParams.value.allEarNumbers.slice(0, defaultShowCount)
}
})
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
@@ -196,10 +293,6 @@ const dateRange = ref([])
const sheepfoldOptions = ref([]) const sheepfoldOptions = ref([])
const userOptions = ref([]) const userOptions = ref([])
// 耳号搜索变量
const earNumberLoading = ref(false)
const earNumberOptions = ref([])
// 新增:技术员选项 // 新增:技术员选项
const technicalOptions = ref([]) const technicalOptions = ref([])
@@ -209,6 +302,7 @@ const data = reactive({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
manageTags: null, manageTags: null,
allEarNumbers: [], // ✅ 添加这一行!
manageTagsList: [], manageTagsList: [],
technicianList: [], // 技术员多选列表 technicianList: [], // 技术员多选列表
tecahnician: null, // 兼容旧字段 tecahnician: null, // 兼容旧字段
@@ -244,21 +338,122 @@ const fetchTechnicalList = () => {
/** 远程搜索耳号 */ /** 远程搜索耳号 */
function searchEarNumber(query) { function searchEarNumber(query) {
if (query) { if (query !== '') {
earNumberLoading.value = true; earNumberLoading.value = true
searchEarNumbers(query).then(res => {
earNumberLoading.value = false; const queries = query.trim().split(/[\s,]+/).filter(q => q)
const list = res.data || res.msg || res;
earNumberOptions.value = Array.isArray(list) ? list : []; if (queries.length === 1) {
}).catch(err => { // 单个耳号模糊搜索
earNumberLoading.value = false; searchEarNumbers(query).then(response => {
earNumberOptions.value = []; earNumberOptions.value = response.data || []
}); earNumberLoading.value = false
}).catch(() => {
earNumberOptions.value = []
earNumberLoading.value = false
})
} else {
// 多个耳号直接作为选项
earNumberOptions.value = queries
earNumberLoading.value = false
}
} else { } else {
earNumberOptions.value = []; earNumberOptions.value = []
} }
} }
/** 处理粘贴事件 */
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 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(() => {})
}
/** 查询干奶记录列表 */ /** 查询干奶记录列表 */
function getList() { function getList() {
loading.value = true loading.value = true
@@ -314,14 +509,6 @@ function reset() {
proxy.resetForm("drymilkRef") proxy.resetForm("drymilkRef")
} }
function resetQuery() {
dateRange.value = []
queryParams.value.manageTagsList = []
queryParams.value.technicianList = []
earNumberOptions.value = []
proxy.resetForm("queryRef")
handleQuery()
}
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1
@@ -398,10 +585,60 @@ function handleExport() {
} }
proxy.download('drymilk/drymilk/export', params, `drymilk_${new Date().getTime()}.xlsx`) proxy.download('drymilk/drymilk/export', params, `drymilk_${new Date().getTime()}.xlsx`)
} }
/** 重置按钮操作 */
function resetQuery() {
queryParams.value.allEarNumbers = [] // 🔥 必须添加
earNumberOptions.value = []
isExpanded.value = false
proxy.resetForm("queryRef")
handleQuery()
}
// 初始化 // 初始化
getList() getList()
getSheepfoldList() getSheepfoldList()
getUserList() getUserList()
fetchTechnicalList() // 初始化加载技术员列表 fetchTechnicalList() // 初始化加载技术员列表
</script> </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>

View File

@@ -2,28 +2,101 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="管理耳号" prop="manageTagsList"> <el-form-item label="耳号" prop="allEarNumbers">
<el-select <div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
v-model="queryParams.manageTagsList" <!-- 主选择器不显示已选标签 -->
multiple <el-select
filterable v-model="queryParams.allEarNumbers"
remote multiple
reserve-keyword filterable
placeholder="请输入耳号" remote
:remote-method="remoteSearchEarNo" reserve-keyword
:loading="loadingSearch" placeholder="输入耳号搜索"
allow-create :remote-method="searchEarNumber"
default-first-option :loading="earNumberLoading"
clearable allow-create
style="width: 240px" 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;"
> >
<el-option <!-- 显示前2个或全部耳号 -->
v-for="item in earNoOptions" <el-tag
:key="item" v-for="(tag, index) in displayedEarNumbers"
:label="item" :key="tag"
:value="item" closable
/> @close="handleRemoveEarNumber(tag)"
</el-select> 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>
<el-form-item label="死亡日期" prop="deathDateRange"> <el-form-item label="死亡日期" prop="deathDateRange">
@@ -379,11 +452,15 @@ import {
getSheepInfo, getSheepInfo,
getDiseaseTree, getDiseaseTree,
searchEarNo, searchEarNo,
searchEarNumbers,
// searchTechnician, // 移除旧的远程搜索接口 // searchTechnician, // 移除旧的远程搜索接口
// searchHandler // 移除旧的远程搜索接口 // searchHandler // 移除旧的远程搜索接口
} from "@/api/sheep_death/death" } from "@/api/sheep_death/death"
import { getUserByPost } from '@/api/common/user' // 新增引入 import { getUserByPost } from '@/api/common/user' // 新增引入
import { ref, computed, nextTick } from 'vue'
import { ArrowUp, ArrowDown, Plus, Delete } from '@element-plus/icons-vue'
// 响应式数据 // 响应式数据
const pasteInput = ref('') // 批量粘贴输入框 const pasteInput = ref('') // 批量粘贴输入框
const earNumberOptions = ref([]) // 耳号下拉选项 const earNumberOptions = ref([]) // 耳号下拉选项
@@ -391,9 +468,6 @@ const earNumberLoading = ref(false) // 耳号加载状态
const isExpanded = ref(false) // 控制耳号展开/折叠状态 const isExpanded = ref(false) // 控制耳号展开/折叠状态
const defaultShowCount = 2 // 默认显示的耳号数量 const defaultShowCount = 2 // 默认显示的耳号数量
// 新增:技术员选项
const technicalOptions = ref([])
// 计算属性:控制显示的耳号列表 // 计算属性:控制显示的耳号列表
const displayedEarNumbers = computed(() => { const displayedEarNumbers = computed(() => {
if (!queryParams.value.allEarNumbers || queryParams.value.allEarNumbers.length === 0) { if (!queryParams.value.allEarNumbers || queryParams.value.allEarNumbers.length === 0) {
@@ -407,6 +481,10 @@ const displayedEarNumbers = computed(() => {
return queryParams.value.allEarNumbers.slice(0, defaultShowCount) return queryParams.value.allEarNumbers.slice(0, defaultShowCount)
} }
}) })
// 新增:技术员选项
const technicalOptions = ref([])
const {proxy} = getCurrentInstance() const {proxy} = getCurrentInstance()
// 获取字典数据 // 获取字典数据
const { sys_sheep_type } = proxy.useDict("sys_sheep_type"); const { sys_sheep_type } = proxy.useDict("sys_sheep_type");
@@ -483,56 +561,6 @@ function handlePaste(event) {
}) })
} }
/** 处理粘贴内容提交 */
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) { function searchEarNumber(query) {
@@ -560,41 +588,13 @@ function searchEarNumber(query) {
} }
} }
/** 耳号选择变化处理 */
function handleEarNumberChange(value) {
queryParams.value.allEarNumbers = [...new Set(value)]
console.log(`已选择 ${queryParams.value.allEarNumbers.length} 个耳号:`, queryParams.value.allEarNumbers)
}
/** 切换展开/收起状态 */ /** 切换展开/收起状态 */
function toggleExpand() { function toggleExpand() {
isExpanded.value = !isExpanded.value 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(() => {})
}
/** 查询羊只死淘记录列表 */ /** 查询羊只死淘记录列表 */
function getList() { function getList() {
loading.value = true loading.value = true
@@ -711,6 +711,9 @@ function resetQuery() {
queryParams.value.handlerList = [] queryParams.value.handlerList = []
queryParams.value.workGroupList = [] queryParams.value.workGroupList = []
queryParams.value.deathDateRange = [] queryParams.value.deathDateRange = []
queryParams.value.allEarNumbers = [] // ✅ 添加这一行
earNumberOptions.value = [] // ✅ 添加这一行
isExpanded.value = false // ✅ 添加这一行
proxy.resetForm("queryRef") proxy.resetForm("queryRef")
handleQuery() handleQuery()
@@ -792,6 +795,93 @@ function handleExport() {
proxy.download('sheep_death/death/export', params, `death_${new Date().getTime()}.xlsx`) proxy.download('sheep_death/death/export', params, `death_${new Date().getTime()}.xlsx`)
} }
/** 处理粘贴内容提交 */
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 handleEarNumberChange(value) {
queryParams.value.allEarNumbers = [...new Set(value)]
console.log(`已选择 ${queryParams.value.allEarNumbers.length} 个耳号:`, queryParams.value.allEarNumbers)
}
/** 移除单个耳号 */
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(() => {})
}
// 初始化 // 初始化
getDiseaseTreeData() getDiseaseTreeData()
getList() getList()