bug修复以及数据分离操作

This commit is contained in:
zyh
2026-02-09 20:22:46 +08:00
parent ab3efaa405
commit 5555d6a747
11 changed files with 975 additions and 448 deletions

View File

@@ -140,6 +140,7 @@
<span>{{ genderMap[scope.row.gender] || '未知' }}</span>
</template>
</el-table-column>
<el-table-column label="月龄" align="center" prop="monthAge" width="80" sortable />
<el-table-column label="胎次" align="center" prop="parity" width="100" />
<el-table-column label="出生体重(kg)" align="center" prop="birthWeight" width="150" sortable />
<el-table-column label="断奶体重(kg)" align="center" prop="weaningWeight" width="150" sortable />
@@ -154,6 +155,12 @@
<el-table-column label="尻高" align="center" prop="rumpHeignt" width="100" sortable />
<el-table-column label="腰角宽" align="center" prop="hipWidth" width="150" sortable />
<el-table-column label="十字部高" align="center" prop="hipCrossHeight" width="150" sortable />
<el-table-column label="乳房深度" align="center" prop="breastDepth" width="100" />
<el-table-column label="乳头位置" align="center" prop="breastPosition" width="100" />
<el-table-column label="乳头长度" align="center" prop="breastLength" width="100" />
<el-table-column label="乳房附着" align="center" prop="breastAdbere" width="100" />
<el-table-column label="乳房间隔度" align="center" prop="breastSpacing" width="100" />
<el-table-column label="乳房评分" align="center" prop="breastScore" width="100" sortable />
<el-table-column label="繁育状态" align="center" prop="breedStatusName" width="100">
<template #default="scope">
<span>{{ scope.row.breedStatusName || '未设置' }}</span>
@@ -162,15 +169,15 @@
<el-table-column label="泌乳天数" align="center" prop="lactationDay" width="150" sortable />
<el-table-column label="怀孕天数" align="center" prop="gestationDay" width="150" sortable />
<el-table-column label="配后天数" align="center" prop="postMatingDay" width="150" sortable />
<el-table-column label="备注" align="center" prop="comment" width="100" />
<el-table-column label="体况评分" align="center" prop="bodyScore" width="100" sortable />
<el-table-column label="技术员" align="center" prop="technician" width="100" />
<el-table-column label="创建人" align="center" prop="createBy" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="comment" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" min-width="130">
<template #default="scope">
<!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
@@ -253,8 +260,8 @@
</el-col>
</el-row>
<el-form-item label="备注" prop="comment" v-if="!isAdd">
<el-input v-model="form.comment" placeholder="请输入备注" :disabled="!isAdd" clearable type="textarea" rows="3" />
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" placeholder="请输入备注" clearable type="textarea" rows="3" />
</el-form-item>
</el-form>
@@ -423,16 +430,13 @@ function onManageTagsBlur() {
getSheepByManageTags(tag).then(res => {
if (res.code === 200 && res.data) {
form.value.sheepId = res.data.id;
form.value.comment = res.data.comment || "";
} else {
form.value.sheepId = null;
form.value.comment = null;
proxy.$modal.msgWarning("未找到该管理耳号对应的羊只");
}
}).catch(err => {
console.error("耳号验证失败:", err);
form.value.sheepId = null;
form.value.comment = null;
proxy.$modal.msgError("查询失败,请重试");
});
}

View File

@@ -40,7 +40,7 @@
</el-button>
</div>
</el-form-item>
<el-form-item label="品种" prop="varietyId">
<!-- <el-form-item label="品种" prop="varietyId">
<el-select v-model="queryParams.varietyId" placeholder="请选择品种" clearable style="min-width:150px">
<el-option v-for="item in varietyOptions" :key="item.id" :label="item.variety" :value="item.id" />
</el-select>
@@ -49,6 +49,15 @@
<el-select v-model="queryParams.sheepfold" placeholder="请选择羊舍" style="min-width:150px" clearable>
<el-option v-for="item in sheepfoldOptions" :key="item.id" :label="item.sheepfoldName" :value="item.id" />
</el-select>
</el-form-item> -->
<el-form-item label="体况评分" prop="score">
<el-input-number v-model="queryParams.score" :min="1" :max="5" :step="0.1" :precision="1" placeholder="请输入评分"
style="width: 150px" clearable />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -94,7 +103,6 @@
</el-table-column>
<el-table-column label="体况评分" align="center" prop="score" min-width="100px" sortable />
<el-table-column label="羊舍" align="center" prop="sheepfoldName" min-width="100px" />
<el-table-column label="备注" align="center" prop="comment" min-width="130px" />
<el-table-column label="技术员" align="center" prop="technician" />
<el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="createTime" min-width="130px">
@@ -102,6 +110,7 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="comment" min-width="130px" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="150px" fixed="right">
<template #default="scope">
<!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
@@ -122,24 +131,39 @@
<el-input v-model="form.manageTags" placeholder="请输入管理耳号" @blur="onManageTagsBlur" :disabled="!isAdd"
clearable />
</el-form-item>
<!-- 新增弹窗已选耳号展示区 -->
<div v-if="batchTags.length" class="selected-ear-numbers">
<el-tag v-for="(tag, idx) in batchTags" :key="tag" closable @close="removeBatchTag(idx)" style="margin:4px"
type="success">
{{ tag }}
</el-tag>
<el-button type="primary" link @click="clearBatchTags">
一键清空 ({{ batchTags.length }})
</el-button>
</div>
<el-form-item label="事件日期" prop="datetime">
<el-date-picker clearable v-model="form.datetime" type="date" value-format="YYYY-MM-DD" placeholder="请选择事件日期"
:disabled="!isAdd">
</el-date-picker>
</el-form-item>
<el-form-item label="体况评分" prop="score">
<el-input v-model="form.score" placeholder="请输入体况评分" :disabled="!isAdd" />
<el-input-number v-model="form.score" :min="1" :max="5" :step="0.1" :precision="1" :disabled="!isAdd"
controls-position="right" style="width: 150px" />
</el-form-item>
<el-form-item label="羊舍" v-if="!isAdd">
<el-select v-model="form.sheepfold" disabled>
<el-option v-for="item in sheepfoldList" :key="item.id" :label="item.sheepfoldName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="备注" v-if="!isAdd">
<el-input v-model="form.comment" disabled type="textarea" rows="2" />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" :disabled="!isAdd" />
<el-select v-model="form.technician" placeholder="请选择技术员" :disabled="!isAdd" clearable filterable
style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.comment" placeholder="请填写备注" type="textarea" rows="2" />
</el-form-item>
</el-form>
<template #footer>
@@ -156,8 +180,9 @@
import { listBody_score, getBody_score, delBody_score, addBody_score, updateBody_score, searchEarNumbers } from "@/api/produce/bodyManage/body_score"
import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/sheepfold_management'
import { checkSheepByManageTags, getVarietyOptions } from "@/api/produce/other/fixHoof"
import { getCurrentInstance, ref, reactive, toRefs } from 'vue'
import { getCurrentInstance, ref, reactive, toRefs, computed } from 'vue'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
import { nextTick } from 'vue'
import dayjs from 'dayjs'
const { proxy } = getCurrentInstance()
@@ -175,14 +200,11 @@ const daterangeDatetime = ref([])
const daterangeCreateTime = ref([])
const sheepfoldList = ref([])
const isAdd = ref(false)
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
const pasteText = ref('')
const isExpanded = ref(false)
const defaultShowCount = 2
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList
if (isExpanded.value || list.length <= defaultShowCount) return list
return list.slice(0, defaultShowCount)
})
const defaultShowCount = 2 // 默认展示条数
const data = reactive({
form: {
id: null,
@@ -221,8 +243,33 @@ const data = reactive({
})
const { queryParams, form, rules } = toRefs(data)
/* 计算:控制实际展示的耳号 */
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList
if (isExpanded.value || list.length <= defaultShowCount) return list
return list.slice(0, defaultShowCount)
})
const earOptions = ref([])
const earLoading = ref(false)
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
/** 查询体况评分列表 */
function getList() {
loading.value = true
@@ -235,6 +282,12 @@ function getList() {
queryParams.value.params["beginCreateTime"] = daterangeCreateTime.value[0]
queryParams.value.params["endCreateTime"] = daterangeCreateTime.value[1]
}
if (queryParams.value.technician) {
queryParams.value.params["technician"] = queryParams.value.technician
}
if (queryParams.value.score !== null && queryParams.value.score !== undefined && queryParams.value.score !== '') {
queryParams.value.params["score"] = queryParams.value.score
}
listBody_score(queryParams.value).then(response => {
body_scoreList.value = response.rows
total.value = response.total
@@ -243,33 +296,49 @@ function getList() {
}
function onManageTagsBlur() {
const tag = form.value.manageTags?.trim();
if (!tag) {
form.value.sheepId = null;
form.value.sheepfold = null;
form.value.comment = null;
return;
async function onManageTagsBlur() {
const raw = form.value.manageTags?.trim()
if (!raw) return
//按分隔符拆
const separators = /[\p{White_Space},]+/u
const tags = [...new Set(raw.split(separators).filter(v => v))]
if (tags.length === 1) {
const sheep = await checkSheepByManageTags(tags[0]).then(r => r.data).catch(() => null)
if (sheep) {
batchTags.value = [tags[0]]
batchSheep.value = [sheep]
form.value.sheepId = sheep.id
form.value.sheepfold = sheep.sheepfoldId
} else {
proxy.$modal.msgWarning('未找到该耳号对应羊只')
batchTags.value = []
batchSheep.value = []
form.value.sheepId = null
form.value.sheepfold = null
}
return
}
checkSheepByManageTags(tag).then(res => {
if (res.code === 200 && res.data) {
form.value.sheepId = res.data.id;
form.value.sheepfold = res.data.sheepfoldId;
form.value.comment = res.data.comment || '';
//批量校验
batchTags.value = []
batchSheep.value = []
for (const tag of tags) {
const sheep = await checkSheepByManageTags(tag).then(r => r.data).catch(() => null)
if (sheep) {
batchTags.value.push(tag)
batchSheep.value.push(sheep)
} else {
form.value.sheepId = null;
form.value.sheepfold = null;
form.value.comment = null;
proxy.$modal.msgWarning("未找到该管理耳号对应的羊只");
proxy.$modal.msgWarning(`耳号 ${tag} 未找到对应羊只,已跳过`)
}
}).catch(err => {
console.error("耳号验证失败:", err);
form.value.sheepId = null;
form.value.sheepfold = null;
form.value.comment = null;
proxy.$modal.msgError("查询失败,请重试");
});
}
if (!batchTags.value.length) return // 全部无效
// 把合法耳号重新拼回输入框,用半角空格分隔
form.value.manageTags = batchTags.value.join(' ')
form.value.sheepId = batchSheep.value[0].id
form.value.sheepfold = batchSheep.value[0].sheepfoldId
}
//加载羊舍数据
@@ -291,14 +360,17 @@ function reset() {
form.value = {
id: null,
sheepId: null,
manageTags: null,
datetime: null,
score: null,
score: 0,
sheepfold: null,
comment: null,
technician: null,
createBy: null,
createTime: null
}
batchTags.value = []
batchSheep.value = []
proxy.resetForm("body_scoreRef")
}
@@ -314,6 +386,8 @@ function resetQuery() {
daterangeCreateTime.value = []
queryParams.value.varietyId = null
queryParams.value.manageTagsList = []
queryParams.value.score = null
queryParams.value.technician = null
queryParams.value.isDelete = null
proxy.resetForm("queryRef")
handleQuery()
@@ -349,27 +423,69 @@ function handleUpdate(row) {
/** 提交按钮 */
function submitForm() {
proxy.$refs["body_scoreRef"].validate(valid => {
if (valid) {
if (!form.value.id && !form.value.sheepId) {
proxy.$modal.msgError("请输入有效的管理耳号");
return;
}
if (form.value.id != null) {
updateBody_score(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addBody_score(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()
})
}
}
})
if (batchTags.value.length === 0) {
proxy.$modal.msgError('请输入至少一个有效耳号')
return
}
// 表单校验(评分、日期、技术员)
if (!form.value.score) {
proxy.$modal.msgError('请输入体况评分')
return
}
if (!form.value.datetime) {
proxy.$modal.msgError('请选择事件日期')
return
}
if (!form.value.technician) {
proxy.$modal.msgError('请输入技术员')
return
}
// 批量新增
Promise.all(
batchTags.value.map((tag, idx) =>
addBody_score({
sheepId: batchSheep.value[idx].id,
manageTags: tag,
datetime: form.value.datetime,
score: form.value.score,
technician: form.value.technician,
sheepfold: batchSheep.value[idx].sheepfoldId,
comment: form.value.comment || ''
})
)
).then(() => {
proxy.$modal.msgSuccess(`成功新增 ${batchTags.value.length} 条体况评分`)
open.value = false
getList()
}).catch(() => { })
}
/* 删除单个批量耳号 */
function removeBatchTag(idx) {
batchTags.value.splice(idx, 1)
batchSheep.value.splice(idx, 1)
// 实时回写输入框,保持可视化
form.value.manageTags = batchTags.value.join(' ')
// 如果删完了,清空回显
if (batchTags.value.length === 0) {
form.value.sheepId = null
form.value.sheepfold = null
} else {
// 回显第一只的信息
form.value.sheepId = batchSheep.value[0].id
form.value.sheepfold = batchSheep.value[0].sheepfoldId
}
}
/* 一键清空批量耳号 */
function clearBatchTags() {
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
form.value.sheepId = null
form.value.sheepfold = null
}
/** 删除按钮操作 */
@@ -397,6 +513,7 @@ function loadVarietyOptions() {
varietyOptions.value = res.rows;
})
}
function searchEarNumber(query) {
if (!query) { earOptions.value = []; return }
earLoading.value = true
@@ -404,6 +521,7 @@ function searchEarNumber(query) {
earOptions.value = res.data || []
}).finally(() => earLoading.value = false)
}
function clearEarNumbers() {
queryParams.value.manageTagsList = []
}
@@ -412,6 +530,7 @@ function removeEarNumber(tag) {
const idx = queryParams.value.manageTagsList.indexOf(tag)
if (idx > -1) queryParams.value.manageTagsList.splice(idx, 1)
}
function handlePaste() {
nextTick(() => handlePasteSubmit())
}
@@ -443,9 +562,11 @@ function toggleExpand() {
function handleEarChange(val) {
queryParams.value.manageTagsList = [...new Set(val)]
}
onMounted(() => {
loadVarietyOptions()
getSheepfoldOptions()
fetchTechnicalList()
getList()
})
</script>

View File

@@ -44,7 +44,7 @@
</el-button>
</div>
</el-form-item>
<el-form-item label="品种" prop="varietyId">
<!-- <el-form-item label="品种" prop="varietyId">
<el-select v-model="queryParams.varietyId" placeholder="请选择品种" clearable style="min-width:150px">
<el-option v-for="item in varietyOptions" :key="item.id" :label="item.variety" :value="item.id" />
</el-select>
@@ -53,6 +53,16 @@
<el-select v-model="queryParams.sheepfoldId" placeholder="请选择羊舍" style="min-width:150px" clearable>
<el-option v-for="item in sheepfoldOptions" :key="item.id" :label="item.sheepfoldName" :value="item.id" />
</el-select>
</el-form-item> -->
<el-form-item label="乳房评分" prop="score">
<el-select v-model="queryParams.score" placeholder="请选择评分" clearable style="width: 120px">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -95,14 +105,13 @@
<span>{{ parseTime(scope.row.eventDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="羊舍" align="center" prop="sheepfoldName" min-width="120px" />
<el-table-column label="乳房深度" align="center" prop="depth" min-width="150px" sortable />
<el-table-column label="乳房长度" align="center" prop="length" min-width="150px" sortable />
<el-table-column label="乳房位置" align="center" prop="position" />
<el-table-column label="乳房长度" align="center" prop="length" min-width="150px" sortable />
<el-table-column label="乳房附着" align="center" prop="adbere" />
<el-table-column label="乳房间隔度" align="center" prop="spacing" min-width="120px" />
<el-table-column label="乳房评分" align="center" prop="score" min-width="150px" sortable />
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="羊舍" align="center" prop="sheepfoldName" min-width="120px" />
<el-table-column label="技术员" align="center" prop="technician" />
<el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="createTime" min-width="150">
@@ -110,6 +119,7 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="120px" fixed="right">
<template #default="scope">
<!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
@@ -130,34 +140,59 @@
<el-input v-model="form.manageTags" placeholder="请输入管理耳号" @blur="onManageTagsBlur" clearable
:disabled="!isAdd" />
</el-form-item>
<!-- 新增弹窗已选耳号展示区 -->
<div v-if="batchTags.length" class="selected-ear-numbers">
<el-tag v-for="(tag, idx) in batchTags" :key="tag" closable @close="removeBatchTag(idx)" style="margin:4px"
type="success">
{{ tag }}
</el-tag>
<el-button type="primary" link @click="clearBatchTags">
一键清空 ({{ batchTags.length }})
</el-button>
</div>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker clearable v-model="form.eventDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择事件日期"
:disabled="!isAdd">
</el-date-picker>
</el-form-item>
<el-form-item label="乳房深度" prop="depth">
<el-input v-model="form.depth" placeholder="请输入乳房深度" :disabled="!isAdd" />
<el-select v-model="form.depth" placeholder="请选择乳房深度" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="乳房长度" prop="length">
<el-input v-model="form.length" placeholder="请输入乳房长度" :disabled="!isAdd" />
<el-select v-model="form.length" placeholder="请选择乳房长度" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="乳房位置" prop="position">
<el-input v-model="form.position" placeholder="请输入乳房位置" :disabled="!isAdd" />
<el-select v-model="form.position" placeholder="请选择乳房位置" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="乳房附着" prop="adbere">
<el-input v-model="form.adbere" placeholder="请输入乳房附着" :disabled="!isAdd" />
<el-select v-model="form.adbere" placeholder="请选择乳房附着" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="乳房间隔度" prop="spacing">
<el-input v-model="form.spacing" placeholder="请输入乳房间隔度" :disabled="!isAdd" />
<el-select v-model="form.spacing" placeholder="请选择乳房间隔度" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="乳房评分" prop="score">
<el-input v-model="form.score" placeholder="请输入乳房评分" :disabled="!isAdd" />
</el-form-item>
<el-form-item label="羊只备注" v-if="!isAdd">
<el-input v-model="form.comment" type="textarea" :rows="2" placeholder="羊只备注" :disabled="!isAdd" />
<el-select v-model="form.score" placeholder="请选择乳房评分" :disabled="!isAdd" clearable style="width: 100%">
<el-option v-for="num in 9" :key="num" :label="num" :value="num" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" :disabled="!isAdd" />
<el-select v-model="form.technician" placeholder="请选择技术员" :disabled="!isAdd" clearable filterable
style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.comment" type="textarea" :rows="2" placeholder="备注" />
</el-form-item>
</el-form>
<template #footer>
@@ -177,6 +212,7 @@ import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/
import { getCurrentInstance, reactive, ref } from "vue"
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { nextTick } from 'vue'
import { getUserByPost } from '@/api/common/user'
import dayjs from 'dayjs'
const { proxy } = getCurrentInstance()
@@ -195,11 +231,8 @@ const isAdd = ref(false)
const pasteText = ref('')
const isExpanded = ref(false)
const defaultShowCount = 2
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList
if (isExpanded.value || list.length <= defaultShowCount) return list
return list.slice(0, defaultShowCount)
})
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
const data = reactive({
form: {
id: null,
@@ -219,7 +252,8 @@ const data = reactive({
pageSize: 20,
manageTags: null,
manageTagsList: [],
isDelete: null
isDelete: null,
score: null,
},
rules: {
manageTags: [
@@ -229,17 +263,41 @@ const data = reactive({
{ required: true, message: '请选择事件日期', trigger: 'blur' }
],
score: [
{ required: true, message: '请输入乳房评分', trigger: 'blur' }
{ required: true, message: '请输入乳房评分', trigger: 'change' }
],
technician: [
{ required: true, message: '请输入技术员', trigger: 'blur' }
{ required: true, message: '请输入技术员', trigger: 'change' }
],
}
})
const { queryParams, form, rules } = toRefs(data)
const earOptions = ref([])
const earLoading = ref(false)
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList
if (isExpanded.value || list.length <= defaultShowCount) return list
return list.slice(0, defaultShowCount)
})
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
/** 查询乳房评分列表 */
function getList() {
loading.value = true
@@ -252,7 +310,12 @@ function getList() {
queryParams.value.params["beginCreateTime"] = daterangeCreateTime.value[0]
queryParams.value.params["endCreateTime"] = daterangeCreateTime.value[1]
}
if (queryParams.value.technician) {
queryParams.value.params["technician"] = queryParams.value.technician
}
if (queryParams.value.score !== null && queryParams.value.score !== undefined && queryParams.value.score !== '') {
queryParams.value.params["score"] = queryParams.value.score
}
listBreast_rating(queryParams.value).then(response => {
breast_ratingList.value = response.rows
total.value = response.total
@@ -261,28 +324,47 @@ function getList() {
}
// 根据耳号查询羊只信息
function onManageTagsBlur() {
const tag = form.value.manageTags?.trim();
if (!tag) {
form.value.sheepId = null;
form.value.comment = null;
return;
async function onManageTagsBlur() {
const raw = form.value.manageTags?.trim()
if (!raw) return
// 1. 先按分隔符拆
const separators = /[\p{White_Space},]+/u
const tags = [...new Set(raw.split(separators).filter(v => v))]
// 2. 只有一条 → 走老逻辑
if (tags.length === 1) {
const sheep = await checkSheepByManageTags(tags[0]).then(r => r.data).catch(() => null)
if (sheep) {
batchTags.value = [tags[0]]
batchSheep.value = [sheep]
form.value.sheepId = sheep.id
} else {
proxy.$modal.msgWarning('未找到该耳号对应羊只')
batchTags.value = []
batchSheep.value = []
form.value.sheepId = null
}
return
}
checkSheepByManageTags(tag).then(res => {
if (res.code === 200 && res.data) {
form.value.sheepId = res.data.id;
form.value.comment = res.data.comment || '';
// 3. 多条 → 批量校验
batchTags.value = []
batchSheep.value = []
for (const tag of tags) {
const sheep = await checkSheepByManageTags(tag).then(r => r.data).catch(() => null)
if (sheep) {
batchTags.value.push(tag)
batchSheep.value.push(sheep)
} else {
form.value.sheepId = null;
form.value.comment = null;
proxy.$modal.msgWarning("未找到该耳号对应的羊只");
proxy.$modal.msgWarning(`耳号 ${tag} 未找到对应羊只,已跳过`)
}
}).catch(err => {
console.error("耳号查询失败:", err);
form.value.sheepId = null;
form.value.comment = null;
});
}
if (!batchTags.value.length) return // 全部无效
// 把合法耳号重新拼回输入框,用半角空格分隔
form.value.manageTags = batchTags.value.join(' ')
form.value.sheepId = batchSheep.value[0].id
}
// 取消按钮
@@ -290,7 +372,6 @@ function cancel() {
open.value = false
reset()
}
// 表单重置
function reset() {
form.value = {
@@ -308,6 +389,8 @@ function reset() {
createBy: null,
createTime: null
}
batchTags.value = [] // 新增:清空批量耳号
batchSheep.value = [] // 新增:清空羊只对象
proxy.resetForm("breast_ratingRef")
}
@@ -323,6 +406,8 @@ function resetQuery() {
daterangeEventDate.value = []
queryParams.value.manageTagsList = []
queryParams.value.isDelete = null
queryParams.value.score = null
queryParams.value.technician = null
proxy.resetForm("queryRef")
handleQuery()
}
@@ -357,29 +442,61 @@ function handleUpdate(row) {
/** 提交按钮 */
function submitForm() {
// 先校验表单(走 rules 规则)
proxy.$refs["breast_ratingRef"].validate(valid => {
if (valid) {
if (!form.value.sheepId) {
proxy.$modal.msgError("请输入有效的管理耳号");
return;
}
if (form.value.id) {
updateBreast_rating(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addBreast_rating(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()
})
}
if (!valid) return // 校验不通过,直接返回
if (batchTags.value.length === 0) {
proxy.$modal.msgError('请输入至少一个有效耳号')
return
}
// 批量新增
Promise.all(
batchTags.value.map((tag, idx) =>
addBreast_rating({
sheepId: batchSheep.value[idx].id,
manageTags: tag,
eventDate: form.value.eventDate,
depth: form.value.depth,
length: form.value.length,
position: form.value.position,
adbere: form.value.adbere,
spacing: form.value.spacing,
score: form.value.score,
technician: form.value.technician,
comment: form.value.comment
})
)
).then(() => {
proxy.$modal.msgSuccess(`成功新增 ${batchTags.value.length} 条乳房评分`)
open.value = false
getList()
}).catch(() => { })
})
}
/* 删除单个批量耳号 */
function removeBatchTag(idx) {
batchTags.value.splice(idx, 1)
batchSheep.value.splice(idx, 1)
form.value.manageTags = batchTags.value.join(' ')
if (batchTags.value.length === 0) {
form.value.sheepId = null
form.value.comment = null
} else {
form.value.sheepId = batchSheep.value[0].id
}
}
/* 一键清空批量耳号 */
function clearBatchTags() {
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
form.value.sheepId = null
}
/** 删除按钮操作 */
function handleDelete(row) {
const _ids = row.id || ids.value
@@ -462,6 +579,7 @@ function handleEarChange(val) {
onMounted(() => {
loadVarietyOptions()
getSheepfoldOptions()
fetchTechnicalList()
getList()
})
</script>

View File

@@ -107,7 +107,9 @@
</el-col>
<el-col :span="10">
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" />
<el-select v-model="form.technician" placeholder="请选择技术员" clearable filterable style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
@@ -130,7 +132,7 @@
<el-dialog title="导入羊只信息" v-model="importOpen" width="400px" append-to-body>
<el-upload ref="uploadRef" :limit="1" accept=".xlsx,.xls" :action="importUrl" :headers="headers"
:auto-upload="false" :on-success="handleImportSuccess" :on-error="handleImportError" drag>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
@@ -154,7 +156,7 @@ import { listRanch } from '@/api/produce/manage_sheep/trans_group'
import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/sheepfold_management'
import { getToken } from '@/utils/auth'
import { UploadFilled } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
const form = ref({
@@ -178,6 +180,27 @@ const formRef = ref(null)
const sheepfoldOptions = ref([])
const varietyOptions = ref([])
const ranchOptions = ref([])
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
const rules = {
earNumber: [
{ required: true, message: '请输入耳号', trigger: 'blur' },
@@ -380,6 +403,7 @@ onMounted(() => {
loadSheepTypeList()
loadRanchList()
getVarietyOptions()
fetchTechnicalList()
})
</script>

View File

@@ -64,8 +64,9 @@
</el-select>
</el-form-item> -->
<el-form-item label="技术员" prop="technician">
<el-input v-model="queryParams.technician" placeholder="请输入技术员" clearable style="max-width: 160px"
@keyup.enter="handleQuery" />
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -152,14 +153,17 @@
<el-input v-model="form.newComment" placeholder="请输入新备注" :disabled="!isAddMode" />
</el-form-item>
<el-form-item label="原备注" prop="oldComment">
<el-input v-model="form.oldComment" placeholder="自动回显,多只羊只回显第一只" disabled />
<el-input v-model="form.oldComment" placeholder="页面自动获取展示" disabled />
</el-form-item>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker v-model="form.eventDate" type="date" placeholder="请选择事件日期" value-format="YYYY-MM-DD"
:disabled="!isAddMode" />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" :disabled="!isAddMode" />
<el-select v-model="form.technician" placeholder="请选择技术员" :disabled="!isAddMode" clearable filterable
style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
@@ -179,6 +183,7 @@ import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/
import dayjs from 'dayjs'
import { nextTick } from 'vue'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
const changeCommentList = ref([])
@@ -198,6 +203,27 @@ const isExpanded = ref(false) // 折叠状态
const defaultShowCount = 2 // 默认展示条数
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
/* 计算:控制实际展示的耳号 */
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList
@@ -489,10 +515,9 @@ function clearBatchTags() {
form.value.oldComment = ''
}
/* 计算属性:实时显示已选数量(可选) */
const batchCount = computed(() => batchTags.value.length)
onMounted(() => {
getSheepfoldOptions();
fetchTechnicalList();
getList();
});
</script>

View File

@@ -59,8 +59,9 @@
style="max-width: 160px;" />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="queryParams.technician" placeholder="请输入技术员" clearable @keyup.enter="handleQuery"
style="max-width: 160px;" />
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="inGroup">
<el-select v-model="queryParams.inGroup" placeholder="全部" clearable style="width:120px">
@@ -155,10 +156,13 @@
<el-input v-model="form.newTag" placeholder="请输入新耳号/电子耳号" @blur="checkNewTagExists" :disabled="!isAddMode" />
</el-form-item>
<el-form-item label="旧耳号" prop="oldTag">
<el-input v-model="form.oldTag" placeholder="请输入旧耳号/电子耳号" disabled />
<el-input v-model="form.oldTag" placeholder="自动获取" disabled />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" :disabled="!isAddMode" />
<el-select v-model="form.technician" placeholder="请选择技术员" :disabled="!isAddMode" clearable filterable
style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker v-model="form.eventDate" type="date" placeholder="请选择事件日期" value-format="YYYY-MM-DD"
@@ -184,8 +188,28 @@ import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/
import dayjs from 'dayjs'
import { nextTick } from 'vue'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
const changeEarList = ref([])
const open = ref(false)
const loading = ref(true)
@@ -240,9 +264,6 @@ const data = reactive({
],
eventDate: [
{ required: true, message: "请选择事件日期", trigger: "change" }
],
comment: [
{ required: true, message: "请输入备注", trigger: "blur" }
]
}
})
@@ -417,7 +438,6 @@ function fetchOldTag() {
if (!earNumber || earType === null) {
form.value.oldTag = '';
form.value.sheepId = null;
form.value.comment = '';
return;
}
@@ -429,20 +449,16 @@ function fetchOldTag() {
if (!sheep || !sheep.id) {
form.value.oldTag = '';
form.value.sheepId = null;
form.value.comment = '';
proxy.$modal.warning("未找到对应的羊只");
return;
}
form.value.oldTag = oldTag;
form.value.sheepId = sheep.id;
form.value.comment = sheep.comment || '';
}).catch(error => {
console.error("查询失败:", error);
form.value.oldTag = '';
form.value.sheepId = null;
form.value.comment = '';
proxy.$modal.error("查询耳号失败,请重试");
});
}
@@ -531,6 +547,7 @@ function removeEarNumber(tag) {
onMounted(() => {
getSheepfoldOptions();
getList();
fetchTechnicalList();
});
</script>

View File

@@ -67,8 +67,9 @@
</el-select>
</el-form-item> -->
<el-form-item label="技术员" prop="technician">
<el-input v-model="queryParams.technician" placeholder="请输入技术员" clearable style="max-width: 160px"
@keyup.enter="handleQuery" />
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -156,7 +157,7 @@
</el-button>
</div>
<el-form-item label="原品种" prop="varietyOld">
<el-input v-model="form.varietyOld" placeholder="请输入原品种" disabled />
<el-input v-model="form.varietyOld" placeholder="页面自动获取展示" disabled />
</el-form-item>
<el-form-item label="新品种" prop="varietyNew">
<el-select v-model="form.varietyNew" placeholder="请选择新品种" clearable filterable :disabled="!isAdd">
@@ -168,10 +169,13 @@
:disabled="!isAdd" />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" :disabled="!isAdd" />
<el-select v-model="form.technician" placeholder="请选择技术员" :disabled="!isAdd" clearable filterable
style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="comment" v-if="!isAdd">
<el-input v-model="form.comment" placeholder="请输入备注" :disabled="!isAdd" />
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" placeholder="请填写备注" type="textarea" rows="2" />
</el-form-item>
</el-form>
<template #footer>
@@ -191,8 +195,28 @@ import { listVariety } from '@/api/fileManagement/variety'
import dayjs from 'dayjs'
import { nextTick } from 'vue'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
const changeVarietyList = ref([])
const open = ref(false)
const loading = ref(true)
@@ -384,7 +408,7 @@ function submitForm() {
varietyNew: form.value.varietyNew,
eventDate: form.value.eventDate,
technician: form.value.technician,
comment: sheep.comment || ""
comment: form.value.comment || ''
}
return addChangeVariety(submitData)
})
@@ -513,7 +537,6 @@ async function onManageTagsBlur() {
if (!batchTags.value.length) { // 全部无效
form.value.varietyOld = ""
form.value.sheepId = null
form.value.comment = ""
return
}
@@ -524,7 +547,6 @@ async function onManageTagsBlur() {
const firstSheep = batchSheep.value[0]
form.value.varietyOld = firstSheep.varietyName || ""
form.value.sheepId = firstSheep.id
form.value.comment = firstSheep.comment || ""
}
// 删除单个批量耳号
@@ -551,6 +573,7 @@ function clearBatchTags() {
onMounted(() => {
loadVarietyOptions();
getSheepfoldOptions();
fetchTechnicalList();
getList();
});
</script>

View File

@@ -77,7 +77,8 @@
</el-select>
</el-form-item>
<el-form-item label="技术员">
<el-select v-model="queryParams.technician" filterable allow-create clearable placeholder="请输入或选择技术员">
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="min-width:150px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
@@ -95,15 +96,15 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"
v-hasPermi="['trans_group:trans_group:add']">新增</el-button>
v-hasPermi="['produce:trans_group:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPermi="['trans_group:trans_group:remove']">删除</el-button>
v-hasPermi="['produce:trans_group:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['trans_group:trans_group:export']">导出</el-button>
v-hasPermi="['produce:trans_group:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -129,7 +130,7 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="160" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['trans_group:trans_group:remove']">
v-hasPermi="['produce:trans_group:remove']">
删除
</el-button>
</template>
@@ -200,7 +201,12 @@
style="width: 100%" required />
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" />
<el-select v-model="form.technician" placeholder="请选择技术员" clearable filterable style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" placeholder="请填写备注" type="textarea" rows="2" />
</el-form-item>
</el-form>
<template #footer>
@@ -210,8 +216,6 @@
</div>
</template>
</el-dialog>
</div>
</template>
@@ -224,10 +228,29 @@ import request from '@/utils/request'
import dayjs from 'dayjs'
import { nextTick } from 'vue'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance();
const { trans_group_reason, trans_group_event_type } = proxy.useDict('trans_group_reason', 'status', 'trans_group_event_type');
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
const trans_groupList = ref([]);
const open = ref(false);
const loading = ref(true);
@@ -268,6 +291,7 @@ const data = reactive({
eventType: null,
transDate: null,
currentFoldAllEars: ref([]),
comment: null,
},
queryParams: {
pageNum: 1,
@@ -347,7 +371,9 @@ async function handleSheepFilter() {
let isOnlyTypeOrCombined = false;
if (!ranchId) {
form.value.manageTags = [];
form.value.manageTags = '';
batchTags.value = [];
batchSheep.value = [];
sheepOptions.value = [];
proxy.$message.warning("请先选择牧场");
return;
@@ -360,25 +386,33 @@ async function handleSheepFilter() {
} else if (foldFrom && sheepTypeId) {
isOnlyTypeOrCombined = true;
} else {
form.value.manageTags = [];
form.value.manageTags = '';
batchTags.value = [];
batchSheep.value = [];
sheepOptions.value = [];
return;
}
if (isOnlySheepfold) {
const list = await loadSheepBySheepfoldOnly(foldFrom, ranchId);
if (list.length === 0) {
const sheepList = await loadSheepBySheepfoldOnly(foldFrom, ranchId);
if (sheepList.length === 0) {
proxy.$message.info('当前转出羊舍暂无羊只');
form.value.manageTags = [];
form.value.manageTags = '';
batchTags.value = [];
batchSheep.value = [];
return;
}
form.value.manageTags = [...list];
await loadSheepInfo();
// 关键修改:填充 batchTags 和 batchSheep和手动输入保持一致
batchTags.value = sheepList.map(s => s.manageTags);
batchSheep.value = sheepList;
form.value.manageTags = batchTags.value.join(' ');
return;
}
if (isOnlyTypeOrCombined) {
form.value.manageTags = [];
form.value.manageTags = '';
batchTags.value = [];
batchSheep.value = [];
let loadedSheep = [];
if (foldFrom && sheepTypeId) {
@@ -388,9 +422,10 @@ async function handleSheepFilter() {
}
if (loadedSheep.length > 0) {
const list = loadedSheep.map(s => s.manageTags);
form.value.manageTags = [...list];
await loadSheepInfo();
// 关键修改:填充 batchTags 和 batchSheep和手动输入保持一致
batchTags.value = loadedSheep.map(s => s.manageTags);
batchSheep.value = loadedSheep;
form.value.manageTags = batchTags.value.join(' ');
} else {
proxy.$message.info("未查询到符合条件的耳号");
}
@@ -455,6 +490,8 @@ async function loadSheepBySheepfoldOnly(sheepfoldId, ranchId) {
id: sheep.id,
manageTags: sheep.manageTags || '未知耳号',
sheepfoldId: Number(sheepfoldId),
sheepTypeId: sheep.typeId, // 确保有 typeId
varietyId: sheep.varietyId, // 确保有 varietyId
})).filter(item => item.manageTags);
if (newSheepList.length === 0) {
@@ -463,8 +500,7 @@ async function loadSheepBySheepfoldOnly(sheepfoldId, ranchId) {
return [];
}
sheepOptions.value = newSheepList;
return newSheepList.map(s => s.manageTags);
return newSheepList; // 返回完整对象数组,不只是 manageTags
} catch (error) {
console.error('根据羊舍加载耳号失败:', error);
const errorMsg = error.response?.data?.msg || '加载羊舍耳号失败,请重试';
@@ -487,6 +523,8 @@ async function loadSheepBySheepfoldAndType(sheepfoldId, typeId) {
id: sheep.id,
manageTags: sheep.manageTags,
sheepfoldId: sheepfoldId,
sheepTypeId: sheep.typeId || typeId, // 确保有 typeId
varietyId: sheep.varietyId, // 确保有 varietyId
}));
sheepOptions.value = formattedSheep;
@@ -517,7 +555,9 @@ async function loadSheepByTypeOnly(typeId, ranchId) {
const formattedSheep = filtered.map(s => ({
id: s.id,
manageTags: s.manageTags,
sheepfoldId: s.sheepfoldId
sheepfoldId: s.sheepfoldId,
sheepTypeId: s.typeId || typeId, // 确保有 typeId
varietyId: s.varietyId, // 确保有 varietyId
}));
sheepOptions.value = formattedSheep;
return formattedSheep;
@@ -528,6 +568,7 @@ async function loadSheepByTypeOnly(typeId, ranchId) {
}
}
//加载羊只信息
async function loadSheepInfo() {
const tags = form.value.manageTags;
@@ -642,7 +683,8 @@ async function handleUpdate(row) {
reason: Number(d.reason || 0),
status: Number(d.status || 0),
varietyId: Number(d.varietyId || 0),
isEdit: true
isEdit: true,
comment: d.comment || '',
};
form.value = formData;
@@ -692,7 +734,7 @@ function submitForm() {
reason: form.value.reason,
transDate: form.value.transDate,
technician: form.value.technician,
comment: sheep.comment || ''
comment: form.value.comment || ''
})
})
).then(() => {
@@ -859,18 +901,18 @@ function removeBatchTag(idx) {
form.value.manageTags = batchTags.value.join(' ')
}
// 一键清空批量耳号(和改备注一致)
// 一键清空批量耳号
function clearBatchTags() {
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
form.value.oldComment = ''
}
onMounted(() => {
loadSheepfold();
loadRanchList();
getVarietyOptions();
loadSheepTypeList();
fetchTechnicalList();
getList();
});
</script>

View File

@@ -90,10 +90,8 @@
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-select v-model="queryParams.technician" filterable allow-create clearable placeholder="请输入或选择技术员"
style="min-width:150px">
<!-- 如果有字典可以放开下面这行没有就纯输入 -->
<!-- <el-option v-for="item in technicianOptions" :key="item" :label="item" :value="item" /> -->
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="min-width:150px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
@@ -112,7 +110,7 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"
v-hasPermi="['transition_info:transition_info:add']">新增</el-button>
v-hasPermi="['produce:transition_info:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
@@ -120,11 +118,11 @@
</el-col> -->
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPermi="['transition_info:transition_info:remove']">删除</el-button>
v-hasPermi="['produce:transition_info:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['transition_info:transition_info:export']">导出</el-button>
v-hasPermi="['produce:transition_info:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@@ -167,9 +165,9 @@
<el-table-column label="操作" align="center" min-width="180" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleApprove(scope.row)"
v-hasPermi="['transition_info:transition_info:approve']" :disabled="scope.row.status !== 0">审批</el-button>
v-hasPermi="['produce:transition_info:approve']" :disabled="scope.row.status !== 0">审批</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['transition_info:transition_info:remove']">删除</el-button>
v-hasPermi="['produce:transition_info:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -231,7 +229,13 @@
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" />
<el-select v-model="form.technician" placeholder="请选择技术员" clearable filterable style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" placeholder="请填写备注" type="textarea" rows="2" />
</el-form-item>
</el-form>
@@ -296,10 +300,29 @@ import { listTransition_info, getTransition_info, delTransition_info, addTransit
import { checkSheepByManageTags, getSheepBySheepfoldId } from '@/api/produce/other/fixHoof'
import request from '@/utils/request'
import dayjs from 'dayjs'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
const { status, trans_type } = proxy.useDict('status', 'trans_type')
// 技术员下拉选项
const technicalOptions = ref([])
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
const transition_infoList = ref([])
const open = ref(false)
const loading = ref(true)
@@ -341,6 +364,7 @@ const data = reactive({
transType: null,
transitionDate: null,
technician: null,
comment: null,
tagDetails: {}
},
queryParams: {
@@ -415,6 +439,7 @@ function reset() {
transType: null,
technician: null,
createBy: null,
comment: null,
tagDetails: {}
};
batchTags.value = [];
@@ -667,7 +692,8 @@ async function handleUpdate(row) {
getTransition_info(_id).then(response => {
form.value = {
...response.data,
manageTags: [response.data.manageTags]
manageTags: [response.data.manageTags],
comment: response.data.comment || ''
};
open.value = true;
title.value = "修改转场";
@@ -700,7 +726,7 @@ async function submitForm() {
eventType: form.value.eventType,
transitionDate: form.value.transitionDate,
technician: form.value.technician,
comment: batchSheep.value[idx].comment || '',
comment: form.value.comment || '',
status: 0,
}))
@@ -746,7 +772,9 @@ function searchEarNumber(query) {
async function handleRanchChange(ranchName) {
if (!ranchName) {
sheepOptions.value = [];
form.value.manageTags = [];
batchTags.value = []; // 清空
batchSheep.value = []; // 清空
form.value.manageTags = '';
return;
}
@@ -754,9 +782,23 @@ async function handleRanchChange(ranchName) {
if (!matched) return;
const res = await getSheepByRanchId(matched.id);
sheepOptions.value = (res.data || []).map(s => ({ id: s.id, manageTags: s.manageTags }));
form.value.manageTags = (res.data || []).map(s => s.manageTags);
await loadSheepInfo();
const sheepList = (res.data || []).map(s => ({
id: s.id,
manageTags: s.manageTags,
varietyId: s.varietyId, // 确保有品种ID
varietyName: s.varietyName || '', // 确保有品种名
ranchId: s.ranchId
}));
// 关键修改:填充 batchTags 和 batchSheep和手动输入保持一致
batchTags.value = sheepList.map(s => s.manageTags);
batchSheep.value = sheepList;
form.value.manageTags = batchTags.value.join(' ');
// 自动填充转出牧场(当前选中的牧场)
form.value.transFrom = ranchName;
sheepOptions.value = sheepList;
}
/* 粘贴事件 */
function handlePaste() {
@@ -888,6 +930,7 @@ onMounted(() => {
getVarietyOptions();
loadSheepOptions();
loadSheepTypeList();
fetchTechnicalList();
getList();
});
</script>

View File

@@ -41,7 +41,7 @@
</el-button>
</div>
</el-form-item>
<el-form-item label="羊舍" prop="sheepfold">
<!-- <el-form-item label="羊舍" prop="sheepfold">
<el-select v-model="queryParams.sheepfold" placeholder="请选择羊舍" style="min-width:150px" clearable>
<el-option v-for="item in sheepfoldOptions" :key="item.id" :label="item.sheepfoldName" :value="item.id" />
</el-select>
@@ -50,6 +50,11 @@
<el-select v-model="queryParams.varietyId" placeholder="请选择品种" style="min-width:150px" clearable>
<el-option v-for="item in varietyOptions" :key="item.id" :label="item.variety" :value="item.id" />
</el-select>
</el-form-item> -->
<el-form-item label="技术员" prop="technician">
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -89,15 +94,14 @@
<el-table v-loading="loading" :data="castrateList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="耳号" align="center" prop="manageTags" />
<el-table-column label="品种" align="center" prop="varietyName" />
<el-table-column label="事件类型" align="center" prop="eventType" width="120" />
<el-table-column label="事件日期" align="center" prop="eventDate" width="130">
<el-table-column label="去势日期" align="center" prop="eventDate" width="130">
<template #default="scope">
<span>{{ parseTime(scope.row.eventDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="羊舍" align="center" prop="sheepfoldName" />
<el-table-column label="品种" align="center" prop="varietyName" />
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="技术员" align="center" prop="technician" />
<el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
@@ -105,6 +109,7 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<!-- 按需添加 是否需要修改功能 -->
@@ -115,7 +120,6 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" :page-sizes="[20, 50, 100, 200, 500, 1000, 2000]" />
@@ -123,27 +127,35 @@
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="castrateRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="管理耳号" prop="manageTags">
<div style="display: flex; align-items: center; gap: 8px;">
<el-select v-model="form.manageTags" placeholder="请输入或选择耳号" multiple filterable allow-create
style="width: 100%; flex:1;" @change="loadSheepInfo" @blur="loadSheepInfo">
<el-option v-for="sheep in sheepOptions" :key="sheep.id" :label="sheep.manageTags"
:value="sheep.manageTags" />
</el-select>
<span v-if="form.manageTags?.length > 0" class="tag-count">
已选 {{ form.manageTags.length }}
</span>
</div>
<el-input v-model="form.manageTags" placeholder="请输入管理耳号" @blur="onManageTagsBlur" clearable />
</el-form-item>
<!-- 新增弹窗已选耳号展示区 -->
<div v-if="batchTags.length" class="selected-ear-numbers">
<el-tag v-for="(tag, idx) in batchTags" :key="tag" closable @close="removeBatchTag(idx)" style="margin:4px"
type="success">
{{ tag }}
</el-tag>
<el-button type="primary" link @click="clearBatchTags">
一键清空 ({{ batchTags.length }})
</el-button>
</div>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker v-model="form.eventDate" value-format="YYYY-MM-DD" type="date" placeholder="请选择事件日期" />
</el-form-item>
<el-form-item label="羊舍" prop="sheepfold">
<el-select v-model="form.sheepfold" placeholder="请选择羊舍" clearable @change="loadSheepBySheepfold">
<el-option v-for="fold in sheepfoldOptions" :key="fold.id" :label="fold.sheepfoldName" :value="fold.id" />
<el-select v-model="form.sheepfold" placeholder="请选择或输入羊舍" clearable filterable
:filter-method="filterSheepfold" @change="loadSheepBySheepfold">
<el-option v-for="fold in filteredSheepfoldOptions" :key="fold.id" :label="fold.sheepfoldName"
:value="fold.id" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" />
<el-select v-model="form.technician" placeholder="请选择技术员" clearable filterable style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker v-model="form.eventDate" value-format="YYYY-MM-DD" type="date" placeholder="请选择事件日期" />
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
@@ -161,6 +173,7 @@ import { listCastrate, getCastrate, delCastrate, addCastrate, searchEarNumbers }
import { checkSheepByManageTags, getVarietyOptions, getSheepBySheepfoldId } from '@/api/produce/other/fixHoof'
import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/sheepfold_management'
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { getUserByPost } from '@/api/common/user'
import { nextTick } from 'vue'
import dayjs from 'dayjs'
const { proxy } = getCurrentInstance()
@@ -175,9 +188,13 @@ const multiple = ref(true)
const total = ref(0)
const title = ref('')
const daterangeCreateTime = ref([])
const sheepOptions = ref([])
const filteredSheepfoldOptions = ref([])
const varietyOptions = ref([])
const daterangeEventDate = ref([])
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
// 技术员下拉选项
const technicalOptions = ref([])
const pasteText = ref('')
const isExpanded = ref(false)
const defaultShowCount = 2
@@ -194,6 +211,7 @@ const data = reactive({
technician: null,
tagDetails: {},
eventDate: null,
comment: null,
},
queryParams: {
pageNum: 1,
@@ -225,84 +243,81 @@ const data = reactive({
const { queryParams, form, rules } = toRefs(data)
const earOptions = ref([])
const earLoading = ref(false)
//通过羊舍获取羊只
// 羊舍变化处理:自动加载该羊舍下所有公羊(清空已有)
function loadSheepBySheepfold() {
const sheepfoldId = form.value.sheepfold;
const sheepfoldId = form.value.sheepfold
if (!sheepfoldId) {
sheepOptions.value = [];
return;
// 清空羊舍时,不清空耳号
return
}
// 清空已有耳号
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
getSheepBySheepfoldId(sheepfoldId)
.then(res => {
let sheepList = res.data || [];
sheepList = sheepList.filter(sheep => sheep.gender === 2);
let sheepList = res.data || []
sheepList = sheepList.filter(sheep => sheep.gender === 2)
if (sheepList.length === 0) {
proxy.$modal.msgInfo('该羊舍下暂无性别为公的羊只');
sheepOptions.value = [];
return;
proxy.$modal.msgInfo('该羊舍下暂无公羊')
return
}
sheepOptions.value = sheepList.map(sheep => ({
id: sheep.id,
manageTags: sheep.manageTags,
gender: sheep.gender
}));
// 填充该羊舍所有公羊
for (const sheep of sheepList) {
batchTags.value.push(sheep.manageTags)
batchSheep.value.push(sheep)
}
form.value.manageTags = batchTags.value.join(' ')
proxy.$modal.msgSuccess(`已加载 ${batchTags.value.length} 只公羊`)
})
.catch(error => {
console.error('加载羊舍耳号失败', error);
proxy.$modal.msgError('加载耳号失败,请重试');
sheepOptions.value = [];
});
console.error('加载羊舍耳号失败', error)
proxy.$modal.msgError('加载耳号失败,请重试')
})
}
// 输入耳号后校验并获取品种/羊舍等信息
async function loadSheepInfo() {
const tags = form.value.manageTags;
if (!tags || tags.length === 0) return;
// 输入耳号后校验并追加(不清空已有)
async function onManageTagsBlur() {
const raw = form.value.manageTags?.trim()
if (!raw) return
const tagDetails = {};
const validResults = [];
// 按分隔符拆分
const separators = /[\p{White_Space},]+/u
const newTags = [...new Set(raw.split(separators).filter(v => v))]
for (const tag of tags) {
try {
const { data: sheepData } = await checkSheepByManageTags(tag.trim());
// 过滤掉已存在的
const existTags = new Set(batchTags.value)
const tagsToAdd = newTags.filter(tag => !existTags.has(tag))
if (!sheepData) {
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 不存在`);
} else if (sheepData.gender !== 2) {
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 对应的羊只性别不是公,无法添加`);
} else {
validResults.push(true);
tagDetails[tag] = {
sheepId: sheepData.id,
gender: sheepData.gender,
sheepfoldId: sheepData.sheepfoldId,
varietyId: sheepData.varietyId,
comment: sheepData.comment || ''
};
if (validResults.length === 1) {
form.value.sheepfold = sheepData.sheepfoldId;
}
}
} catch (error) {
console.error(`耳号 ${tag} 校验失败`, error);
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 校验异常,请重试`);
if (tagsToAdd.length === 0) {
// 全部已存在,回写现有
form.value.manageTags = batchTags.value.join(' ')
return
}
// 校验并追加新耳号
for (const tag of tagsToAdd) {
const sheep = await checkSheepByManageTags(tag).then(r => r.data).catch(() => null)
if (!sheep) {
proxy.$modal.msgWarning(`耳号 ${tag} 不存在,已跳过`)
continue
}
if (Number(sheep.gender)) {
proxy.$modal.msgWarning(`耳号 ${tag} 不是公羊,已跳过`)
continue
}
batchTags.value.push(tag)
batchSheep.value.push(sheep)
}
form.value.manageTags = tags.filter((_, index) => validResults[index]);
form.value.tagDetails = tagDetails;
if (validResults.includes(false)) {
proxy.$message.warning('部分耳号不符合条件(不存在或非公羊),已自动过滤');
}
// 回写所有耳号
form.value.manageTags = batchTags.value.join(' ')
}
/** 查询列表 */
function getList() {
loading.value = true;
@@ -319,7 +334,9 @@ function getList() {
q.params.beginEventDate = daterangeEventDate.value[0];
q.params.endEventDate = daterangeEventDate.value[1];
}
if (q.technician) {
q.params.technician = q.technician;
}
listCastrate(q).then(res => {
castrateList.value = res.rows;
console.log("后端返回的列表数据:", res.rows);
@@ -342,13 +359,16 @@ function cancel() {
function reset() {
form.value = {
id: null,
manageTags: [],
manageTags: null,
sheepfold: null,
technician: null,
eventDate: null
};
sheepOptions.value = [];
proxy.resetForm('castrateRef');
eventDate: null,
comment: null
}
batchTags.value = []
batchSheep.value = []
filteredSheepfoldOptions.value = sheepfoldOptions.value
proxy.resetForm('castrateRef')
}
//搜索
@@ -362,6 +382,7 @@ function resetQuery() {
daterangeCreateTime.value = []
daterangeEventDate.value = []
queryParams.value.manageTagsList = []
queryParams.value.technician = null
queryParams.value.isDelete = null
proxy.resetForm('queryRef')
handleQuery()
@@ -428,49 +449,52 @@ async function validateAllTags() {
//提交
function submitForm() {
proxy.$refs.castrateRef.validate(valid => {
if (!valid) return;
if (batchTags.value.length === 0) {
proxy.$modal.msgError('请输入至少一个有效耳号')
return
}
if (!form.value.technician) {
proxy.$modal.msgError('请输入技术员')
return
}
if (!form.value.eventDate) {
proxy.$modal.msgError('请选择事件日期')
return
}
const allTags = form.value.manageTags;
const tagDetails = form.value.tagDetails || {};
if (!allTags || allTags.length === 0) {
proxy.$modal.msgError('请选择有效的公羊耳号');
return;
}
const invalidTags = allTags.filter(tag => {
const details = tagDetails[tag];
return !details || details.gender !== 2;
});
if (invalidTags.length > 0) {
proxy.$modal.msgError(`耳号 ${invalidTags.join(',')} 性别不符合要求,无法提交`);
return;
}
const requests = allTags.map(tag => {
const details = tagDetails[tag];
return addCastrate({
// 批量新增
Promise.all(
batchTags.value.map((tag, idx) =>
addCastrate({
sheepId: batchSheep.value[idx].id,
manageTags: tag,
sheepId: details.sheepId,
sheepfold: details.sheepfoldId || form.value.sheepfold,
sheepfold: batchSheep.value[idx].sheepfoldId,
varietyId: batchSheep.value[idx].varietyId,
technician: form.value.technician,
varietyId: details.varietyId,
comment: details.comment,
eventDate: form.value.eventDate
});
});
Promise.all(requests)
.then(() => {
proxy.$modal.msgSuccess('新增成功');
open.value = false;
getList();
eventDate: form.value.eventDate,
comment: form.value.comment || ''
})
.catch(error => {
proxy.$modal.msgError(`新增失败:${error.message}`);
});
});
)
).then(() => {
proxy.$modal.msgSuccess(`成功新增 ${batchTags.value.length} 条去势记录`)
open.value = false
getList()
}).catch(() => { })
}
/* 删除单个批量耳号 */
function removeBatchTag(idx) {
batchTags.value.splice(idx, 1)
batchSheep.value.splice(idx, 1)
form.value.manageTags = batchTags.value.join(' ')
// 不再自动更新羊舍
}
/* 一键清空批量耳号 */
function clearBatchTags() {
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
}
//删除
@@ -488,14 +512,41 @@ function handleDelete(row) {
function handleExport() {
proxy.download('/produce/other/castrate/export', { ...queryParams.value }, `castrate_${new Date().getTime()}.xlsx`)
}
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
//加载羊舍数据
const sheepfoldOptions = ref([])
function getSheepfoldOptions() {
listSheepfold({ pageNum: 1, pageSize: 9999 }).then(res => {
sheepfoldOptions.value = res.rows
filteredSheepfoldOptions.value = res.rows
})
}
// 羊舍模糊过滤
function filterSheepfold(query) {
if (!query) {
filteredSheepfoldOptions.value = sheepfoldOptions.value
return
}
filteredSheepfoldOptions.value = sheepfoldOptions.value.filter(fold =>
fold.sheepfoldName.includes(query)
)
}
function searchEarNumber(query) {
if (!query) { earOptions.value = []; return }
earLoading.value = true
@@ -546,6 +597,7 @@ function handleEarChange(val) {
onMounted(() => {
getSheepfoldOptions()
getVarietyOptions()
fetchTechnicalList()
getList()
})
</script>

View File

@@ -40,7 +40,7 @@
</el-button>
</div>
</el-form-item>
<el-form-item label="羊舍" prop="sheepfold">
<!-- <el-form-item label="羊舍" prop="sheepfold">
<el-select v-model="queryParams.sheepfold" placeholder="请选择羊舍" style="min-width:150px" clearable>
<el-option v-for="item in sheepfoldOptions" :key="item.id" :label="item.sheepfoldName" :value="item.id" />
</el-select>
@@ -49,6 +49,11 @@
<el-select v-model="queryParams.varietyId" placeholder="请选择品种" style="min-width:150px" clearable>
<el-option v-for="item in varietyOptions" :key="item.id" :label="item.variety" :value="item.id" />
</el-select>
</el-form-item> -->
<el-form-item label="技术员" prop="technician">
<el-select v-model="queryParams.technician" placeholder="请选择技术员" clearable filterable style="max-width: 160px">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否在群" prop="isDelete">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
@@ -66,20 +71,20 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"
v-hasPermi="['fixHoof:fixHoof:add']">新增</el-button>
v-hasPermi="['produce:fixHoof:add']">新增</el-button>
</el-col>
<!-- 按需添加 是否需要修改功能 -->
<!-- <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
v-hasPermi="['fixHoof:fixHoof:edit']">修改</el-button>
v-hasPermi="['produce:fixHoof:edit']">修改</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPermi="['fixHoof:fixHoof:remove']">删除</el-button>
v-hasPermi="['produce:fixHoof:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['fixHoof:fixHoof:export']">导出</el-button>
v-hasPermi="['produce:fixHoof:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
@@ -88,15 +93,14 @@
<el-table v-loading="loading" :data="fixHoofList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="耳号" align="center" prop="manageTags" />
<el-table-column label="品种" align="center" prop="varietyName" />
<el-table-column label="事件类型" align="center" prop="eventType" width="120" />
<el-table-column label="事件日期" align="center" prop="eventDate" width="130">
<el-table-column label="修蹄日期" align="center" prop="eventDate" width="130">
<template #default="scope">
<span>{{ parseTime(scope.row.eventDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="羊舍" align="center" prop="sheepfoldName" />
<el-table-column label="品种" align="center" prop="varietyName" />
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="技术员" align="center" prop="technician" />
<el-table-column label="创建人" align="center" prop="createBy" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
@@ -104,13 +108,14 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="comment" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<!-- 按需添加 是否需要修改功能 -->
<!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
v-hasPermi="['fixHoof:fixHoof:edit']">修改</el-button> -->
v-hasPermi="['produce:fixHoof:edit']">修改</el-button> -->
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['fixHoof:fixHoof:remove']">删除</el-button>
v-hasPermi="['produce:fixHoof:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -121,30 +126,38 @@
<!-- 弹窗 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="fixHoofRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="耳号" prop="manageTags">
<div style="display: flex; align-items: center; gap: 8px;">
<el-select v-model="form.manageTags" placeholder="请输入或选择耳号" multiple filterable allow-create
style="width: 100%; flex:1;" @change="loadSheepInfo">
<el-option v-for="sheep in sheepOptions" :key="sheep.id" :label="sheep.manageTags"
:value="sheep.manageTags" />
</el-select>
<span v-if="form.manageTags?.length > 0" class="tag-count">
已选 {{ form.manageTags.length }}
</span>
</div>
</el-form-item>
<el-form-item label="羊舍" prop="sheepfolds">
<el-select v-model="form.sheepfold" filterable style="width: 100%" @change="loadSheepBySheepfold">
<el-option v-for="fold in sheepfoldOptions" :key="fold.id" :label="fold.sheepfoldName" :value="fold.id" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-input v-model="form.technician" placeholder="请输入技术员" />
<el-form-item label="管理耳号" prop="manageTags">
<el-input v-model="form.manageTags" placeholder="请输入管理耳号" @blur="onManageTagsBlur" clearable />
</el-form-item>
<!-- 新增弹窗已选耳号展示区 -->
<div v-if="batchTags.length" class="selected-ear-numbers">
<el-tag v-for="(tag, idx) in batchTags" :key="tag" closable @close="removeBatchTag(idx)" style="margin:4px"
type="success">
{{ tag }}
</el-tag>
<el-button type="primary" link @click="clearBatchTags">
一键清空 ({{ batchTags.length }})
</el-button>
</div>
<el-form-item label="事件日期" prop="eventDate">
<el-date-picker v-model="form.eventDate" value-format="YYYY-MM-DD" type="date" placeholder="请选择事件日期"
clearable />
</el-form-item>
<el-form-item label="羊舍" prop="sheepfold">
<el-select v-model="form.sheepfold" placeholder="请选择羊舍" clearable filterable :filter-method="filterSheepfold"
@change="loadSheepBySheepfold">
<el-option v-for="fold in filteredSheepfoldOptions" :key="fold.id" :label="fold.sheepfoldName"
:value="fold.id" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<el-select v-model="form.technician" placeholder="请选择技术员" clearable filterable style="width: 100%">
<el-option v-for="item in technicalOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="comment">
<el-input v-model="form.comment" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
@@ -162,6 +175,7 @@ import { listSheepfold_management as listSheepfold } from '@/api/fileManagement/
import { Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import { nextTick } from 'vue'
import dayjs from 'dayjs'
import { getUserByPost } from '@/api/common/user'
const { proxy } = getCurrentInstance()
const fixHoofList = ref([])
@@ -175,7 +189,11 @@ const total = ref(0)
const title = ref('')
const daterangeCreateTime = ref([])
const varietyOptions = ref([])
const sheepOptions = ref([])
const filteredSheepfoldOptions = ref([])
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
// 技术员下拉选项
const technicalOptions = ref([])
const daterangeEventDate = ref([])
const pasteText = ref('')
const isExpanded = ref(false)
@@ -207,9 +225,6 @@ const data = reactive({
manageTags: [
{ required: true, message: '请输入管理耳号', trigger: 'blur' }
],
sheepfold: [
{ required: true, message: '请选择羊舍', trigger: 'change' }
],
technician: [
{ required: true, message: '请输入技术员', trigger: 'blur' }
],
@@ -222,73 +237,79 @@ const data = reactive({
const { queryParams, form, rules } = toRefs(data)
const earOptions = ref([])
const earLoading = ref(false)
//根据羊舍获取羊只
// 羊舍变化处理:自动加载该羊舍下所有羊(清空已有)
function loadSheepBySheepfold() {
const currentSheepfoldId = form.value.sheepfold;
const sheepfoldId = form.value.sheepfold
if (!sheepfoldId) return
if (!currentSheepfoldId) {
sheepOptions.value = [];
return;
}
// 清空已有耳号
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
getSheepBySheepfoldId(currentSheepfoldId)
getSheepBySheepfoldId(sheepfoldId)
.then(res => {
const newSheepList = res.data || [];
sheepOptions.value = newSheepList.map(sheep => ({
id: sheep.id,
manageTags: sheep.manageTags,
sheepfoldId: currentSheepfoldId
}));
const sheepList = res.data || []
if (sheepList.length === 0) {
proxy.$modal.msgInfo('该羊舍下暂无羊只')
return
}
// 填充该羊舍所有羊
for (const sheep of sheepList) {
batchTags.value.push(sheep.manageTags)
batchSheep.value.push(sheep)
}
form.value.manageTags = batchTags.value.join(' ')
proxy.$modal.msgSuccess(`已加载 ${batchTags.value.length} 只羊`)
})
.catch(error => {
console.error('加载羊舍耳号失败', error);
proxy.$modal.msgError('切换羊舍失败,请重试');
sheepOptions.value = [];
});
console.error('加载羊舍耳号失败', error)
proxy.$modal.msgError('加载耳号失败,请重试')
})
}
async function loadSheepInfo() {
const tags = form.value.manageTags;
if (!tags || tags.length === 0) return;
function filterSheepfold(query) {
if (!query) {
filteredSheepfoldOptions.value = sheepfoldOptions.value
return
}
filteredSheepfoldOptions.value = sheepfoldOptions.value.filter(fold =>
fold.sheepfoldName.includes(query)
)
}
// 输入耳号后校验并追加(不清空已有)
async function onManageTagsBlur() {
const raw = form.value.manageTags?.trim()
if (!raw) return
const tagDetails = {};
const validResults = [];
const validTags = [];
// 按分隔符拆分
const separators = /[\p{White_Space},]+/u
const newTags = [...new Set(raw.split(separators).filter(v => v))]
for (const tag of tags) {
try {
const { data: sheepData } = await checkSheepByManageTags(tag.trim());
if (!sheepData) {
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 不存在`);
} else {
validResults.push(true);
validTags.push(tag);
tagDetails[tag] = {
comment: sheepData.comment || '',
varietyId: sheepData.varietyId,
sheepfoldId: sheepData.sheepfoldId,
sheepId: sheepData.id
};
}
} catch (error) {
console.error('获取耳号信息失败:', error);
proxy.$modal.msgError(`耳号 ${tag} 验证失败`);
validResults.push(false);
// 过滤掉已存在的
const existTags = new Set(batchTags.value)
const tagsToAdd = newTags.filter(tag => !existTags.has(tag))
if (tagsToAdd.length === 0) {
form.value.manageTags = batchTags.value.join(' ')
return
}
// 校验并追加新耳号
for (const tag of tagsToAdd) {
const sheep = await checkSheepByManageTags(tag).then(r => r.data).catch(() => null)
if (!sheep) {
proxy.$modal.msgWarning(`耳号 ${tag} 不存在,已跳过`)
continue
}
}
if (validResults.includes(false)) {
form.value.manageTags = tags.filter((_, index) => validResults[index]);
proxy.$message.warning('部分耳号不合法,已过滤');
batchTags.value.push(tag)
batchSheep.value.push(sheep)
}
form.value.tagDetails = tagDetails;
if (validTags.length > 0) {
const firstValidTag = validTags[0];
const firstSheepfoldId = tagDetails[firstValidTag].sheepfoldId;
form.value.sheepfold = firstSheepfoldId;
}
// 回写所有耳号
form.value.manageTags = batchTags.value.join(' ')
}
//获取修蹄列表
@@ -307,7 +328,9 @@ function getList() {
q.params.beginEventDate = daterangeEventDate.value[0]
q.params.endEventDate = daterangeEventDate.value[1]
}
if (q.technician) {
q.params.technician = q.technician
}
listFixHoof(q).then(res => {
fixHoofList.value = res.rows
total.value = res.total
@@ -328,13 +351,15 @@ function cancel() {
function reset() {
form.value = {
id: null,
manageTags: '',
sheepfold: null,
manageTags: [],
varietyId: null,
varietyName: null,
eventDate: null
technician: null,
eventDate: null,
comment: null
}
sheepOptions.value = [];
batchTags.value = []
batchSheep.value = []
filteredSheepfoldOptions.value = sheepfoldOptions.value
proxy.resetForm('fixHoofRef')
}
@@ -350,6 +375,7 @@ function resetQuery() {
daterangeEventDate.value = []
queryParams.value.varietyId = null
queryParams.value.manageTagsList = []
queryParams.value.technician = null
queryParams.value.isDelete = null
proxy.resetForm('queryRef')
handleQuery()
@@ -384,40 +410,53 @@ function handleUpdate(row) {
//提交
function submitForm() {
proxy.$refs.fixHoofRef.validate(valid => {
if (!valid) return;
if (batchTags.value.length === 0) {
proxy.$modal.msgError('请输入至少一个有效耳号')
return
}
if (!form.value.technician) {
proxy.$modal.msgError('请选择技术员')
return
}
if (!form.value.eventDate) {
proxy.$modal.msgError('请选择事件日期')
return
}
const allTags = form.value.manageTags;
const tagDetails = form.value.tagDetails || {};
if (!allTags || allTags.length === 0) {
proxy.$modal.msgError('请选择至少一个耳号');
return;
}
const fixHoofList = batchTags.value.map((tag, idx) => ({
manageTags: tag,
sheepId: batchSheep.value[idx].id,
sheepfold: batchSheep.value[idx].sheepfoldId,
varietyId: batchSheep.value[idx].varietyId,
technician: form.value.technician,
eventDate: form.value.eventDate,
comment: form.value.comment || ''
}))
const fixHoofList = allTags.map(tag => {
const details = tagDetails[tag] || {};
return {
...form.value,
manageTags: tag,
comment: details.comment || '',
sheepId: details.sheepId,
varietyId: details.varietyId,
sheepfold: details.sheepfoldId || form.value.sheepfold
};
});
addFixHoof(fixHoofList)
.then(() => {
proxy.$modal.msgSuccess('操作成功');
open.value = false;
getList();
})
.catch(error => {
proxy.$modal.msgError(`处理失败:${error.message}`);
});
});
addFixHoof(fixHoofList)
.then(() => {
proxy.$modal.msgSuccess(`成功新增 ${batchTags.value.length} 条修蹄记录`)
open.value = false
getList()
})
.catch(error => {
proxy.$modal.msgError(`处理失败:${error.message}`)
})
}
/* 删除单个批量耳号 */
function removeBatchTag(idx) {
batchTags.value.splice(idx, 1)
batchSheep.value.splice(idx, 1)
form.value.manageTags = batchTags.value.join(' ')
}
/* 一键清空批量耳号 */
function clearBatchTags() {
batchTags.value = []
batchSheep.value = []
form.value.manageTags = ''
form.value.sheepfold = null
}
//删除
function handleDelete(row) {
const _ids = row.id || ids.value
@@ -439,8 +478,26 @@ const sheepfoldOptions = ref([])
function getSheepfoldOptions() {
listSheepfold({ pageNum: 1, pageSize: 9999 }).then(res => {
sheepfoldOptions.value = res.rows
filteredSheepfoldOptions.value = res.rows
})
}
// 获取技术员列表岗位编码techs
const fetchTechnicalList = () => {
getUserByPost({ postCode: "techs" })
.then(res => {
if (res.code === 200 && Array.isArray(res.data)) {
technicalOptions.value = res.data.map(item => ({
value: item.nickName,
label: item.nickName
}))
} else {
technicalOptions.value = []
}
})
.catch(() => {
technicalOptions.value = []
})
}
function searchEarNumber(query) {
if (!query) { earOptions.value = []; return }
earLoading.value = true
@@ -490,6 +547,7 @@ function handleEarChange(val) {
onMounted(() => {
getSheepfoldOptions()
getVarietyOptions()
fetchTechnicalList()
getList()
})
</script>