2025-12-10 20:27:35 +08:00

687 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<!-- ================= 搜索区 ================= -->
<el-form v-show="showSearch" ref="queryRef" :model="queryParams" inline label-width="110px" class="search-form">
<el-form-item label="业务单号" prop="orderNo">
<el-input v-model="queryParams.orderNo" placeholder="请输入" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="任务标题" prop="title">
<el-input v-model="queryParams.title" placeholder="关键词" clearable />
</el-form-item>
<el-form-item label="执行部门" prop="department">
<el-input v-model="queryParams.department" placeholder="请选择" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="执行人" prop="executorIds">
<el-input v-model="queryParams.executorIds" placeholder="用户 ID" clearable />
</el-form-item>
<el-form-item label="计划日期" prop="planDate">
<el-date-picker v-model="daterangePlanDate" type="daterange" range-separator="~" start-placeholder="开始"
end-placeholder="结束" value-format="YYYY-MM-DD" style="width: 240px" @change="handleQuery" />
</el-form-item>
<el-form-item label="完成日期" prop="finishDate">
<el-date-picker v-model="daterangeFinishDate" type="daterange" range-separator="~" start-placeholder="开始"
end-placeholder="结束" value-format="YYYY-MM-DD" style="width: 240px" @change="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 110px">
<el-option label="待派工" :value="0" />
<el-option label="已派工" :value="1" />
<el-option label="执行中" :value="2" />
<el-option label="已完成" :value="3" />
<el-option label="已取消" :value="4" />
<el-option label="异常" :value="5" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<el-icon>
<Search />
</el-icon> 搜索
</el-button>
<el-button @click="resetQuery">
<el-icon>
<Refresh />
</el-icon> 重置
</el-button>
</el-form-item>
</el-form>
<!-- ================= 按钮区 ================= -->
<el-row :gutter="10" class="mb8">
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['work:work:add']">新增</el-button>
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate"
v-hasPermi="['work:work:edit']">修改</el-button>
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPermi="['work:work:remove']">删除</el-button>
<el-button type="warning" icon="Download" @click="handleExport" v-hasPermi="['work:work:export']">导出</el-button>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
</el-row>
<!-- ================= 表格 ================= -->
<el-table v-loading="loading" :data="workList" stripe border size="small" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="业务单号" prop="orderNo" min-width="80" show-overflow-tooltip />
<el-table-column label="业务类型" prop="bizType" width="90" align="center">
<template #default="{ row }">
<el-tag
:type="['primary', 'success', 'info', 'warning', 'danger', 'info', 'danger', 'info', 'success'][row.bizType - 1]"
size="small">
{{ ['免疫', '保健', '转群', '称重', '配种', '干奶', '淘汰', '消毒', '饲喂'][row.bizType - 1] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="任务标题" prop="title" min-width="120" show-overflow-tooltip />
<el-table-column label="执行部门" prop="department" width="110" show-overflow-tooltip />
<el-table-column label="计划日期" prop="executeDate" width="110" align="center" />
<el-table-column label="状态" prop="status" width="90" align="center">
<template #default="{ row }">
<el-tag :type="['info', 'primary', 'warning', 'success', 'info', 'danger'][row.status]" size="small">
{{ ['待派工', '已派工', '执行中', '已完成', '已取消', '异常'][row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="优先级" prop="priority" width="90" align="center">
<template #default="{ row }">
<el-tag :type="['info', 'primary', 'danger'][row.priority - 1]" size="small">
{{ ['普通', '重要', '紧急'][row.priority - 1] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="{ row }">
<el-button link type="primary" icon="View" @click="handleDetail(row)">详情</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)"
v-hasPermi="['work:work:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(row)"
v-hasPermi="['work:work:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
<!-- ================= 新增 / 修改弹窗 ================= -->
<el-dialog v-model="open" :title="title" width="680px" top="8vh" append-to-body>
<el-form ref="workRef" :model="form" :rules="rules" label-width="110px">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="业务单号" prop="orderNo">
<el-input v-model="form.orderNo" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务类型" prop="bizType">
<el-select v-model="form.bizType" placeholder="请选择" style="width: 100%" @change="onBizTypeChange">
<el-option v-for="(t, idx) in ['免疫', '保健', '转群', '称重', '配种', '干奶', '淘汰', '消毒', '饲喂']" :key="idx"
:label="t" :value="idx + 1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="优先级" prop="priority">
<el-select v-model="form.priority" placeholder="请选择" style="width: 100%">
<el-option label="普通" :value="1" />
<el-option label="重要" :value="2" />
<el-option label="紧急" :value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="任务标题" prop="title">
<el-input v-model="form.title" placeholder="简短标题" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="详细说明">
<Editor v-model="form.content" :min-height="180" />
</el-form-item>
</el-col>
<!-- 消毒/饲喂:仅显示羊舍多选 -->
<template v-if="isDisinfectOrFeed">
<el-col :span="24">
<el-form-item label="执行羊舍" prop="barnIds">
<el-select v-model="selectedBarnIds" multiple collapse-tags placeholder="请选择羊舍" style="width: 100%">
<el-option v-for="b in barnOptions" :key="b.id" :label="b.sheepfoldName" :value="b.id" />
</el-select>
</el-form-item>
</el-col>
</template>
<!-- 其他业务:羊舍+耳号完整面板 -->
<template v-else>
<el-col :span="12">
<el-form-item label="羊舍" prop="barnIds">
<el-select v-model="selectedBarnIds" multiple collapse-tags placeholder="请选择羊舍" style="width: 100%"
@change="handleBarnChange">
<el-option v-for="b in barnOptions" :key="b.id" :label="b.sheepfoldName" :value="b.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="耳号追加">
<el-input v-model="sheepInput" placeholder="输入耳号回车添加" @keyup.enter="handleSheepInput" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="已选耳号">
<el-tag v-for="(tag, idx) in selectedSheepList" :key="tag.sheepId" closable @close="removeSheep(idx)"
style="margin-right:6px">
{{ tag.sheepNo }}
</el-tag>
</el-form-item>
</el-col>
</template>
<el-col :span="12">
<el-form-item label="执行部门" prop="department">
<el-select v-model="form.department" placeholder="请选择" style="width: 100%">
<el-option label="免疫组" value="免疫组" />
<el-option label="饲喂组" value="饲喂组" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行人" prop="executorIds">
<el-input v-model="form.executorIds" placeholder="用户" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划日期" prop="executeDate">
<el-date-picker v-model="form.executeDate" type="date" :disabled-date="disabledBeforeToday"
value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="时段" prop="executeTime">
<el-input v-model="form.executeTime" placeholder="例 08:00-10:00" />
</el-form-item>
</el-col>
<template v-if="isUpdate">
<el-col :span="12">
<el-form-item label="派工人" prop="issuerId">
<el-input v-model="form.issuerId" placeholder="用户 ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="派工时间" prop="issueTime">
<el-date-picker v-model="form.issueTime" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="接工人" prop="receiverId">
<el-input v-model="form.receiverId" placeholder="用户 ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="接工时间" prop="receiveTime">
<el-date-picker v-model="form.receiveTime" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="完成时间" prop="finishTime">
<el-date-picker v-model="form.finishTime" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
</template>
<el-col :span="24">
<el-form-item label="执行结果">
<el-input v-model="form.result" type="textarea" :rows="3" placeholder="可空" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="备注信息" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="cancel">取 消</el-button>
<el-button type="primary" @click="submitForm">确 定</el-button>
</template>
</el-dialog>
<!-- ================= 详情弹窗 ================= -->
<el-dialog v-model="detailOpen" title="派工单详情" width="680px" top="8vh" append-to-body>
<el-form label-width="110px" class="detail-form">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="业务单号:">
<span>{{ detailForm.orderNo }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务类型:">
<el-tag size="small"
:type="['primary', 'success', 'info', 'warning', 'danger', 'info', 'danger', 'info', 'success'][detailForm.bizType - 1]">
{{ ['免疫', '保健', '转群', '称重', '配种', '干奶', '淘汰', '消毒', '饲喂'][detailForm.bizType - 1] }}
</el-tag>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="优先级:">
<el-tag size="small" :type="['info', 'primary', 'danger'][detailForm.priority - 1]">
{{ ['普通', '重要', '紧急'][detailForm.priority - 1] }}
</el-tag>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="任务标题:">
<span>{{ detailForm.title }}</span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="详细说明:">
<div v-html="detailForm.content || '-'" class="detail-content"></div>
</el-form-item>
</el-col>
<!-- 通用展示:羊舍 -->
<el-col :span="24">
<el-form-item label="执行羊舍:">
<span>{{(detailForm.location || []).map(b => b.sheepfoldName).join('、') || '-'}}</span>
</el-form-item>
</el-col>
<!-- 通用展示:耳号 -->
<el-col :span="24">
<el-form-item label="执行耳号:">
<span>{{(detailForm.sheepScope || []).map(s => s.sheepNo).join('、') || '-'}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行部门:">
<span>{{ detailForm.department }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行人:">
<span>{{ detailForm.executorIds }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划日期:">
<span>{{ detailForm.executeDate }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="时段:">
<span>{{ detailForm.executeTime || '-' }}</span>
</el-form-item>
</el-col>
<template v-if="detailForm.id">
<el-col :span="12">
<el-form-item label="派工人:">
<span>{{ detailForm.issuerId || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="派工时间:">
<span>{{ detailForm.issueTime || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="接工人:">
<span>{{ detailForm.receiverId || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="接工时间:">
<span>{{ detailForm.receiveTime || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="完成时间:">
<span>{{ detailForm.finishTime || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="执行结果:">
<span>{{ detailForm.result || '-' }}</span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注:">
<span>{{ detailForm.remark || '-' }}</span>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
<template #footer>
<el-button @click="detailOpen = false"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script setup name="Work">
import {
listWork,
getWork,
delWork,
addWork,
updateWork,
} from '@/api/work/work'
import Editor from '@/components/Editor/index.vue'
import dayjs from 'dayjs'
import request from '@/utils/request'
const { proxy } = getCurrentInstance()
/* -------------------------------------------------
* 基础变量
* ------------------------------------------------- */
const workList = 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 detailOpen = ref(false)
const detailForm = ref({})
const daterangePlanDate = ref([])
const daterangeFinishDate = ref([])
const isUpdate = ref(false)
/* 羊舍 & 耳号 相关 */
const barnOptions = ref([])
const selectedBarnIds = ref([])
const selectedSheepList = ref([])
const sheepInput = ref('')
/* 表单 / 查询 / 校验 */
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
orderNo: null,
title: null,
department: null,
executorIds: null,
status: null,
},
rules: {
orderNo: [{ required: true, message: '业务单号不能为空', trigger: 'blur' }],
bizType: [{ required: true, message: '请选择业务类型', trigger: 'change' }],
title: [{ required: true, message: '任务标题不能为空', trigger: 'blur' }],
department: [{ required: true, message: '执行部门不能为空', trigger: 'change' }],
executorIds: [{ required: true, message: '执行人不能为空', trigger: 'blur' }],
executeDate: [{ required: true, message: '计划日期不能为空', trigger: 'change' }],
priority: [{ required: true, message: '请选择优先级', trigger: 'change' }],
},
})
const { queryParams, form, rules } = toRefs(data)
/* 计算属性:是否消毒/饲喂 */
const isDisinfectOrFeed = computed(() => {
const type = Number(form.value.bizType)
return type === 8 || type === 9 // 8-消毒 9-饲喂
})
/* 日期禁用函数 */
const disabledBeforeToday = time => dayjs(time).isBefore(dayjs().startOf('day'))
/* -------------------------------------------------
* 业务单号自动生成
* ------------------------------------------------- */
const typeMap = {
1: 'MY',
2: 'BJ',
3: 'ZQ',
4: 'CZ',
5: 'PZ',
6: 'GN',
7: 'TT',
8: 'XD',
9: 'WS'
}
function genOrderNo(type) {
const prefix = typeMap[Number(type)] || 'WD'
const dateStr = dayjs().format('YYYYMMDDHHmmss')
return `${prefix}-${dateStr}`
}
/* -------------------------------------------------
* 羊舍 & 耳号 功能函数
* ------------------------------------------------- */
function getBarns() {
request({ url: '/sheepfold_management/sheepfold_management/list', method: 'get' })
.then(res => barnOptions.value = res.rows || [])
}
function handleBarnChange(barnIds = []) {
if (isDisinfectOrFeed.value) return
const currentIds = new Set(barnIds)
selectedSheepList.value = selectedSheepList.value.filter(s => currentIds.has(s.barnId))
const needFetch = [...currentIds].filter(id =>
!selectedSheepList.value.some(s => s.barnId === id)
)
if (!needFetch.length) return
Promise.all(
needFetch.map(id =>
request({ url: '/sheepfold_management/sheepfold_management/getSheepById', params: { id } })
)
).then(resArr => {
const list = resArr.flatMap(r => (r.data || []).map(s => ({
sheepNo: s.manageTags,
sheepId: s.id,
barnId: s.barnId
})))
const map = new Map(selectedSheepList.value.map(i => [i.sheepId, i]))
list.forEach(item => map.set(item.sheepId, item))
selectedSheepList.value = [...map.values()]
})
}
function handleSheepInput() {
if (isDisinfectOrFeed.value) return
const no = sheepInput.value.trim()
if (!no) return
if (selectedSheepList.value.some(s => s.sheepNo === no)) {
proxy.$modal.msgWarning('该耳号已存在')
sheepInput.value = ''
return
}
request.get(`/sheep_file/sheep_file/byNo/${no}`).then(res => {
if (!res.data) {
proxy.$modal.msgError('羊只耳号不存在')
} else {
selectedSheepList.value.push({
sheepNo: res.data.sheepNo || res.data.bsManageTags,
sheepId: res.data.id,
barnId: res.data.barnId
})
}
sheepInput.value = ''
})
}
function removeSheep(idx) {
selectedSheepList.value.splice(idx, 1)
}
function onBizTypeChange() {
form.value.orderNo = genOrderNo(form.value.bizType)
}
/* -------------------------------------------------
* 原有业务函数
* ------------------------------------------------- */
function getList() {
loading.value = true
const qp = { ...queryParams.value }
qp.params = qp.params || {}
if (daterangePlanDate.value?.length) {
qp.params.beginPlanDate = daterangePlanDate.value[0] + ' 00:00:00'
qp.params.endPlanDate = daterangePlanDate.value[1] + ' 23:59:59'
}
if (daterangeFinishDate.value?.length) {
qp.params.beginFinishDate = daterangeFinishDate.value[0] + ' 00:00:00'
qp.params.endFinishDate = daterangeFinishDate.value[1] + ' 23:59:59'
}
listWork(qp).then(res => {
workList.value = res.rows
total.value = res.total
loading.value = false
})
}
function cancel() {
open.value = false
reset()
}
function reset() {
form.value = {}
selectedBarnIds.value = []
selectedSheepList.value = []
sheepInput.value = ''
proxy.resetForm('workRef')
}
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
function resetQuery() {
daterangePlanDate.value = []
daterangeFinishDate.value = []
proxy.resetForm('queryRef')
handleQuery()
}
function handleSelectionChange(selection) {
ids.value = selection.map(i => i.id)
single.value = selection.length !== 1
multiple.value = !selection.length
}
function handleAdd() {
reset()
isUpdate.value = false
title.value = '添加派工单'
getBarns()
open.value = true
}
function handleUpdate(row) {
reset()
isUpdate.value = true
title.value = '修改派工单'
const _id = row.id || ids.value
getWork(_id).then(res => {
form.value = res.data
// 回显羊舍
if (res.data.location) {
selectedBarnIds.value = res.data.location.map(b => b.id)
}
// 回显耳号(使用 sheepScope 而不是 sheepList
if (!isDisinfectOrFeed.value && res.data.sheepScope) {
selectedSheepList.value = res.data.sheepScope.map(s => ({
sheepNo: s.sheepNo,
sheepId: s.id,
barnId: s.barnId || null
}))
}
// 加载羊舍下拉框
getBarns()
open.value = true
})
}
function submitForm() {
proxy.$refs.workRef.validate(valid => {
if (!valid) return
const payload = {
...form.value,
location: selectedBarnIds.value.map(id => {
const item = barnOptions.value.find(v => v.id === id)
return { id, sheepfoldName: item?.sheepfoldName || '' }
}),
sheepScope: selectedSheepList.value.map(s => ({
id: s.sheepId,
sheepNo: s.sheepNo
}))
}
/* 非消毒/饲喂再传 sheepIds */
if (!isDisinfectOrFeed.value) {
payload.sheepIds = selectedSheepList.value.map(s => s.sheepId)
}
if (form.value.id) {
updateWork(payload).then(() => {
proxy.$modal.msgSuccess('修改成功')
open.value = false
getList()
})
} else {
addWork(payload).then(() => {
proxy.$modal.msgSuccess('新增成功')
open.value = false
getList()
})
}
})
}
function handleDelete(row) {
const _ids = row.id || ids.value
proxy.$modal.confirm('是否确认删除派工单编号为“' + _ids + '”的数据项?')
.then(() => delWork(_ids))
.then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
})
}
function handleExport() {
proxy.download('work/work/export', { ...queryParams.value }, `work_${Date.now()}.xlsx`)
}
function handleDetail(row) {
detailForm.value = { ...row }
// 新增:把羊舍、耳号展平为字符串用于显示
detailForm.value._locationStr = (row.location || []).map(b => b.sheepfoldName).join('、') || '-'
detailForm.value._sheepStr = (row.sheepScope || []).map(s => s.sheepNo).join('、') || '-'
detailOpen.value = true
}
/* 初始加载 */
getList()
</script>
<style lang="scss" scoped>
.search-form {
margin-bottom: 6px
}
.mb8 {
margin-bottom: 8px
}
:deep(.el-table) {
font-size: 13px;
th {
background-color: #fafafa;
font-weight: 600;
color: #333
}
}
.detail-form .el-form-item {
margin-bottom: 8px
}
.detail-content {
padding: 6px 10px;
background: #fafafa;
border-radius: 4px;
min-height: 60px;
line-height: 1.6
}
</style>