feat(mes): 优化 materialstock 的代码实现风格
parent
79af870afe
commit
e313de09c4
|
|
@ -1,11 +1,427 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesProCardApi } from '#/api/mes/pro/card';
|
||||
import type { MesProCardProcessApi } from '#/api/mes/pro/card/process';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||
import { MdItemSelect } from '#/views/mes/md/item/components';
|
||||
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
|
||||
import { ProProcessSelect } from '#/views/mes/pro/process/components';
|
||||
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
|
||||
import {
|
||||
MesAutoCodeRuleCode,
|
||||
MesProWorkOrderStatusEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
import { UserSelect } from '#/views/system/user/components';
|
||||
|
||||
/** 表单类型 */
|
||||
export type FormType = 'create' | 'detail' | 'finish' | 'update';
|
||||
|
||||
/** 表头是否只读(完成、详情态) */
|
||||
function isHeaderReadonly(formType: FormType): boolean {
|
||||
return formType === 'detail' || formType === 'finish';
|
||||
}
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(
|
||||
formType: FormType,
|
||||
formApi?: VbenFormApi,
|
||||
): VbenFormSchema[] {
|
||||
const headerReadonly = isHeaderReadonly(formType);
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '流转卡编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: headerReadonly,
|
||||
placeholder: '请输入流转卡编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix:
|
||||
formType === 'create' || formType === 'update'
|
||||
? () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
const code = await generateAutoCode(
|
||||
MesAutoCodeRuleCode.PRO_CARD_CODE,
|
||||
);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
fieldName: 'workOrderId',
|
||||
label: '生产工单',
|
||||
component: markRaw(ProWorkOrderSelect),
|
||||
componentProps: {
|
||||
disabled: headerReadonly,
|
||||
placeholder: '请选择生产工单',
|
||||
status: MesProWorkOrderStatusEnum.CONFIRMED,
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'itemId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
disabled: headerReadonly,
|
||||
placeholder: '请选择产品',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'transferedQuantity',
|
||||
label: '流转数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: headerReadonly,
|
||||
min: 0,
|
||||
placeholder: '请输入流转数量',
|
||||
precision: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'batchCode',
|
||||
label: '批次号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: headerReadonly,
|
||||
placeholder: '请输入批次号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
disabled: headerReadonly,
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '流转卡编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入流转卡编码',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'workOrderId',
|
||||
label: '生产工单',
|
||||
component: markRaw(ProWorkOrderSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择生产工单',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'itemId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择产品',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'batchCode',
|
||||
label: '批次号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入批次号',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesProCardApi.Card>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '流转卡编码',
|
||||
width: 160,
|
||||
slots: { default: 'code' },
|
||||
},
|
||||
{
|
||||
field: 'workOrderCode',
|
||||
title: '生产工单编号',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
field: 'workOrderName',
|
||||
title: '工单名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'batchCode',
|
||||
title: '批次号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'itemCode',
|
||||
title: '产品物料编码',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'itemName',
|
||||
title: '产品物料名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'specification',
|
||||
title: '规格型号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'unitMeasureName',
|
||||
title: '单位',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'transferedQuantity',
|
||||
title: '流转数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '单据状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 240,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 工序记录子表的字段 */
|
||||
export function useProcessGridColumns(
|
||||
editable: boolean,
|
||||
): VxeTableGridOptions<MesProCardProcessApi.CardProcess>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'sort',
|
||||
title: '序号',
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
field: 'processName',
|
||||
title: '工序名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'processCode',
|
||||
title: '工序编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'inputTime',
|
||||
title: '进入工序时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'outputTime',
|
||||
title: '出工序时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'inputQuantity',
|
||||
title: '投入数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'outputQuantity',
|
||||
title: '产出数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'unqualifiedQuantity',
|
||||
title: '不良品数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'workstationCode',
|
||||
title: '工位编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'workstationName',
|
||||
title: '工位名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '操作人',
|
||||
width: 100,
|
||||
},
|
||||
...(editable
|
||||
? [
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
} as const,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
/** 工序记录新增/修改的表单 */
|
||||
export function useProcessFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '序号',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0,
|
||||
placeholder: '请输入序号',
|
||||
precision: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'processId',
|
||||
label: '工序',
|
||||
component: markRaw(ProProcessSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择工序',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'inputTime',
|
||||
label: '进入工序时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
placeholder: '请选择进入工序时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'outputTime',
|
||||
label: '出工序时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
placeholder: '请选择出工序时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'inputQuantity',
|
||||
label: '投入数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0,
|
||||
placeholder: '请输入投入数量',
|
||||
precision: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'outputQuantity',
|
||||
label: '产出数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0,
|
||||
placeholder: '请输入产出数量',
|
||||
precision: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'unqualifiedQuantity',
|
||||
label: '不合格数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0,
|
||||
placeholder: '请输入不合格数量',
|
||||
precision: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'workstationId',
|
||||
label: '工位',
|
||||
component: markRaw(MdWorkstationSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择工位',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'userId',
|
||||
label: '操作人',
|
||||
component: markRaw(UserSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择操作人',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 流转卡选择弹窗的搜索表单 */
|
||||
export function useCardSelectGridFormSchema(): VbenFormSchema[] {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as GanttChart } from './gantt-chart.vue';
|
||||
export { default as ProTaskSelectDialog } from './pro-task-select-dialog.vue';
|
||||
export { default as ProTaskSelect } from './pro-task-select.vue';
|
||||
|
|
|
|||
|
|
@ -1,15 +1,464 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesProTaskApi } from '#/api/mes/pro/task';
|
||||
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import MdClientSelect from '#/views/mes/md/client/components/md-client-select.vue';
|
||||
import { MdItemSelect } from '#/views/mes/md/item/components';
|
||||
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
|
||||
import { ProProcessSelect } from '#/views/mes/pro/process/components';
|
||||
import { RouteColorPicker } from '#/views/mes/pro/route/components';
|
||||
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
|
||||
|
||||
/** 待排产工单列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '工单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入工单编码',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '工单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入工单名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceCode',
|
||||
label: '来源单据',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入来源单据编号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择产品',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'clientId',
|
||||
label: '客户',
|
||||
component: markRaw(MdClientSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择客户',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requestDate',
|
||||
label: '需求日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 待排产工单列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '工单编码',
|
||||
fixed: 'left',
|
||||
width: 200,
|
||||
treeNode: true,
|
||||
slots: { default: 'code' },
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '工单名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'orderSourceType',
|
||||
title: '工单来源',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_SOURCE_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orderSourceCode',
|
||||
title: '来源单据编号',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'productCode',
|
||||
title: '产品编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'productName',
|
||||
title: '产品名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'productSpecification',
|
||||
title: '规格型号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'unitMeasureName',
|
||||
title: '单位',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '工单数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'quantityChanged',
|
||||
title: '调整数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'quantityProduced',
|
||||
title: '已生产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'clientCode',
|
||||
title: '客户编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'clientName',
|
||||
title: '客户名称',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'requestDate',
|
||||
title: '需求日期',
|
||||
width: 120,
|
||||
formatter: 'formatDate',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '排产状态',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 排产对话框只读工单信息的表单 */
|
||||
export function useScheduleFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '工单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '工单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceType',
|
||||
label: '工单来源',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_SOURCE_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceCode',
|
||||
label: '来源单据编号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '工单类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productSpecification',
|
||||
label: '规格型号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'unitMeasureName',
|
||||
label: '单位',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'quantity',
|
||||
label: '工单数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
precision: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'clientId',
|
||||
label: '客户',
|
||||
component: markRaw(MdClientSelect),
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'batchCode',
|
||||
label: '批次号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requestDate',
|
||||
label: '需求日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 生产任务子表的字段 */
|
||||
export function useTaskGridColumns(
|
||||
editable: boolean,
|
||||
): VxeTableGridOptions<MesProTaskApi.Task>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '任务编码',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '任务名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'workstationCode',
|
||||
title: '工作站编号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'workstationName',
|
||||
title: '工作站名称',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '排产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'producedQuantity',
|
||||
title: '已生产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
title: '开始生产时间',
|
||||
width: 170,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'duration',
|
||||
title: '生产时长',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '预计完成时间',
|
||||
width: 170,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'colorCode',
|
||||
title: '显示颜色',
|
||||
width: 100,
|
||||
slots: { default: 'colorCode' },
|
||||
},
|
||||
...(editable
|
||||
? [
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
} as const,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
/** 生产任务新增/修改的表单 */
|
||||
export function useTaskFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'workstationId',
|
||||
label: '工作站',
|
||||
component: markRaw(MdWorkstationSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择工作站',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'quantity',
|
||||
label: '排产数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0.01,
|
||||
placeholder: '请输入排产数量',
|
||||
precision: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'colorCode',
|
||||
label: '甘特颜色',
|
||||
component: markRaw(RouteColorPicker),
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
placeholder: '请选择开始时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
// 开始时间变更:重新计算结束时间
|
||||
onChange: () => recalcEndTime(formApi),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'duration',
|
||||
label: '生产时长',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 1,
|
||||
placeholder: '请输入生产时长',
|
||||
precision: 0,
|
||||
// 生产时长变更:重新计算结束时间
|
||||
onChange: () => recalcEndTime(formApi),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 计算结束时间:开始时间 + 生产时长 × 8 小时 */
|
||||
async function recalcEndTime(formApi?: VbenFormApi) {
|
||||
if (!formApi) {
|
||||
return;
|
||||
}
|
||||
const values = await formApi.getValues();
|
||||
if (values.startTime && values.duration) {
|
||||
const start = Number(values.startTime);
|
||||
await formApi.setFieldValue(
|
||||
'endTime',
|
||||
start + values.duration * 8 * 3600 * 1000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** 任务选择弹窗的搜索表单 */
|
||||
export function useTaskSelectGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export { default as ProWorkOrderSelectDialog } from './pro-work-order-select-dialog.vue';
|
||||
export { default as ProWorkOrderSelect } from './pro-work-order-select.vue';
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
|
||||
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { computed, ref, useAttrs, watch } from 'vue';
|
||||
|
||||
import { Select, Tag, Tooltip } from 'ant-design-vue';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { getWorkOrder, getWorkOrderPage } from '#/api/mes/pro/workorder';
|
||||
import { Input, Tooltip } from 'ant-design-vue';
|
||||
|
||||
import { getWorkOrder } from '#/api/mes/pro/workorder';
|
||||
|
||||
import ProWorkOrderSelectDialog from './pro-work-order-select-dialog.vue';
|
||||
|
||||
/**
|
||||
* MES 生产工单选择器(轻量版)
|
||||
*
|
||||
* 当前用于安灯记录等只需要单选工单 ID 的业务页面:
|
||||
* - 默认按 `status` 过滤拉取首页 100 条工单作为下拉
|
||||
* - 编辑回显走 `getWorkOrder(id)`
|
||||
* - 后续 `mes/pro/workorder` 完整迁移后,可替换为带弹窗的复杂选择器
|
||||
*/
|
||||
defineOptions({ name: 'ProWorkOrderSelect', inheritAttrs: false });
|
||||
|
||||
const props = withDefaults(
|
||||
|
|
@ -22,7 +18,6 @@ const props = withDefaults(
|
|||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
pageSize?: number;
|
||||
placeholder?: string;
|
||||
status?: number;
|
||||
type?: number;
|
||||
|
|
@ -31,7 +26,6 @@ const props = withDefaults(
|
|||
allowClear: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
pageSize: 100,
|
||||
placeholder: '请选择工单',
|
||||
status: undefined,
|
||||
type: undefined,
|
||||
|
|
@ -43,102 +37,104 @@ const emit = defineEmits<{
|
|||
'update:modelValue': [value: number | undefined];
|
||||
}>();
|
||||
|
||||
const allList = ref<MesProWorkOrderApi.WorkOrder[]>([]);
|
||||
const selectedItem = ref<MesProWorkOrderApi.WorkOrder>();
|
||||
const attrs = useAttrs(); // 透传属性
|
||||
const dialogRef = ref<InstanceType<typeof ProWorkOrderSelectDialog>>(); // 工单选择弹窗
|
||||
const hovering = ref(false); // 是否悬停
|
||||
const selectedItem = ref<MesProWorkOrderApi.WorkOrder>(); // 当前选中工单
|
||||
|
||||
const selectValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: number | undefined) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
const displayLabel = computed(() => selectedItem.value?.code ?? ''); // 选择器展示编码
|
||||
const showClear = computed(() => // 是否显示清空图标
|
||||
props.allowClear &&
|
||||
!props.disabled &&
|
||||
hovering.value &&
|
||||
props.modelValue != null,
|
||||
);
|
||||
|
||||
/** 前端过滤:按工单编码或名称模糊匹配 */
|
||||
function handleFilter(input: string, option: any) {
|
||||
const keyword = input.toLowerCase();
|
||||
const item = option?.item as MesProWorkOrderApi.WorkOrder | undefined;
|
||||
return Boolean(
|
||||
item?.code?.toLowerCase().includes(keyword) ||
|
||||
item?.name?.toLowerCase().includes(keyword),
|
||||
);
|
||||
}
|
||||
|
||||
/** 同步选中工单详情,未在列表内时单独拉取 */
|
||||
async function syncSelectedItem(value: number | undefined) {
|
||||
if (value === undefined) {
|
||||
/** 根据编号单条查询工单信息(用于编辑回显) */
|
||||
async function resolveItemById(id: number | undefined) {
|
||||
if (id == null) {
|
||||
selectedItem.value = undefined;
|
||||
return;
|
||||
}
|
||||
const found = allList.value.find((item) => item.id === value);
|
||||
if (found) {
|
||||
selectedItem.value = found;
|
||||
if (selectedItem.value?.id === id) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
selectedItem.value = await getWorkOrder(value);
|
||||
} catch (error) {
|
||||
console.error('[ProWorkOrderSelect] resolveItemById failed:', error);
|
||||
selectedItem.value = await getWorkOrder(id);
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, resolveItemById, { immediate: true });
|
||||
|
||||
/** 清空已选工单 */
|
||||
function clearSelected() {
|
||||
selectedItem.value = undefined;
|
||||
emit('update:modelValue', undefined);
|
||||
emit('change', undefined);
|
||||
}
|
||||
|
||||
/** 打开工单选择弹窗 */
|
||||
function handleClick(event: MouseEvent) {
|
||||
if (props.disabled) {
|
||||
return;
|
||||
}
|
||||
const target = event.target as HTMLElement;
|
||||
if (showClear.value && target.closest('.ant-input-suffix')) {
|
||||
event.stopPropagation();
|
||||
clearSelected();
|
||||
return;
|
||||
}
|
||||
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||
dialogRef.value?.open(selectedIds, { multiple: false });
|
||||
}
|
||||
|
||||
/** 除 v-model 外,额外抛出完整工单对象给业务表单使用 */
|
||||
function handleChange(value: any) {
|
||||
const nextValue = value === undefined ? undefined : Number(value);
|
||||
syncSelectedItem(nextValue);
|
||||
emit('change', selectedItem.value);
|
||||
/** 弹窗选中回调 */
|
||||
function handleSelected(rows: MesProWorkOrderApi.WorkOrder[]) {
|
||||
const item = rows[0];
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
selectedItem.value = item;
|
||||
emit('update:modelValue', item.id);
|
||||
emit('change', item);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
syncSelectedItem(value);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await getWorkOrderPage({
|
||||
pageNo: 1,
|
||||
pageSize: props.pageSize,
|
||||
status: props.status,
|
||||
type: props.type,
|
||||
});
|
||||
allList.value = data.list ?? [];
|
||||
syncSelectedItem(props.modelValue);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
|
||||
<template #title>
|
||||
<div v-if="selectedItem" class="leading-6">
|
||||
<div>编码:{{ selectedItem.code || '-' }}</div>
|
||||
<div>名称:{{ selectedItem.name || '-' }}</div>
|
||||
<div>产品:{{ selectedItem.productName || '-' }}</div>
|
||||
<div>数量:{{ selectedItem.quantity ?? '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<Select
|
||||
v-bind="$attrs"
|
||||
v-model:value="selectValue"
|
||||
:allow-clear="allowClear"
|
||||
:disabled="disabled"
|
||||
:filter-option="handleFilter"
|
||||
:placeholder="placeholder"
|
||||
class="w-full"
|
||||
show-search
|
||||
@change="handleChange"
|
||||
>
|
||||
<Select.Option
|
||||
v-for="item in allList"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:value="item.id"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{{ item.code }}</span>
|
||||
<Tag v-if="item.name" color="default">{{ item.name }}</Tag>
|
||||
<div
|
||||
v-bind="attrs"
|
||||
class="w-full"
|
||||
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||
@click="handleClick"
|
||||
@mouseenter="hovering = true"
|
||||
@mouseleave="hovering = false"
|
||||
>
|
||||
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
|
||||
<template #title>
|
||||
<div v-if="selectedItem" class="leading-6">
|
||||
<div>编码:{{ selectedItem.code || '-' }}</div>
|
||||
<div>名称:{{ selectedItem.name || '-' }}</div>
|
||||
<div>产品:{{ selectedItem.productName || '-' }}</div>
|
||||
<div>数量:{{ selectedItem.quantity ?? '-' }}</div>
|
||||
</div>
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<Input
|
||||
:disabled="disabled"
|
||||
:placeholder="placeholder"
|
||||
:value="displayLabel"
|
||||
readonly
|
||||
>
|
||||
<template #suffix>
|
||||
<IconifyIcon
|
||||
class="size-4"
|
||||
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
|
||||
/>
|
||||
</template>
|
||||
</Input>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ProWorkOrderSelectDialog
|
||||
ref="dialogRef"
|
||||
:status="status"
|
||||
:type="type"
|
||||
@selected="handleSelected"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as WmBatchDetail } from './wm-batch-detail.vue';
|
||||
export { default as WmBatchSelectDialog } from './wm-batch-select-dialog.vue';
|
||||
export { default as WmBatchSelect } from './wm-batch-select.vue';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesWmBatchApi } from '#/api/mes/wm/batch';
|
||||
import type { DescriptionItemSchema } from '#/components/description';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import MdClientSelect from '#/views/mes/md/client/components/md-client-select.vue';
|
||||
import MdItemSelect from '#/views/mes/md/item/components/md-item-select.vue';
|
||||
import MdVendorSelect from '#/views/mes/md/vendor/components/md-vendor-select.vue';
|
||||
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
|
||||
import { ProTaskSelect } from '#/views/mes/pro/task/components';
|
||||
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
|
||||
import { TmToolSelect } from '#/views/mes/tm/tool/components';
|
||||
|
||||
|
|
@ -66,6 +71,14 @@ export function useBatchSelectGridFormSchema(): VbenFormSchema[] {
|
|||
placeholder: '请选择工作站',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'taskId',
|
||||
label: '生产任务',
|
||||
component: markRaw(ProTaskSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择生产任务',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'toolId',
|
||||
label: '工具',
|
||||
|
|
@ -74,6 +87,15 @@ export function useBatchSelectGridFormSchema(): VbenFormSchema[] {
|
|||
placeholder: '请选择工具',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'moldId',
|
||||
label: '模具编号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入模具编号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'salesOrderCode',
|
||||
label: '销售订单编号',
|
||||
|
|
@ -111,6 +133,33 @@ export function useBatchSelectGridFormSchema(): VbenFormSchema[] {
|
|||
placeholder: '请选择质量状态',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'produceDate',
|
||||
label: '生产日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'expireDate',
|
||||
label: '有效期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'receiptDate',
|
||||
label: '入库日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -232,3 +281,92 @@ export function useBatchSelectGridColumns(
|
|||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 批次详情的描述字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
label: '批次编号',
|
||||
},
|
||||
{
|
||||
field: 'itemCode',
|
||||
label: '物料编码',
|
||||
},
|
||||
{
|
||||
field: 'itemName',
|
||||
label: '物料名称',
|
||||
},
|
||||
{
|
||||
field: 'itemSpecification',
|
||||
label: '规格型号',
|
||||
},
|
||||
{
|
||||
field: 'unitName',
|
||||
label: '单位',
|
||||
},
|
||||
{
|
||||
field: 'lotNumber',
|
||||
label: '生产批号',
|
||||
},
|
||||
{
|
||||
field: 'produceDate',
|
||||
label: '生产日期',
|
||||
render: (value) => (value ? formatDate(value, 'YYYY-MM-DD') : '-'),
|
||||
},
|
||||
{
|
||||
field: 'expireDate',
|
||||
label: '有效期',
|
||||
render: (value) => (value ? formatDate(value, 'YYYY-MM-DD') : '-'),
|
||||
},
|
||||
{
|
||||
field: 'receiptDate',
|
||||
label: '入库日期',
|
||||
render: (value) => (value ? formatDate(value, 'YYYY-MM-DD') : '-'),
|
||||
},
|
||||
{
|
||||
field: 'vendorName',
|
||||
label: '供应商',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'clientName',
|
||||
label: '客户',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'workstationCode',
|
||||
label: '工作站',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'purchaseOrderCode',
|
||||
label: '采购订单编号',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'salesOrderCode',
|
||||
label: '销售订单编号',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'workOrderCode',
|
||||
label: '生产工单',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
field: 'qualityStatus',
|
||||
label: '质量状态',
|
||||
render: (value) =>
|
||||
value == null
|
||||
? '-'
|
||||
: h(DictTag, { type: DICT_TYPE.MES_WM_QUALITY_STATUS, value }),
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: '备注',
|
||||
span: 3,
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const open = ref(false);
|
||||
const multiple = ref(true);
|
||||
const syncingSingleSelection = ref(false);
|
||||
const multiple = ref(false); // 是否多选;默认按单选选择器使用
|
||||
const selectedRows = ref<MesWmMaterialStockApi.MaterialStock[]>([]);
|
||||
const preSelectedIds = ref<number[]>([]);
|
||||
const searchItemTypeId = ref<number>();
|
||||
|
|
@ -64,50 +63,37 @@ const alertTitle = computed(() => {
|
|||
return `已按${parts.join('/')}预过滤`;
|
||||
});
|
||||
|
||||
/** 单选模式同步 VXE 勾选状态 */
|
||||
async function syncSingleSelection(row?: MesWmMaterialStockApi.MaterialStock) {
|
||||
syncingSingleSelection.value = true;
|
||||
await nextTick();
|
||||
await gridApi.grid.clearCheckboxRow();
|
||||
if (row) {
|
||||
await gridApi.grid.setCheckboxRow(row, true);
|
||||
}
|
||||
await nextTick();
|
||||
syncingSingleSelection.value = false;
|
||||
/** 获取多选记录,包含 VXE reserve 跨页记录 */
|
||||
function getMultipleSelectedRows() {
|
||||
const selectedMap = new Map<number, MesWmMaterialStockApi.MaterialStock>();
|
||||
const records = [
|
||||
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
|
||||
...(gridApi.grid.getCheckboxRecords?.() ?? []),
|
||||
] as MesWmMaterialStockApi.MaterialStock[];
|
||||
records.forEach((row) => {
|
||||
const rowId = row.id;
|
||||
if (rowId !== undefined) {
|
||||
selectedMap.set(rowId, row);
|
||||
}
|
||||
});
|
||||
return [...selectedMap.values()];
|
||||
}
|
||||
|
||||
/** 处理勾选变化 */
|
||||
async function handleCheckboxChange({
|
||||
checked,
|
||||
records,
|
||||
row,
|
||||
}: {
|
||||
checked: boolean;
|
||||
records: MesWmMaterialStockApi.MaterialStock[];
|
||||
row?: MesWmMaterialStockApi.MaterialStock;
|
||||
}) {
|
||||
if (syncingSingleSelection.value) {
|
||||
return;
|
||||
}
|
||||
if (!multiple.value) {
|
||||
const selected = checked && row ? [row] : [];
|
||||
selectedRows.value = selected;
|
||||
await syncSingleSelection(selected[0]);
|
||||
return;
|
||||
}
|
||||
selectedRows.value = records;
|
||||
/** 处理多选勾选变化 */
|
||||
function handleCheckboxSelectChange() {
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
|
||||
/** 处理全选变化 */
|
||||
function handleCheckboxAll({
|
||||
records,
|
||||
}: {
|
||||
records: MesWmMaterialStockApi.MaterialStock[];
|
||||
}) {
|
||||
if (syncingSingleSelection.value) {
|
||||
return;
|
||||
}
|
||||
selectedRows.value = records;
|
||||
/** 处理单选切换 */
|
||||
function handleRadioChange(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
selectedRows.value = [row];
|
||||
}
|
||||
|
||||
/** 多选模式下切换行勾选 */
|
||||
async function toggleMultipleRow(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
|
||||
await gridApi.grid.setCheckboxRow(row, !selected);
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
|
||||
/** 双击行:单选直接确认;多选切换勾选 */
|
||||
|
|
@ -117,33 +103,34 @@ async function handleRowDblclick({
|
|||
row: MesWmMaterialStockApi.MaterialStock;
|
||||
}) {
|
||||
if (multiple.value) {
|
||||
const checked = !gridApi.grid.isCheckedByCheckboxRow(row);
|
||||
await gridApi.grid.setCheckboxRow(row, checked);
|
||||
handleCheckboxChange({
|
||||
checked,
|
||||
records: gridApi.grid.getCheckboxRecords() as MesWmMaterialStockApi.MaterialStock[],
|
||||
row,
|
||||
});
|
||||
await toggleMultipleRow(row);
|
||||
return;
|
||||
}
|
||||
selectedRows.value = [row];
|
||||
await syncSingleSelection(row);
|
||||
await gridApi.grid.setRadioRow(row);
|
||||
handleConfirm();
|
||||
}
|
||||
|
||||
/** 回显预选 */
|
||||
function applyPreSelection() {
|
||||
async function applyPreSelection() {
|
||||
if (preSelectedIds.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const rows = gridApi.grid.getData() as MesWmMaterialStockApi.MaterialStock[];
|
||||
for (const row of rows) {
|
||||
if (row.id && preSelectedIds.value.includes(row.id)) {
|
||||
gridApi.grid.setCheckboxRow(row, true);
|
||||
if (!multiple.value) {
|
||||
selectedRows.value = [row];
|
||||
}
|
||||
if (row.id === undefined || !preSelectedIds.value.includes(row.id)) {
|
||||
continue;
|
||||
}
|
||||
if (multiple.value) {
|
||||
await gridApi.grid.setCheckboxRow(row, true);
|
||||
} else {
|
||||
await gridApi.grid.setRadioRow(row);
|
||||
selectedRows.value = [row];
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (multiple.value) {
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,10 +139,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
schema: useSelectGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useSelectGridColumns(),
|
||||
columns: useSelectGridColumns(false),
|
||||
height: 480,
|
||||
keepSource: true,
|
||||
checkboxConfig: { highlight: true, range: true, reserve: true },
|
||||
radioConfig: { highlight: true, trigger: 'row' },
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
|
|
@ -186,8 +174,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
} as VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>,
|
||||
gridEvents: {
|
||||
cellDblclick: handleRowDblclick,
|
||||
checkboxAll: handleCheckboxAll,
|
||||
checkboxChange: handleCheckboxChange,
|
||||
checkboxAll: handleCheckboxSelectChange,
|
||||
checkboxChange: handleCheckboxSelectChange,
|
||||
radioChange: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) => {
|
||||
handleRadioChange(row);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -202,6 +193,8 @@ async function resetQueryState() {
|
|||
selectedRows.value = [];
|
||||
searchItemTypeId.value = undefined;
|
||||
await gridApi.grid.clearCheckboxRow();
|
||||
await gridApi.grid.clearCheckboxReserve();
|
||||
await gridApi.grid.clearRadioRow();
|
||||
await gridApi.formApi.resetForm();
|
||||
}
|
||||
|
||||
|
|
@ -211,13 +204,16 @@ async function openModal(
|
|||
options?: { multiple?: boolean },
|
||||
) {
|
||||
open.value = true;
|
||||
multiple.value = options?.multiple ?? true;
|
||||
multiple.value = options?.multiple ?? false;
|
||||
preSelectedIds.value = selectedIds || [];
|
||||
await nextTick();
|
||||
gridApi.setGridOptions({
|
||||
columns: useSelectGridColumns(multiple.value),
|
||||
});
|
||||
await resetQueryState();
|
||||
await gridApi.query();
|
||||
await nextTick();
|
||||
applyPreSelection();
|
||||
await applyPreSelection();
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
|
|
@ -228,14 +224,12 @@ async function closeModal() {
|
|||
|
||||
/** 确认选择 */
|
||||
function handleConfirm() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
|
||||
if (rows.length === 0) {
|
||||
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
|
||||
return;
|
||||
}
|
||||
emit(
|
||||
'selected',
|
||||
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
|
||||
);
|
||||
emit('selected', multiple.value ? rows : [rows[0]!]);
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,11 +71,7 @@ async function resolveItemById(id: number | undefined) {
|
|||
if (selectedItem.value?.id === id) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
selectedItem.value = await getMaterialStock(id);
|
||||
} catch (error) {
|
||||
console.error('[WmMaterialStockSelect] resolveItemById failed:', error);
|
||||
}
|
||||
selectedItem.value = await getMaterialStock(id);
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, resolveItemById, { immediate: true });
|
||||
|
|
|
|||
|
|
@ -72,8 +72,12 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
// TODO @AI:看看别的模块,是不是会叫 onFrozenChange?还是一般叫什么梗合适???
|
||||
export function useGridColumns(
|
||||
onFrozenChange: (row: MesWmMaterialStockApi.MaterialStock) => void,
|
||||
onFrozenChange: (
|
||||
newFrozen: boolean,
|
||||
row: MesWmMaterialStockApi.MaterialStock,
|
||||
) => Promise<boolean | undefined>,
|
||||
): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
return [
|
||||
{
|
||||
|
|
@ -206,10 +210,12 @@ export function useSelectGridFormSchema(): VbenFormSchema[] {
|
|||
}
|
||||
|
||||
/** 选择弹窗的字段 */
|
||||
export function useSelectGridColumns(): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
export function useSelectGridColumns(
|
||||
multiple = false,
|
||||
): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
return [
|
||||
{
|
||||
type: 'checkbox',
|
||||
type: multiple ? 'checkbox' : 'radio',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from '#/api/mes/wm/materialstock';
|
||||
import { $t } from '#/locales';
|
||||
import MdItemTypeTree from '#/views/mes/md/item/type/components/md-item-type-tree.vue';
|
||||
import { WmBatchDetail } from '#/views/mes/wm/batch/components';
|
||||
import AreaForm from '#/views/mes/wm/warehouse/area/modules/form.vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
|
@ -26,6 +27,8 @@ const [AreaModal, areaModalApi] = useVbenModal({
|
|||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const batchDetailRef = ref<InstanceType<typeof WmBatchDetail>>();
|
||||
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
|
|
@ -55,11 +58,20 @@ function handleOpenAreaDetail(row: MesWmMaterialStockApi.MaterialStock) {
|
|||
areaModalApi.setData({ formType: 'detail', id: row.areaId }).open();
|
||||
}
|
||||
|
||||
/** 打开批次详情弹窗 */
|
||||
function handleOpenBatchDetail(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
if (!row.batchId) {
|
||||
return;
|
||||
}
|
||||
batchDetailRef.value?.open(row.batchId);
|
||||
}
|
||||
|
||||
/** 处理冻结状态切换 */
|
||||
async function handleFrozenChange(
|
||||
newFrozen: boolean,
|
||||
row: MesWmMaterialStockApi.MaterialStock,
|
||||
): Promise<boolean | undefined> {
|
||||
const text = row.frozen ? '冻结' : '解冻';
|
||||
const text = newFrozen ? '冻结' : '解冻';
|
||||
try {
|
||||
await confirm(`确认要"${text}"该库存记录吗?`);
|
||||
} catch {
|
||||
|
|
@ -68,7 +80,7 @@ async function handleFrozenChange(
|
|||
// 更新冻结状态
|
||||
await updateMaterialStockFrozen({
|
||||
id: row.id!,
|
||||
frozen: row.frozen!,
|
||||
frozen: newFrozen,
|
||||
});
|
||||
// 提示并返回成功
|
||||
message.success(`${text}成功`);
|
||||
|
|
@ -117,6 +129,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
</template>
|
||||
|
||||
<AreaModal />
|
||||
<WmBatchDetail ref="batchDetailRef" />
|
||||
|
||||
<div class="flex h-full w-full">
|
||||
<!-- 左侧物料分类树 -->
|
||||
|
|
@ -140,9 +153,15 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
/>
|
||||
</template>
|
||||
<template #batchCode="{ row }">
|
||||
<span v-if="row.batchId" :title="row.batchCode">
|
||||
<Button
|
||||
v-if="row.batchId"
|
||||
:title="row.batchCode"
|
||||
size="small"
|
||||
type="link"
|
||||
@click="handleOpenBatchDetail(row)"
|
||||
>
|
||||
{{ row.batchCode }}
|
||||
</span>
|
||||
</Button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #areaName="{ row }">
|
||||
|
|
|
|||
|
|
@ -39,6 +39,15 @@ const routes: RouteRecordRaw[] = [
|
|||
},
|
||||
component: () => import('#/views/mes/wm/barcode/config/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'pro/task/edit',
|
||||
name: 'MesProTaskGanttEdit',
|
||||
meta: {
|
||||
title: '甘特图编辑',
|
||||
activePath: '/mes/pro/task',
|
||||
},
|
||||
component: () => import('#/views/mes/pro/task/edit/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as GanttChart } from './gantt-chart.vue';
|
||||
export { default as ProTaskSelectDialog } from './pro-task-select-dialog.vue';
|
||||
export { default as ProTaskSelect } from './pro-task-select.vue';
|
||||
|
|
|
|||
|
|
@ -1,15 +1,467 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesProTaskApi } from '#/api/mes/pro/task';
|
||||
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import MdClientSelect from '#/views/mes/md/client/components/md-client-select.vue';
|
||||
import { MdItemSelect } from '#/views/mes/md/item/components';
|
||||
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
|
||||
import { ProProcessSelect } from '#/views/mes/pro/process/components';
|
||||
import { RouteColorPicker } from '#/views/mes/pro/route/components';
|
||||
import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
|
||||
|
||||
/** 待排产工单列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '工单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入工单编码',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '工单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入工单名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceCode',
|
||||
label: '来源单据',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入来源单据编号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择产品',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'clientId',
|
||||
label: '客户',
|
||||
component: markRaw(MdClientSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择客户',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requestDate',
|
||||
label: '需求日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 待排产工单列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesProWorkOrderApi.WorkOrder>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '工单编码',
|
||||
fixed: 'left',
|
||||
width: 200,
|
||||
treeNode: true,
|
||||
slots: { default: 'code' },
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '工单名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'orderSourceType',
|
||||
title: '工单来源',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_SOURCE_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orderSourceCode',
|
||||
title: '来源单据编号',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'productCode',
|
||||
title: '产品编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'productName',
|
||||
title: '产品名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'productSpecification',
|
||||
title: '规格型号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'unitMeasureName',
|
||||
title: '单位',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '工单数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'quantityChanged',
|
||||
title: '调整数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'quantityProduced',
|
||||
title: '已生产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'clientCode',
|
||||
title: '客户编码',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'clientName',
|
||||
title: '客户名称',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'requestDate',
|
||||
title: '需求日期',
|
||||
width: 120,
|
||||
formatter: 'formatDate',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '排产状态',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 排产对话框只读工单信息的表单 */
|
||||
export function useScheduleFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '工单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '工单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceType',
|
||||
label: '工单来源',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_SOURCE_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'orderSourceCode',
|
||||
label: '来源单据编号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '工单类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productId',
|
||||
label: '产品',
|
||||
component: markRaw(MdItemSelect),
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'productSpecification',
|
||||
label: '规格型号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'unitMeasureName',
|
||||
label: '单位',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'quantity',
|
||||
label: '工单数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
disabled: true,
|
||||
precision: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'clientId',
|
||||
label: '客户',
|
||||
component: markRaw(MdClientSelect),
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'batchCode',
|
||||
label: '批次号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requestDate',
|
||||
label: '需求日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
type: 'date',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 生产任务子表的字段 */
|
||||
export function useTaskGridColumns(
|
||||
editable: boolean,
|
||||
): VxeTableGridOptions<MesProTaskApi.Task>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '任务编码',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '任务名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'workstationCode',
|
||||
title: '工作站编号',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'workstationName',
|
||||
title: '工作站名称',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '排产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'producedQuantity',
|
||||
title: '已生产数量',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
title: '开始生产时间',
|
||||
width: 170,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'duration',
|
||||
title: '生产时长',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '预计完成时间',
|
||||
width: 170,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'colorCode',
|
||||
title: '显示颜色',
|
||||
width: 100,
|
||||
slots: { default: 'colorCode' },
|
||||
},
|
||||
...(editable
|
||||
? [
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
} as const,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
/** 生产任务新增/修改的表单 */
|
||||
export function useTaskFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'workstationId',
|
||||
label: '工作站',
|
||||
component: markRaw(MdWorkstationSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择工作站',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'quantity',
|
||||
label: '排产数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 0.01,
|
||||
placeholder: '请输入排产数量',
|
||||
precision: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'colorCode',
|
||||
label: '甘特颜色',
|
||||
component: markRaw(RouteColorPicker),
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
placeholder: '请选择开始时间',
|
||||
type: 'datetime',
|
||||
valueFormat: 'x',
|
||||
// 开始时间变更:重新计算结束时间
|
||||
onChange: () => recalcEndTime(formApi),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'duration',
|
||||
label: '生产时长',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 1,
|
||||
placeholder: '请输入生产时长',
|
||||
precision: 0,
|
||||
// 生产时长变更:重新计算结束时间
|
||||
onChange: () => recalcEndTime(formApi),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
type: 'datetime',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 计算结束时间:开始时间 + 生产时长 × 8 小时 */
|
||||
async function recalcEndTime(formApi?: VbenFormApi) {
|
||||
if (!formApi) {
|
||||
return;
|
||||
}
|
||||
const values = await formApi.getValues();
|
||||
if (values.startTime && values.duration) {
|
||||
const start = Number(values.startTime);
|
||||
await formApi.setFieldValue(
|
||||
'endTime',
|
||||
start + values.duration * 8 * 3600 * 1000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** 任务选择弹窗的搜索表单 */
|
||||
export function useTaskSelectGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as WmBatchDetail } from './wm-batch-detail.vue';
|
||||
export { default as WmBatchSelectDialog } from './wm-batch-select-dialog.vue';
|
||||
export { default as WmBatchSelect } from './wm-batch-select.vue';
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const open = ref(false);
|
||||
const multiple = ref(true);
|
||||
const syncingSingleSelection = ref(false);
|
||||
const multiple = ref(false); // 是否多选;默认按单选选择器使用
|
||||
const selectedRows = ref<MesWmMaterialStockApi.MaterialStock[]>([]);
|
||||
const preSelectedIds = ref<number[]>([]);
|
||||
const searchItemTypeId = ref<number>();
|
||||
|
|
@ -64,50 +63,37 @@ const alertTitle = computed(() => {
|
|||
return `已按${parts.join('/')}预过滤`;
|
||||
});
|
||||
|
||||
/** 单选模式同步 VXE 勾选状态 */
|
||||
async function syncSingleSelection(row?: MesWmMaterialStockApi.MaterialStock) {
|
||||
syncingSingleSelection.value = true;
|
||||
await nextTick();
|
||||
await gridApi.grid.clearCheckboxRow();
|
||||
if (row) {
|
||||
await gridApi.grid.setCheckboxRow(row, true);
|
||||
}
|
||||
await nextTick();
|
||||
syncingSingleSelection.value = false;
|
||||
/** 获取多选记录,包含 VXE reserve 跨页记录 */
|
||||
function getMultipleSelectedRows() {
|
||||
const selectedMap = new Map<number, MesWmMaterialStockApi.MaterialStock>();
|
||||
const records = [
|
||||
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
|
||||
...(gridApi.grid.getCheckboxRecords?.() ?? []),
|
||||
] as MesWmMaterialStockApi.MaterialStock[];
|
||||
records.forEach((row) => {
|
||||
const rowId = row.id;
|
||||
if (rowId !== undefined) {
|
||||
selectedMap.set(rowId, row);
|
||||
}
|
||||
});
|
||||
return [...selectedMap.values()];
|
||||
}
|
||||
|
||||
/** 处理勾选变化 */
|
||||
async function handleCheckboxChange({
|
||||
checked,
|
||||
records,
|
||||
row,
|
||||
}: {
|
||||
checked: boolean;
|
||||
records: MesWmMaterialStockApi.MaterialStock[];
|
||||
row?: MesWmMaterialStockApi.MaterialStock;
|
||||
}) {
|
||||
if (syncingSingleSelection.value) {
|
||||
return;
|
||||
}
|
||||
if (!multiple.value) {
|
||||
const selected = checked && row ? [row] : [];
|
||||
selectedRows.value = selected;
|
||||
await syncSingleSelection(selected[0]);
|
||||
return;
|
||||
}
|
||||
selectedRows.value = records;
|
||||
/** 处理多选勾选变化 */
|
||||
function handleCheckboxSelectChange() {
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
|
||||
/** 处理全选变化 */
|
||||
function handleCheckboxAll({
|
||||
records,
|
||||
}: {
|
||||
records: MesWmMaterialStockApi.MaterialStock[];
|
||||
}) {
|
||||
if (syncingSingleSelection.value) {
|
||||
return;
|
||||
}
|
||||
selectedRows.value = records;
|
||||
/** 处理单选切换 */
|
||||
function handleRadioChange(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
selectedRows.value = [row];
|
||||
}
|
||||
|
||||
/** 多选模式下切换行勾选 */
|
||||
async function toggleMultipleRow(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
|
||||
await gridApi.grid.setCheckboxRow(row, !selected);
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
|
||||
/** 双击行:单选直接确认;多选切换勾选 */
|
||||
|
|
@ -117,34 +103,34 @@ async function handleRowDblclick({
|
|||
row: MesWmMaterialStockApi.MaterialStock;
|
||||
}) {
|
||||
if (multiple.value) {
|
||||
const checked = !gridApi.grid.isCheckedByCheckboxRow(row);
|
||||
await gridApi.grid.setCheckboxRow(row, checked);
|
||||
handleCheckboxChange({
|
||||
checked,
|
||||
records:
|
||||
gridApi.grid.getCheckboxRecords() as MesWmMaterialStockApi.MaterialStock[],
|
||||
row,
|
||||
});
|
||||
await toggleMultipleRow(row);
|
||||
return;
|
||||
}
|
||||
selectedRows.value = [row];
|
||||
await syncSingleSelection(row);
|
||||
await gridApi.grid.setRadioRow(row);
|
||||
handleConfirm();
|
||||
}
|
||||
|
||||
/** 回显预选 */
|
||||
function applyPreSelection() {
|
||||
async function applyPreSelection() {
|
||||
if (preSelectedIds.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const rows = gridApi.grid.getData() as MesWmMaterialStockApi.MaterialStock[];
|
||||
for (const row of rows) {
|
||||
if (row.id && preSelectedIds.value.includes(row.id)) {
|
||||
gridApi.grid.setCheckboxRow(row, true);
|
||||
if (!multiple.value) {
|
||||
selectedRows.value = [row];
|
||||
}
|
||||
if (row.id === undefined || !preSelectedIds.value.includes(row.id)) {
|
||||
continue;
|
||||
}
|
||||
if (multiple.value) {
|
||||
await gridApi.grid.setCheckboxRow(row, true);
|
||||
} else {
|
||||
await gridApi.grid.setRadioRow(row);
|
||||
selectedRows.value = [row];
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (multiple.value) {
|
||||
selectedRows.value = getMultipleSelectedRows();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +139,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
schema: useSelectGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useSelectGridColumns(),
|
||||
columns: useSelectGridColumns(false),
|
||||
height: 480,
|
||||
keepSource: true,
|
||||
checkboxConfig: {
|
||||
|
|
@ -161,6 +147,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
range: true,
|
||||
reserve: true,
|
||||
},
|
||||
radioConfig: {
|
||||
highlight: true,
|
||||
trigger: 'row',
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
|
|
@ -191,8 +181,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
} as VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>,
|
||||
gridEvents: {
|
||||
cellDblclick: handleRowDblclick,
|
||||
checkboxAll: handleCheckboxAll,
|
||||
checkboxChange: handleCheckboxChange,
|
||||
checkboxAll: handleCheckboxSelectChange,
|
||||
checkboxChange: handleCheckboxSelectChange,
|
||||
radioChange: ({ row }: { row: MesWmMaterialStockApi.MaterialStock }) => {
|
||||
handleRadioChange(row);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -207,6 +200,8 @@ async function resetQueryState() {
|
|||
selectedRows.value = [];
|
||||
searchItemTypeId.value = undefined;
|
||||
await gridApi.grid.clearCheckboxRow();
|
||||
await gridApi.grid.clearCheckboxReserve();
|
||||
await gridApi.grid.clearRadioRow();
|
||||
await gridApi.formApi.resetForm();
|
||||
}
|
||||
|
||||
|
|
@ -216,13 +211,16 @@ async function openModal(
|
|||
options?: { multiple?: boolean },
|
||||
) {
|
||||
open.value = true;
|
||||
multiple.value = options?.multiple ?? true;
|
||||
multiple.value = options?.multiple ?? false;
|
||||
preSelectedIds.value = selectedIds || [];
|
||||
await nextTick();
|
||||
gridApi.setGridOptions({
|
||||
columns: useSelectGridColumns(multiple.value),
|
||||
});
|
||||
await resetQueryState();
|
||||
await gridApi.query();
|
||||
await nextTick();
|
||||
applyPreSelection();
|
||||
await applyPreSelection();
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
|
|
@ -233,14 +231,12 @@ async function closeModal() {
|
|||
|
||||
/** 确认选择 */
|
||||
function handleConfirm() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
|
||||
if (rows.length === 0) {
|
||||
ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
|
||||
return;
|
||||
}
|
||||
emit(
|
||||
'selected',
|
||||
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
|
||||
);
|
||||
emit('selected', multiple.value ? rows : [rows[0]!]);
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,11 +71,7 @@ async function resolveItemById(id: number | undefined) {
|
|||
if (selectedItem.value?.id === id) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
selectedItem.value = await getMaterialStock(id);
|
||||
} catch (error) {
|
||||
console.error('[WmMaterialStockSelect] resolveItemById failed:', error);
|
||||
}
|
||||
selectedItem.value = await getMaterialStock(id);
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, resolveItemById, { immediate: true });
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(
|
||||
onFrozenChange: (row: MesWmMaterialStockApi.MaterialStock) => void,
|
||||
onFrozenChange: (
|
||||
newFrozen: boolean,
|
||||
row: MesWmMaterialStockApi.MaterialStock,
|
||||
) => Promise<boolean | undefined>,
|
||||
): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
return [
|
||||
{
|
||||
|
|
@ -206,10 +209,12 @@ export function useSelectGridFormSchema(): VbenFormSchema[] {
|
|||
}
|
||||
|
||||
/** 选择弹窗的字段 */
|
||||
export function useSelectGridColumns(): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
export function useSelectGridColumns(
|
||||
multiple = false,
|
||||
): VxeTableGridOptions<MesWmMaterialStockApi.MaterialStock>['columns'] {
|
||||
return [
|
||||
{
|
||||
type: 'checkbox',
|
||||
type: multiple ? 'checkbox' : 'radio',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from '#/api/mes/wm/materialstock';
|
||||
import { $t } from '#/locales';
|
||||
import MdItemTypeTree from '#/views/mes/md/item/type/components/md-item-type-tree.vue';
|
||||
import { WmBatchDetail } from '#/views/mes/wm/batch/components';
|
||||
import AreaForm from '#/views/mes/wm/warehouse/area/modules/form.vue';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
|
@ -26,9 +27,14 @@ const [AreaModal, areaModalApi] = useVbenModal({
|
|||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const batchDetailRef = ref<InstanceType<typeof WmBatchDetail>>();
|
||||
|
||||
/** 处理冻结状态切换 */
|
||||
async function handleFrozenChange(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
const text = row.frozen ? '冻结' : '解冻';
|
||||
async function handleFrozenChange(
|
||||
newFrozen: boolean,
|
||||
row: MesWmMaterialStockApi.MaterialStock,
|
||||
): Promise<boolean | undefined> {
|
||||
const text = newFrozen ? '冻结' : '解冻';
|
||||
try {
|
||||
await confirm(`确认要"${text}"该库存记录吗?`);
|
||||
} catch {
|
||||
|
|
@ -36,7 +42,7 @@ async function handleFrozenChange(row: MesWmMaterialStockApi.MaterialStock) {
|
|||
}
|
||||
await updateMaterialStockFrozen({
|
||||
id: row.id!,
|
||||
frozen: row.frozen!,
|
||||
frozen: newFrozen,
|
||||
});
|
||||
ElMessage.success(`${text}成功`);
|
||||
return true;
|
||||
|
|
@ -90,6 +96,14 @@ function handleOpenAreaDetail(row: MesWmMaterialStockApi.MaterialStock) {
|
|||
areaModalApi.setData({ formType: 'detail', id: row.areaId }).open();
|
||||
}
|
||||
|
||||
/** 打开批次详情弹窗 */
|
||||
function handleOpenBatchDetail(row: MesWmMaterialStockApi.MaterialStock) {
|
||||
if (!row.batchId) {
|
||||
return;
|
||||
}
|
||||
batchDetailRef.value?.open(row.batchId);
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportMaterialStock({
|
||||
|
|
@ -110,6 +124,7 @@ async function handleExport() {
|
|||
</template>
|
||||
|
||||
<AreaModal />
|
||||
<WmBatchDetail ref="batchDetailRef" />
|
||||
|
||||
<div class="flex h-full gap-3">
|
||||
<div class="bg-card w-1/6 rounded p-3">
|
||||
|
|
@ -131,9 +146,16 @@ async function handleExport() {
|
|||
/>
|
||||
</template>
|
||||
<template #batchCode="{ row }">
|
||||
<span v-if="row.batchId" :title="row.batchCode">
|
||||
<ElButton
|
||||
v-if="row.batchId"
|
||||
link
|
||||
size="small"
|
||||
:title="row.batchCode"
|
||||
type="primary"
|
||||
@click="handleOpenBatchDetail(row)"
|
||||
>
|
||||
{{ row.batchCode }}
|
||||
</span>
|
||||
</ElButton>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #areaName="{ row }">
|
||||
|
|
|
|||
|
|
@ -285,6 +285,9 @@ catalogs:
|
|||
defu:
|
||||
specifier: ^6.1.7
|
||||
version: 6.1.7
|
||||
dhtmlx-gantt:
|
||||
specifier: ^9.1.1
|
||||
version: 9.1.4
|
||||
diagram-js:
|
||||
specifier: ^12.8.1
|
||||
version: 12.8.1
|
||||
|
|
@ -525,6 +528,9 @@ catalogs:
|
|||
tw-animate-css:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
tyme4ts:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
|
|
@ -819,6 +825,9 @@ importers:
|
|||
dayjs:
|
||||
specifier: 'catalog:'
|
||||
version: 1.11.20
|
||||
dhtmlx-gantt:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.4
|
||||
diagram-js:
|
||||
specifier: 'catalog:'
|
||||
version: 12.8.1
|
||||
|
|
@ -1071,6 +1080,9 @@ importers:
|
|||
dayjs:
|
||||
specifier: 'catalog:'
|
||||
version: 1.11.20
|
||||
dhtmlx-gantt:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.4
|
||||
diagram-js:
|
||||
specifier: 'catalog:'
|
||||
version: 12.8.1
|
||||
|
|
@ -1293,14 +1305,14 @@ importers:
|
|||
version: 2.9.8(vue@3.5.34(typescript@6.0.3))
|
||||
vitepress-plugin-group-icons:
|
||||
specifier: 'catalog:'
|
||||
version: 1.7.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))
|
||||
version: 1.7.5(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))
|
||||
devDependencies:
|
||||
'@nolebase/vitepress-plugin-git-changelog':
|
||||
specifier: 'catalog:'
|
||||
version: 2.18.2(vitepress@2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3))
|
||||
'@tailwindcss/vite':
|
||||
specifier: 'catalog:'
|
||||
version: 4.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))
|
||||
version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))
|
||||
'@vben/tailwind-config':
|
||||
specifier: workspace:*
|
||||
version: link:../internal/tailwind-config
|
||||
|
|
@ -1309,7 +1321,7 @@ importers:
|
|||
version: link:../internal/vite-config
|
||||
'@vite-pwa/vitepress':
|
||||
specifier: 'catalog:'
|
||||
version: 1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))
|
||||
version: 1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))
|
||||
vitepress:
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0)
|
||||
|
|
@ -7944,6 +7956,9 @@ packages:
|
|||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
dhtmlx-gantt@9.1.4:
|
||||
resolution: {integrity: sha512-XCNA5QUiuV79Xq1ykNpH9LFNR2IVpDZMqnmBV6dsBeOkHyPMOpkyQ/gqAPCcK2GAvYHoN2nGAMYb2LldCWhMuQ==}
|
||||
|
||||
diagram-js-direct-editing@3.3.0:
|
||||
resolution: {integrity: sha512-EjXYb35J3qBU8lLz5U81hn7wNykVmF7U5DXZ7BvPok2IX7rmPz+ZyaI5AEMiqaC6lpSnHqPxFcPgKEiJcAiv5w==}
|
||||
peerDependencies:
|
||||
|
|
@ -15694,6 +15709,13 @@ snapshots:
|
|||
tailwindcss: 4.3.0
|
||||
vite: 8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)
|
||||
|
||||
'@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.3.0
|
||||
'@tailwindcss/oxide': 4.3.0
|
||||
tailwindcss: 4.3.0
|
||||
vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)
|
||||
|
||||
'@tanstack/store@0.11.0': {}
|
||||
|
||||
'@tanstack/virtual-core@3.15.0': {}
|
||||
|
|
@ -16590,9 +16612,9 @@ snapshots:
|
|||
global: 4.4.0
|
||||
is-function: 1.0.2
|
||||
|
||||
'@vite-pwa/vitepress@1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))':
|
||||
'@vite-pwa/vitepress@1.1.0(vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1))':
|
||||
dependencies:
|
||||
vite-plugin-pwa: 1.3.0(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1)
|
||||
vite-plugin-pwa: 1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1)
|
||||
|
||||
'@vitejs/plugin-vue-jsx@5.1.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3))':
|
||||
dependencies:
|
||||
|
|
@ -18132,6 +18154,8 @@ snapshots:
|
|||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
dhtmlx-gantt@9.1.4: {}
|
||||
|
||||
diagram-js-direct-editing@3.3.0(diagram-js@14.11.3):
|
||||
dependencies:
|
||||
diagram-js: 14.11.3
|
||||
|
|
@ -22536,6 +22560,17 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-pwa@1.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(workbox-build@7.4.1)(workbox-window@7.4.1):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
pretty-bytes: 6.1.1
|
||||
tinyglobby: 0.2.16
|
||||
vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)
|
||||
workbox-build: 7.4.1
|
||||
workbox-window: 7.4.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-devtools@8.1.2(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-core': 8.1.2(vue@3.5.34(typescript@6.0.3))
|
||||
|
|
@ -22620,13 +22655,13 @@ snapshots:
|
|||
terser: 5.48.0
|
||||
yaml: 2.9.0
|
||||
|
||||
vitepress-plugin-group-icons@1.7.5(vite@8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)):
|
||||
vitepress-plugin-group-icons@1.7.5(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)):
|
||||
dependencies:
|
||||
'@iconify-json/logos': 1.2.11
|
||||
'@iconify-json/vscode-icons': 1.2.50
|
||||
'@iconify/utils': 3.1.3
|
||||
optionalDependencies:
|
||||
vite: 8.0.10(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)
|
||||
vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.48.0)(yaml@2.9.0)
|
||||
|
||||
vitepress@2.0.0-alpha.17(@types/node@25.9.1)(async-validator@4.2.5)(axios@1.16.1)(change-case@5.4.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(nprogress@0.2.0)(postcss@8.5.15)(qrcode@1.5.4)(sass-embedded@1.100.0)(sass@1.100.0)(sortablejs@1.15.7)(terser@5.48.0)(typescript@6.0.3)(yaml@2.9.0):
|
||||
dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue