feat(mes): 添加生产任务选择器和工单选择器组件,支持前端过滤功能

pull/871/MERGE
YunaiV 2026-02-21 12:21:36 +08:00
parent 7b274a0990
commit 335f367e00
11 changed files with 208 additions and 21 deletions

View File

@ -1,7 +1,5 @@
import request from '@/config/axios'
// TODO @AI挪到 plan/shift 目录下
// MES 计划班次 VO
export interface CalPlanShiftVO {
id: number

View File

@ -14,8 +14,6 @@ export interface CalPlanTeamVO {
attribute4: number
}
// TODO @AI挪到 team/index.ts 中
// MES 计划班组关联 API
export const CalPlanTeamApi = {
// 查询指定排班计划的班组列表

View File

@ -1,6 +1,5 @@
import request from '@/config/axios'
// TODO @AI放到 machinery/index.ts
// MES 点检保养方案设备 VO
export interface DvCheckPlanMachineryVO {
id: number

View File

@ -1,6 +1,5 @@
import request from '@/config/axios'
// TODO @AI放到 subject/index.ts
// MES 点检保养方案项目 VO
export interface DvCheckPlanSubjectVO {
id: number

View File

@ -79,5 +79,10 @@ export const ProWorkOrderApi = {
// 确认工单
confirmWorkOrder: async (id: number) => {
return await request.put({ url: `/mes/pro/work-order/confirm?id=` + id })
},
// 获得工单精简列表(下拉选项)
getWorkOrderSimpleList: async () => {
return await request.get({ url: `/mes/pro/work-order/simple-list` })
}
}

View File

@ -59,7 +59,7 @@
</template>
<script setup lang="ts">
import { CalPlanShiftApi, CalPlanShiftVO } from '@/api/mes/cal/shift'
import { CalPlanShiftApi, CalPlanShiftVO } from '@/api/mes/cal/plan/shift'
defineOptions({ name: 'CalShiftList' })

View File

@ -1,5 +1,4 @@
<!-- MES 甘特图编辑全屏 Dialog -->
<!-- TODO @AI独立路由 -->
<template>
<Dialog title="甘特图编辑" v-model="dialogVisible" fullscreen>
<div class="mb-10px flex items-center justify-between">
@ -59,7 +58,6 @@ const open = async () => {
/** 加载甘特图数据(复用 page 接口,传大 pageSize */
const loadGanttData = async () => {
// TODO @AI:admin-api/mes/pro/task/page
const data = await ProTaskApi.getTaskPage({ pageNo: 1, pageSize: 999 })
taskList.value = data.list
}

View File

@ -1,5 +1,4 @@
<!-- dhtmlx-gantt Vue 3 封装 -->
<!-- TODO @芋艿更深度的注释 -->
<!-- dhtmlx-gantt Vue 3 封装组件基于 dhtmlx-gantt 实现甘特图展示和拖拽编辑 -->
<template>
<div ref="ganttContainer" :style="{ width: '100%', height: height + 'px' }"></div>
</template>
@ -8,7 +7,15 @@
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
// TODO @AI
/**
* GanttChart - 甘特图组件
*
* 功能
* 1. 按工单分组展示生产任务工单为 project 任务为子行
* 2. 支持只读预览和拖拽编辑两种模式
* 3. 拖拽后触发 task-update 事件通知父组件批量保存
* 4. 时间刻度 工作日单位1 工作日 = 8 小时
*/
defineOptions({ name: 'GanttChart' })
@ -71,7 +78,6 @@ const initGantt = () => {
day_full: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
day_short: ['日', '一', '二', '三', '四', '五', '六']
},
// TODO @AI linter
labels: {
new_task: '新任务',
icon_save: '保存',
@ -166,8 +172,7 @@ const initGantt = () => {
id: task.taskId || id,
startTime: task.start_date,
endTime: task.end_date,
// TODO @AIlinter
duration: task.duration / 8 //
duration: (task.duration as number) / 8 //
})
})
}

View File

@ -0,0 +1,105 @@
<!-- MES 生产任务选择器纯下拉前端过滤支持 codename可按工单过滤 -->
<template>
<el-select
v-model="selectValue"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
filterable
:filter-method="handleFilter"
class="!w-1/1"
@change="handleChange"
>
<el-option v-for="item in filteredList" :key="item.id" :label="item.code" :value="item.id">
<div class="flex items-center gap-8px">
<span>{{ item.code }}</span>
<el-tag v-if="item.name" size="small" type="info" class="ml-4px">
{{ item.name }}
</el-tag>
</div>
</el-option>
</el-select>
</template>
<script setup lang="ts">
import { ProTaskApi, ProTaskVO } from '@/api/mes/pro/task'
defineOptions({ name: 'ProTaskSelect' })
const props = withDefaults(
defineProps<{
modelValue?: number
workOrderId?: number
disabled?: boolean
clearable?: boolean
placeholder?: string
}>(),
{
disabled: false,
clearable: true,
placeholder: '请选择任务'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
change: [item: ProTaskVO | undefined]
}>()
const allList = ref<ProTaskVO[]>([])
const filteredList = ref<ProTaskVO[]>([])
const selectValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
/** 前端过滤code + name */
const handleFilter = (query: string) => {
if (!query) {
filteredList.value = allList.value
return
}
const keyword = query.toLowerCase()
filteredList.value = allList.value.filter(
(item) =>
item.code?.toLowerCase().includes(keyword) ||
item.name?.toLowerCase().includes(keyword)
)
}
/** 选中变化 */
const handleChange = (val: number | undefined) => {
const item = allList.value.find((o) => o.id === val)
emit('change', item)
}
/** 加载任务列表 */
const loadData = async () => {
try {
allList.value = await ProTaskApi.getTaskSimpleList(props.workOrderId)
filteredList.value = allList.value
} catch {
allList.value = []
filteredList.value = []
}
}
/** 监听 workOrderId 变化,重新加载 */
watch(
() => props.workOrderId,
() => {
allList.value = []
filteredList.value = []
if (props.workOrderId) {
loadData()
}
}
)
onMounted(() => {
if (props.workOrderId) {
loadData()
}
})
</script>

View File

@ -57,7 +57,6 @@
</ContentWrap>
<!-- 待排产工单列表 -->
<!-- TODO @AI这里是父子在参考下修复也可以看看 workorder -->
<ContentWrap title="待排产工单">
<el-table
v-loading="loading"
@ -75,10 +74,10 @@
<el-table-column label="规格型号" align="center" prop="productSpec" width="120" />
<el-table-column label="单位" align="center" prop="unitMeasureName" width="80" />
<el-table-column label="工单数量" align="center" prop="quantity" width="100" />
<!-- TODO @AI调整数量 -->
<el-table-column label="调整数量" align="center" prop="quantityChanged" width="100" />
<el-table-column label="已排产" align="center" prop="quantityScheduled" width="80" />
<el-table-column label="已生产数量" align="center" prop="quantityProduced" width="80" />
<!-- TODO @AI客户编码 -->
<el-table-column label="客户编码" align="center" prop="clientCode" width="120" />
<el-table-column label="客户名称" align="center" prop="clientName" width="120" />
<el-table-column
label="需求日期"
@ -123,7 +122,6 @@
</ContentWrap>
<!-- 排产 Drawer -->
<!-- TODO @AI是不是要抽出去一个独立组件是不是使用弹窗在对齐下 -->
<el-drawer v-model="scheduleDrawerVisible" title="排产" size="75%" :destroy-on-close="true">
<div v-if="currentWorkOrder">
<!-- 工单概要信息 -->
@ -190,6 +188,7 @@ import { ProTaskApi } from '@/api/mes/pro/task'
import { ProRouteProcessApi, ProRouteProcessVO } from '@/api/mes/pro/route/process'
import { ProRouteProductApi } from '@/api/mes/pro/route/product'
import { MesProWorkOrderStatusEnum, MesProWorkOrderTypeEnum } from '@/views/mes/utils/constants'
import MdItemSelect from '@/views/mes/md/item/components/MdItemSelect.vue'
import GanttChart from './components/GanttChart.vue'
import GanttEdit from './GanttEdit.vue'
import ProTaskList from './ProTaskList.vue'
@ -205,6 +204,7 @@ const queryParams = reactive({
pageSize: 10,
code: undefined,
name: undefined,
productId: undefined,
requestDate: undefined,
// +
status: MesProWorkOrderStatusEnum.CONFIRMED,
@ -274,9 +274,8 @@ const openScheduleDrawer = async (row: any) => {
// 线
try {
// 线"线"
const routeProducts = await ProRouteProductApi.getRouteProductListByRoute(0)
// TODO @"线"
// 线
const matched = routeProducts?.find((rp: any) => rp.itemId === row.productId)
if (matched) {
currentRouteId.value = matched.routeId

View File

@ -0,0 +1,81 @@
<!-- MES 生产工单选择器纯下拉前端过滤支持 codename -->
<template>
<el-select
v-model="selectValue"
:placeholder="placeholder"
:disabled="disabled"
:clearable="clearable"
filterable
:filter-method="handleFilter"
class="!w-1/1"
@change="handleChange"
>
<el-option v-for="item in filteredList" :key="item.id" :label="item.code" :value="item.id">
<div class="flex items-center gap-8px">
<span>{{ item.code }}</span>
<el-tag v-if="item.name" size="small" type="info" class="ml-4px">
{{ item.name }}
</el-tag>
</div>
</el-option>
</el-select>
</template>
<script setup lang="ts">
import { ProWorkOrderApi, ProWorkOrderVO } from '@/api/mes/pro/workorder'
defineOptions({ name: 'ProWorkOrderSelect' })
const props = withDefaults(
defineProps<{
modelValue?: number
disabled?: boolean
clearable?: boolean
placeholder?: string
}>(),
{
disabled: false,
clearable: true,
placeholder: '请选择工单'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
change: [item: ProWorkOrderVO | undefined]
}>()
const allList = ref<ProWorkOrderVO[]>([])
const filteredList = ref<ProWorkOrderVO[]>([])
const selectValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
/** 前端过滤code + name */
const handleFilter = (query: string) => {
if (!query) {
filteredList.value = allList.value
return
}
const keyword = query.toLowerCase()
filteredList.value = allList.value.filter(
(item) =>
item.code?.toLowerCase().includes(keyword) ||
item.name?.toLowerCase().includes(keyword)
)
}
/** 选中变化 */
const handleChange = (val: number | undefined) => {
const item = allList.value.find((o) => o.id === val)
emit('change', item)
}
/** 加载工单列表 */
onMounted(async () => {
allList.value = await ProWorkOrderApi.getWorkOrderSimpleList()
filteredList.value = allList.value
})
</script>