feat(mes):完成 cal team 【班组】的迁移
parent
858011bfab
commit
2bcd81dc94
|
|
@ -386,6 +386,9 @@ watch(
|
||||||
|
|
||||||
// 使用 nextTick 确保在下一个 tick 中处理数据
|
// 使用 nextTick 确保在下一个 tick 中处理数据
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
if ((newValue || '') === paramsJson.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
handleDataDisplay(newValue || '');
|
handleDataDisplay(newValue || '');
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
IotRuleSceneTriggerTypeEnum,
|
IotRuleSceneTriggerTypeEnum,
|
||||||
isDeviceTrigger,
|
isDeviceTrigger,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
|
import { CronUtils } from '@vben/utils';
|
||||||
|
|
||||||
import { Form, message } from 'ant-design-vue';
|
import { Form, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
|
@ -158,13 +159,16 @@ function validateTriggers(_rule: any, value: any, callback: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
||||||
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER &&
|
if (!trigger.cronExpression) {
|
||||||
!trigger.cronExpression
|
|
||||||
) {
|
|
||||||
callback(new Error(`触发器 ${i + 1}:CRON 表达式不能为空`));
|
callback(new Error(`触发器 ${i + 1}:CRON 表达式不能为空`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!CronUtils.validate(trigger.cronExpression)) {
|
||||||
|
callback(new Error(`触发器 ${i + 1}:CRON 表达式格式不正确`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 递归校验 conditionGroups(嵌套条件组)
|
// 递归校验 conditionGroups(嵌套条件组)
|
||||||
if (trigger.conditionGroups?.length) {
|
if (trigger.conditionGroups?.length) {
|
||||||
for (const [gi, group] of trigger.conditionGroups.entries()) {
|
for (const [gi, group] of trigger.conditionGroups.entries()) {
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,11 @@ const childDataTypeOptions = getDataTypeOptions().filter(
|
||||||
|
|
||||||
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>;
|
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||||
|
|
||||||
/** 元素类型切到 struct 时,初始化 dataSpecsList 占位 */
|
/** 元素类型切换时,清理旧子类型的结构体属性配置 */
|
||||||
function handleChange(e: any) {
|
function handleChange(e: any) {
|
||||||
const val = e?.target?.value ?? e;
|
const val = e?.target?.value ?? e;
|
||||||
if (val !== IoTDataSpecsDataTypeEnum.STRUCT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dataSpecs.value.dataSpecsList = [];
|
dataSpecs.value.dataSpecsList = [];
|
||||||
|
dataSpecs.value.childDataType = val;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,17 @@ import { IoTDataSpecsDataTypeEnum } from '@vben/constants';
|
||||||
import { cloneDeep, isEmpty } from '@vben/utils';
|
import { cloneDeep, isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { useVModel } from '@vueuse/core';
|
import { useVModel } from '@vueuse/core';
|
||||||
import { Button, Divider, Form, Input } from 'ant-design-vue';
|
import { Button, Divider, Form, Input, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { ThingModelFormRules } from '#/api/iot/thingmodel';
|
import { ThingModelFormRules } from '#/api/iot/thingmodel';
|
||||||
|
|
||||||
import ThingModelProperty from './property.vue';
|
import ThingModelProperty from './property.vue';
|
||||||
|
|
||||||
const props = defineProps<{ direction: string; modelValue: any }>();
|
const props = defineProps<{
|
||||||
|
direction: string;
|
||||||
|
existingIdentifiers?: string[];
|
||||||
|
modelValue: any;
|
||||||
|
}>();
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
const thingModelParams = useVModel(props, 'modelValue', emits) as Ref<any[]>;
|
const thingModelParams = useVModel(props, 'modelValue', emits) as Ref<any[]>;
|
||||||
|
|
||||||
|
|
@ -33,6 +37,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
// 组装表单
|
// 组装表单
|
||||||
const data = formData.value;
|
const data = formData.value;
|
||||||
|
if (
|
||||||
|
data.identifier &&
|
||||||
|
props.existingIdentifiers?.includes(data.identifier)
|
||||||
|
) {
|
||||||
|
message.warning('输入参数和输出参数标识符不能重复');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const item = {
|
const item = {
|
||||||
identifier: data.identifier,
|
identifier: data.identifier,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,17 @@ watch(
|
||||||
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
|
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 提取参数标识符列表,用于输入 / 输出参数跨表去重 */
|
||||||
|
function getParamIdentifiers(params?: any[]) {
|
||||||
|
const identifiers: string[] = [];
|
||||||
|
for (const item of params || []) {
|
||||||
|
if (item.identifier) {
|
||||||
|
identifiers.push(item.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -51,12 +62,14 @@ watch(
|
||||||
<ThingModelInputOutputParam
|
<ThingModelInputOutputParam
|
||||||
v-model="service.inputParams"
|
v-model="service.inputParams"
|
||||||
:direction="IoTThingModelParamDirectionEnum.INPUT"
|
:direction="IoTThingModelParamDirectionEnum.INPUT"
|
||||||
|
:existing-identifiers="getParamIdentifiers(service.outputParams)"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="输出参数">
|
<Form.Item label="输出参数">
|
||||||
<ThingModelInputOutputParam
|
<ThingModelInputOutputParam
|
||||||
v-model="service.outputParams"
|
v-model="service.outputParams"
|
||||||
:direction="IoTThingModelParamDirectionEnum.OUTPUT"
|
:direction="IoTThingModelParamDirectionEnum.OUTPUT"
|
||||||
|
:existing-identifiers="getParamIdentifiers(service.inputParams)"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
|
||||||
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getTeamPage } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { useTeamSelectGridColumns, useTeamSelectGridFormSchema } from '../data';
|
||||||
|
|
||||||
|
const emit = defineEmits<{ selected: [rows: MesCalTeamApi.Team[]] }>();
|
||||||
|
const open = ref(false); // 弹窗是否打开
|
||||||
|
const multiple = ref(true); // 是否多选
|
||||||
|
const selectedRows = ref<MesCalTeamApi.Team[]>([]); // 已选班组列表
|
||||||
|
const preSelectedIds = ref<number[]>([]); // 预选班组编号列表
|
||||||
|
|
||||||
|
/** 处理勾选变化 */
|
||||||
|
function handleCheckboxChange({ records }: { records: MesCalTeamApi.Team[] }) {
|
||||||
|
selectedRows.value = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理全选变化 */
|
||||||
|
function handleCheckboxAll({ records }: { records: MesCalTeamApi.Team[] }) {
|
||||||
|
selectedRows.value = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 双击行:多选切换勾选,单选直接确认 */
|
||||||
|
function handleCellDblclick({ row }: { row: MesCalTeamApi.Team }) {
|
||||||
|
if (multiple.value) {
|
||||||
|
const records = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
const checked = records.some((item) => item.id === row.id);
|
||||||
|
gridApi.grid.setCheckboxRow(row, !checked);
|
||||||
|
selectedRows.value = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedRows.value = [row];
|
||||||
|
handleConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回显预选班组 */
|
||||||
|
function applyPreSelection() {
|
||||||
|
if (preSelectedIds.value.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rows = gridApi.grid.getData() as MesCalTeamApi.Team[];
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.id && preSelectedIds.value.includes(row.id)) {
|
||||||
|
gridApi.grid.setCheckboxRow(row, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedRows.value = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useTeamSelectGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useTeamSelectGridColumns(),
|
||||||
|
height: 520,
|
||||||
|
keepSource: true,
|
||||||
|
checkboxConfig: {
|
||||||
|
highlight: true,
|
||||||
|
range: true,
|
||||||
|
reserve: true,
|
||||||
|
},
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) =>
|
||||||
|
await getTeamPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: true,
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamApi.Team>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleCheckboxAll,
|
||||||
|
checkboxChange: handleCheckboxChange,
|
||||||
|
cellDblclick: handleCellDblclick,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 重置查询和选择状态 */
|
||||||
|
async function resetQueryState() {
|
||||||
|
selectedRows.value = [];
|
||||||
|
await gridApi.grid.clearCheckboxRow();
|
||||||
|
await gridApi.formApi.resetForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开班组选择弹窗 */
|
||||||
|
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) {
|
||||||
|
open.value = true;
|
||||||
|
multiple.value = options?.multiple ?? true;
|
||||||
|
preSelectedIds.value = selectedIds || [];
|
||||||
|
await nextTick();
|
||||||
|
await resetQueryState();
|
||||||
|
await gridApi.query();
|
||||||
|
await nextTick();
|
||||||
|
applyPreSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭班组选择弹窗 */
|
||||||
|
async function closeModal() {
|
||||||
|
open.value = false;
|
||||||
|
await resetQueryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认选择班组 */
|
||||||
|
function handleConfirm() {
|
||||||
|
if (selectedRows.value.length === 0) {
|
||||||
|
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]);
|
||||||
|
open.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open: openModal });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
v-model:open="open"
|
||||||
|
title="班组选择"
|
||||||
|
width="720px"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@ok="handleConfirm"
|
||||||
|
@cancel="closeModal"
|
||||||
|
>
|
||||||
|
<Grid table-title="班组列表" />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||||
|
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { Button, Select } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { getTeamList } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import CalTeamSelectDialog from './cal-team-select-dialog.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
allowClear?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
modelValue?: number;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
allowClear: true,
|
||||||
|
disabled: false,
|
||||||
|
modelValue: undefined,
|
||||||
|
placeholder: '请选择班组',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const emit = defineEmits<{
|
||||||
|
change: [row?: MesCalTeamApi.Team];
|
||||||
|
'update:modelValue': [value?: number];
|
||||||
|
}>();
|
||||||
|
const teamList = ref<MesCalTeamApi.Team[]>([]); // 班组选项
|
||||||
|
const dialogRef = ref<InstanceType<typeof CalTeamSelectDialog>>(); // 班组选择弹窗
|
||||||
|
|
||||||
|
/** 加载班组选项 */
|
||||||
|
async function loadTeamList() {
|
||||||
|
teamList.value = await getTeamList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理下拉选择变化 */
|
||||||
|
function handleChange(value: SelectValue) {
|
||||||
|
const teamId = typeof value === 'number' ? value : undefined;
|
||||||
|
emit('update:modelValue', teamId);
|
||||||
|
emit(
|
||||||
|
'change',
|
||||||
|
teamList.value.find((item) => item.id === teamId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开班组选择弹窗 */
|
||||||
|
function openDialog() {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dialogRef.value?.open(props.modelValue ? [props.modelValue] : [], { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理弹窗选择 */
|
||||||
|
function handleSelected(rows: MesCalTeamApi.Team[]) {
|
||||||
|
const row = rows[0];
|
||||||
|
emit('update:modelValue', row?.id);
|
||||||
|
emit('change', row);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadTeamList);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full gap-2">
|
||||||
|
<Select
|
||||||
|
:allow-clear="allowClear"
|
||||||
|
:disabled="disabled"
|
||||||
|
:field-names="{ label: 'name', value: 'id' }"
|
||||||
|
:options="teamList"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="modelValue"
|
||||||
|
class="flex-1"
|
||||||
|
option-filter-prop="name"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
<Button :disabled="disabled" @click="openDialog">选择</Button>
|
||||||
|
<CalTeamSelectDialog ref="dialogRef" :multiple="false" @selected="handleSelected" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as CalTeamSelectDialog } from './cal-team-select-dialog.vue';
|
||||||
|
export { default as CalTeamSelect } from './cal-team-select.vue';
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { z } from '#/adapter/form';
|
||||||
|
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||||
|
import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants';
|
||||||
|
|
||||||
|
/** 新增/修改班组的表单 */
|
||||||
|
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 64,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
rules: z.string().min(1, '班组编码不能为空').max(64),
|
||||||
|
suffix: () =>
|
||||||
|
h(
|
||||||
|
Button,
|
||||||
|
{
|
||||||
|
type: 'default',
|
||||||
|
onClick: async () => {
|
||||||
|
try {
|
||||||
|
const code = await generateAutoCode(MesAutoCodeRuleCode.CAL_TEAM_CODE);
|
||||||
|
await formApi?.setFieldValue('code', code);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => '生成' },
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 100,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
rules: z.string().min(1, '班组名称不能为空').max(100),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'calendarType',
|
||||||
|
label: '班组类型',
|
||||||
|
component: 'RadioGroup',
|
||||||
|
componentProps: {
|
||||||
|
buttonStyle: 'solid',
|
||||||
|
optionType: 'button',
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE, 'number'),
|
||||||
|
},
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 250,
|
||||||
|
placeholder: '请输入备注',
|
||||||
|
rows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'calendarType',
|
||||||
|
label: '班组类型',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE, 'number'),
|
||||||
|
placeholder: '请选择班组类型',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns(): VxeTableGridOptions<MesCalTeamApi.Team>['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'code',
|
||||||
|
title: '班组编码',
|
||||||
|
minWidth: 150,
|
||||||
|
slots: {
|
||||||
|
default: 'code',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ field: 'name', title: '班组名称', minWidth: 150 },
|
||||||
|
{
|
||||||
|
field: 'calendarType',
|
||||||
|
title: '班组类型',
|
||||||
|
width: 140,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_CAL_CALENDAR_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 180 },
|
||||||
|
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 180,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: {
|
||||||
|
default: 'actions',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 班组选择弹窗搜索表单 */
|
||||||
|
export function useTeamSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 班组选择弹窗字段 */
|
||||||
|
export function useTeamSelectGridColumns(): VxeTableGridOptions<MesCalTeamApi.Team>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: 'checkbox', width: 50 },
|
||||||
|
{ field: 'code', title: '班组编码', minWidth: 140 },
|
||||||
|
{ field: 'name', title: '班组名称', minWidth: 140 },
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 160 },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { deleteTeam, exportTeam, getTeamPage } from '#/api/mes/cal/team';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import Form from './modules/form.vue';
|
||||||
|
|
||||||
|
const [FormModal, formModalApi] = useVbenModal({
|
||||||
|
connectedComponent: Form,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function handleRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建班组 */
|
||||||
|
function handleCreate() {
|
||||||
|
formModalApi.setData({ type: 'create' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看班组 */
|
||||||
|
function handleDetail(row: MesCalTeamApi.Team) {
|
||||||
|
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑班组 */
|
||||||
|
function handleEdit(row: MesCalTeamApi.Team) {
|
||||||
|
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除班组 */
|
||||||
|
async function handleDelete(row: MesCalTeamApi.Team) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteTeam(row.id!);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
|
handleRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出班组 */
|
||||||
|
async function handleExport() {
|
||||||
|
const data = await exportTeam(await gridApi.formApi.getValues());
|
||||||
|
downloadFileFromBlobPart({ fileName: '班组.xls', source: data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getTeamPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: true,
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamApi.Team>,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<FormModal @success="handleRefresh" />
|
||||||
|
<Grid table-title="班组列表">
|
||||||
|
<template #toolbar-tools>
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('ui.actionTitle.create', ['班组']),
|
||||||
|
type: 'primary',
|
||||||
|
icon: ACTION_ICON.ADD,
|
||||||
|
auth: ['mes:cal-team:create'],
|
||||||
|
onClick: handleCreate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('ui.actionTitle.export'),
|
||||||
|
type: 'primary',
|
||||||
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
|
auth: ['mes:cal-team:export'],
|
||||||
|
onClick: handleExport,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #code="{ row }">
|
||||||
|
<Button type="link" @click="handleDetail(row)">{{ row.code }}</Button>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['mes:cal-team:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['mes:cal-team:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { message, Tabs } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { createTeam, getTeam, updateTeam } from '#/api/mes/cal/team';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useFormSchema } from '../data';
|
||||||
|
import MemberList from './member-list.vue';
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['success']);
|
||||||
|
const formMode = ref<'create' | 'detail' | 'update'>('create'); // 表单模式
|
||||||
|
const subTabsName = ref('member'); // 当前资源页签
|
||||||
|
const formData = ref<MesCalTeamApi.Team>();
|
||||||
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
if (formMode.value === 'detail') {
|
||||||
|
return $t('ui.actionTitle.view', ['班组']);
|
||||||
|
}
|
||||||
|
return formMode.value === 'update'
|
||||||
|
? $t('ui.actionTitle.edit', ['班组'])
|
||||||
|
: $t('ui.actionTitle.create', ['班组']);
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
labelWidth: 100,
|
||||||
|
},
|
||||||
|
wrapperClass: 'grid-cols-3',
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: [],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */
|
||||||
|
formApi.setState({ schema: useFormSchema(formApi) });
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onConfirm() {
|
||||||
|
if (isDetail.value) {
|
||||||
|
await modalApi.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
|
const data = (await formApi.getValues()) as MesCalTeamApi.Team;
|
||||||
|
try {
|
||||||
|
if (formMode.value === 'create') {
|
||||||
|
const id = await createTeam(data);
|
||||||
|
formData.value = { ...data, id: id as number };
|
||||||
|
await formApi.setFieldValue('id', id);
|
||||||
|
formMode.value = 'update';
|
||||||
|
} else {
|
||||||
|
await updateTeam(data);
|
||||||
|
formData.value = { ...formData.value, ...data };
|
||||||
|
}
|
||||||
|
emit('success');
|
||||||
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
formData.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await formApi.resetForm();
|
||||||
|
subTabsName.value = 'member';
|
||||||
|
const data = modalApi.getData<{ id?: number; type?: 'create' | 'detail' | 'update' }>();
|
||||||
|
formMode.value = data?.type || 'create';
|
||||||
|
formApi.setDisabled(formMode.value === 'detail');
|
||||||
|
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||||
|
if (!data?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getTeam(data.id);
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal :title="getTitle" class="w-4/5">
|
||||||
|
<Form class="mx-4" />
|
||||||
|
<Tabs
|
||||||
|
v-if="formMode !== 'create' && formData?.id"
|
||||||
|
v-model:active-key="subTabsName"
|
||||||
|
class="mx-4 mt-4"
|
||||||
|
>
|
||||||
|
<Tabs.TabPane key="member" tab="班组成员">
|
||||||
|
<MemberList :form-type="formMode" :team-id="formData.id" />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamMemberApi } from '#/api/mes/cal/team/member';
|
||||||
|
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import {
|
||||||
|
createTeamMember,
|
||||||
|
deleteTeamMember,
|
||||||
|
getTeamMemberListByTeam,
|
||||||
|
} from '#/api/mes/cal/team/member';
|
||||||
|
import { getSimpleUserList } from '#/api/system/user';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{ formType?: string; teamId: number }>(), {
|
||||||
|
formType: 'update',
|
||||||
|
});
|
||||||
|
const isEditable = computed(() => ['create', 'update'].includes(props.formType)); // 是否可编辑
|
||||||
|
const formOpen = ref(false); // 成员表单是否打开
|
||||||
|
const formLoading = ref(false); // 成员表单提交中
|
||||||
|
const list = ref<MesCalTeamMemberApi.TeamMember[]>([]); // 成员列表
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
labelWidth: 80,
|
||||||
|
},
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
fieldName: 'teamId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'userId',
|
||||||
|
label: '用户',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
api: getSimpleUserList,
|
||||||
|
labelField: 'nickname',
|
||||||
|
placeholder: '请选择用户',
|
||||||
|
showSearch: true,
|
||||||
|
valueField: 'id',
|
||||||
|
},
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入备注',
|
||||||
|
rows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
autoResize: true,
|
||||||
|
border: true,
|
||||||
|
columns: [
|
||||||
|
{ field: 'userId', title: '用户编号', width: 100 },
|
||||||
|
{ field: 'nickname', title: '用户昵称', minWidth: 120 },
|
||||||
|
{ field: 'telephone', title: '手机号', minWidth: 120 },
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 160 },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 90,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: {
|
||||||
|
default: 'actions',
|
||||||
|
},
|
||||||
|
visible: isEditable.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: list.value,
|
||||||
|
minHeight: 240,
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
isHover: true,
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
showOverflow: true,
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamMemberApi.TeamMember>,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 加载成员列表 */
|
||||||
|
async function getList() {
|
||||||
|
gridApi.setLoading(true);
|
||||||
|
try {
|
||||||
|
list.value = await getTeamMemberListByTeam(props.teamId);
|
||||||
|
gridApi.setGridOptions({ data: list.value });
|
||||||
|
} finally {
|
||||||
|
gridApi.setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开成员表单 */
|
||||||
|
async function openForm() {
|
||||||
|
formOpen.value = true;
|
||||||
|
await formApi.resetForm();
|
||||||
|
await formApi.setValues({ teamId: props.teamId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交成员表单 */
|
||||||
|
async function submitForm() {
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formLoading.value = true;
|
||||||
|
try {
|
||||||
|
const data = (await formApi.getValues()) as MesCalTeamMemberApi.TeamMember;
|
||||||
|
await createTeamMember(data);
|
||||||
|
formOpen.value = false;
|
||||||
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
await getList();
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除成员 */
|
||||||
|
async function handleDelete(id: number) {
|
||||||
|
await deleteTeamMember(id);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess', ['成员']));
|
||||||
|
await getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.teamId,
|
||||||
|
(value) => {
|
||||||
|
if (value) {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
|
||||||
|
<TableAction :actions="[{ label: '添加成员', type: 'primary', onClick: openForm }]" />
|
||||||
|
</div>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除该成员吗?',
|
||||||
|
confirm: handleDelete.bind(null, row.id!),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
v-model:open="formOpen"
|
||||||
|
title="添加成员"
|
||||||
|
width="520px"
|
||||||
|
:confirm-loading="formLoading"
|
||||||
|
@ok="submitForm"
|
||||||
|
>
|
||||||
|
<Form class="mx-4" />
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -386,6 +386,9 @@ watch(
|
||||||
|
|
||||||
// 使用 nextTick 确保在下一个 tick 中处理数据
|
// 使用 nextTick 确保在下一个 tick 中处理数据
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
if ((newValue || '') === paramsJson.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
handleDataDisplay(newValue || '');
|
handleDataDisplay(newValue || '');
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
IotRuleSceneTriggerTypeEnum,
|
IotRuleSceneTriggerTypeEnum,
|
||||||
isDeviceTrigger,
|
isDeviceTrigger,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
|
import { CronUtils } from '@vben/utils';
|
||||||
|
|
||||||
import { ElForm, ElMessage } from 'element-plus';
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
|
@ -158,13 +159,16 @@ function validateTriggers(_rule: any, value: any, callback: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
||||||
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER &&
|
if (!trigger.cronExpression) {
|
||||||
!trigger.cronExpression
|
|
||||||
) {
|
|
||||||
callback(new Error(`触发器 ${i + 1}:CRON 表达式不能为空`));
|
callback(new Error(`触发器 ${i + 1}:CRON 表达式不能为空`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!CronUtils.validate(trigger.cronExpression)) {
|
||||||
|
callback(new Error(`触发器 ${i + 1}:CRON 表达式格式不正确`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 递归校验 conditionGroups(嵌套条件组)
|
// 递归校验 conditionGroups(嵌套条件组)
|
||||||
if (trigger.conditionGroups?.length) {
|
if (trigger.conditionGroups?.length) {
|
||||||
for (const [gi, group] of trigger.conditionGroups.entries()) {
|
for (const [gi, group] of trigger.conditionGroups.entries()) {
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,10 @@ const childDataTypeOptions = getDataTypeOptions().filter(
|
||||||
|
|
||||||
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>;
|
const dataSpecs = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||||
|
|
||||||
/** 元素类型切到 struct 时,初始化 dataSpecsList 占位 */
|
/** 元素类型切换时,清理旧子类型的结构体属性配置 */
|
||||||
function handleChange(val: any) {
|
function handleChange(val: any) {
|
||||||
if (val !== IoTDataSpecsDataTypeEnum.STRUCT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dataSpecs.value.dataSpecsList = [];
|
dataSpecs.value.dataSpecsList = [];
|
||||||
|
dataSpecs.value.childDataType = val;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,18 @@ import {
|
||||||
ElForm,
|
ElForm,
|
||||||
ElFormItem,
|
ElFormItem,
|
||||||
ElInput,
|
ElInput,
|
||||||
|
ElMessage,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
|
|
||||||
import { ThingModelFormRules } from '#/api/iot/thingmodel';
|
import { ThingModelFormRules } from '#/api/iot/thingmodel';
|
||||||
|
|
||||||
import ThingModelProperty from './property.vue';
|
import ThingModelProperty from './property.vue';
|
||||||
|
|
||||||
const props = defineProps<{ direction: string; modelValue: any }>();
|
const props = defineProps<{
|
||||||
|
direction: string;
|
||||||
|
existingIdentifiers?: string[];
|
||||||
|
modelValue: any;
|
||||||
|
}>();
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
const thingModelParams = useVModel(props, 'modelValue', emits) as Ref<any[]>;
|
const thingModelParams = useVModel(props, 'modelValue', emits) as Ref<any[]>;
|
||||||
|
|
||||||
|
|
@ -39,6 +44,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
// 组装表单
|
// 组装表单
|
||||||
const data = formData.value;
|
const data = formData.value;
|
||||||
|
if (
|
||||||
|
data.identifier &&
|
||||||
|
props.existingIdentifiers?.includes(data.identifier)
|
||||||
|
) {
|
||||||
|
ElMessage.warning('输入参数和输出参数标识符不能重复');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const item = {
|
const item = {
|
||||||
identifier: data.identifier,
|
identifier: data.identifier,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,17 @@ watch(
|
||||||
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
|
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 提取参数标识符列表,用于输入 / 输出参数跨表去重 */
|
||||||
|
function getParamIdentifiers(params?: any[]) {
|
||||||
|
const identifiers: string[] = [];
|
||||||
|
for (const item of params || []) {
|
||||||
|
if (item.identifier) {
|
||||||
|
identifiers.push(item.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return identifiers;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -51,12 +62,14 @@ watch(
|
||||||
<ThingModelInputOutputParam
|
<ThingModelInputOutputParam
|
||||||
v-model="service.inputParams"
|
v-model="service.inputParams"
|
||||||
:direction="IoTThingModelParamDirectionEnum.INPUT"
|
:direction="IoTThingModelParamDirectionEnum.INPUT"
|
||||||
|
:existing-identifiers="getParamIdentifiers(service.outputParams)"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="输出参数">
|
<ElFormItem label="输出参数">
|
||||||
<ThingModelInputOutputParam
|
<ThingModelInputOutputParam
|
||||||
v-model="service.outputParams"
|
v-model="service.outputParams"
|
||||||
:direction="IoTThingModelParamDirectionEnum.OUTPUT"
|
:direction="IoTThingModelParamDirectionEnum.OUTPUT"
|
||||||
|
:existing-identifiers="getParamIdentifiers(service.inputParams)"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
|
||||||
|
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getTeamPage } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { useTeamSelectGridColumns, useTeamSelectGridFormSchema } from '../data';
|
||||||
|
|
||||||
|
const emit = defineEmits<{ selected: [rows: MesCalTeamApi.Team[]] }>();
|
||||||
|
const open = ref(false); // 弹窗是否打开
|
||||||
|
const multiple = ref(true); // 是否多选
|
||||||
|
const selectedRows = ref<MesCalTeamApi.Team[]>([]); // 已选班组列表
|
||||||
|
const preSelectedIds = ref<number[]>([]); // 预选班组编号列表
|
||||||
|
|
||||||
|
/** 处理勾选变化 */
|
||||||
|
function handleCheckboxChange({ records }: { records: MesCalTeamApi.Team[] }) {
|
||||||
|
selectedRows.value = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理全选变化 */
|
||||||
|
function handleCheckboxAll({ records }: { records: MesCalTeamApi.Team[] }) {
|
||||||
|
selectedRows.value = records;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 双击行:多选切换勾选,单选直接确认 */
|
||||||
|
function handleCellDblclick({ row }: { row: MesCalTeamApi.Team }) {
|
||||||
|
if (multiple.value) {
|
||||||
|
const records = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
const checked = records.some((item) => item.id === row.id);
|
||||||
|
gridApi.grid.setCheckboxRow(row, !checked);
|
||||||
|
selectedRows.value = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedRows.value = [row];
|
||||||
|
handleConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回显预选班组 */
|
||||||
|
function applyPreSelection() {
|
||||||
|
if (preSelectedIds.value.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rows = gridApi.grid.getData() as MesCalTeamApi.Team[];
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.id && preSelectedIds.value.includes(row.id)) {
|
||||||
|
gridApi.grid.setCheckboxRow(row, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedRows.value = gridApi.grid.getCheckboxRecords() as MesCalTeamApi.Team[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useTeamSelectGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useTeamSelectGridColumns(),
|
||||||
|
height: 520,
|
||||||
|
keepSource: true,
|
||||||
|
checkboxConfig: {
|
||||||
|
highlight: true,
|
||||||
|
range: true,
|
||||||
|
reserve: true,
|
||||||
|
},
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) =>
|
||||||
|
await getTeamPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: true,
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamApi.Team>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleCheckboxAll,
|
||||||
|
checkboxChange: handleCheckboxChange,
|
||||||
|
cellDblclick: handleCellDblclick,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 重置查询和选择状态 */
|
||||||
|
async function resetQueryState() {
|
||||||
|
selectedRows.value = [];
|
||||||
|
await gridApi.grid.clearCheckboxRow();
|
||||||
|
await gridApi.formApi.resetForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开班组选择弹窗 */
|
||||||
|
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) {
|
||||||
|
open.value = true;
|
||||||
|
multiple.value = options?.multiple ?? true;
|
||||||
|
preSelectedIds.value = selectedIds || [];
|
||||||
|
await nextTick();
|
||||||
|
await resetQueryState();
|
||||||
|
await gridApi.query();
|
||||||
|
await nextTick();
|
||||||
|
applyPreSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭班组选择弹窗 */
|
||||||
|
async function closeModal() {
|
||||||
|
open.value = false;
|
||||||
|
await resetQueryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认选择班组 */
|
||||||
|
function handleConfirm() {
|
||||||
|
if (selectedRows.value.length === 0) {
|
||||||
|
ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]);
|
||||||
|
open.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open: openModal });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElDialog v-model="open" title="班组选择" width="720px" destroy-on-close @close="closeModal">
|
||||||
|
<Grid table-title="班组列表" />
|
||||||
|
<template #footer>
|
||||||
|
<ElButton @click="closeModal">取消</ElButton>
|
||||||
|
<ElButton type="primary" @click="handleConfirm">确定</ElButton>
|
||||||
|
</template>
|
||||||
|
</ElDialog>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { ElButton, ElOption, ElSelect } from 'element-plus';
|
||||||
|
|
||||||
|
import { getTeamList } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import CalTeamSelectDialog from './cal-team-select-dialog.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
clearable?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
modelValue?: number;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
clearable: true,
|
||||||
|
disabled: false,
|
||||||
|
modelValue: undefined,
|
||||||
|
placeholder: '请选择班组',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const emit = defineEmits<{
|
||||||
|
change: [row?: MesCalTeamApi.Team];
|
||||||
|
'update:modelValue': [value?: number];
|
||||||
|
}>();
|
||||||
|
const teamList = ref<MesCalTeamApi.Team[]>([]); // 班组选项
|
||||||
|
const dialogRef = ref<InstanceType<typeof CalTeamSelectDialog>>(); // 班组选择弹窗
|
||||||
|
|
||||||
|
/** 加载班组选项 */
|
||||||
|
async function loadTeamList() {
|
||||||
|
teamList.value = await getTeamList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理下拉选择变化 */
|
||||||
|
function handleChange(value: number | string | undefined) {
|
||||||
|
const teamId = typeof value === 'number' ? value : undefined;
|
||||||
|
emit('update:modelValue', teamId);
|
||||||
|
emit(
|
||||||
|
'change',
|
||||||
|
teamList.value.find((item) => item.id === teamId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开班组选择弹窗 */
|
||||||
|
function openDialog() {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dialogRef.value?.open(props.modelValue ? [props.modelValue] : [], { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理弹窗选择 */
|
||||||
|
function handleSelected(rows: MesCalTeamApi.Team[]) {
|
||||||
|
const row = rows[0];
|
||||||
|
emit('update:modelValue', row?.id);
|
||||||
|
emit('change', row);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadTeamList);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full gap-2">
|
||||||
|
<ElSelect
|
||||||
|
:clearable="clearable"
|
||||||
|
:disabled="disabled"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
class="flex-1"
|
||||||
|
@change="handleChange"
|
||||||
|
>
|
||||||
|
<ElOption v-for="item in teamList" :key="item.id" :label="item.name" :value="item.id!" />
|
||||||
|
</ElSelect>
|
||||||
|
<ElButton :disabled="disabled" @click="openDialog">选择</ElButton>
|
||||||
|
<CalTeamSelectDialog ref="dialogRef" :multiple="false" @selected="handleSelected" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as CalTeamSelectDialog } from './cal-team-select-dialog.vue';
|
||||||
|
export { default as CalTeamSelect } from './cal-team-select.vue';
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
|
import { ElButton } from 'element-plus';
|
||||||
|
|
||||||
|
import { z } from '#/adapter/form';
|
||||||
|
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||||
|
import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants';
|
||||||
|
|
||||||
|
/** 新增/修改班组的表单 */
|
||||||
|
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 64,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
rules: z.string().min(1, '班组编码不能为空').max(64),
|
||||||
|
suffix: () =>
|
||||||
|
h(
|
||||||
|
ElButton,
|
||||||
|
{
|
||||||
|
type: 'default',
|
||||||
|
onClick: async () => {
|
||||||
|
try {
|
||||||
|
const code = await generateAutoCode(MesAutoCodeRuleCode.CAL_TEAM_CODE);
|
||||||
|
await formApi?.setFieldValue('code', code);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => '生成' },
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 100,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
rules: z.string().min(1, '班组名称不能为空').max(100),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'calendarType',
|
||||||
|
label: '班组类型',
|
||||||
|
component: 'RadioGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE, 'number'),
|
||||||
|
},
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
formItemClass: 'col-span-3',
|
||||||
|
componentProps: {
|
||||||
|
maxLength: 250,
|
||||||
|
placeholder: '请输入备注',
|
||||||
|
rows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'calendarType',
|
||||||
|
label: '班组类型',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE, 'number'),
|
||||||
|
placeholder: '请选择班组类型',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns(): VxeTableGridOptions<MesCalTeamApi.Team>['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'code',
|
||||||
|
title: '班组编码',
|
||||||
|
minWidth: 150,
|
||||||
|
slots: {
|
||||||
|
default: 'code',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ field: 'name', title: '班组名称', minWidth: 150 },
|
||||||
|
{
|
||||||
|
field: 'calendarType',
|
||||||
|
title: '班组类型',
|
||||||
|
width: 140,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_CAL_CALENDAR_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 180 },
|
||||||
|
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 180,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: {
|
||||||
|
default: 'actions',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 班组选择弹窗搜索表单 */
|
||||||
|
export function useTeamSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '班组编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入班组编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '班组名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入班组名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 班组选择弹窗字段 */
|
||||||
|
export function useTeamSelectGridColumns(): VxeTableGridOptions<MesCalTeamApi.Team>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: 'checkbox', width: 50 },
|
||||||
|
{ field: 'code', title: '班组编码', minWidth: 140 },
|
||||||
|
{ field: 'name', title: '班组名称', minWidth: 140 },
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 160 },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
|
import { ElButton, ElLoading, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { deleteTeam, exportTeam, getTeamPage } from '#/api/mes/cal/team';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import Form from './modules/form.vue';
|
||||||
|
|
||||||
|
const [FormModal, formModalApi] = useVbenModal({
|
||||||
|
connectedComponent: Form,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function handleRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建班组 */
|
||||||
|
function handleCreate() {
|
||||||
|
formModalApi.setData({ type: 'create' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看班组 */
|
||||||
|
function handleDetail(row: MesCalTeamApi.Team) {
|
||||||
|
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑班组 */
|
||||||
|
function handleEdit(row: MesCalTeamApi.Team) {
|
||||||
|
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除班组 */
|
||||||
|
async function handleDelete(row: MesCalTeamApi.Team) {
|
||||||
|
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||||
|
try {
|
||||||
|
await deleteTeam(row.id!);
|
||||||
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||||
|
handleRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出班组 */
|
||||||
|
async function handleExport() {
|
||||||
|
const data = await exportTeam(await gridApi.formApi.getValues());
|
||||||
|
downloadFileFromBlobPart({ fileName: '班组.xls', source: data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getTeamPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: true,
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamApi.Team>,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<FormModal @success="handleRefresh" />
|
||||||
|
<Grid table-title="班组列表">
|
||||||
|
<template #toolbar-tools>
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('ui.actionTitle.create', ['班组']),
|
||||||
|
type: 'primary',
|
||||||
|
icon: ACTION_ICON.ADD,
|
||||||
|
auth: ['mes:cal-team:create'],
|
||||||
|
onClick: handleCreate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('ui.actionTitle.export'),
|
||||||
|
type: 'primary',
|
||||||
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
|
auth: ['mes:cal-team:export'],
|
||||||
|
onClick: handleExport,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #code="{ row }">
|
||||||
|
<ElButton link type="primary" @click="handleDetail(row)">{{ row.code }}</ElButton>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'primary',
|
||||||
|
link: true,
|
||||||
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['mes:cal-team:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'danger',
|
||||||
|
link: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['mes:cal-team:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { createTeam, getTeam, updateTeam } from '#/api/mes/cal/team';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useFormSchema } from '../data';
|
||||||
|
import MemberList from './member-list.vue';
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['success']);
|
||||||
|
const formMode = ref<'create' | 'detail' | 'update'>('create'); // 表单模式
|
||||||
|
const subTabsName = ref('member'); // 当前资源页签
|
||||||
|
const formData = ref<MesCalTeamApi.Team>();
|
||||||
|
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
if (formMode.value === 'detail') {
|
||||||
|
return $t('ui.actionTitle.view', ['班组']);
|
||||||
|
}
|
||||||
|
return formMode.value === 'update'
|
||||||
|
? $t('ui.actionTitle.edit', ['班组'])
|
||||||
|
: $t('ui.actionTitle.create', ['班组']);
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
labelWidth: 100,
|
||||||
|
},
|
||||||
|
wrapperClass: 'grid-cols-3',
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: [],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */
|
||||||
|
formApi.setState({ schema: useFormSchema(formApi) });
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onConfirm() {
|
||||||
|
if (isDetail.value) {
|
||||||
|
await modalApi.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
|
const data = (await formApi.getValues()) as MesCalTeamApi.Team;
|
||||||
|
try {
|
||||||
|
if (formMode.value === 'create') {
|
||||||
|
const id = await createTeam(data);
|
||||||
|
formData.value = { ...data, id: id as number };
|
||||||
|
await formApi.setFieldValue('id', id);
|
||||||
|
formMode.value = 'update';
|
||||||
|
} else {
|
||||||
|
await updateTeam(data);
|
||||||
|
formData.value = { ...formData.value, ...data };
|
||||||
|
}
|
||||||
|
emit('success');
|
||||||
|
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
formData.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await formApi.resetForm();
|
||||||
|
subTabsName.value = 'member';
|
||||||
|
const data = modalApi.getData<{ id?: number; type?: 'create' | 'detail' | 'update' }>();
|
||||||
|
formMode.value = data?.type || 'create';
|
||||||
|
formApi.setDisabled(formMode.value === 'detail');
|
||||||
|
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||||
|
if (!data?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getTeam(data.id);
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal :title="getTitle" class="w-4/5">
|
||||||
|
<Form class="mx-4" />
|
||||||
|
<ElTabs v-if="formMode !== 'create' && formData?.id" v-model="subTabsName" class="mx-4 mt-4">
|
||||||
|
<ElTabPane label="班组成员" name="member">
|
||||||
|
<MemberList :form-type="formMode" :team-id="formData.id" />
|
||||||
|
</ElTabPane>
|
||||||
|
</ElTabs>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { MesCalTeamMemberApi } from '#/api/mes/cal/team/member';
|
||||||
|
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import {
|
||||||
|
createTeamMember,
|
||||||
|
deleteTeamMember,
|
||||||
|
getTeamMemberListByTeam,
|
||||||
|
} from '#/api/mes/cal/team/member';
|
||||||
|
import { getSimpleUserList } from '#/api/system/user';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{ formType?: string; teamId: number }>(), {
|
||||||
|
formType: 'update',
|
||||||
|
});
|
||||||
|
const isEditable = computed(() => ['create', 'update'].includes(props.formType)); // 是否可编辑
|
||||||
|
const formOpen = ref(false); // 成员表单是否打开
|
||||||
|
const formLoading = ref(false); // 成员表单提交中
|
||||||
|
const list = ref<MesCalTeamMemberApi.TeamMember[]>([]); // 成员列表
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
labelWidth: 80,
|
||||||
|
},
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
fieldName: 'teamId',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'userId',
|
||||||
|
label: '用户',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
api: getSimpleUserList,
|
||||||
|
labelField: 'nickname',
|
||||||
|
placeholder: '请选择用户',
|
||||||
|
showSearch: true,
|
||||||
|
valueField: 'id',
|
||||||
|
},
|
||||||
|
rules: 'selectRequired',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入备注',
|
||||||
|
rows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
gridOptions: {
|
||||||
|
autoResize: true,
|
||||||
|
border: true,
|
||||||
|
columns: [
|
||||||
|
{ field: 'userId', title: '用户编号', width: 100 },
|
||||||
|
{ field: 'nickname', title: '用户昵称', minWidth: 120 },
|
||||||
|
{ field: 'telephone', title: '手机号', minWidth: 120 },
|
||||||
|
{ field: 'remark', title: '备注', minWidth: 160 },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 90,
|
||||||
|
fixed: 'right',
|
||||||
|
slots: {
|
||||||
|
default: 'actions',
|
||||||
|
},
|
||||||
|
visible: isEditable.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: list.value,
|
||||||
|
minHeight: 240,
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
isHover: true,
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
showOverflow: true,
|
||||||
|
toolbarConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<MesCalTeamMemberApi.TeamMember>,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 加载成员列表 */
|
||||||
|
async function getList() {
|
||||||
|
gridApi.setLoading(true);
|
||||||
|
try {
|
||||||
|
list.value = await getTeamMemberListByTeam(props.teamId);
|
||||||
|
gridApi.setGridOptions({ data: list.value });
|
||||||
|
} finally {
|
||||||
|
gridApi.setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开成员表单 */
|
||||||
|
async function openForm() {
|
||||||
|
formOpen.value = true;
|
||||||
|
await formApi.resetForm();
|
||||||
|
await formApi.setValues({ teamId: props.teamId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交成员表单 */
|
||||||
|
async function submitForm() {
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formLoading.value = true;
|
||||||
|
try {
|
||||||
|
const data = (await formApi.getValues()) as MesCalTeamMemberApi.TeamMember;
|
||||||
|
await createTeamMember(data);
|
||||||
|
formOpen.value = false;
|
||||||
|
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||||
|
await getList();
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除成员 */
|
||||||
|
async function handleDelete(id: number) {
|
||||||
|
await deleteTeamMember(id);
|
||||||
|
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['成员']));
|
||||||
|
await getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.teamId,
|
||||||
|
(value) => {
|
||||||
|
if (value) {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
|
||||||
|
<TableAction :actions="[{ label: '添加成员', type: 'primary', onClick: openForm }]" />
|
||||||
|
</div>
|
||||||
|
<Grid class="w-full">
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
link: true,
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除该成员吗?',
|
||||||
|
confirm: handleDelete.bind(null, row.id!),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ElDialog v-model="formOpen" title="添加成员" width="520px">
|
||||||
|
<Form class="mx-4" />
|
||||||
|
<template #footer>
|
||||||
|
<ElButton @click="formOpen = false">取消</ElButton>
|
||||||
|
<ElButton type="primary" :loading="formLoading" @click="submitForm">确定</ElButton>
|
||||||
|
</template>
|
||||||
|
</ElDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue