Files
zhyc-sheep-ui/src/views/produce/manage_sheep/transition_info/index.vue
2026-03-05 21:39:44 +08:00

992 lines
34 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container"
style="height: calc(100vh - 84px); display: flex; flex-direction: column; overflow: hidden;">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="转场日期" style="width: 308px">
<el-date-picker v-model="transitionDateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item label="管理耳号" prop="manageTagsList">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
<!-- 主选择器使用 collapse-tags 显示 +n -->
<el-select v-model="queryParams.manageTagsList" multiple filterable remote reserve-keyword
placeholder="输入耳号搜索" :remote-method="searchEarNumber" :loading="earLoading" allow-create
default-first-option collapse-tags :max-collapse-tags="0" style="width:300px" @change="handleEarChange">
<el-option v-for="item in earOptions" :key="item" :label="item" :value="item" />
</el-select>
<!-- 批量粘贴框 -->
<el-input v-model="pasteText" 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.manageTagsList && queryParams.manageTagsList.length" type="info" effect="plain"
size="large">
已选: {{ queryParams.manageTagsList.length }}
</el-tag>
<!-- 一键清空 -->
<el-button type="danger" plain :icon="Delete" @click="clearEarNumbers"
v-if="queryParams.manageTagsList && queryParams.manageTagsList.length">
清空全部
</el-button>
</div>
<!-- 已选耳号展示区可折叠 -->
<div v-if="queryParams.manageTagsList && queryParams.manageTagsList.length" class="selected-ear-numbers">
<el-tag v-for="tag in displayedEarTags" :key="tag" closable @close="removeEarNumber(tag)" style="margin:4px"
type="success">
{{ tag }}
</el-tag>
<el-button v-if="queryParams.manageTagsList.length > defaultShowCount" type="primary" link
@click="toggleExpand">
{{ isExpanded ? '收起' : `展开剩余 ${queryParams.manageTagsList.length - defaultShowCount} ` }}
<el-icon class="el-icon--right">
<component :is="isExpanded ? ArrowUp : ArrowDown" />
</el-icon>
</el-button>
</div>
</el-form-item>
<el-form-item label="品种" prop="varietyId">
<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="sheepTypeId">
<el-select v-model="queryParams.sheepTypeId" placeholder="请选择羊只类型" clearable style="min-width:150px">
<el-option v-for="type in sheepTypeList" :key="type.id" :label="type.name" :value="type.id" />
</el-select>
</el-form-item>
<el-form-item label="转场类型" prop="transType" style="width: 308px">
<el-select v-model="queryParams.transType" placeholder="请选择转场类型" clearable style="min-width:150px">
<el-option label="内部调拨" :value="0" />
<el-option label="内部销售" :value="1" />
<el-option label="育肥调拨" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="转入牧场" prop="transTo">
<el-select v-model="queryParams.transTo" placeholder="请选择转入牧场" clearable style="min-width:150px">
<el-option v-for="ranch in ranchOptions" :key="ranch.id" :label="ranch.ranchName" :value="ranch.ranchName" />
</el-select>
</el-form-item>
<!-- <el-form-item label="转出牧场" prop="transFrom">
<el-select v-model="queryParams.transFrom" placeholder="请选择转出牧场" clearable style="min-width:150px">
<el-option v-for="ranch in ranchOptions" :key="ranch.id" :label="ranch.ranchName" :value="ranch.ranchName" />
</el-select>
</el-form-item> -->
<el-form-item label="当前场区" prop="currentRanchId">
<el-select v-model="queryParams.currentRanchId" placeholder="请选择当前场区" clearable style="min-width:150px">
<el-option v-for="ranch in ranchOptions" :key="ranch.id" :label="ranch.ranchName" :value="ranch.id" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" style="width: 150px" placeholder="请选择状态" clearable>
<el-option v-for="dict in status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="技术员" prop="technician">
<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">
<el-select v-model="queryParams.isDelete" placeholder="全部" clearable style="min-width:120px">
<el-option label="全部" value="" />
<el-option label="在群" :value="0" />
<el-option label="离群" :value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> -->
</el-form-item>
</el-form>
<!-- 按钮行 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd"
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"
v-hasPermi="['transition_info:transition_info:edit']">修改</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
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="['produce:transition_info:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<!-- 列表 -->
<div style="flex: 1; overflow: hidden;">
<el-table v-loading="loading" :data="transition_infoList" @selection-change="handleSelectionChange" height="100%"
style="width: 100%">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="管理耳号" align="center" prop="manageTags" min-width="100" fixed />
<el-table-column label="品种" align="center" prop="varietyName" />
<el-table-column label="事件类型" align="center" prop="eventType" min-width="120" />
<el-table-column label="转场日期" align="center" prop="transitionDate" min-width="150">
<template #default="scope">
<span>{{ parseTime(scope.row.transitionDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="转场类型" align="center" prop="transTypeText" />
<el-table-column label="转入牧场" align="center" prop="transTo" min-width="120" />
<el-table-column label="转出牧场" align="center" prop="transFrom" />
<el-table-column label="羊舍" align="center" prop="sheepfoldName" min-width="120" />
<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="130">
<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" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="status" :value="scope.row.status" />
</template>
</el-table-column>
<!-- <el-table-column label="当前状态" align="center" prop="status" min-width="120">
<template #default="scope">
<span v-if="scope.row.status === 0">待审批</span>
<span v-else-if="scope.row.status === 1 || scope.row.status === 2">审批完成</span>
<span v-else>未知状态</span>
</template>
</el-table-column> -->
<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="['produce:transition_info:approve']" :disabled="scope.row.status !== 0">审批</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['produce:transition_info:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div style="flex-shrink: 0; padding: 10px 0;">
<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]" />
</div>
<!-- 新增/修改 -->
<el-dialog :title="title" v-model="open" width="700px" append-to-body>
<el-form ref="transition_infoRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="管理耳号" prop="manageTags">
<el-input v-model="form.manageTags" placeholder="请输入管理耳号,支持批量输入(空格/换行/逗号分隔)" @blur="onManageTagsBlur"
:disabled="!isAdd" />
</el-form-item>
<!-- 新增弹窗已选耳号展示区和改备注转群完全一致 -->
<div v-if="batchTags.length" class="selected-ear-numbers" style="margin-left: 80px; margin-bottom: 18px;">
<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="transTo">
<el-select v-model="form.transTo" placeholder="请选择转入牧场" clearable :disabled="!isAdd">
<el-option v-for="ranch in ranchOptions" :key="ranch.id" :label="ranch.ranchName"
:value="ranch.ranchName" />
</el-select>
</el-form-item>
<el-form-item label="转出牧场" prop="transFrom">
<el-select v-model="form.transFrom" placeholder="请选择转出牧场" clearable @change="handleRanchChange"
:disabled="!isAdd">
<el-option v-for="ranch in ranchOptions" :key="ranch.id" :label="ranch.ranchName"
:value="ranch.ranchName" />
</el-select>
</el-form-item>
<el-form-item label="事件类型" prop="eventType">
<el-select v-model="form.eventType" placeholder="请选择事件类型">
<el-option label="转场转入" value="转场转入" />
<el-option label="转场转出" value="转场转出" />
</el-select>
</el-form-item>
<el-form-item label="转场类型" prop="transType">
<el-select v-model="form.transType" placeholder="请选择转场类型">
<el-option v-for="dict in trans_type" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="转场日期" prop="transitionDate">
<el-date-picker v-model="form.transitionDate" value-format="YYYY-MM-DD" type="date" placeholder="请选择转场日期"
:disabled="!isAdd"></el-date-picker>
</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" placeholder="请填写备注" type="textarea" rows="2" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 审批对话框 -->
<el-dialog :title="'审批转场记录'" v-model="approveDialog" width="500px" append-to-body>
<el-form ref="approveFormRef" :model="approveForm" label-width="120px">
<el-form-item label="管理耳号">
<el-input v-model="displayManageTags" disabled />
</el-form-item>
<el-form-item label="品种">
<el-input :value="approveForm.varietyName" disabled />
</el-form-item>
<el-form-item label="事件类型">
<el-input :value="approveForm.eventType" disabled />
</el-form-item>
<el-form-item label="转入牧场">
<el-input :value="approveForm.transTo" disabled />
</el-form-item>
<el-form-item label="转出牧场">
<el-input :value="approveForm.transFrom" disabled />
</el-form-item>
<el-form-item label="转场类型">
<el-input :value="approveForm.transTypeText || '未知类型'" disabled />
</el-form-item>
<el-form-item label="技术员">
<el-input v-model="approveForm.technician" disabled />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="approveForm.comment" disabled />
</el-form-item>
<el-form-item label="状态">
<el-input :value="getStatusLabel(approveForm.status)" disabled />
</el-form-item>
<el-form-item label="转入羊舍" prop="sheepfoldId">
<el-select v-model="approveForm.sheepfoldId" placeholder="请输入或选择转入羊舍" filterable clearable allow-create
default-first-option @change="handleSheepfoldInputChange" style="width: 100%;">
<el-option v-for="sf in acceptSheepfold" :key="sf.id" :label="sf.sheepfoldName" :value="sf.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleApproveAgree">同意</el-button>
<el-button type="danger" @click="handleApproveReject">驳回</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Transition_info">
import { listTransition_info, getTransition_info, delTransition_info, addTransition_info, getSheepByRanchId, updateTransition_info, listRanch, approveTransitionInfo, searchEarNumbers } from "@/api/produce/manage_sheep/transition_info"
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)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref('')
const transitionDateRange = ref([])
const approveDialog = ref(false)
const approveForm = ref({})
const displayManageTags = ref('')
const varietyOptions = ref([])
const ranchOptions = ref([])
const sheepOptions = ref([])
const isAdd = ref(true)
const isEdit = ref(false)
const acceptSheepfold = ref([])
const sheepTypeList = ref([])
const pasteText = ref('') // 批量粘贴输入框
const isExpanded = ref(false) // 折叠状态
const defaultShowCount = 2 // 默认展示条数
const batchTags = ref([]) // 本次要新增的多条耳号
const batchSheep = ref([]) // 对应的羊只对象
// 计算属性:控制实际展示的耳号列表
const displayedEarTags = computed(() => {
const list = queryParams.value.manageTagsList || []
if (isExpanded.value || list.length <= defaultShowCount) return list
return list.slice(0, defaultShowCount)
})
const data = reactive({
form: {
id: null,
manageTags: [],
transTo: null,
transFrom: null,
eventType: null,
transType: null,
transitionDate: null,
technician: null,
comment: null,
tagDetails: {}
},
queryParams: {
pageNum: 1,
pageSize: 20,
manageTags: [],
varietyId: null,
transType: null,
transTo: null,
transFrom: null,
status: null,
transitionDate: null,
technician: null,
isDelete: null,
sheepTypeId: null,
currentRanchId: null
},
rules: {
manageTags: [
{ required: true, message: "请输入管理耳号", trigger: "blur" }
],
transTo: [
{ required: true, message: "请输入转入牧场", trigger: "blur" }
],
transFrom: [
{ required: false, message: "请选择转出牧场", trigger: "blur" }
],
eventType: [
{ required: true, message: '请选择事件类型', trigger: 'change' }
],
transType: [
{ required: true, message: "请选择转场类型", trigger: "change" }
],
transitionDate: [
{ required: true, message: "请选择转场日期", trigger: "change" }
],
technician: [
{ required: true, message: "请输入技术员", trigger: "blur" }
]
}
})
const { queryParams, form, rules } = toRefs(data)
const earOptions = ref([])
const earLoading = ref(false)
//获取转场记录列表
function getList() {
loading.value = true
queryParams.value.params = {}
if (queryParams.value.transType !== null) {
queryParams.value.params.transType = queryParams.value.transType;
}
if (queryParams.value.currentRanchId !== null) {
queryParams.value.params.currentRanchId = queryParams.value.currentRanchId
}
if (transitionDateRange.value && transitionDateRange.value.length > 0) {
queryParams.value.params["beginTransitionDate"] = transitionDateRange.value[0];
queryParams.value.params["endTransitionDate"] = transitionDateRange.value[1];
}
listTransition_info(queryParams.value).then(res => {
transition_infoList.value = res.rows
total.value = res.total
loading.value = false
})
}
//重置
function reset() {
form.value = {
id: null,
manageTags: '',
transTo: null,
transFrom: null,
transType: null,
technician: null,
createBy: null,
comment: null,
tagDetails: {}
};
batchTags.value = [];
batchSheep.value = [];
sheepOptions.value = [];
proxy.resetForm("transition_infoRef");
isAdd.value = true;
isEdit.value = false;
}
// 加载牧场列表
function loadRanchOptions() {
return listRanch()
.then((res) => {
if (res.rows && Array.isArray(res.rows)) {
const mapped = res.rows.map((ranch) => ({
id: Number(ranch.id),
ranchName: ranch.ranch || '未知牧场',
}));
ranchOptions.value = mapped;
} else {
}
})
.catch(() => {
proxy.$modal.msgError('获取牧场数据失败');
});
}
// 加载耳号选项
function loadSheepOptions() {
getSheepBySheepfoldId().then(res => {
sheepOptions.value = res.data?.map(sheep => ({
id: sheep.id,
manageTags: sheep.manageTags
})) || [];
}).catch(() => {
proxy.$modal.msgError('加载耳号选项失败,请重试');
});
}
//加载转入牧场下的羊舍
function loadSheepfoldsByRanch(ranchName) {
const matchedRanch = ranchOptions.value.find(
ranch => ranch.ranchName === ranchName
);
if (!matchedRanch) {
proxy.$modal.msgWarning(`未找到牧场“${ranchName}”的信息`);
acceptSheepfold.value = [];
return;
}
const ranchId = matchedRanch.id;
request({
url: '/sheepfold_management/sheepfold_management/list',
method: 'get',
params: {
ranchId: ranchId,
pageNum: 1,
pageSize: 999
}
}).then(res => {
acceptSheepfold.value = res.rows || [];
if (acceptSheepfold.value.length === 0) {
proxy.$modal.msgWarning(`转入牧场“${ranchName}”没有可用的羊舍`);
}
}).catch(() => {
proxy.$modal.msgError('获取转入羊舍数据失败');
});
}
async function getRanchNameByRanchId(ranchId) {
if (!ranchId) return '';
try {
if (ranchOptions.value.length === 0) {
await loadRanchOptions();
}
const matchedRanch = ranchOptions.value.find(
(item) => item.id === Number(ranchId)
);
return matchedRanch ? matchedRanch.ranchName : '';
} catch (error) {
return '';
}
}
async function loadSheepInfo() {
if (isEdit.value) {
return;
}
const tags = form.value.manageTags;
if (!tags || tags.length === 0) return;
const tagDetails = {};
const validResults = [];
for (const tag of tags) {
try {
const { data: sheepData } = await checkSheepByManageTags(tag.trim());
if (!sheepData) {
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 不存在或已删除`);
continue;
}
const ranchName = await getRanchNameByRanchId(sheepData.ranchId);
tagDetails[tag] = {
sheepId: sheepData.id,
varietyId: sheepData.varietyId,
varietyName: sheepData.varietyName || '',
ranchName: ranchName,
comment: sheepData.comment || ''
};
validResults.push(true);
if (!form.value.transFrom && tags.indexOf(tag) === 0) {
form.value.transFrom = ranchName;
}
} catch (error) {
validResults.push(false);
proxy.$modal.msgError(`耳号 ${tag} 验证失败`);
}
}
if (validResults.includes(false)) {
form.value.manageTags = tags.filter((_, index) => validResults[index]);
proxy.$message.warning('部分耳号不合法,已过滤');
}
form.value.tagDetails = tagDetails;
}
// 导出
function handleExport() {
queryParams.value.ids = ids.value;
try {
proxy.download('/produce/manage_sheep/transition_info/export', { ...queryParams.value }, `转场记录${Date.now()}.xlsx`);
} finally {
queryParams.value.ids = null;
}
}
// 审批
function handleApprove(row) {
if (row.status !== 0) {
proxy.$modal.msgWarning("该记录已完成审批,无法重复操作");
return;
}
approveForm.value = { ...row };
approveDialog.value = true;
displayManageTags.value = Array.isArray(row.manageTags)
? row.manageTags.join(',')
: row.manageTags || '';
loadSheepfoldsByRanch(row.transTo);
}
function handleApproveAgree() {
// ① 先置状态为“通过”
approveForm.value.status = 1;
// ② 再校验接收羊舍(仅同意时)
if (approveForm.value.eventType === "转场转入" && !approveForm.value.sheepfoldId) {
proxy.$modal.msgError('转场转入时,转入羊舍不能为空');
return;
}
// ③ 提交后台
approveTransitionInfo(approveForm.value).then(() => {
approveDialog.value = false;
proxy.$modal.msgSuccess('审批通过');
getList();
}).catch(error => {
proxy.$modal.msgError(`审批失败:${error.message}`);
});
}
function handleApproveReject() {
approveForm.value.status = 2;
approveForm.value.sheepfoldId = null;
approveTransitionInfo(approveForm.value).then(() => {
approveDialog.value = false;
proxy.$modal.msgSuccess('已驳回');
getList();
});
}
// 获取转场类型文本
function getTransTypeLabel(value) {
const transTypeArray = trans_type.value;
if (!Array.isArray(transTypeArray)) {
return '未知类型';
}
const item = transTypeArray.find(item => item.value === value);
return item ? item.label : '未知类型';
}
// 获取状态文本
function getStatusLabel(value) {
const statusArray = status.value;
if (!Array.isArray(statusArray)) {
return '未知状态';
}
const item = statusArray.find(item => item.value === value);
return item ? item.label : '未知状态';
}
//取消
function cancel() {
open.value = false
reset()
}
//搜索
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
//重置
function resetQuery() {
queryParams.value.transType = null;
transitionDateRange.value = [];
queryParams.value.manageTagsList = [];
queryParams.value.sheepTypeId = null;
queryParams.value.currentRanchId = null;
queryParams.value.isDelete = null;
queryParams.value.technician = null;
proxy.resetForm("queryRef");
handleQuery();
}
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
//新增
function handleAdd() {
reset();
open.value = true;
title.value = '添加转场';
isAdd.value = true;
isEdit.value = false;
form.value.transitionDate = dayjs().format('YYYY-MM-DD');
}
//修改
async function handleUpdate(row) {
reset();
const _id = row.id || ids.value;
getTransition_info(_id).then(response => {
form.value = {
...response.data,
manageTags: [response.data.manageTags],
comment: response.data.comment || ''
};
open.value = true;
title.value = "修改转场";
isAdd.value = false;
isEdit.value = true;
}).catch(error => {
proxy.$modal.msgError('加载数据失败,请重试');
});
}
//提交
async function submitForm() {
proxy.$refs.transition_infoRef.validate(async (valid) => {
if (!valid) return
if (batchTags.value.length === 0) {
proxy.$modal.msgError('请输入至少一个有效耳号')
return
}
// 批量构建提交数据
const submitData = batchTags.value.map((tag, idx) => ({
sheepId: batchSheep.value[idx].id,
varietyId: batchSheep.value[idx].varietyId,
varietyName: batchSheep.value[idx].varietyName || '',
manageTags: tag,
transTo: form.value.transTo,
transFrom: form.value.transFrom,
transType: form.value.transType,
eventType: form.value.eventType,
transitionDate: form.value.transitionDate,
technician: form.value.technician,
comment: form.value.comment || '',
status: 0,
}))
try {
await addTransition_info(submitData)
proxy.$modal.msgSuccess(`成功新增 ${batchTags.value.length} 条转场记录`)
open.value = false
getList()
} catch (error) {
proxy.$modal.msgError('提交失败,请重试')
}
})
}
//加载品种信息
function getVarietyOptions() {
request({
url: '/base/variety/list',
method: 'get',
params: { pageNum: 1, pageSize: 9999 },
}).then((res) => {
varietyOptions.value = res.rows || [];
});
}
//删除
function handleDelete(row) {
const _ids = row.id || ids.value
proxy.$modal.confirm('是否确认删除这条记录数据')
.then(() => delTransition_info(_ids))
.then(() => {
getList()
proxy.$modal.msgSuccess("删除成功")
})
}
// 搜索耳号的方法(用于下拉框远程搜索)
const searchEarNumber = async (query) => {
if (!query || query.trim() === '') {
earOptions.value = []
return
}
earLoading.value = true
try {
// 调用API搜索耳号
const res = await searchEarNumbers(query.trim())
if (res.code === 200 && Array.isArray(res.data)) {
earOptions.value = res.data
} else {
earOptions.value = []
}
} catch (error) {
console.error('搜索耳号失败:', error)
earOptions.value = []
proxy.$modal.msgError('搜索耳号失败')
} finally {
earLoading.value = false
}
}
async function handleRanchChange(ranchName) {
if (!ranchName) {
sheepOptions.value = [];
batchTags.value = []; // 清空
batchSheep.value = []; // 清空
form.value.manageTags = '';
return;
}
const matched = ranchOptions.value.find(r => r.ranchName === ranchName);
if (!matched) return;
const res = await getSheepByRanchId(matched.id);
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() {
nextTick(() => handlePasteSubmit())
}
/* 批量提交:拆词 → 去重 → 回填 */
function handlePasteSubmit() {
if (!pasteText.value || !pasteText.value.trim()) return
const separators = /[\p{White_Space},\n\r\t]+/u
const raw = pasteText.value.trim().split(separators).filter(v => v)
const exist = new Set(queryParams.value.manageTagsList || [])
const adds = []
const dups = []
raw.forEach(v => {
if (!exist.has(v)) { adds.push(v); exist.add(v) }
else dups.push(v)
})
if (adds.length) {
queryParams.value.manageTagsList = [...(queryParams.value.manageTagsList || []), ...adds]
proxy.$modal.msgSuccess(`成功添加 ${adds.length} 个耳号,当前共 ${queryParams.value.manageTagsList.length}` +
(dups.length > 0 ? `,已忽略 ${dups.length} 个重复耳号` : ''))
} else if (dups.length > 0) {
proxy.$modal.msgWarning('所有耳号均已存在')
}
pasteText.value = ''
}
/* 切换展开/收起 */
function toggleExpand() {
isExpanded.value = !isExpanded.value
}
/* 选择器变化时去重 */
function handleEarChange(val) {
queryParams.value.manageTagsList = [...new Set(val)]
}
/* 一键清空 */
function clearEarNumbers() {
queryParams.value.manageTagsList = []
pasteText.value = ''
isExpanded.value = false
}
/* 删除单个耳号 */
function removeEarNumber(tag) {
const idx = queryParams.value.manageTagsList.indexOf(tag)
if (idx > -1) queryParams.value.manageTagsList.splice(idx, 1)
}
// 校验和回显(和改备注、转群完全一致)
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 { data: sheep } = await checkSheepByManageTags(tags[0].trim()).catch(() => ({ data: null }))
if (sheep) {
batchTags.value = [tags[0]]
batchSheep.value = [sheep]
// 自动填充转出牧场
const ranchName = await getRanchNameByRanchId(sheep.ranchId)
form.value.transFrom = ranchName
} else {
proxy.$modal.msgWarning('未找到该耳号对应羊只')
batchTags.value = []
batchSheep.value = []
}
return
}
// 3. 多条 → 批量校验
batchTags.value = []
batchSheep.value = []
for (const tag of tags) {
const { data: sheep } = await checkSheepByManageTags(tag.trim()).catch(() => ({ data: null }))
if (sheep) {
batchTags.value.push(tag)
batchSheep.value.push(sheep)
} else {
proxy.$modal.msgWarning(`耳号 ${tag} 未找到对应羊只,已跳过`)
}
}
if (!batchTags.value.length) return // 全部无效
// 把合法耳号重新拼回输入框,用半角空格分隔
form.value.manageTags = batchTags.value.join(' ')
// 自动填充第一条的转出牧场
const firstSheep = batchSheep.value[0]
const ranchName = await getRanchNameByRanchId(firstSheep.ranchId)
form.value.transFrom = ranchName
}
// 删除单个批量耳号
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.transFrom = ''
}
// 加载羊只类型数据
function loadSheepTypeList() {
request({
url: '/base/base/list',
method: 'get',
params: { pageNum: 1, pageSize: 9999 }
}).then(res => {
sheepTypeList.value = res.rows || []
})
}
onMounted(() => {
loadRanchOptions();
getVarietyOptions();
loadSheepOptions();
loadSheepTypeList();
fetchTechnicalList();
getList();
});
</script>
<style scoped>
.tag-count {
color: #606266;
background-color: #f5f7fa;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
}
.selected-ear-numbers {
max-height: 150px;
overflow-y: auto;
padding: 8px;
background: #f5f7fa;
border-radius: 4px;
border: 1px dashed #dcdfe6;
display: flex;
flex-wrap: wrap;
align-items: center;
margin-top: 8px;
}
.selected-ear-numbers::-webkit-scrollbar {
width: 6px;
}
.selected-ear-numbers::-webkit-scrollbar-thumb {
background-color: #dcdfe6;
border-radius: 3px;
}
</style>