Merge remote-tracking branch 'origin/main'

pull/1/head
liutao 1 month ago
commit c104147447

@ -99,6 +99,9 @@ export const DeviceApi = {
getDeviceList2ByNoUsed: async (id: number) => {
return await request.get({ url: `/iot/device/noUsedlist2?id=` + id })
},
getAvailableList: async () => {
return await request.get({ url: `/iot/device/available-list` })
},
// 新增物联设备
createDevice: async (data: DeviceVO) => {
return await request.post({ url: `/iot/device/create`, data })

@ -3755,7 +3755,7 @@ export default {
tableNameColumn: 'Name',
tableOrganizationColumn: 'Station',
tableMachineColumn: 'Machine',
tableMachineColumn: 'Related Collection Device',
tableTeamColumn: 'Team',
tableStartTimeColumn: 'Start Time',
tableEndTimeColumn: 'End Time',

@ -3599,7 +3599,7 @@ export default {
tableNameColumn: '名称',
tableOrganizationColumn: '工位',
tableMachineColumn: '机台',
tableMachineColumn: '关联采集设备',
tableTeamColumn: '班组',
tableStartTimeColumn: '开始时间',
tableEndTimeColumn: '结束时间',

@ -3,6 +3,17 @@
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
<div class="schedule-detail-panel">
<div class="schedule-detail-title">计划信息</div>
<div v-if="!editable" class="schedule-status-legend">
<div
v-for="item in sortedPlanStatusList"
:key="item.key"
class="legend-item"
:style="{ borderLeftColor: item.color }"
>
<span class="legend-color" :style="{ backgroundColor: item.color }"></span>
<span class="legend-label">{{ item.label }}</span>
</div>
</div>
<template v-if="activePreviewDevice">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="设备名称">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
@ -21,6 +32,13 @@
>
<div class="schedule-plan-item-head">
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span>
<span
v-if="plan.planStatus && PLAN_STATUS_COLOR_MAP[plan.planStatus]"
class="plan-status-tag"
:style="{ backgroundColor: PLAN_STATUS_COLOR_MAP[plan.planStatus].color }"
>
{{ PLAN_STATUS_COLOR_MAP[plan.planStatus].label }}
</span>
</div>
<div>计划编码{{ plan.taskCode ?? '-' }}</div>
<div>计划数量{{ plan.planNumber ?? '-' }}</div>
@ -87,6 +105,19 @@ import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
defineOptions({ name: 'ScheduleGanttPanel' })
//
const PLAN_STATUS_COLOR_MAP = {
1: { label: '已排产', color: '#409eff', textColor: '#ffffff', sort: 1 },
8: { label: '已开工', color: '#67c23a', textColor: '#ffffff', sort: 2 },
3: { label: '暂停', color: '#e6a23c', textColor: '#ffffff', sort: 3 },
4: { label: '待入库', color: '#f56c6c', textColor: '#ffffff', sort: 4 },
5: { label: '已入库', color: '#8e7cc3', textColor: '#ffffff', sort: 5 },
}
const sortedPlanStatusList = Object.entries(PLAN_STATUS_COLOR_MAP)
.map(([key, val]) => ({ key: Number(key), ...val }))
.sort((a, b) => a.sort - b.sort)
const props = withDefaults(
defineProps<{
scheduleList: any[]
@ -726,7 +757,30 @@ const initGanttPreview = () => {
gantt.templates.tooltip_text = (start, end, task: any) => buildTaskTooltipHtml(task, start, end)
gantt.templates.task_class = (_start, _end, task: any) => {
if (!task?._planData) return ''
return String(task?._planData?.sourceType ?? '').toUpperCase() === 'HISTORY' ? 'schedule-plan-task-history' : 'schedule-plan-task'
// editable false planStatus
if (!props.editable) {
const planStatus = task._planData?.planStatus
const statusMap: Record<number | string, string> = {
'1': 'schedule-plan-task-status-1',
1: 'schedule-plan-task-status-1',
'8': 'schedule-plan-task-status-8',
8: 'schedule-plan-task-status-8',
'3': 'schedule-plan-task-status-3',
3: 'schedule-plan-task-status-3',
'4': 'schedule-plan-task-status-4',
4: 'schedule-plan-task-status-4',
'5': 'schedule-plan-task-status-5',
5: 'schedule-plan-task-status-5',
'2': 'schedule-plan-task-status-2',
2: 'schedule-plan-task-status-2'
}
return statusMap[planStatus] || 'schedule-plan-task-default'
}
// editable true 使
const sourceType = String(task?._planData?.sourceType ?? '').toUpperCase()
return sourceType === 'HISTORY' ? 'schedule-plan-task-history' : 'schedule-plan-task'
}
const globalRange = getGlobalDateRange(previewScheduleList.value)
@ -737,6 +791,12 @@ const initGanttPreview = () => {
const ganttData = buildPreviewGanttData(previewScheduleList.value)
gantt.parse(ganttData)
//
gantt.eachTask((task: any) => {
gantt.refreshTask(task.id)
})
initTaskTooltips()
if (ganttData.data.length) {
@ -946,6 +1006,39 @@ onBeforeUnmount(() => {
font-weight: 600;
}
.schedule-status-legend {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 8px;
margin-bottom: 12px;
padding: 8px;
background: var(--el-fill-color-light);
border-radius: 4px;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-left: 3px solid;
font-size: 12px;
}
.legend-color {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
flex-shrink: 0;
}
.legend-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.schedule-plan-list-title {
margin-top: 14px;
margin-bottom: 10px;
@ -979,6 +1072,17 @@ onBeforeUnmount(() => {
text-overflow: ellipsis;
white-space: nowrap;
}
.plan-status-tag {
display: inline-block;
padding: 2px 8px;
border-radius: 2px;
font-size: 12px;
color: #ffffff;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
}
.schedule-plan-item-active {
border: 1px solid var(--el-color-success-light-5);
background: var(--el-color-success-light-9);
@ -1016,6 +1120,76 @@ onBeforeUnmount(() => {
color: #ffffff;
}
/* 已排产状态 - 蓝色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-1) {
background: #409eff !important;
border-color: #409eff !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-1 .gantt_task_content) {
color: #ffffff !important;
}
/* 已开工状态 - 绿色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-8) {
background: #67c23a !important;
border-color: #67c23a !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-8 .gantt_task_content) {
color: #ffffff !important;
}
/* 暂停状态 - 橙色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-3) {
background: #e6a23c !important;
border-color: #e6a23c !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-3 .gantt_task_content) {
color: #ffffff !important;
}
/* 待入库状态 - 红色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-4) {
background: #f56c6c !important;
border-color: #f56c6c !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-4 .gantt_task_content) {
color: #ffffff !important;
}
/* 已入库状态 - 紫色 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-5) {
background: #8e7cc3 !important;
border-color: #8e7cc3 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-5 .gantt_task_content) {
color: #ffffff !important;
}
/* 其他状态 - 深灰 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-2) {
background: #606266 !important;
border-color: #606266 !important;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-status-2 .gantt_task_content) {
color: #ffffff !important;
}
/* 默认状态 */
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-default) {
background: #909399;
border-color: #909399;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-default .gantt_task_content) {
color: #ffffff;
}
.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-history) {
background: #909399;
border-color: #909399;

@ -113,6 +113,7 @@
:key="item.id"
:label="item.deviceName"
:value="item.id"
:disabled="item.selected === true"
/>
</el-select>
</el-form-item>
@ -162,6 +163,7 @@ const formData = ref({
sort: undefined,
workerUserId: undefined,
machineId: undefined,
machineName: undefined,
phone: undefined,
email: undefined,
isEnable: undefined,
@ -169,7 +171,8 @@ const formData = ref({
orgClass: undefined,
orgType: undefined,
isCode: undefined,
dvId: undefined
dvId: undefined,
dvName: undefined
})
const formRules = reactive({
name: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorNameRequired'), trigger: 'blur' }],
@ -198,12 +201,18 @@ const open = async (type: string, id?: number) => {
deviceList.value = await DeviceLedgerApi.getDeviceLedgerListByNoUsed()
} else {
deviceList.value = await DeviceLedgerApi.getDeviceLedgerList2ByNoUsed(formData.value.machineId)
const exists = deviceList.value.some((item) => item.id === formData.value.machineId)
if (!exists && formData.value.machineName) {
deviceList.value.unshift({ id: formData.value.machineId, deviceName: formData.value.machineName } as DeviceLedgerVO)
}
}
//await getMachineComponentTree()
if (type == 'create' || typeof formData.value.dvId != 'number') {
dvList.value = await DeviceApi.getDeviceListByNoUsed()
} else {
dvList.value = await DeviceApi.getDeviceList2ByNoUsed(formData.value.dvId)
dvList.value = await DeviceApi.getAvailableList()
if (type !== 'create' && typeof formData.value.dvId === 'number') {
const exists = dvList.value.some((item) => item.id === formData.value.dvId)
if (!exists && formData.value.dvName) {
dvList.value.unshift({ id: formData.value.dvId, deviceName: formData.value.dvName, selected: true } as DeviceVO)
}
}
}
defineExpose({ open }) // open
@ -242,6 +251,7 @@ const resetForm = () => {
sort: 0,
workerUserId: undefined,
machineId: undefined,
machineName: undefined,
phone: undefined,
email: undefined,
isEnable: undefined,
@ -249,7 +259,8 @@ const resetForm = () => {
orgClass: undefined,
orgType: undefined,
isCode: true,
dvId: undefined
dvId: undefined,
dvName: undefined
}
formRef.value?.resetFields()
}

@ -204,7 +204,7 @@
{{ t('ProductionPlan.Plan.actionFinishLabel') }}
</el-button>
<el-button link type="primary"
@click="handleStatus(scope.row.code, scope.row.id, 'store', 5, t('ProductionPlan.Plan.actionStoreLabel'), scope.row.isZj)"
@click="openStoreDialog(scope.row.code, scope.row.id, scope.row.isZj)"
v-hasPermi="['mes:plan:update']" v-if="scope.row.status === 4">
{{ t('ProductionPlan.Plan.actionStoreLabel') }}
</el-button>
@ -237,6 +237,26 @@
<!-- 详情弹窗 -->
<PlanDetail ref="detailRef" />
<!-- 入库仓库选择弹窗 -->
<Dialog v-model="storeDialogVisible" title="入库" width="400px">
<el-form label-width="80px">
<el-form-item label="选择仓库">
<el-select v-model="storeWarehouseId" placeholder="请选择仓库" filterable style="width: 100%">
<el-option
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="storeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmStore" :disabled="!storeWarehouseId">确定</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
@ -256,6 +276,7 @@ import ItemRequisitionDetailList from "@/views/mes/itemrequisition/components/It
import ZjProductPreList from "@/views/mes/zjproduct/components/ZjProductPreList.vue";
import BaogongRecordList from "@/views/mes/baogongrecord/components/BaogongRecordList.vue";
import { ZjTaskApi } from '@/api/mes/zjtask'
import { WarehouseApi, WarehouseVO } from '@/api/erp/stock/warehouse'
const productList = ref<ProductVO[]>([]) //
const taskList = ref<TaskVO[]>([]) //
@ -295,6 +316,10 @@ const queryParams = reactive({
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
const warehouseList = ref<WarehouseVO[]>([]) //
const storeDialogVisible = ref(false) //
const storeWarehouseId = ref<number>() // ID
const storePlanInfo = ref<{ planCode: string; id: number; isZj: number }>() //
/** 详情操作 */
const detailRef = ref()
@ -437,9 +462,34 @@ const refreshInspectableMap = async (rows: PlanVO[]) => {
}
/** 入库 - 打开仓库选择弹窗 */
const openStoreDialog = async (planCode: string, id: number, isZj: number) => {
storePlanInfo.value = { planCode, id, isZj }
storeWarehouseId.value = undefined
if (!warehouseList.value.length) {
warehouseList.value = await WarehouseApi.getWarehouseSimpleList()
}
storeDialogVisible.value = true
}
/** 入库 - 确认入库 */
const confirmStore = async () => {
if (!storePlanInfo.value || !storeWarehouseId.value) return
storeDialogVisible.value = false
await handleStatus(
storePlanInfo.value.planCode,
storePlanInfo.value.id,
'store',
5,
t('ProductionPlan.Plan.actionStoreLabel'),
storePlanInfo.value.isZj,
storeWarehouseId.value
)
}
/** 开工 */
const handleStatus = async (planCode: string, id: number,
type: string, status: number, tip: string, isZj: number) => {
type: string, status: number, tip: string, isZj: number, warehouseId?: number) => {
try {
// if((isZj === 0 || isZj === null) && tip === "") {
// // message.alertError("")
@ -452,10 +502,13 @@ const handleStatus = async (planCode: string, id: number,
t('ProductionPlan.Plan.statusConfirmTitle')
)
//
const data = {
const data: any = {
id: id,
code: type,
status: status //
status: status
}
if (warehouseId) {
data.warehouseId = warehouseId
}
await PlanApi.updatePlanStatus(data)
message.success(t('common.success'))

Loading…
Cancel
Save