feat(mes):完成 dv 的整体迁移(需要评审)
parent
53a00f6e15
commit
617d50f68f
|
|
@ -0,0 +1,49 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { HolidayType } from '#/views/mes/utils/constants';
|
||||
|
||||
/** 假期设置表单 */
|
||||
export function useHolidayFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'day',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'dayDisplay',
|
||||
label: '日期',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_HOLIDAY_TYPE, 'number'),
|
||||
},
|
||||
rules: z.number().default(HolidayType.WORKDAY),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message, Tag } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { SolarDay } from 'tyme4ts';
|
||||
|
||||
import { getHolidayList } from '#/api/mes/cal/holiday';
|
||||
import { HolidayType } from '#/views/mes/utils/constants';
|
||||
|
||||
import HolidayForm from './modules/form.vue';
|
||||
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
dayjs.locale('zh-cn');
|
||||
|
||||
const currentDate = ref(dayjs()); // 当前日历月份
|
||||
const holidaySet = ref(new Set<string>()); // 节假日日期集合
|
||||
const lastFetchedMonth = ref(''); // 上次加载月份
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
const [HolidayFormModal, holidayFormModalApi] = useVbenModal({
|
||||
connectedComponent: HolidayForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const weekLabels = ['日', '一', '二', '三', '四', '五', '六']; // 星期标题
|
||||
const calendarDays = computed(() => {
|
||||
const monthStart = currentDate.value.startOf('month');
|
||||
const start = monthStart.subtract(monthStart.day(), 'day');
|
||||
return Array.from({ length: 42 }, (_, index) => {
|
||||
const date = start.add(index, 'day');
|
||||
return { day: date.format('YYYY-MM-DD'), inMonth: date.month() === currentDate.value.month() };
|
||||
});
|
||||
});
|
||||
const currentMonthTitle = computed(() => currentDate.value.format('YYYY 年 MM 月'));
|
||||
|
||||
/** 加载假期列表 */
|
||||
async function getList() {
|
||||
holidaySet.value = new Set<string>();
|
||||
const current = currentDate.value;
|
||||
const startDay = current.subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00');
|
||||
const endDay = current.add(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59');
|
||||
const list = await getHolidayList({ startDay, endDay });
|
||||
const days = new Set<string>();
|
||||
for (const item of list || []) {
|
||||
const day = item.day ? dayjs(item.day).format('YYYY-MM-DD') : '';
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
days.add(day);
|
||||
}
|
||||
}
|
||||
holidaySet.value = days;
|
||||
lastFetchedMonth.value = current.format('YYYY-MM');
|
||||
}
|
||||
|
||||
/** 打开假期设置表单 */
|
||||
function handleDayClick(item: { day: string; inMonth: boolean }) {
|
||||
if (!item.inMonth) {
|
||||
return;
|
||||
}
|
||||
if (!hasAccessByCodes(['mes:cal-holiday:create'])) {
|
||||
message.warning('没有假期设置权限');
|
||||
return;
|
||||
}
|
||||
holidayFormModalApi.setData({ day: item.day }).open();
|
||||
}
|
||||
|
||||
/** 切换到上月 */
|
||||
function handlePrevMonth() {
|
||||
currentDate.value = currentDate.value.subtract(1, 'month');
|
||||
}
|
||||
|
||||
/** 切换到下月 */
|
||||
function handleNextMonth() {
|
||||
currentDate.value = currentDate.value.add(1, 'month');
|
||||
}
|
||||
|
||||
/** 切换到今天 */
|
||||
function handleToday() {
|
||||
currentDate.value = dayjs();
|
||||
}
|
||||
|
||||
/** 判断是否周末 */
|
||||
function isWeekend(day: string) {
|
||||
const weekday = dayjs(day).day();
|
||||
return weekday === 0 || weekday === 6;
|
||||
}
|
||||
|
||||
/** 获取农历信息 */
|
||||
function getLunarInfo(day: string) {
|
||||
const [year, month, date] = day.split('-').map(Number);
|
||||
try {
|
||||
const solarDay = SolarDay.fromYmd(year!, month!, date!);
|
||||
const lunarDay = solarDay.getLunarDay();
|
||||
const solarFestival = solarDay.getFestival();
|
||||
const lunarFestival = lunarDay.getFestival();
|
||||
const termDay = solarDay.getTermDay();
|
||||
const termName = termDay.getDayIndex() === 0 ? termDay.getSolarTerm().getName() : '';
|
||||
return {
|
||||
solarFestival: solarFestival ? solarFestival.getName() : '',
|
||||
lunarFestival: lunarFestival ? lunarFestival.getName() : '',
|
||||
termName,
|
||||
lunarText: lunarDay.getLunarMonth().getName() + lunarDay.getName(),
|
||||
};
|
||||
} catch {
|
||||
return { solarFestival: '', lunarFestival: '', termName: '', lunarText: '' };
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取农历显示文本 */
|
||||
function getLunarDisplay(day: string) {
|
||||
const info = getLunarInfo(day);
|
||||
return info.solarFestival || info.lunarFestival || info.termName || info.lunarText;
|
||||
}
|
||||
|
||||
/** 判断是否有节日 */
|
||||
function hasFestival(day: string) {
|
||||
const info = getLunarInfo(day);
|
||||
return !!(info.solarFestival || info.lunarFestival || info.termName);
|
||||
}
|
||||
|
||||
watch(currentDate, (newDate) => {
|
||||
const newMonth = newDate.format('YYYY-MM');
|
||||
if (newMonth !== lastFetchedMonth.value) {
|
||||
getList();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="【排班】班组设置、节假日设置" url="https://doc.iocoder.cn/mes/cal/team/" />
|
||||
</template>
|
||||
<HolidayFormModal @success="getList" />
|
||||
<div class="flex h-full flex-col overflow-hidden rounded border bg-background">
|
||||
<div class="flex items-center justify-between border-b p-4">
|
||||
<div class="text-lg font-semibold">{{ currentMonthTitle }}</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button @click="handlePrevMonth">上月</Button>
|
||||
<Button @click="handleToday">今天</Button>
|
||||
<Button @click="handleNextMonth">下月</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 border-b text-center text-sm text-muted-foreground">
|
||||
<div v-for="label in weekLabels" :key="label" class="py-2">{{ label }}</div>
|
||||
</div>
|
||||
<div class="grid flex-1 grid-cols-7 grid-rows-6">
|
||||
<button
|
||||
v-for="item in calendarDays"
|
||||
:key="item.day"
|
||||
class="min-h-[104px] border-b border-r p-2 text-left transition hover:bg-muted/50"
|
||||
:class="{
|
||||
'cursor-pointer': item.inMonth,
|
||||
'bg-muted/30 text-muted-foreground': !item.inMonth,
|
||||
}"
|
||||
type="button"
|
||||
@click="handleDayClick(item)"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span
|
||||
class="text-base font-medium"
|
||||
:class="{ 'text-red-500': isWeekend(item.day) && item.inMonth }"
|
||||
>
|
||||
{{ item.day.slice(8) }}
|
||||
</span>
|
||||
<template v-if="item.inMonth">
|
||||
<span v-if="holidaySet.has(item.day)"><Tag color="green">休</Tag></span>
|
||||
<span v-else><Tag color="blue">班</Tag></span>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="mt-1 text-xs"
|
||||
:class="{
|
||||
'text-green-600': hasFestival(item.day),
|
||||
'text-muted-foreground': !hasFestival(item.day),
|
||||
}"
|
||||
>
|
||||
{{ getLunarDisplay(item.day) }}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesCalHolidayApi } from '#/api/mes/cal/holiday';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getHolidayByDay, saveHoliday } from '#/api/mes/cal/holiday';
|
||||
import { HolidayType } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useHolidayFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useHolidayFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MesCalHolidayApi.Holiday & { dayDisplay?: string };
|
||||
try {
|
||||
await saveHoliday({ day: data.day, type: data.type, remark: data.remark });
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success('设置成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<{ day: string }>();
|
||||
if (!data?.day) {
|
||||
return;
|
||||
}
|
||||
const timestamp = dayjs(data.day + ' 00:00:00').valueOf();
|
||||
await formApi.setValues({
|
||||
day: timestamp,
|
||||
dayDisplay: data.day,
|
||||
type: HolidayType.WORKDAY,
|
||||
remark: '',
|
||||
});
|
||||
modalApi.lock();
|
||||
try {
|
||||
const holiday = await getHolidayByDay(dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss'));
|
||||
if (holiday) {
|
||||
await formApi.setValues({
|
||||
type: holiday.type ?? HolidayType.WORKDAY,
|
||||
remark: holiday.remark ?? '',
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="假期设置" class="w-1/3">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -16,8 +16,10 @@ import { useFormSchema } from '../data';
|
|||
import ShiftList from './shift-list.vue';
|
||||
import PlanTeamList from './team-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<'create' | 'detail' | 'update'>('create'); // 表单模式
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('shift'); // 当前资源页签
|
||||
const formData = ref<MesCalPlanApi.Plan>();
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
|
|
@ -106,7 +108,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
}
|
||||
await formApi.resetForm();
|
||||
subTabsName.value = 'shift';
|
||||
const data = modalApi.getData<{ id?: number; type?: 'create' | 'detail' | 'update' }>();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ import { useFormSchema } from '../data';
|
|||
import MemberList from './member-list.vue';
|
||||
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<'create' | 'detail' | 'update'>('create'); // 表单模式
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('member'); // 当前资源页签
|
||||
const formData = ref<MesCalTeamApi.Team>();
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
|
|
@ -82,7 +84,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
}
|
||||
await formApi.resetForm();
|
||||
subTabsName.value = 'member';
|
||||
const data = modalApi.getData<{ id?: number; type?: 'create' | 'detail' | 'update' }>();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<script lang="ts" setup>
|
||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { Select } from 'ant-design-vue';
|
||||
|
||||
import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
status?: number;
|
||||
type?: number;
|
||||
}>(),
|
||||
{
|
||||
allowClear: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择计划',
|
||||
status: undefined,
|
||||
type: undefined,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvCheckPlanApi.CheckPlan];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvCheckPlanApi.CheckPlan[]>([]); // 点检计划列表
|
||||
|
||||
/** 加载点检计划列表 */
|
||||
async function getList() {
|
||||
const data = await getCheckPlanPage({
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
status: props.status,
|
||||
type: props.type,
|
||||
});
|
||||
list.value = data.list || [];
|
||||
}
|
||||
|
||||
/** 处理点检计划选择变化 */
|
||||
function handleChange(value: SelectValue) {
|
||||
const planId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', planId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === planId),
|
||||
);
|
||||
}
|
||||
|
||||
watch(() => [props.status, props.type], getList);
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Select
|
||||
:allow-clear="allowClear"
|
||||
:disabled="disabled"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
:options="list"
|
||||
:placeholder="placeholder"
|
||||
:value="modelValue"
|
||||
class="w-full"
|
||||
option-filter-prop="name"
|
||||
show-search
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvCheckPlanSelect } from './dv-check-plan-select.vue';
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
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 { getRangePickerDefaultProps } from '#/utils';
|
||||
import {
|
||||
MesAutoCodeRuleCode,
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改点检保养方案的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvCheckPlanStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '方案编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入方案编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_CHECK_PLAN_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '方案名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入方案名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '方案类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvSubjectTypeEnum.CHECK),
|
||||
},
|
||||
{
|
||||
fieldName: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择开始日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endDate',
|
||||
label: '结束日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择结束日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'cycleType',
|
||||
label: '周期类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CYCLE_TYPE, 'number'),
|
||||
placeholder: '请选择周期类型',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'cycleCount',
|
||||
label: '周期数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 1,
|
||||
precision: 0,
|
||||
},
|
||||
rules: z.number().default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'type',
|
||||
label: '方案类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
placeholder: '请选择方案类型',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_PLAN_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '方案编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '方案名称', minWidth: 150 },
|
||||
{
|
||||
field: 'type',
|
||||
title: '方案类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'startDate', title: '开始日期', width: 150, formatter: 'formatDate' },
|
||||
{ field: 'endDate', title: '结束日期', width: 150, formatter: 'formatDate' },
|
||||
{
|
||||
field: 'cycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'cycleCount', title: '周期数量', width: 100 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_PLAN_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 240,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
import type { ActionItem } from '#/components/table-action/typing';
|
||||
|
||||
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 {
|
||||
deleteCheckPlan,
|
||||
disableCheckPlan,
|
||||
enableCheckPlan,
|
||||
exportCheckPlan,
|
||||
getCheckPlanPage,
|
||||
} from '#/api/mes/dv/checkplan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvCheckPlanApi.CheckPlan) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检计划 */
|
||||
function handleEdit(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检计划 */
|
||||
async function handleDelete(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteCheckPlan(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检计划 */
|
||||
async function handleExport() {
|
||||
const data = await exportCheckPlan(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '点检保养方案.xls', source: data });
|
||||
}
|
||||
|
||||
/** 启用点检计划 */
|
||||
async function handleEnable(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
await enableCheckPlan(row.id!);
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
/** 停用点检计划 */
|
||||
async function handleDisable(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
await disableCheckPlan(row.id!);
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
/** 获取行操作按钮 */
|
||||
function getTableActions(row: MesDvCheckPlanApi.CheckPlan): ActionItem[] {
|
||||
const actions: ActionItem[] = [];
|
||||
if (row.status === MesDvCheckPlanStatusEnum.PREPARE) {
|
||||
actions.push(
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-check-plan:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
actions.push(
|
||||
row.status === MesDvCheckPlanStatusEnum.PREPARE
|
||||
? {
|
||||
label: '启用',
|
||||
type: 'link',
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleEnable.bind(null, row),
|
||||
}
|
||||
: {
|
||||
label: '停用',
|
||||
type: 'link',
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleDisable.bind(null, row),
|
||||
},
|
||||
);
|
||||
return actions;
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) =>
|
||||
await getCheckPlanPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>,
|
||||
});
|
||||
</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:dv-check-plan:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-check-plan:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #code="{ row }">
|
||||
<Button type="link" @click="handleDetail(row)">{{ row.code }}</Button>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction :actions="getTableActions(row)" />
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createCheckPlan, getCheckPlan, updateCheckPlan } from '#/api/mes/dv/checkplan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import MachineryList from './machinery-list.vue';
|
||||
import SubjectList from './subject-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create');
|
||||
const subTabsName = ref('machinery');
|
||||
const formData = ref<MesDvCheckPlanApi.CheckPlan>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检保养方案', update: '修改点检保养方案', detail: '查看点检保养方案' })[
|
||||
formMode.value
|
||||
],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
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 MesDvCheckPlanApi.CheckPlan;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createCheckPlan(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvCheckPlanStatusEnum.PREPARE };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateCheckPlan(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 = 'machinery';
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getCheckPlan(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="machinery" tab="设备">
|
||||
<MachineryList :form-type="formMode" :plan-id="formData.id" />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="subject" tab="项目">
|
||||
<SubjectList :form-type="formMode" :plan-id="formData.id" :plan-type="formData.type" />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanMachineryApi } from '#/api/mes/dv/checkplan/machinery';
|
||||
|
||||
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 {
|
||||
createCheckPlanMachinery,
|
||||
deleteCheckPlanMachinery,
|
||||
getCheckPlanMachineryListByPlan,
|
||||
} from '#/api/mes/dv/checkplan/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ formType?: string; planId: number; planType?: number }>(),
|
||||
{ formType: 'update', planType: undefined },
|
||||
);
|
||||
const isEditable = computed(() => props.formType !== 'detail');
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const list = ref<MesDvCheckPlanMachineryApi.CheckPlanMachinery[]>([]);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'planId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: DvMachinerySelect,
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{ field: 'machineryName', title: '设备名称', minWidth: 150 },
|
||||
{ field: 'machineryBrand', title: '品牌', minWidth: 120 },
|
||||
{ field: 'machinerySpecification', title: '规格型号', minWidth: 140 },
|
||||
{ field: 'remark', title: '备注', minWidth: 140 },
|
||||
{
|
||||
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<MesDvCheckPlanMachineryApi.CheckPlanMachinery>,
|
||||
});
|
||||
|
||||
/** 加载点检设备列表 */
|
||||
async function getList() {
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
list.value = await getCheckPlanMachineryListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开点检设备表单 */
|
||||
async function openForm() {
|
||||
formOpen.value = true;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues({ planId: props.planId });
|
||||
}
|
||||
|
||||
/** 提交点检设备表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
await createCheckPlanMachinery(
|
||||
(await formApi.getValues()) as MesDvCheckPlanMachineryApi.CheckPlanMachinery,
|
||||
);
|
||||
formOpen.value = false;
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await getList();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检设备 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckPlanMachinery(id);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', ['设备']));
|
||||
await getList();
|
||||
}
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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>
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanSubjectApi } from '#/api/mes/dv/checkplan/subject';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createCheckPlanSubject,
|
||||
deleteCheckPlanSubject,
|
||||
getCheckPlanSubjectListByPlan,
|
||||
} from '#/api/mes/dv/checkplan/subject';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ formType?: string; planId: number; planType?: number }>(),
|
||||
{ formType: 'update', planType: undefined },
|
||||
);
|
||||
const isEditable = computed(() => props.formType !== 'detail');
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const list = ref<MesDvCheckPlanSubjectApi.CheckPlanSubject[]>([]);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'planId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: props.planType,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'subjectCode', title: '项目编码', minWidth: 140 },
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{
|
||||
field: 'subjectType',
|
||||
title: '项目类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '标准', minWidth: 160 },
|
||||
{ field: 'remark', title: '备注', minWidth: 140 },
|
||||
{
|
||||
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<MesDvCheckPlanSubjectApi.CheckPlanSubject>,
|
||||
});
|
||||
|
||||
/** 加载点检项目列表 */
|
||||
async function getList() {
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
list.value = await getCheckPlanSubjectListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开点检项目表单 */
|
||||
async function openForm() {
|
||||
formOpen.value = true;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues({ planId: props.planId });
|
||||
}
|
||||
|
||||
/** 提交点检项目表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
await createCheckPlanSubject(
|
||||
(await formApi.getValues()) as MesDvCheckPlanSubjectApi.CheckPlanSubject,
|
||||
);
|
||||
formOpen.value = false;
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await getList();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检项目 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckPlanSubject(id);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', ['项目']));
|
||||
await getList();
|
||||
}
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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>
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvCheckPlanSelect } from '#/views/mes/dv/checkplan/components';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import {
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvCheckRecordStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改点检记录的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'planId',
|
||||
label: '点检计划',
|
||||
component: markRaw(DvCheckPlanSelect),
|
||||
componentProps: {
|
||||
status: MesDvCheckPlanStatusEnum.ENABLED,
|
||||
type: MesDvSubjectTypeEnum.CHECK,
|
||||
placeholder: '请选择计划',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'userId',
|
||||
label: '点检人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择点检人',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkTime',
|
||||
label: '点检时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择点检时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'checkTime',
|
||||
label: '点检时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_RECORD_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>['columns'] {
|
||||
return [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{
|
||||
field: 'machineryName',
|
||||
title: '设备名称',
|
||||
minWidth: 150,
|
||||
slots: {
|
||||
default: 'machineryName',
|
||||
},
|
||||
},
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{ field: 'checkTime', title: '点检时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '点检人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
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 { deleteCheckRecord, exportCheckRecord, getCheckRecordPage } from '#/api/mes/dv/checkrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvCheckRecordApi.CheckRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检记录 */
|
||||
function handleEdit(row: MesDvCheckRecordApi.CheckRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检记录 */
|
||||
async function handleDelete(row: MesDvCheckRecordApi.CheckRecord) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.machineryName]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteCheckRecord(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.machineryName]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检记录 */
|
||||
async function handleExport() {
|
||||
const data = await exportCheckRecord(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) =>
|
||||
await getCheckRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>,
|
||||
});
|
||||
</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:dv-check-record:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-check-record:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #machineryName="{ row }">
|
||||
<Button type="link" @click="handleDetail(row)">{{ row.machineryName }}</Button>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-check-record:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
ifShow: row.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-check-record:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.machineryName]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
ifShow: row.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-check-record:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message, Popconfirm } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createCheckRecord,
|
||||
getCheckRecord,
|
||||
submitCheckRecord,
|
||||
updateCheckRecord,
|
||||
} from '#/api/mes/dv/checkrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create');
|
||||
const formData = ref<MesDvCheckRecordApi.CheckRecord>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const canSubmit = computed(
|
||||
() => formMode.value === 'update' && formData.value?.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检记录', update: '修改点检记录', detail: '查看点检记录' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 提交点检记录 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateCheckRecord((await formApi.getValues()) as MesDvCheckRecordApi.CheckRecord);
|
||||
await submitCheckRecord(formData.value.id);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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 MesDvCheckRecordApi.CheckRecord;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createCheckRecord(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvCheckRecordStatusEnum.DRAFT };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateCheckRecord(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getCheckRecord(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isDetail" :record-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<Popconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该点检记录?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<Button type="primary">提交</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordLineApi } from '#/api/mes/dv/checkrecord/line';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createCheckRecordLine,
|
||||
deleteCheckRecordLine,
|
||||
getCheckRecordLine,
|
||||
getCheckRecordLinePage,
|
||||
updateCheckRecordLine,
|
||||
} from '#/api/mes/dv/checkrecord/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
import { MesDvCheckResultEnum, MesDvSubjectTypeEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; recordId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() => (lineFormType.value === 'create' ? '添加明细' : '修改明细'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'recordId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: MesDvSubjectTypeEnum.CHECK,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkStatus',
|
||||
label: '结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_RESULT, 'number'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkResult',
|
||||
label: '异常描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入异常描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{
|
||||
field: 'checkStatus',
|
||||
title: '结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RESULT },
|
||||
},
|
||||
},
|
||||
{ field: 'checkResult', title: '异常描述', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getCheckRecordLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
recordId: props.recordId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordLineApi.CheckRecordLine>,
|
||||
});
|
||||
|
||||
/** 打开点检明细表单 */
|
||||
async function openForm(type: 'create' | 'update', row?: MesDvCheckRecordLineApi.CheckRecordLine) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getCheckRecordLine(row.id!)
|
||||
: {
|
||||
recordId: props.recordId,
|
||||
checkStatus: MesDvCheckResultEnum.NORMAL,
|
||||
checkResult: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交点检明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvCheckRecordLineApi.CheckRecordLine;
|
||||
await (data.id ? updateCheckRecordLine(data) : createCheckRecordLine(data));
|
||||
formOpen.value = false;
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckRecordLine(id);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', ['明细']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.recordId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[{ label: '添加明细', type: 'primary', onClick: openForm.bind(null, 'create') }]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="明细列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ label: '编辑', type: 'link', onClick: openForm.bind(null, 'update', row) },
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该明细吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<Modal
|
||||
v-model:open="formOpen"
|
||||
:title="formTitle"
|
||||
width="620px"
|
||||
:confirm-loading="formLoading"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts" setup>
|
||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { Select } from 'ant-design-vue';
|
||||
|
||||
import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{ allowClear: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' },
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvMachineryApi.Machinery];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvMachineryApi.Machinery[]>([]); // 设备列表
|
||||
|
||||
/** 加载设备列表 */
|
||||
async function getList() {
|
||||
list.value = await getMachinerySimpleList();
|
||||
}
|
||||
|
||||
/** 处理设备选择变化 */
|
||||
function handleChange(value: SelectValue) {
|
||||
const machineryId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', machineryId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === machineryId),
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Select
|
||||
:allow-clear="allowClear"
|
||||
:disabled="disabled"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
:options="list"
|
||||
:placeholder="placeholder"
|
||||
:value="modelValue"
|
||||
class="w-full"
|
||||
option-filter-prop="name"
|
||||
show-search
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvMachinerySelect } from './dv-machinery-select.vue';
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
import type { Ref } from 'vue';
|
||||
|
||||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { h, markRaw } 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 { MdWorkshopSelect } from '#/views/mes/md/workstation/components';
|
||||
import { MesAutoCodeRuleCode, MesDvMachineryStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { DvMachineryTypeSelect } from './type/components';
|
||||
|
||||
/** 新增/修改设备的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi, formMode?: Ref<string>): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '设备编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入设备编码',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
componentProps: (values) => ({ disabled: !!values.id }),
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_MACHINERY_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'brand',
|
||||
label: '品牌',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入品牌',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryTypeId',
|
||||
label: '设备类型',
|
||||
component: markRaw(DvMachineryTypeSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备类型',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'workshopId',
|
||||
label: '所属车间',
|
||||
component: markRaw(MdWorkshopSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择所属车间',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '设备状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_MACHINERY_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvMachineryStatusEnum.STOP),
|
||||
},
|
||||
{
|
||||
fieldName: 'specification',
|
||||
label: '规格型号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入规格型号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'lastCheckTime',
|
||||
label: '最近点检时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
show: () => formMode?.value === 'detail',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'lastMaintenTime',
|
||||
label: '最近保养时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
disabled: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
show: () => formMode?.value === 'detail',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'workshopId',
|
||||
label: '所属车间',
|
||||
component: markRaw(MdWorkshopSelect),
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择所属车间',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '设备状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_MACHINERY_STATUS, 'number'),
|
||||
placeholder: '请选择设备状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMachineryApi.Machinery>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '设备编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '设备名称', minWidth: 140 },
|
||||
{ field: 'brand', title: '品牌', minWidth: 120 },
|
||||
{ field: 'specification', title: '规格型号', minWidth: 140 },
|
||||
{ field: 'machineryTypeName', title: '设备类型', minWidth: 140 },
|
||||
{ field: 'workshopName', title: '所属车间', minWidth: 140 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '设备状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_MACHINERY_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'lastCheckTime',
|
||||
title: '最近点检时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'lastMaintenTime',
|
||||
title: '最近保养时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 导入表单 */
|
||||
export function useImportFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'file',
|
||||
label: '设备数据',
|
||||
component: 'Upload',
|
||||
rules: 'required',
|
||||
help: '仅允许导入 xls、xlsx 格式文件',
|
||||
},
|
||||
{
|
||||
fieldName: 'updateSupport',
|
||||
label: '是否覆盖',
|
||||
component: 'Switch',
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
rules: z.boolean().default(false),
|
||||
help: '是否更新已经存在的设备数据',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
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 { deleteMachinery, exportMachinery, getMachineryPage } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { MachineryTypeTree } from '#/views/mes/dv/machinery/type/components';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
import ImportForm from './modules/import-form.vue';
|
||||
|
||||
const selectedMachineryTypeId = ref<number>(); // 选中设备类型编号
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
const [ImportModal, importModalApi] = useVbenModal({
|
||||
connectedComponent: ImportForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建设备 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({ type: 'create' }).open();
|
||||
}
|
||||
|
||||
/** 查看设备 */
|
||||
function handleDetail(row: MesDvMachineryApi.Machinery) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑设备 */
|
||||
function handleEdit(row: MesDvMachineryApi.Machinery) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除设备 */
|
||||
async function handleDelete(row: MesDvMachineryApi.Machinery) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteMachinery(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出设备 */
|
||||
async function handleExport() {
|
||||
const data = await exportMachinery(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '设备台账.xls', source: data });
|
||||
}
|
||||
|
||||
/** 导入设备 */
|
||||
function handleImport() {
|
||||
importModalApi.open();
|
||||
}
|
||||
|
||||
/** 按设备类型筛选 */
|
||||
function handleTypeNodeClick(row: MesDvMachineryTypeApi.MachineryType | undefined) {
|
||||
selectedMachineryTypeId.value = row?.id;
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) =>
|
||||
await getMachineryPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
machineryTypeId: selectedMachineryTypeId.value,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMachineryApi.Machinery>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="handleRefresh" />
|
||||
<ImportModal @success="handleRefresh" />
|
||||
<div class="flex h-full w-full">
|
||||
<div class="mr-4 h-full w-1/6">
|
||||
<MachineryTypeTree @node-click="handleTypeNodeClick" />
|
||||
</div>
|
||||
<div class="w-5/6">
|
||||
<Grid table-title="设备台账列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['设备']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['mes:dv-machinery:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.import'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.UPLOAD,
|
||||
auth: ['mes:dv-machinery:import'],
|
||||
onClick: handleImport,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-machinery: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:dv-machinery:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-machinery:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-machinery:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCheckRecordPage } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{
|
||||
field: 'planCycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'checkTime', title: '点检时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getCheckRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="点检记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createMachinery, getMachinery, updateMachinery } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { BarcodeBizTypeEnum } from '#/views/mes/utils/constants';
|
||||
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import CheckRecordList from './check-record-list.vue';
|
||||
import MaintenRecordList from './mainten-record-list.vue';
|
||||
import RepairList from './repair-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('check'); // 当前资源页签
|
||||
const formData = ref<MesDvMachineryApi.Machinery>();
|
||||
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // 条码详情弹窗
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
const getTitle = computed(
|
||||
() => ({ create: '新增设备', update: '修改设备', detail: '查看设备' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
formApi.setState({ schema: useFormSchema(formApi, formMode) });
|
||||
|
||||
/** 查看设备条码 */
|
||||
function handleBarcode() {
|
||||
if (!formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
barcodeDetailRef.value?.openByBusiness(
|
||||
formData.value.id,
|
||||
BarcodeBizTypeEnum.MACHINERY,
|
||||
formData.value.code,
|
||||
formData.value.name,
|
||||
);
|
||||
}
|
||||
|
||||
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 MesDvMachineryApi.Machinery;
|
||||
try {
|
||||
await (data.id ? updateMachinery(data) : createMachinery(data));
|
||||
await modalApi.close();
|
||||
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 = 'check';
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getMachinery(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="check" tab="点检记录">
|
||||
<CheckRecordList :machinery-id="formData.id" />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="mainten" tab="保养记录">
|
||||
<MaintenRecordList :machinery-id="formData.id" />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="repair" tab="维修记录">
|
||||
<RepairList :machinery-id="formData.id" />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<Button v-if="isDetail && formData?.id" type="primary" @click="handleBarcode">
|
||||
查看条码
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
<BarcodeDetail ref="barcodeDetailRef" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<script lang="ts" setup>
|
||||
import type { FileType } from 'ant-design-vue/es/upload/interface';
|
||||
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||
|
||||
import { Button, message, Upload } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { importMachinery, importMachineryTemplate } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useImportFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useImportFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = await formApi.getValues();
|
||||
try {
|
||||
const result = await importMachinery(data.file, data.updateSupport);
|
||||
const importData = result as MesDvMachineryApi.MachineryImportRespVO;
|
||||
let text = `上传成功数量:${importData.createCodes?.length || 0};`;
|
||||
text += `更新成功数量:${importData.updateCodes?.length || 0};`;
|
||||
text += `更新失败数量:${Object.keys(importData.failureCodes || {}).length};`;
|
||||
message.info(text);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** 上传前 */
|
||||
function beforeUpload(file: FileType) {
|
||||
formApi.setFieldValue('file', file);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 下载模版 */
|
||||
async function handleDownload() {
|
||||
const data = await importMachineryTemplate();
|
||||
downloadFileFromBlobPart({ fileName: '设备导入模板.xls', source: data });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="$t('ui.actionTitle.import', ['设备'])" class="w-1/3">
|
||||
<Form class="mx-4">
|
||||
<template #file>
|
||||
<div class="w-full">
|
||||
<Upload :before-upload="beforeUpload" :max-count="1" accept=".xls,.xlsx">
|
||||
<Button type="primary">选择 Excel 文件</Button>
|
||||
</Upload>
|
||||
</div>
|
||||
</template>
|
||||
</Form>
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<Button @click="handleDownload">下载导入模板</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getMaintenRecordPage } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{
|
||||
field: 'planCycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'maintenTime', title: '保养时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getMaintenRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="保养记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getRepairPage } from '#/api/mes/dv/repair';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'code', title: '维修单号', minWidth: 140 },
|
||||
{ field: 'name', title: '维修单名称', minWidth: 150 },
|
||||
{
|
||||
field: 'result',
|
||||
title: '维修结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_RESULT },
|
||||
},
|
||||
},
|
||||
{ field: 'requireDate', title: '报修日期', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getRepairPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairApi.Repair>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="维修记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Tooltip, TreeSelect } from 'ant-design-vue';
|
||||
|
||||
import { getMachineryTypeSimpleList } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
allowClear: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择设备类型',
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [item: MesDvMachineryTypeApi.MachineryType | undefined];
|
||||
'update:modelValue': [value: number | undefined];
|
||||
}>();
|
||||
|
||||
type MachineryTypeNode = MesDvMachineryTypeApi.MachineryType & {
|
||||
children?: MachineryTypeNode[];
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const allList = ref<MesDvMachineryTypeApi.MachineryType[]>([]); // 设备类型列表
|
||||
const machineryTypeTree = ref<MachineryTypeNode[]>([]); // 设备类型树
|
||||
const selectedItem = ref<MesDvMachineryTypeApi.MachineryType>(); // 选中设备类型
|
||||
|
||||
const selectValue = computed({
|
||||
// 选择器绑定值
|
||||
get: () => props.modelValue,
|
||||
set: (value: number | undefined) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
/** 递归将有子节点的分支节点标记为 disabled */
|
||||
function markParentsDisabled(nodes: MesDvMachineryTypeApi.MachineryType[]): MachineryTypeNode[] {
|
||||
return nodes.map((node) => {
|
||||
const children = node.children?.length ? markParentsDisabled(node.children) : undefined;
|
||||
return { ...node, children, disabled: Boolean(children?.length) };
|
||||
});
|
||||
}
|
||||
|
||||
/** 根据当前值同步 tooltip 展示的设备类型详情 */
|
||||
function syncSelectedItem(value: number | undefined) {
|
||||
selectedItem.value =
|
||||
value === undefined ? undefined : allList.value.find((item) => item.id === value);
|
||||
}
|
||||
|
||||
/** 除 v-model 外,额外抛出完整设备类型对象给业务表单使用 */
|
||||
function handleChange(value: number | undefined) {
|
||||
syncSelectedItem(value);
|
||||
emit('change', selectedItem.value);
|
||||
}
|
||||
|
||||
/** 查询设备类型树 */
|
||||
async function getMachineryTypeTree() {
|
||||
allList.value = await getMachineryTypeSimpleList();
|
||||
machineryTypeTree.value = markParentsDisabled(handleTree(allList.value));
|
||||
syncSelectedItem(props.modelValue);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
syncSelectedItem(value);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getMachineryTypeTree();
|
||||
});
|
||||
</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.remark || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<TreeSelect
|
||||
v-bind="$attrs"
|
||||
v-model:value="selectValue"
|
||||
:allow-clear="allowClear"
|
||||
:disabled="disabled"
|
||||
:field-names="{ children: 'children', label: 'name', value: 'id' }"
|
||||
:placeholder="placeholder"
|
||||
:tree-data="machineryTypeTree"
|
||||
class="w-full"
|
||||
show-search
|
||||
tree-default-expand-all
|
||||
tree-node-filter-prop="name"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</Tooltip>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { default as DvMachineryTypeSelect } from './dv-machinery-type-select.vue';
|
||||
export { default as MachineryTypeTree } from './machinery-type-tree.vue';
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Card, Input } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
const emit = defineEmits<{
|
||||
nodeClick: [row?: MesDvMachineryTypeApi.MachineryType];
|
||||
}>();
|
||||
const selectedId = ref<number>(); // 选中设备类型编号
|
||||
const searchKeyword = ref(''); // 设备类型搜索关键字
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: false,
|
||||
columns: [{ field: 'name', title: '类型名称', treeNode: true }],
|
||||
height: 'auto',
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async () =>
|
||||
handleTree(
|
||||
await getMachineryTypeList({
|
||||
name: searchKeyword.value || undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
keyField: 'id',
|
||||
},
|
||||
showHeader: false,
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
treeConfig: { parentField: 'parentId', rowField: 'id', transform: true },
|
||||
} as VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>,
|
||||
gridEvents: {
|
||||
cellClick: ({ row }: { row: MesDvMachineryTypeApi.MachineryType }) => {
|
||||
if (selectedId.value === row.id) {
|
||||
selectedId.value = undefined;
|
||||
gridApi.grid.clearCurrentRow();
|
||||
emit('nodeClick', undefined);
|
||||
return;
|
||||
}
|
||||
selectedId.value = row.id;
|
||||
emit('nodeClick', row);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** 重置选中状态 */
|
||||
function reset() {
|
||||
selectedId.value = undefined;
|
||||
searchKeyword.value = '';
|
||||
gridApi.grid.clearCurrentRow();
|
||||
emit('nodeClick', undefined);
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 搜索设备类型 */
|
||||
function handleSearch() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
defineExpose({ reset });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="h-full" size="small" title="设备类型">
|
||||
<Input
|
||||
v-model:value="searchKeyword"
|
||||
allow-clear
|
||||
class="mb-3"
|
||||
placeholder="搜索设备类型"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Grid class="h-full" />
|
||||
</Card>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
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: 'parentId',
|
||||
label: '上级类型',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: async () => [
|
||||
{ id: 0, name: '顶级类型', children: handleTree(await getMachineryTypeList()) },
|
||||
],
|
||||
childrenField: 'children',
|
||||
labelField: 'name',
|
||||
placeholder: '请选择上级类型',
|
||||
treeDefaultExpandAll: true,
|
||||
treeNodeFilterProp: 'name',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
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.DV_MACHINERY_TYPE_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '类型名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入类型名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '显示排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
min: 0,
|
||||
precision: 0,
|
||||
},
|
||||
rules: z.number().default(0),
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '类型名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入类型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>['columns'] {
|
||||
return [
|
||||
{ field: 'name', title: '类型名称', minWidth: 200, align: 'left', treeNode: true },
|
||||
{ field: 'code', title: '类型编码', width: 160, align: 'center' },
|
||||
{ field: 'sort', title: '排序', width: 100, align: 'center' },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 260,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteMachineryType, getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const isExpanded = ref(true); // 树形表格是否展开
|
||||
|
||||
/** 切换树形展开/收缩状态 */
|
||||
function handleExpand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
gridApi.grid.setAllTreeExpand(isExpanded.value);
|
||||
}
|
||||
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建设备类型 */
|
||||
function handleCreate(parentId = 0) {
|
||||
formModalApi.setData({ parentId, type: 'create' }).open();
|
||||
}
|
||||
|
||||
/** 编辑设备类型 */
|
||||
function handleEdit(row: MesDvMachineryTypeApi.MachineryType) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除设备类型 */
|
||||
async function handleDelete(row: MesDvMachineryTypeApi.MachineryType) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteMachineryType(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: { query: async (_, formValues) => handleTree(await getMachineryTypeList(formValues)) },
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
treeConfig: {
|
||||
parentField: 'parentId',
|
||||
rowField: 'id',
|
||||
transform: true,
|
||||
expandAll: true,
|
||||
reserve: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>,
|
||||
});
|
||||
</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:dv-machinery-type:create'],
|
||||
onClick: handleCreate.bind(null, 0),
|
||||
},
|
||||
{
|
||||
label: isExpanded ? '收缩' : '展开',
|
||||
type: 'primary',
|
||||
onClick: handleExpand,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['下级']),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['mes:dv-machinery-type:create'],
|
||||
onClick: handleCreate.bind(null, row.id),
|
||||
},
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-machinery-type:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-machinery-type:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createMachineryType,
|
||||
getMachineryType,
|
||||
updateMachineryType,
|
||||
} from '#/api/mes/dv/machinery/type';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
let formMode: 'create' | 'update' = 'create';
|
||||
const getTitle = computed(() => (formMode === 'create' ? '新增设备类型' : '修改设备类型'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */
|
||||
formApi.setState({ schema: useFormSchema(formApi) });
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MesDvMachineryTypeApi.MachineryType;
|
||||
try {
|
||||
await (data.id ? updateMachineryType(data) : createMachineryType(data));
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<{ id?: number; parentId?: number; type?: 'create' | 'update' }>();
|
||||
formMode = data?.type || 'create';
|
||||
if (!data?.id) {
|
||||
await formApi.setValues({ parentId: data?.parentId ?? 0 });
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues(await getMachineryType(data.id));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-1/3">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvCheckPlanSelect } from '#/views/mes/dv/checkplan/components';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import {
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvMaintenRecordStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改保养记录的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'planId',
|
||||
label: '保养计划',
|
||||
component: markRaw(DvCheckPlanSelect),
|
||||
componentProps: {
|
||||
status: MesDvCheckPlanStatusEnum.ENABLED,
|
||||
type: MesDvSubjectTypeEnum.MAINTENANCE,
|
||||
placeholder: '请选择计划',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'userId',
|
||||
label: '保养人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择保养人',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'maintenTime',
|
||||
label: '保养时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择保养时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'maintenTime',
|
||||
label: '保养时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_MAINTEN_RECORD_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>['columns'] {
|
||||
return [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{
|
||||
field: 'machineryName',
|
||||
title: '设备名称',
|
||||
minWidth: 150,
|
||||
slots: {
|
||||
default: 'machineryName',
|
||||
},
|
||||
},
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{ field: 'maintenTime', title: '保养时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '保养人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
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 {
|
||||
deleteMaintenRecord,
|
||||
exportMaintenRecord,
|
||||
getMaintenRecordPage,
|
||||
} from '#/api/mes/dv/maintenrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvMaintenRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑保养记录 */
|
||||
function handleEdit(row: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除保养记录 */
|
||||
async function handleDelete(row: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.machineryName]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteMaintenRecord(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.machineryName]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出保养记录 */
|
||||
async function handleExport() {
|
||||
const data = await exportMaintenRecord(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) =>
|
||||
await getMaintenRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>,
|
||||
});
|
||||
</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:dv-mainten-record:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-mainten-record:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #machineryName="{ row }">
|
||||
<Button type="link" @click="handleDetail(row)">{{ row.machineryName }}</Button>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-mainten-record:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
ifShow: row.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-mainten-record:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.machineryName]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
ifShow: row.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-mainten-record:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
|
||||
import { Button, message, Popconfirm } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createMaintenRecord,
|
||||
getMaintenRecord,
|
||||
submitMaintenRecord,
|
||||
updateMaintenRecord,
|
||||
} from '#/api/mes/dv/maintenrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvMaintenRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const userStore = useUserStore();
|
||||
const formMode = ref<FormMode>('create');
|
||||
const formData = ref<MesDvMaintenRecordApi.MaintenRecord>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const canSubmit = computed(
|
||||
() =>
|
||||
formMode.value === 'update' && formData.value?.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增保养记录', update: '修改保养记录', detail: '查看保养记录' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 提交保养记录 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateMaintenRecord((await formApi.getValues()) as MesDvMaintenRecordApi.MaintenRecord);
|
||||
await submitMaintenRecord(formData.value.id);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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 MesDvMaintenRecordApi.MaintenRecord;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createMaintenRecord(data);
|
||||
formData.value = {
|
||||
...data,
|
||||
id: id as number,
|
||||
status: MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
};
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateMaintenRecord(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
if (!data?.id) {
|
||||
await formApi.setFieldValue('userId', userStore.userInfo?.id);
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getMaintenRecord(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isDetail" :record-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<Popconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该保养记录?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<Button type="primary">提交</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordLineApi } from '#/api/mes/dv/maintenrecord/line';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createMaintenRecordLine,
|
||||
deleteMaintenRecordLine,
|
||||
getMaintenRecordLine,
|
||||
getMaintenRecordLinePage,
|
||||
updateMaintenRecordLine,
|
||||
} from '#/api/mes/dv/maintenrecord/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
import { MesDvMaintenStatusEnum, MesDvSubjectTypeEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; recordId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() => (lineFormType.value === 'create' ? '添加明细' : '修改明细'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'recordId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: MesDvSubjectTypeEnum.MAINTENANCE,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_MAINTEN_STATUS, 'number'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '异常描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入异常描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'result', title: '异常描述', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getMaintenRecordLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
recordId: props.recordId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordLineApi.MaintenRecordLine>,
|
||||
});
|
||||
|
||||
/** 打开保养明细表单 */
|
||||
async function openForm(
|
||||
type: 'create' | 'update',
|
||||
row?: MesDvMaintenRecordLineApi.MaintenRecordLine,
|
||||
) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getMaintenRecordLine(row.id!)
|
||||
: {
|
||||
recordId: props.recordId,
|
||||
status: MesDvMaintenStatusEnum.NORMAL,
|
||||
result: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交保养明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvMaintenRecordLineApi.MaintenRecordLine;
|
||||
await (data.id ? updateMaintenRecordLine(data) : createMaintenRecordLine(data));
|
||||
formOpen.value = false;
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除保养明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteMaintenRecordLine(id);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', ['明细']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.recordId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[{ label: '添加明细', type: 'primary', onClick: openForm.bind(null, 'create') }]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="明细列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ label: '编辑', type: 'link', onClick: openForm.bind(null, 'update', row) },
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该明细吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<Modal
|
||||
v-model:open="formOpen"
|
||||
:title="formTitle"
|
||||
width="620px"
|
||||
:confirm-loading="formLoading"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import { MesAutoCodeRuleCode, MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
/** 新增/修改维修工单的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvRepairStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '维修单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修单编码',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
componentProps: (values) => ({ disabled: !!values.id }),
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_REPAIR_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '维修单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修单名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'requireDate',
|
||||
label: '报修日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择报修日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'acceptedUserId',
|
||||
label: '维修人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择维修人',
|
||||
valueField: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'finishDate',
|
||||
label: '维修完成日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择完成日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'confirmUserId',
|
||||
label: '验收人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择验收人',
|
||||
valueField: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'confirmDate',
|
||||
label: '验收日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择验收日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '维修结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_RESULT, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requireDate',
|
||||
label: '报修日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '维修结果',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_RESULT, 'number'),
|
||||
placeholder: '请选择维修结果',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvRepairApi.Repair>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '维修单编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '维修单名称', minWidth: 150 },
|
||||
{ field: 'machineryName', title: '设备名称', minWidth: 150 },
|
||||
{ field: 'requireDate', title: '报修日期', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'finishDate', title: '完成日期', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
field: 'result',
|
||||
title: '维修结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_RESULT },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 260,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
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 { deleteRepair, exportRepair, getRepairPage } from '#/api/mes/dv/repair';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑维修工单 */
|
||||
function handleEdit(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 完成维修 */
|
||||
function handleConfirm(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'confirm' }).open();
|
||||
}
|
||||
|
||||
/** 验收维修 */
|
||||
function handleFinish(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'finish' }).open();
|
||||
}
|
||||
|
||||
/** 删除维修工单 */
|
||||
async function handleDelete(row: MesDvRepairApi.Repair) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteRepair(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出维修工单 */
|
||||
async function handleExport() {
|
||||
const data = await exportRepair(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) =>
|
||||
await getRepairPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairApi.Repair>,
|
||||
});
|
||||
</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:dv-repair:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-repair: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:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.PREPARE,
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-repair:delete'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.PREPARE,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '完成维修',
|
||||
type: 'link',
|
||||
auth: ['mes:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.CONFIRMED,
|
||||
onClick: handleConfirm.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '验收',
|
||||
type: 'link',
|
||||
auth: ['mes:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.APPROVING,
|
||||
onClick: handleFinish.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-repair:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message, Popconfirm } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
confirmRepair,
|
||||
createRepair,
|
||||
finishRepair,
|
||||
getRepair,
|
||||
submitRepair,
|
||||
updateRepair,
|
||||
} from '#/api/mes/dv/repair';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvRepairResultEnum, MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<'confirm' | 'create' | 'detail' | 'finish' | 'update'>('create');
|
||||
const formData = ref<MesDvRepairApi.Repair>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const isReadonly = computed(() => ['confirm', 'detail', 'finish'].includes(formMode.value));
|
||||
const canSubmit = computed(
|
||||
() => formMode.value === 'update' && formData.value?.status === MesDvRepairStatusEnum.PREPARE,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({
|
||||
create: '新增维修工单',
|
||||
update: '修改维修工单',
|
||||
confirm: '完成维修',
|
||||
finish: '验收维修',
|
||||
detail: '查看维修工单',
|
||||
})[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
formApi.setState({ schema: useFormSchema(formApi) });
|
||||
|
||||
/** 提交维修工单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
await doSubmit();
|
||||
}
|
||||
|
||||
/** 执行维修工单提交 */
|
||||
async function doSubmit() {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateRepair((await formApi.getValues()) as MesDvRepairApi.Repair);
|
||||
await submitRepair(formData.value!.id!);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 完成维修 */
|
||||
async function handleConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
const values = (await formApi.getValues()) as MesDvRepairApi.Repair;
|
||||
await doConfirm(values);
|
||||
}
|
||||
|
||||
/** 执行完成维修 */
|
||||
async function doConfirm(values: MesDvRepairApi.Repair) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await confirmRepair({
|
||||
id: formData.value!.id,
|
||||
finishDate: values.finishDate,
|
||||
});
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 验收维修 */
|
||||
async function handleFinish(result: number) {
|
||||
if (!formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
await doFinish(result);
|
||||
}
|
||||
|
||||
/** 执行维修验收 */
|
||||
async function doFinish(result: number) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await finishRepair(formData.value!.id!, result);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
if (isDetail.value || formMode.value === 'confirm' || formMode.value === 'finish') {
|
||||
await modalApi.close();
|
||||
return;
|
||||
}
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
const data = (await formApi.getValues()) as MesDvRepairApi.Repair;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createRepair(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvRepairStatusEnum.PREPARE };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateRepair(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: 'confirm' | 'create' | 'detail' | 'finish' | 'update' }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(isReadonly.value);
|
||||
modalApi.setState({ showConfirmButton: ['create', 'update'].includes(formMode.value) });
|
||||
if (!data?.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getRepair(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isReadonly" :repair-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center gap-2">
|
||||
<Popconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该维修工单?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<Button type="primary">提交</Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
v-if="formMode === 'confirm'"
|
||||
title="确认完成维修?完成后进入待验收。"
|
||||
@confirm="handleConfirm"
|
||||
>
|
||||
<Button type="primary">完成维修</Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
v-if="formMode === 'finish'"
|
||||
title="确认完成验收?"
|
||||
@confirm="handleFinish(MesDvRepairResultEnum.PASS)"
|
||||
>
|
||||
<Button type="primary">验收通过</Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
v-if="formMode === 'finish'"
|
||||
title="确认完成验收?"
|
||||
@confirm="handleFinish(MesDvRepairResultEnum.FAIL)"
|
||||
>
|
||||
<Button>不通过</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairLineApi } from '#/api/mes/dv/repair/line';
|
||||
|
||||
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 {
|
||||
createRepairLine,
|
||||
deleteRepairLine,
|
||||
getRepairLine,
|
||||
getRepairLinePage,
|
||||
updateRepairLine,
|
||||
} from '#/api/mes/dv/repair/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; repairId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() =>
|
||||
lineFormType.value === 'create' ? '添加维修项目' : '修改维修项目',
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'repairId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'malfunction',
|
||||
label: '故障描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入故障描述',
|
||||
rows: 3,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'malfunctionUrl',
|
||||
label: '故障图片',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入故障图片 URL',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '维修描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{ field: 'malfunction', title: '故障描述', minWidth: 180 },
|
||||
{ field: 'malfunctionUrl', title: '故障图片', minWidth: 180 },
|
||||
{ field: 'description', title: '维修描述', minWidth: 180 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getRepairLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
repairId: props.repairId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairLineApi.RepairLine>,
|
||||
});
|
||||
|
||||
/** 打开维修明细表单 */
|
||||
async function openForm(type: 'create' | 'update', row?: MesDvRepairLineApi.RepairLine) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getRepairLine(row.id!)
|
||||
: {
|
||||
repairId: props.repairId,
|
||||
malfunction: '',
|
||||
malfunctionUrl: '',
|
||||
description: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交维修明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvRepairLineApi.RepairLine;
|
||||
await (data.id ? updateRepairLine(data) : createRepairLine(data));
|
||||
formOpen.value = false;
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除维修明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteRepairLine(id);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', ['维修项目']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.repairId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ label: '添加维修项目', type: 'primary', onClick: openForm.bind(null, 'create') },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="维修项目明细">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ label: '编辑', type: 'link', onClick: openForm.bind(null, 'update', row) },
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该维修项目吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<Modal
|
||||
v-model:open="formOpen"
|
||||
:title="formTitle"
|
||||
width="620px"
|
||||
:confirm-loading="formLoading"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<script lang="ts" setup>
|
||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { Select } from 'ant-design-vue';
|
||||
|
||||
import { getSubjectSimpleList } from '#/api/mes/dv/subject';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
type?: number;
|
||||
}>(),
|
||||
{
|
||||
allowClear: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择项目',
|
||||
type: undefined,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvSubjectApi.Subject];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvSubjectApi.Subject[]>([]); // 项目列表
|
||||
const filteredList = computed( // 筛选后的项目列表
|
||||
() => list.value.filter((item) => !props.type || item.type === props.type),
|
||||
);
|
||||
|
||||
/** 加载项目列表 */
|
||||
async function getList() {
|
||||
list.value = await getSubjectSimpleList();
|
||||
}
|
||||
|
||||
/** 处理选择变化 */
|
||||
function handleChange(value: SelectValue) {
|
||||
const subjectId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', subjectId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === subjectId),
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Select
|
||||
:allow-clear="allowClear"
|
||||
:disabled="disabled"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
:options="filteredList"
|
||||
:placeholder="placeholder"
|
||||
:value="modelValue"
|
||||
class="w-full"
|
||||
option-filter-prop="name"
|
||||
show-search
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvSubjectSelect } from './dv-subject-select.vue';
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { CommonStatusEnum, 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, MesDvSubjectTypeEnum } 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: {
|
||||
placeholder: '请输入项目编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_SUBJECT_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '项目名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入项目名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '项目类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvSubjectTypeEnum.CHECK),
|
||||
},
|
||||
{
|
||||
fieldName: 'content',
|
||||
label: '项目内容',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
placeholder: '请输入项目内容',
|
||||
rows: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'standard',
|
||||
label: '标准',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
placeholder: '请输入标准',
|
||||
rows: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
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: 'type',
|
||||
label: '项目类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
placeholder: '请选择项目类型',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvSubjectApi.Subject>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '项目编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '项目名称', minWidth: 140 },
|
||||
{
|
||||
field: 'type',
|
||||
title: '项目类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'content', title: '项目内容', minWidth: 180 },
|
||||
{ field: 'standard', title: '标准', minWidth: 180 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
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 { deleteSubject, exportSubject, getSubjectPage } from '#/api/mes/dv/subject';
|
||||
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: MesDvSubjectApi.Subject) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检保养项目 */
|
||||
function handleEdit(row: MesDvSubjectApi.Subject) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检保养项目 */
|
||||
async function handleDelete(row: MesDvSubjectApi.Subject) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteSubject(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检保养项目 */
|
||||
async function handleExport() {
|
||||
const data = await exportSubject(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) =>
|
||||
await getSubjectPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvSubjectApi.Subject>,
|
||||
});
|
||||
</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:dv-subject:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-subject: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:dv-subject:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-subject:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createSubject, getSubject, updateSubject } from '#/api/mes/dv/subject';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检保养项目', update: '修改点检保养项目', detail: '查看点检保养项目' })[
|
||||
formMode.value
|
||||
],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-2',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
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 MesDvSubjectApi.Subject;
|
||||
try {
|
||||
await (data.id ? updateSubject(data) : createSubject(data));
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
if (!data?.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues(await getSubject(data.id));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-2/5">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesCalPlanApi } from '#/api/mes/cal/plan';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElButton } from 'element-plus';
|
||||
|
||||
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import {
|
||||
MesAutoCodeRuleCode,
|
||||
MesCalPlanStatusEnum,
|
||||
MesCalShiftMethodEnum,
|
||||
MesCalShiftTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改排班计划的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesCalPlanStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '计划编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入计划编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.CAL_PLAN_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '计划名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入计划名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'calendarType',
|
||||
label: '班组类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE, 'number'),
|
||||
placeholder: '请选择班组类型',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择开始日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endDate',
|
||||
label: '结束日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择结束日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'shiftType',
|
||||
label: '轮班方式',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_SHIFT_TYPE, 'number'),
|
||||
placeholder: '请选择轮班方式',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'shiftMethod',
|
||||
label: '倒班方式',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_SHIFT_METHOD, 'number'),
|
||||
placeholder: '请选择倒班方式',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['shiftType'],
|
||||
show: (values) => !!values.shiftType && values.shiftType !== MesCalShiftTypeEnum.SINGLE,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'shiftCount',
|
||||
label: '倒班天数',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 1,
|
||||
precision: 0,
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['shiftMethod'],
|
||||
show: (values) => values.shiftMethod === MesCalShiftMethodEnum.DAY,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'endDate',
|
||||
label: '结束日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'shiftType',
|
||||
label: '轮班方式',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_SHIFT_TYPE, 'number'),
|
||||
placeholder: '请选择轮班方式',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_CAL_PLAN_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesCalPlanApi.Plan>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '计划编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '计划名称', minWidth: 150 },
|
||||
{
|
||||
field: 'calendarType',
|
||||
title: '班组类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_CAL_CALENDAR_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'startDate', title: '开始日期', width: 150, formatter: 'formatDate' },
|
||||
{ field: 'endDate', title: '结束日期', width: 150, formatter: 'formatDate' },
|
||||
{
|
||||
field: 'shiftType',
|
||||
title: '轮班方式',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_CAL_SHIFT_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'shiftMethod',
|
||||
title: '倒班方式',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_CAL_SHIFT_METHOD },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '单据状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_CAL_PLAN_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesCalPlanApi } from '#/api/mes/cal/plan';
|
||||
|
||||
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 { deletePlan, exportPlan, getPlanPage } from '#/api/mes/cal/plan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesCalPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesCalPlanApi.Plan) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑排班计划 */
|
||||
function handleEdit(row: MesCalPlanApi.Plan) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除排班计划 */
|
||||
async function handleDelete(row: MesCalPlanApi.Plan) {
|
||||
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||
try {
|
||||
await deletePlan(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出排班计划 */
|
||||
async function handleExport() {
|
||||
const data = await exportPlan(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) =>
|
||||
await getPlanPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesCalPlanApi.Plan>,
|
||||
});
|
||||
</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-plan:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:cal-plan:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #code="{ row }">
|
||||
<ElButton link type="primary" @click="handleDetail(row)">{{ row.code }}</ElButton>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
v-if="row.status === MesCalPlanStatusEnum.PREPARE"
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:cal-plan:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:cal-plan:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesCalPlanApi } from '#/api/mes/cal/plan';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElButton, ElMessage, ElPopconfirm, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { confirmPlan, createPlan, getPlan, updatePlan } from '#/api/mes/cal/plan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesCalPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import ShiftList from './shift-list.vue';
|
||||
import PlanTeamList from './team-list.vue';
|
||||
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('shift'); // 当前资源页签
|
||||
const formData = ref<MesCalPlanApi.Plan>();
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
const canConfirm = computed(
|
||||
() => formMode.value === 'update' && formData.value?.status === MesCalPlanStatusEnum.PREPARE,
|
||||
); // 是否可确认计划
|
||||
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) });
|
||||
|
||||
/** 确认排班计划 */
|
||||
async function handleConfirmPlan() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesCalPlanApi.Plan;
|
||||
await updatePlan(data);
|
||||
await confirmPlan(formData.value.id);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success('确认成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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 MesCalPlanApi.Plan;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createPlan(data);
|
||||
formData.value = { ...data, id: id as number, status: MesCalPlanStatusEnum.PREPARE };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updatePlan(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 = 'shift';
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getPlan(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="shift">
|
||||
<ShiftList :form-type="formMode" :plan-id="formData.id" />
|
||||
</ElTabPane>
|
||||
<ElTabPane label="班组" name="team">
|
||||
<PlanTeamList :form-type="formMode" :plan-id="formData.id" />
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<ElPopconfirm
|
||||
v-if="canConfirm"
|
||||
title="确认该排班计划?确认后将不可修改或删除。"
|
||||
@confirm="handleConfirmPlan"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">确认计划</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesCalPlanShiftApi } from '#/api/mes/cal/plan/shift';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createPlanShift,
|
||||
deletePlanShift,
|
||||
getPlanShiftListByPlan,
|
||||
updatePlanShift,
|
||||
} from '#/api/mes/cal/plan/shift';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = withDefaults(defineProps<{ formType?: string; planId: number }>(), {
|
||||
formType: 'update',
|
||||
});
|
||||
const isEditable = computed(() => props.formType !== 'detail'); // 是否可编辑
|
||||
const formOpen = ref(false); // 班次表单是否打开
|
||||
const formLoading = ref(false); // 班次表单提交中
|
||||
const shiftFormType = ref<'create' | 'update'>('create'); // 班次表单模式
|
||||
const formTitle = computed(() => (shiftFormType.value === 'create' ? '添加班次' : '修改班次'));
|
||||
const list = ref<MesCalPlanShiftApi.PlanShift[]>([]); // 班次列表
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'planId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '顺序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 1,
|
||||
precision: 0,
|
||||
},
|
||||
rules: z.number().default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '班次名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入班次名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'TimePicker',
|
||||
componentProps: {
|
||||
format: 'HH:mm',
|
||||
placeholder: '请选择开始时间',
|
||||
valueFormat: 'HH:mm',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'TimePicker',
|
||||
componentProps: {
|
||||
format: 'HH:mm',
|
||||
placeholder: '请选择结束时间',
|
||||
valueFormat: 'HH:mm',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'sort', title: '顺序', width: 80 },
|
||||
{ field: 'name', title: '班次名称', minWidth: 120 },
|
||||
{ field: 'startTime', title: '开始时间', width: 100 },
|
||||
{ field: 'endTime', title: '结束时间', width: 100 },
|
||||
{ field: 'remark', title: '备注', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
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<MesCalPlanShiftApi.PlanShift>,
|
||||
});
|
||||
|
||||
/** 加载班次列表 */
|
||||
async function getList() {
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
list.value = await getPlanShiftListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开班次表单 */
|
||||
async function openForm(type: 'create' | 'update', row?: MesCalPlanShiftApi.PlanShift) {
|
||||
formOpen.value = true;
|
||||
shiftFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(row ? { ...row } : { planId: props.planId, sort: 1 });
|
||||
}
|
||||
|
||||
/** 提交班次表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesCalPlanShiftApi.PlanShift;
|
||||
await (shiftFormType.value === 'create' ? createPlanShift(data) : updatePlanShift(data));
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await getList();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除班次 */
|
||||
async function handleDelete(id: number) {
|
||||
await deletePlanShift(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['班次']));
|
||||
await getList();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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.bind(null, 'create') }]"
|
||||
/>
|
||||
</div>
|
||||
<Grid class="w-full">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: openForm.bind(null, 'update', row),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该班次吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<ElDialog v-model="formOpen" :title="formTitle" 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>
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesCalPlanTeamApi } from '#/api/mes/cal/plan/team';
|
||||
import type { MesCalTeamApi } from '#/api/mes/cal/team';
|
||||
import type { MesCalTeamMemberApi } from '#/api/mes/cal/team/member';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { ElCard, ElEmpty, ElMessage } from 'element-plus';
|
||||
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { createPlanTeam, deletePlanTeam, getPlanTeamListByPlan } from '#/api/mes/cal/plan/team';
|
||||
import { getTeamMemberListByTeam } from '#/api/mes/cal/team/member';
|
||||
import { $t } from '#/locales';
|
||||
import { CalTeamSelectDialog } from '#/views/mes/cal/team/components';
|
||||
|
||||
const props = withDefaults(defineProps<{ formType?: string; planId: number }>(), {
|
||||
formType: 'update',
|
||||
});
|
||||
const isEditable = computed(() => props.formType !== 'detail'); // 是否可编辑
|
||||
const list = ref<MesCalPlanTeamApi.PlanTeam[]>([]); // 计划班组列表
|
||||
const memberList = ref<MesCalTeamMemberApi.TeamMember[]>([]); // 班组成员列表
|
||||
const selectedTeamId = ref<number>(); // 选中班组编号
|
||||
const selectedTeamName = ref(''); // 选中班组名称
|
||||
const teamDialogRef = ref<InstanceType<typeof CalTeamSelectDialog>>(); // 班组选择弹窗
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'teamId', title: '班组编号', width: 100 },
|
||||
{ field: 'teamCode', title: '班组编码', minWidth: 120 },
|
||||
{ field: 'teamName', title: '班组名称', minWidth: 120 },
|
||||
{ field: 'remark', title: '备注', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 90,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: isEditable.value,
|
||||
},
|
||||
],
|
||||
data: list.value,
|
||||
minHeight: 260,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
rowConfig: {
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
keyField: 'id',
|
||||
},
|
||||
showOverflow: true,
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
} as VxeTableGridOptions<MesCalPlanTeamApi.PlanTeam>,
|
||||
gridEvents: {
|
||||
cellClick: ({ row }: { row: MesCalPlanTeamApi.PlanTeam }) => handleTeamSelect(row),
|
||||
},
|
||||
});
|
||||
|
||||
const [MemberGrid, memberGridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'nickname', title: '用户昵称', minWidth: 100 },
|
||||
{ field: 'telephone', title: '手机号', minWidth: 120 },
|
||||
{ field: 'remark', title: '备注', minWidth: 120 },
|
||||
],
|
||||
data: memberList.value,
|
||||
minHeight: 260,
|
||||
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 getPlanTeamListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择班组后加载成员 */
|
||||
async function handleTeamSelect(row?: MesCalPlanTeamApi.PlanTeam) {
|
||||
if (!row?.teamId) {
|
||||
selectedTeamId.value = undefined;
|
||||
selectedTeamName.value = '';
|
||||
memberList.value = [];
|
||||
memberGridApi.setGridOptions({ data: memberList.value });
|
||||
return;
|
||||
}
|
||||
selectedTeamId.value = row.teamId;
|
||||
selectedTeamName.value = row.teamName || '';
|
||||
memberGridApi.setLoading(true);
|
||||
try {
|
||||
memberList.value = await getTeamMemberListByTeam(row.teamId);
|
||||
memberGridApi.setGridOptions({ data: memberList.value });
|
||||
} finally {
|
||||
memberGridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开班组选择弹窗 */
|
||||
function openTeamSelect() {
|
||||
teamDialogRef.value?.open(list.value.map((item) => item.teamId!).filter(Boolean));
|
||||
}
|
||||
|
||||
/** 处理班组选择 */
|
||||
async function handleTeamsSelected(rows: MesCalTeamApi.Team[]) {
|
||||
const existingTeamIds = new Set(list.value.map((item) => item.teamId));
|
||||
const newTeams = rows.filter((team) => team.id && !existingTeamIds.has(team.id));
|
||||
if (newTeams.length === 0) {
|
||||
ElMessage.warning('所选班组已全部添加过');
|
||||
return;
|
||||
}
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
for (const team of newTeams) {
|
||||
await createPlanTeam({ planId: props.planId, teamId: team.id });
|
||||
}
|
||||
ElMessage.success('成功添加 ' + newTeams.length + ' 个班组');
|
||||
await getList();
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除计划班组 */
|
||||
async function handleDelete(row: MesCalPlanTeamApi.PlanTeam) {
|
||||
await deletePlanTeam(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.teamName]));
|
||||
if (row.teamId === selectedTeamId.value) {
|
||||
await handleTeamSelect(undefined);
|
||||
}
|
||||
await getList();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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: openTeamSelect }]" />
|
||||
</div>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div class="col-span-3">
|
||||
<Grid class="w-full">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该班组吗?',
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<ElCard class="h-full" shadow="never">
|
||||
<template #header>
|
||||
{{ selectedTeamName ? `「${selectedTeamName}」班组成员` : '班组成员' }}
|
||||
</template>
|
||||
<div v-if="!selectedTeamId">
|
||||
<ElEmpty description="请点击左侧班组查看成员" :image-size="60" />
|
||||
</div>
|
||||
<MemberGrid v-else class="w-full" />
|
||||
</ElCard>
|
||||
</div>
|
||||
</div>
|
||||
<CalTeamSelectDialog ref="teamDialogRef" :multiple="true" @selected="handleTeamsSelected" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -15,8 +15,10 @@ import { useFormSchema } from '../data';
|
|||
import MemberList from './member-list.vue';
|
||||
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<'create' | 'detail' | 'update'>('create'); // 表单模式
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('member'); // 当前资源页签
|
||||
const formData = ref<MesCalTeamApi.Team>();
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
|
|
@ -82,7 +84,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
}
|
||||
await formApi.resetForm();
|
||||
subTabsName.value = 'member';
|
||||
const data = modalApi.getData<{ id?: number; type?: 'create' | 'detail' | 'update' }>();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
clearable?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
status?: number;
|
||||
type?: number;
|
||||
}>(),
|
||||
{
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择计划',
|
||||
status: undefined,
|
||||
type: undefined,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvCheckPlanApi.CheckPlan];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvCheckPlanApi.CheckPlan[]>([]); // 点检计划列表
|
||||
|
||||
/** 加载点检计划列表 */
|
||||
async function getList() {
|
||||
const data = await getCheckPlanPage({
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
status: props.status,
|
||||
type: props.type,
|
||||
});
|
||||
list.value = data.list || [];
|
||||
}
|
||||
|
||||
/** 处理点检计划选择变化 */
|
||||
function handleChange(value: number | string | undefined) {
|
||||
const planId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', planId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === planId),
|
||||
);
|
||||
}
|
||||
|
||||
watch(() => [props.status, props.type], getList);
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElSelect
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:model-value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
class="w-full"
|
||||
filterable
|
||||
@change="handleChange"
|
||||
>
|
||||
<ElOption v-for="item in list" :key="item.id" :label="item.name" :value="item.id!" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvCheckPlanSelect } from './dv-check-plan-select.vue';
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
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 { getRangePickerDefaultProps } from '#/utils';
|
||||
import {
|
||||
MesAutoCodeRuleCode,
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改点检保养方案的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvCheckPlanStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '方案编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入方案编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_CHECK_PLAN_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '方案名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入方案名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '方案类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvSubjectTypeEnum.CHECK),
|
||||
},
|
||||
{
|
||||
fieldName: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择开始日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endDate',
|
||||
label: '结束日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择结束日期',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'cycleType',
|
||||
label: '周期类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CYCLE_TYPE, 'number'),
|
||||
placeholder: '请选择周期类型',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'cycleCount',
|
||||
label: '周期数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 1,
|
||||
precision: 0,
|
||||
},
|
||||
rules: z.number().default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'type',
|
||||
label: '方案类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
placeholder: '请选择方案类型',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_PLAN_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'startDate',
|
||||
label: '开始日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '方案编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '方案名称', minWidth: 150 },
|
||||
{
|
||||
field: 'type',
|
||||
title: '方案类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'startDate', title: '开始日期', width: 150, formatter: 'formatDate' },
|
||||
{ field: 'endDate', title: '结束日期', width: 150, formatter: 'formatDate' },
|
||||
{
|
||||
field: 'cycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'cycleCount', title: '周期数量', width: 100 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_PLAN_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 240,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
import type { ActionItem } from '#/components/table-action/typing';
|
||||
|
||||
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 {
|
||||
deleteCheckPlan,
|
||||
disableCheckPlan,
|
||||
enableCheckPlan,
|
||||
exportCheckPlan,
|
||||
getCheckPlanPage,
|
||||
} from '#/api/mes/dv/checkplan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvCheckPlanApi.CheckPlan) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检计划 */
|
||||
function handleEdit(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检计划 */
|
||||
async function handleDelete(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||
try {
|
||||
await deleteCheckPlan(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检计划 */
|
||||
async function handleExport() {
|
||||
const data = await exportCheckPlan(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '点检保养方案.xls', source: data });
|
||||
}
|
||||
|
||||
/** 启用点检计划 */
|
||||
async function handleEnable(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
await enableCheckPlan(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
/** 停用点检计划 */
|
||||
async function handleDisable(row: MesDvCheckPlanApi.CheckPlan) {
|
||||
await disableCheckPlan(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
/** 获取行操作按钮 */
|
||||
function getTableActions(row: MesDvCheckPlanApi.CheckPlan): ActionItem[] {
|
||||
const actions: ActionItem[] = [];
|
||||
if (row.status === MesDvCheckPlanStatusEnum.PREPARE) {
|
||||
actions.push(
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-check-plan:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
actions.push(
|
||||
row.status === MesDvCheckPlanStatusEnum.PREPARE
|
||||
? {
|
||||
label: '启用',
|
||||
type: 'warning',
|
||||
link: true,
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleEnable.bind(null, row),
|
||||
}
|
||||
: {
|
||||
label: '停用',
|
||||
type: 'warning',
|
||||
link: true,
|
||||
auth: ['mes:dv-check-plan:update'],
|
||||
onClick: handleDisable.bind(null, row),
|
||||
},
|
||||
);
|
||||
return actions;
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) =>
|
||||
await getCheckPlanPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>,
|
||||
});
|
||||
</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:dv-check-plan:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-check-plan:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #code="{ row }">
|
||||
<ElButton link type="primary" @click="handleDetail(row)">{{ row.code }}</ElButton>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction :actions="getTableActions(row)" />
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createCheckPlan, getCheckPlan, updateCheckPlan } from '#/api/mes/dv/checkplan';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckPlanStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import MachineryList from './machinery-list.vue';
|
||||
import SubjectList from './subject-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create');
|
||||
const subTabsName = ref('machinery');
|
||||
const formData = ref<MesDvCheckPlanApi.CheckPlan>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检保养方案', update: '修改点检保养方案', detail: '查看点检保养方案' })[
|
||||
formMode.value
|
||||
],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
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 MesDvCheckPlanApi.CheckPlan;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createCheckPlan(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvCheckPlanStatusEnum.PREPARE };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateCheckPlan(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 = 'machinery';
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getCheckPlan(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="machinery">
|
||||
<MachineryList :form-type="formMode" :plan-id="formData.id" />
|
||||
</ElTabPane>
|
||||
<ElTabPane label="项目" name="subject">
|
||||
<SubjectList :form-type="formMode" :plan-id="formData.id" :plan-type="formData.type" />
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanMachineryApi } from '#/api/mes/dv/checkplan/machinery';
|
||||
|
||||
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 {
|
||||
createCheckPlanMachinery,
|
||||
deleteCheckPlanMachinery,
|
||||
getCheckPlanMachineryListByPlan,
|
||||
} from '#/api/mes/dv/checkplan/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ formType?: string; planId: number; planType?: number }>(),
|
||||
{ formType: 'update', planType: undefined },
|
||||
);
|
||||
const isEditable = computed(() => props.formType !== 'detail');
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const list = ref<MesDvCheckPlanMachineryApi.CheckPlanMachinery[]>([]);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'planId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: DvMachinerySelect,
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{ field: 'machineryName', title: '设备名称', minWidth: 150 },
|
||||
{ field: 'machineryBrand', title: '品牌', minWidth: 120 },
|
||||
{ field: 'machinerySpecification', title: '规格型号', minWidth: 140 },
|
||||
{ field: 'remark', title: '备注', minWidth: 140 },
|
||||
{
|
||||
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<MesDvCheckPlanMachineryApi.CheckPlanMachinery>,
|
||||
});
|
||||
|
||||
/** 加载点检设备列表 */
|
||||
async function getList() {
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
list.value = await getCheckPlanMachineryListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开点检设备表单 */
|
||||
async function openForm() {
|
||||
formOpen.value = true;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues({ planId: props.planId });
|
||||
}
|
||||
|
||||
/** 提交点检设备表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
await createCheckPlanMachinery(
|
||||
(await formApi.getValues()) as MesDvCheckPlanMachineryApi.CheckPlanMachinery,
|
||||
);
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await getList();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检设备 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckPlanMachinery(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['设备']));
|
||||
await getList();
|
||||
}
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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>
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckPlanSubjectApi } from '#/api/mes/dv/checkplan/subject';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createCheckPlanSubject,
|
||||
deleteCheckPlanSubject,
|
||||
getCheckPlanSubjectListByPlan,
|
||||
} from '#/api/mes/dv/checkplan/subject';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ formType?: string; planId: number; planType?: number }>(),
|
||||
{ formType: 'update', planType: undefined },
|
||||
);
|
||||
const isEditable = computed(() => props.formType !== 'detail');
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const list = ref<MesDvCheckPlanSubjectApi.CheckPlanSubject[]>([]);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'planId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: props.planType,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: true,
|
||||
columns: [
|
||||
{ field: 'subjectCode', title: '项目编码', minWidth: 140 },
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{
|
||||
field: 'subjectType',
|
||||
title: '项目类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '标准', minWidth: 160 },
|
||||
{ field: 'remark', title: '备注', minWidth: 140 },
|
||||
{
|
||||
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<MesDvCheckPlanSubjectApi.CheckPlanSubject>,
|
||||
});
|
||||
|
||||
/** 加载点检项目列表 */
|
||||
async function getList() {
|
||||
gridApi.setLoading(true);
|
||||
try {
|
||||
list.value = await getCheckPlanSubjectListByPlan(props.planId);
|
||||
gridApi.setGridOptions({ data: list.value });
|
||||
} finally {
|
||||
gridApi.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开点检项目表单 */
|
||||
async function openForm() {
|
||||
formOpen.value = true;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues({ planId: props.planId });
|
||||
}
|
||||
|
||||
/** 提交点检项目表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
await createCheckPlanSubject(
|
||||
(await formApi.getValues()) as MesDvCheckPlanSubjectApi.CheckPlanSubject,
|
||||
);
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await getList();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检项目 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckPlanSubject(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['项目']));
|
||||
await getList();
|
||||
}
|
||||
watch(
|
||||
() => props.planId,
|
||||
(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>
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvCheckPlanSelect } from '#/views/mes/dv/checkplan/components';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import {
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvCheckRecordStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改点检记录的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'planId',
|
||||
label: '点检计划',
|
||||
component: markRaw(DvCheckPlanSelect),
|
||||
componentProps: {
|
||||
status: MesDvCheckPlanStatusEnum.ENABLED,
|
||||
type: MesDvSubjectTypeEnum.CHECK,
|
||||
placeholder: '请选择计划',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'userId',
|
||||
label: '点检人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择点检人',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkTime',
|
||||
label: '点检时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择点检时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'checkTime',
|
||||
label: '点检时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_RECORD_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>['columns'] {
|
||||
return [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{
|
||||
field: 'machineryName',
|
||||
title: '设备名称',
|
||||
minWidth: 150,
|
||||
slots: {
|
||||
default: 'machineryName',
|
||||
},
|
||||
},
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{ field: 'checkTime', title: '点检时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '点检人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
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 { deleteCheckRecord, exportCheckRecord, getCheckRecordPage } from '#/api/mes/dv/checkrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvCheckRecordApi.CheckRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检记录 */
|
||||
function handleEdit(row: MesDvCheckRecordApi.CheckRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检记录 */
|
||||
async function handleDelete(row: MesDvCheckRecordApi.CheckRecord) {
|
||||
const hideLoading = ElLoading.service({
|
||||
text: $t('ui.actionMessage.deleting', [row.machineryName]),
|
||||
});
|
||||
try {
|
||||
await deleteCheckRecord(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.machineryName]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检记录 */
|
||||
async function handleExport() {
|
||||
const data = await exportCheckRecord(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) =>
|
||||
await getCheckRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>,
|
||||
});
|
||||
</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:dv-check-record:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-check-record:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #machineryName="{ row }">
|
||||
<ElButton link type="primary" @click="handleDetail(row)">{{ row.machineryName }}</ElButton>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-check-record:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
ifShow: row.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-check-record:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.machineryName]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
ifShow: row.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-check-record:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElButton, ElMessage, ElPopconfirm } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createCheckRecord,
|
||||
getCheckRecord,
|
||||
submitCheckRecord,
|
||||
updateCheckRecord,
|
||||
} from '#/api/mes/dv/checkrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvCheckRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create');
|
||||
const formData = ref<MesDvCheckRecordApi.CheckRecord>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const canSubmit = computed(
|
||||
() => formMode.value === 'update' && formData.value?.status === MesDvCheckRecordStatusEnum.DRAFT,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检记录', update: '修改点检记录', detail: '查看点检记录' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 提交点检记录 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateCheckRecord((await formApi.getValues()) as MesDvCheckRecordApi.CheckRecord);
|
||||
await submitCheckRecord(formData.value.id);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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 MesDvCheckRecordApi.CheckRecord;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createCheckRecord(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvCheckRecordStatusEnum.DRAFT };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateCheckRecord(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getCheckRecord(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isDetail" :record-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<ElPopconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该点检记录?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">提交</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordLineApi } from '#/api/mes/dv/checkrecord/line';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createCheckRecordLine,
|
||||
deleteCheckRecordLine,
|
||||
getCheckRecordLine,
|
||||
getCheckRecordLinePage,
|
||||
updateCheckRecordLine,
|
||||
} from '#/api/mes/dv/checkrecord/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
import { MesDvCheckResultEnum, MesDvSubjectTypeEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; recordId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() => (lineFormType.value === 'create' ? '添加明细' : '修改明细'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'recordId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: MesDvSubjectTypeEnum.CHECK,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkStatus',
|
||||
label: '结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_CHECK_RESULT, 'number'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'checkResult',
|
||||
label: '异常描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入异常描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{
|
||||
field: 'checkStatus',
|
||||
title: '结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RESULT },
|
||||
},
|
||||
},
|
||||
{ field: 'checkResult', title: '异常描述', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getCheckRecordLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
recordId: props.recordId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordLineApi.CheckRecordLine>,
|
||||
});
|
||||
|
||||
/** 打开点检明细表单 */
|
||||
async function openForm(type: 'create' | 'update', row?: MesDvCheckRecordLineApi.CheckRecordLine) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getCheckRecordLine(row.id!)
|
||||
: {
|
||||
recordId: props.recordId,
|
||||
checkStatus: MesDvCheckResultEnum.NORMAL,
|
||||
checkResult: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交点检明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvCheckRecordLineApi.CheckRecordLine;
|
||||
await (data.id ? updateCheckRecordLine(data) : createCheckRecordLine(data));
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除点检明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteCheckRecordLine(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['明细']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.recordId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[{ label: '添加明细', type: 'primary', onClick: openForm.bind(null, 'create') }]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="明细列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: openForm.bind(null, 'update', row),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该明细吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<ElDialog v-model="formOpen" :title="formTitle" width="620px">
|
||||
<Form class="mx-4" />
|
||||
<template #footer>
|
||||
<ElButton @click="formOpen = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="formLoading" @click="submitForm">确定</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
clearable?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{ clearable: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' },
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvMachineryApi.Machinery];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvMachineryApi.Machinery[]>([]); // 设备列表
|
||||
|
||||
/** 加载设备列表 */
|
||||
async function getList() {
|
||||
list.value = await getMachinerySimpleList();
|
||||
}
|
||||
|
||||
/** 处理设备选择变化 */
|
||||
function handleChange(value: number | string | undefined) {
|
||||
const machineryId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', machineryId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === machineryId),
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElSelect
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:model-value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
class="w-full"
|
||||
filterable
|
||||
@change="handleChange"
|
||||
>
|
||||
<ElOption v-for="item in list" :key="item.id" :label="item.name" :value="item.id!" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvMachinerySelect } from './dv-machinery-select.vue';
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
import type { Ref } from 'vue';
|
||||
|
||||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { h, markRaw } 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 { MdWorkshopSelect } from '#/views/mes/md/workstation/components';
|
||||
import { MesAutoCodeRuleCode, MesDvMachineryStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { DvMachineryTypeSelect } from './type/components';
|
||||
|
||||
/** 新增/修改设备的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi, formMode?: Ref<string>): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '设备编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入设备编码',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
componentProps: (values) => ({ disabled: !!values.id }),
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_MACHINERY_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'brand',
|
||||
label: '品牌',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入品牌',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryTypeId',
|
||||
label: '设备类型',
|
||||
component: markRaw(DvMachineryTypeSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备类型',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'workshopId',
|
||||
label: '所属车间',
|
||||
component: markRaw(MdWorkshopSelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择所属车间',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '设备状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_MACHINERY_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvMachineryStatusEnum.STOP),
|
||||
},
|
||||
{
|
||||
fieldName: 'specification',
|
||||
label: '规格型号',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入规格型号',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'lastCheckTime',
|
||||
label: '最近点检时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
show: () => formMode?.value === 'detail',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'lastMaintenTime',
|
||||
label: '最近保养时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
show: () => formMode?.value === 'detail',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'workshopId',
|
||||
label: '所属车间',
|
||||
component: markRaw(MdWorkshopSelect),
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请选择所属车间',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '设备状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_MACHINERY_STATUS, 'number'),
|
||||
placeholder: '请选择设备状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMachineryApi.Machinery>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '设备编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '设备名称', minWidth: 140 },
|
||||
{ field: 'brand', title: '品牌', minWidth: 120 },
|
||||
{ field: 'specification', title: '规格型号', minWidth: 140 },
|
||||
{ field: 'machineryTypeName', title: '设备类型', minWidth: 140 },
|
||||
{ field: 'workshopName', title: '所属车间', minWidth: 140 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '设备状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_MACHINERY_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'lastCheckTime',
|
||||
title: '最近点检时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'lastMaintenTime',
|
||||
title: '最近保养时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 导入表单 */
|
||||
export function useImportFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'file',
|
||||
label: '设备数据',
|
||||
component: 'Upload',
|
||||
rules: 'required',
|
||||
help: '仅允许导入 xls、xlsx 格式文件',
|
||||
},
|
||||
{
|
||||
fieldName: 'updateSupport',
|
||||
label: '是否覆盖',
|
||||
component: 'Switch',
|
||||
componentProps: {
|
||||
activeText: '是',
|
||||
inactiveText: '否',
|
||||
},
|
||||
rules: z.boolean().default(false),
|
||||
help: '是否更新已经存在的设备数据',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
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 { deleteMachinery, exportMachinery, getMachineryPage } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { MachineryTypeTree } from '#/views/mes/dv/machinery/type/components';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
import ImportForm from './modules/import-form.vue';
|
||||
|
||||
const selectedMachineryTypeId = ref<number>(); // 选中设备类型编号
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
const [ImportModal, importModalApi] = useVbenModal({
|
||||
connectedComponent: ImportForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建设备 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({ type: 'create' }).open();
|
||||
}
|
||||
|
||||
/** 查看设备 */
|
||||
function handleDetail(row: MesDvMachineryApi.Machinery) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑设备 */
|
||||
function handleEdit(row: MesDvMachineryApi.Machinery) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除设备 */
|
||||
async function handleDelete(row: MesDvMachineryApi.Machinery) {
|
||||
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||
try {
|
||||
await deleteMachinery(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出设备 */
|
||||
async function handleExport() {
|
||||
const data = await exportMachinery(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '设备台账.xls', source: data });
|
||||
}
|
||||
|
||||
/** 导入设备 */
|
||||
function handleImport() {
|
||||
importModalApi.open();
|
||||
}
|
||||
|
||||
/** 按设备类型筛选 */
|
||||
function handleTypeNodeClick(row: MesDvMachineryTypeApi.MachineryType | undefined) {
|
||||
selectedMachineryTypeId.value = row?.id;
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) =>
|
||||
await getMachineryPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
machineryTypeId: selectedMachineryTypeId.value,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMachineryApi.Machinery>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="handleRefresh" />
|
||||
<ImportModal @success="handleRefresh" />
|
||||
<div class="flex h-full w-full">
|
||||
<div class="mr-4 h-full w-1/6">
|
||||
<MachineryTypeTree @node-click="handleTypeNodeClick" />
|
||||
</div>
|
||||
<div class="w-5/6">
|
||||
<Grid table-title="设备台账列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['设备']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['mes:dv-machinery:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.import'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.UPLOAD,
|
||||
auth: ['mes:dv-machinery:import'],
|
||||
onClick: handleImport,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-machinery: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:dv-machinery:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-machinery:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-machinery:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvCheckRecordApi } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCheckRecordPage } from '#/api/mes/dv/checkrecord';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{
|
||||
field: 'planCycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'checkTime', title: '点检时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CHECK_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getCheckRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvCheckRecordApi.CheckRecord>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="点检记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElButton, ElMessage, ElTabPane, ElTabs } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createMachinery, getMachinery, updateMachinery } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
import { BarcodeBizTypeEnum } from '#/views/mes/utils/constants';
|
||||
import { BarcodeDetail } from '#/views/mes/wm/barcode/components';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import CheckRecordList from './check-record-list.vue';
|
||||
import MaintenRecordList from './mainten-record-list.vue';
|
||||
import RepairList from './repair-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const subTabsName = ref('check'); // 当前资源页签
|
||||
const formData = ref<MesDvMachineryApi.Machinery>();
|
||||
const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // 条码详情弹窗
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
const getTitle = computed(
|
||||
() => ({ create: '新增设备', update: '修改设备', detail: '查看设备' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
formApi.setState({ schema: useFormSchema(formApi, formMode) });
|
||||
|
||||
/** 查看设备条码 */
|
||||
function handleBarcode() {
|
||||
if (!formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
barcodeDetailRef.value?.openByBusiness(
|
||||
formData.value.id,
|
||||
BarcodeBizTypeEnum.MACHINERY,
|
||||
formData.value.code,
|
||||
formData.value.name,
|
||||
);
|
||||
}
|
||||
|
||||
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 MesDvMachineryApi.Machinery;
|
||||
try {
|
||||
await (data.id ? updateMachinery(data) : createMachinery(data));
|
||||
await modalApi.close();
|
||||
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 = 'check';
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
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 getMachinery(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="check">
|
||||
<CheckRecordList :machinery-id="formData.id" />
|
||||
</ElTabPane>
|
||||
<ElTabPane label="保养记录" name="mainten">
|
||||
<MaintenRecordList :machinery-id="formData.id" />
|
||||
</ElTabPane>
|
||||
<ElTabPane label="维修记录" name="repair">
|
||||
<RepairList :machinery-id="formData.id" />
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<ElButton v-if="isDetail && formData?.id" type="primary" @click="handleBarcode">
|
||||
查看条码
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
<BarcodeDetail ref="barcodeDetailRef" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<script lang="ts" setup>
|
||||
import type { UploadRawFile } from 'element-plus';
|
||||
|
||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||
|
||||
import { ElButton, ElMessage, ElUpload } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { importMachinery, importMachineryTemplate } from '#/api/mes/dv/machinery';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useImportFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useImportFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = await formApi.getValues();
|
||||
try {
|
||||
const result = await importMachinery(data.file, data.updateSupport);
|
||||
const importData = result as MesDvMachineryApi.MachineryImportRespVO;
|
||||
let text = `上传成功数量:${importData.createCodes?.length || 0};`;
|
||||
text += `更新成功数量:${importData.updateCodes?.length || 0};`;
|
||||
text += `更新失败数量:${Object.keys(importData.failureCodes || {}).length};`;
|
||||
ElMessage.info(text);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** 上传前 */
|
||||
function beforeUpload(file: UploadRawFile) {
|
||||
formApi.setFieldValue('file', file);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 下载模版 */
|
||||
async function handleDownload() {
|
||||
const data = await importMachineryTemplate();
|
||||
downloadFileFromBlobPart({ fileName: '设备导入模板.xls', source: data });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="$t('ui.actionTitle.import', ['设备'])" class="w-1/3">
|
||||
<Form class="mx-4">
|
||||
<template #file>
|
||||
<div class="w-full">
|
||||
<ElUpload
|
||||
:auto-upload="false"
|
||||
:before-upload="beforeUpload"
|
||||
:limit="1"
|
||||
accept=".xls,.xlsx"
|
||||
>
|
||||
<ElButton type="primary">选择 Excel 文件</ElButton>
|
||||
</ElUpload>
|
||||
</div>
|
||||
</template>
|
||||
</Form>
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<ElButton @click="handleDownload">下载导入模板</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getMaintenRecordPage } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{
|
||||
field: 'planCycleType',
|
||||
title: '周期类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'maintenTime', title: '保养时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getMaintenRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="保养记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getRepairPage } from '#/api/mes/dv/repair';
|
||||
|
||||
const props = defineProps<{ machineryId: number }>();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'code', title: '维修单号', minWidth: 140 },
|
||||
{ field: 'name', title: '维修单名称', minWidth: 150 },
|
||||
{
|
||||
field: 'result',
|
||||
title: '维修结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_RESULT },
|
||||
},
|
||||
},
|
||||
{ field: 'requireDate', title: '报修日期', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '操作人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_STATUS },
|
||||
},
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getRepairPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
machineryId: props.machineryId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairApi.Repair>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.machineryId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="维修记录" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { ElTooltip, ElTreeSelect } from 'element-plus';
|
||||
|
||||
import { getMachineryTypeSimpleList } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
clearable?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
}>(),
|
||||
{
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择设备类型',
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [item: MesDvMachineryTypeApi.MachineryType | undefined];
|
||||
'update:modelValue': [value: number | undefined];
|
||||
}>();
|
||||
|
||||
type MachineryTypeNode = MesDvMachineryTypeApi.MachineryType & {
|
||||
children?: MachineryTypeNode[];
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const allList = ref<MesDvMachineryTypeApi.MachineryType[]>([]); // 设备类型列表
|
||||
const machineryTypeTree = ref<MachineryTypeNode[]>([]); // 设备类型树
|
||||
const selectedItem = ref<MesDvMachineryTypeApi.MachineryType>(); // 选中设备类型
|
||||
|
||||
const selectValue = computed({
|
||||
// 选择器绑定值
|
||||
get: () => props.modelValue,
|
||||
set: (value: number | undefined) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
/** 递归将有子节点的分支节点标记为 disabled */
|
||||
function markParentsDisabled(nodes: MesDvMachineryTypeApi.MachineryType[]): MachineryTypeNode[] {
|
||||
return nodes.map((node) => {
|
||||
const children = node.children?.length ? markParentsDisabled(node.children) : undefined;
|
||||
return { ...node, children, disabled: Boolean(children?.length) };
|
||||
});
|
||||
}
|
||||
|
||||
/** 根据当前值同步 tooltip 展示的设备类型详情 */
|
||||
function syncSelectedItem(value: number | undefined) {
|
||||
selectedItem.value =
|
||||
value === undefined ? undefined : allList.value.find((item) => item.id === value);
|
||||
}
|
||||
|
||||
/** 除 v-model 外,额外抛出完整设备类型对象给业务表单使用 */
|
||||
function handleChange(value: number | undefined) {
|
||||
syncSelectedItem(value);
|
||||
emit('change', selectedItem.value);
|
||||
}
|
||||
|
||||
/** 查询设备类型树 */
|
||||
async function getMachineryTypeTree() {
|
||||
allList.value = await getMachineryTypeSimpleList();
|
||||
machineryTypeTree.value = markParentsDisabled(handleTree(allList.value));
|
||||
syncSelectedItem(props.modelValue);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
syncSelectedItem(value);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getMachineryTypeTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElTooltip :disabled="!selectedItem" placement="top" :show-after="500">
|
||||
<template #content>
|
||||
<div v-if="selectedItem" class="leading-6">
|
||||
<div>编码:{{ selectedItem.code || '-' }}</div>
|
||||
<div>名称:{{ selectedItem.name || '-' }}</div>
|
||||
<div>备注:{{ selectedItem.remark || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<ElTreeSelect
|
||||
v-bind="$attrs"
|
||||
v-model="selectValue"
|
||||
:clearable="clearable"
|
||||
:data="machineryTypeTree"
|
||||
:disabled="disabled"
|
||||
:placeholder="placeholder"
|
||||
:props="{ children: 'children', label: 'name' }"
|
||||
check-strictly
|
||||
class="w-full"
|
||||
default-expand-all
|
||||
filterable
|
||||
node-key="id"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</ElTooltip>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { default as DvMachineryTypeSelect } from './dv-machinery-type-select.vue';
|
||||
export { default as MachineryTypeTree } from './machinery-type-tree.vue';
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { ElCard, ElInput } from 'element-plus';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
const emit = defineEmits<{
|
||||
nodeClick: [row?: MesDvMachineryTypeApi.MachineryType];
|
||||
}>();
|
||||
const selectedId = ref<number>(); // 选中设备类型编号
|
||||
const searchKeyword = ref(''); // 设备类型搜索关键字
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
autoResize: true,
|
||||
border: false,
|
||||
columns: [{ field: 'name', title: '类型名称', treeNode: true }],
|
||||
height: 'auto',
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async () =>
|
||||
handleTree(
|
||||
await getMachineryTypeList({
|
||||
name: searchKeyword.value || undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
keyField: 'id',
|
||||
},
|
||||
showHeader: false,
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
treeConfig: { parentField: 'parentId', rowField: 'id', transform: true },
|
||||
} as VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>,
|
||||
gridEvents: {
|
||||
cellClick: ({ row }: { row: MesDvMachineryTypeApi.MachineryType }) => {
|
||||
if (selectedId.value === row.id) {
|
||||
selectedId.value = undefined;
|
||||
gridApi.grid.clearCurrentRow();
|
||||
emit('nodeClick', undefined);
|
||||
return;
|
||||
}
|
||||
selectedId.value = row.id;
|
||||
emit('nodeClick', row);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** 重置选中状态 */
|
||||
function reset() {
|
||||
selectedId.value = undefined;
|
||||
searchKeyword.value = '';
|
||||
gridApi.grid.clearCurrentRow();
|
||||
emit('nodeClick', undefined);
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 搜索设备类型 */
|
||||
function handleSearch() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
defineExpose({ reset });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="h-full" shadow="never">
|
||||
<template #header>设备类型</template>
|
||||
<ElInput
|
||||
v-model="searchKeyword"
|
||||
clearable
|
||||
class="mb-3"
|
||||
placeholder="搜索设备类型"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Grid class="h-full" />
|
||||
</ElCard>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { ElButton } from 'element-plus';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
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: 'parentId',
|
||||
label: '上级类型',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
api: async () => [
|
||||
{ id: 0, name: '顶级类型', children: handleTree(await getMachineryTypeList()) },
|
||||
],
|
||||
childrenField: 'children',
|
||||
labelField: 'name',
|
||||
placeholder: '请选择上级类型',
|
||||
defaultExpandAll: true,
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
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.DV_MACHINERY_TYPE_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '类型名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入类型名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '显示排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
controlsPosition: 'right',
|
||||
min: 0,
|
||||
precision: 0,
|
||||
},
|
||||
rules: z.number().default(0),
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '类型名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入类型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>['columns'] {
|
||||
return [
|
||||
{ field: 'name', title: '类型名称', minWidth: 200, align: 'left', treeNode: true },
|
||||
{ field: 'code', title: '类型编码', width: 160, align: 'center' },
|
||||
{ field: 'sort', title: '排序', width: 100, align: 'center' },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 260,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { ElLoading, ElMessage } from 'element-plus';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteMachineryType, getMachineryTypeList } from '#/api/mes/dv/machinery/type';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const isExpanded = ref(true); // 树形表格是否展开
|
||||
|
||||
/** 切换树形展开/收缩状态 */
|
||||
function handleExpand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
gridApi.grid.setAllTreeExpand(isExpanded.value);
|
||||
}
|
||||
|
||||
/** 刷新表格 */
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建设备类型 */
|
||||
function handleCreate(parentId = 0) {
|
||||
formModalApi.setData({ parentId, type: 'create' }).open();
|
||||
}
|
||||
|
||||
/** 编辑设备类型 */
|
||||
function handleEdit(row: MesDvMachineryTypeApi.MachineryType) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除设备类型 */
|
||||
async function handleDelete(row: MesDvMachineryTypeApi.MachineryType) {
|
||||
const loadingInstance = ElLoading.service({
|
||||
text: $t('ui.actionMessage.deleting', [row.name]),
|
||||
});
|
||||
try {
|
||||
await deleteMachineryType(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
loadingInstance.close();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: { query: async (_, formValues) => handleTree(await getMachineryTypeList(formValues)) },
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
treeConfig: {
|
||||
parentField: 'parentId',
|
||||
rowField: 'id',
|
||||
transform: true,
|
||||
expandAll: true,
|
||||
reserve: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMachineryTypeApi.MachineryType>,
|
||||
});
|
||||
</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:dv-machinery-type:create'],
|
||||
onClick: handleCreate.bind(null, 0),
|
||||
},
|
||||
{
|
||||
label: isExpanded ? '收缩' : '展开',
|
||||
type: 'primary',
|
||||
onClick: handleExpand,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['下级']),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['mes:dv-machinery-type:create'],
|
||||
onClick: handleCreate.bind(null, row.id),
|
||||
},
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-machinery-type:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-machinery-type:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createMachineryType,
|
||||
getMachineryType,
|
||||
updateMachineryType,
|
||||
} from '#/api/mes/dv/machinery/type';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
let formMode: 'create' | 'update' = 'create';
|
||||
const getTitle = computed(() => (formMode === 'create' ? '新增设备类型' : '修改设备类型'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */
|
||||
formApi.setState({ schema: useFormSchema(formApi) });
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MesDvMachineryTypeApi.MachineryType;
|
||||
try {
|
||||
await (data.id ? updateMachineryType(data) : createMachineryType(data));
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<{ id?: number; parentId?: number; type?: 'create' | 'update' }>();
|
||||
formMode = data?.type || 'create';
|
||||
if (!data?.id) {
|
||||
await formApi.setValues({ parentId: data?.parentId ?? 0 });
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues(await getMachineryType(data.id));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-1/3">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvCheckPlanSelect } from '#/views/mes/dv/checkplan/components';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import {
|
||||
MesDvCheckPlanStatusEnum,
|
||||
MesDvMaintenRecordStatusEnum,
|
||||
MesDvSubjectTypeEnum,
|
||||
} from '#/views/mes/utils/constants';
|
||||
|
||||
/** 新增/修改保养记录的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'planId',
|
||||
label: '保养计划',
|
||||
component: markRaw(DvCheckPlanSelect),
|
||||
componentProps: {
|
||||
status: MesDvCheckPlanStatusEnum.ENABLED,
|
||||
type: MesDvSubjectTypeEnum.MAINTENANCE,
|
||||
placeholder: '请选择计划',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'userId',
|
||||
label: '保养人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择保养人',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'maintenTime',
|
||||
label: '保养时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择保养时间',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'maintenTime',
|
||||
label: '保养时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_MAINTEN_RECORD_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>['columns'] {
|
||||
return [
|
||||
{ field: 'machineryCode', title: '设备编码', minWidth: 140 },
|
||||
{
|
||||
field: 'machineryName',
|
||||
title: '设备名称',
|
||||
minWidth: 150,
|
||||
slots: {
|
||||
default: 'machineryName',
|
||||
},
|
||||
},
|
||||
{ field: 'planName', title: '计划名称', minWidth: 150 },
|
||||
{ field: 'maintenTime', title: '保养时间', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'nickname', title: '保养人', minWidth: 120 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
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 {
|
||||
deleteMaintenRecord,
|
||||
exportMaintenRecord,
|
||||
getMaintenRecordPage,
|
||||
} from '#/api/mes/dv/maintenrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvMaintenRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑保养记录 */
|
||||
function handleEdit(row: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除保养记录 */
|
||||
async function handleDelete(row: MesDvMaintenRecordApi.MaintenRecord) {
|
||||
const hideLoading = ElLoading.service({
|
||||
text: $t('ui.actionMessage.deleting', [row.machineryName]),
|
||||
});
|
||||
try {
|
||||
await deleteMaintenRecord(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.machineryName]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出保养记录 */
|
||||
async function handleExport() {
|
||||
const data = await exportMaintenRecord(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) =>
|
||||
await getMaintenRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordApi.MaintenRecord>,
|
||||
});
|
||||
</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:dv-mainten-record:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-mainten-record:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #machineryName="{ row }">
|
||||
<ElButton link type="primary" @click="handleDetail(row)">{{ row.machineryName }}</ElButton>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['mes:dv-mainten-record:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
ifShow: row.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-mainten-record:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.machineryName]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
ifShow: row.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-mainten-record:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvMaintenRecordApi } from '#/api/mes/dv/maintenrecord';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
|
||||
import { ElButton, ElMessage, ElPopconfirm } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createMaintenRecord,
|
||||
getMaintenRecord,
|
||||
submitMaintenRecord,
|
||||
updateMaintenRecord,
|
||||
} from '#/api/mes/dv/maintenrecord';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvMaintenRecordStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const userStore = useUserStore();
|
||||
const formMode = ref<FormMode>('create');
|
||||
const formData = ref<MesDvMaintenRecordApi.MaintenRecord>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const canSubmit = computed(
|
||||
() =>
|
||||
formMode.value === 'update' && formData.value?.status === MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增保养记录', update: '修改保养记录', detail: '查看保养记录' })[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 提交保养记录 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateMaintenRecord((await formApi.getValues()) as MesDvMaintenRecordApi.MaintenRecord);
|
||||
await submitMaintenRecord(formData.value.id);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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 MesDvMaintenRecordApi.MaintenRecord;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createMaintenRecord(data);
|
||||
formData.value = {
|
||||
...data,
|
||||
id: id as number,
|
||||
status: MesDvMaintenRecordStatusEnum.PREPARE,
|
||||
};
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateMaintenRecord(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
if (!data?.id) {
|
||||
await formApi.setFieldValue('userId', userStore.userInfo?.id);
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getMaintenRecord(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isDetail" :record-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center">
|
||||
<ElPopconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该保养记录?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">提交</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvMaintenRecordLineApi } from '#/api/mes/dv/maintenrecord/line';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
createMaintenRecordLine,
|
||||
deleteMaintenRecordLine,
|
||||
getMaintenRecordLine,
|
||||
getMaintenRecordLinePage,
|
||||
updateMaintenRecordLine,
|
||||
} from '#/api/mes/dv/maintenrecord/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
import { MesDvMaintenStatusEnum, MesDvSubjectTypeEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; recordId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() => (lineFormType.value === 'create' ? '添加明细' : '修改明细'));
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'recordId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
type: MesDvSubjectTypeEnum.MAINTENANCE,
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_MAINTEN_STATUS, 'number'),
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '异常描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入异常描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_MAINTEN_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'result', title: '异常描述', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getMaintenRecordLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
recordId: props.recordId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvMaintenRecordLineApi.MaintenRecordLine>,
|
||||
});
|
||||
|
||||
/** 打开保养明细表单 */
|
||||
async function openForm(
|
||||
type: 'create' | 'update',
|
||||
row?: MesDvMaintenRecordLineApi.MaintenRecordLine,
|
||||
) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getMaintenRecordLine(row.id!)
|
||||
: {
|
||||
recordId: props.recordId,
|
||||
status: MesDvMaintenStatusEnum.NORMAL,
|
||||
result: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交保养明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvMaintenRecordLineApi.MaintenRecordLine;
|
||||
await (data.id ? updateMaintenRecordLine(data) : createMaintenRecordLine(data));
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除保养明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteMaintenRecordLine(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['明细']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.recordId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[{ label: '添加明细', type: 'primary', onClick: openForm.bind(null, 'create') }]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="明细列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: openForm.bind(null, 'update', row),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该明细吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<ElDialog v-model="formOpen" :title="formTitle" width="620px">
|
||||
<Form class="mx-4" />
|
||||
<template #footer>
|
||||
<ElButton @click="formOpen = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="formLoading" @click="submitForm">确定</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { ElButton } from 'element-plus';
|
||||
|
||||
import { generateAutoCode } from '#/api/mes/md/autocode/record';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
import { DvMachinerySelect } from '#/views/mes/dv/machinery/components';
|
||||
import { MesAutoCodeRuleCode, MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
/** 新增/修改维修工单的表单 */
|
||||
export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
defaultValue: MesDvRepairStatusEnum.PREPARE,
|
||||
},
|
||||
{
|
||||
fieldName: 'code',
|
||||
label: '维修单编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修单编码',
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ['id'],
|
||||
componentProps: (values) => ({ disabled: !!values.id }),
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_REPAIR_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '维修单名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修单名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'machineryId',
|
||||
label: '设备',
|
||||
component: markRaw(DvMachinerySelect),
|
||||
componentProps: {
|
||||
placeholder: '请选择设备',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'requireDate',
|
||||
label: '报修日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择报修日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'acceptedUserId',
|
||||
label: '维修人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择维修人',
|
||||
valueField: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'finishDate',
|
||||
label: '维修完成日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择完成日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'confirmUserId',
|
||||
label: '验收人',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
api: getSimpleUserList,
|
||||
labelField: 'nickname',
|
||||
placeholder: '请选择验收人',
|
||||
valueField: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'confirmDate',
|
||||
label: '验收日期',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
class: '!w-full',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择验收日期',
|
||||
showTime: true,
|
||||
valueFormat: 'x',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '维修结果',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_RESULT, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-3',
|
||||
componentProps: {
|
||||
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: 'machineryName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'requireDate',
|
||||
label: '报修日期',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'result',
|
||||
label: '维修结果',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_RESULT, 'number'),
|
||||
placeholder: '请选择维修结果',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_REPAIR_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvRepairApi.Repair>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '维修单编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '维修单名称', minWidth: 150 },
|
||||
{ field: 'machineryName', title: '设备名称', minWidth: 150 },
|
||||
{ field: 'requireDate', title: '报修日期', width: 180, formatter: 'formatDateTime' },
|
||||
{ field: 'finishDate', title: '完成日期', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
field: 'result',
|
||||
title: '维修结果',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_RESULT },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_REPAIR_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'createTime', title: '创建时间', width: 180, formatter: 'formatDateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
width: 260,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
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 { deleteRepair, exportRepair, getRepairPage } from '#/api/mes/dv/repair';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
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: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑维修工单 */
|
||||
function handleEdit(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 完成维修 */
|
||||
function handleConfirm(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'confirm' }).open();
|
||||
}
|
||||
|
||||
/** 验收维修 */
|
||||
function handleFinish(row: MesDvRepairApi.Repair) {
|
||||
formModalApi.setData({ id: row.id, type: 'finish' }).open();
|
||||
}
|
||||
|
||||
/** 删除维修工单 */
|
||||
async function handleDelete(row: MesDvRepairApi.Repair) {
|
||||
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||
try {
|
||||
await deleteRepair(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出维修工单 */
|
||||
async function handleExport() {
|
||||
const data = await exportRepair(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) =>
|
||||
await getRepairPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairApi.Repair>,
|
||||
});
|
||||
</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:dv-repair:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-repair: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:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.PREPARE,
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-repair:delete'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.PREPARE,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '完成维修',
|
||||
type: 'warning',
|
||||
link: true,
|
||||
auth: ['mes:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.CONFIRMED,
|
||||
onClick: handleConfirm.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '验收',
|
||||
type: 'warning',
|
||||
link: true,
|
||||
auth: ['mes:dv-repair:update'],
|
||||
ifShow: row.status === MesDvRepairStatusEnum.APPROVING,
|
||||
onClick: handleFinish.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'primary',
|
||||
link: true,
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['mes:dv-repair:query'],
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvRepairApi } from '#/api/mes/dv/repair';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElButton, ElMessage, ElPopconfirm } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
confirmRepair,
|
||||
createRepair,
|
||||
finishRepair,
|
||||
getRepair,
|
||||
submitRepair,
|
||||
updateRepair,
|
||||
} from '#/api/mes/dv/repair';
|
||||
import { $t } from '#/locales';
|
||||
import { MesDvRepairResultEnum, MesDvRepairStatusEnum } from '#/views/mes/utils/constants';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
import LineList from './line-list.vue';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<'confirm' | 'create' | 'detail' | 'finish' | 'update'>('create');
|
||||
const formData = ref<MesDvRepairApi.Repair>();
|
||||
const isDetail = computed(() => formMode.value === 'detail');
|
||||
const isReadonly = computed(() => ['confirm', 'detail', 'finish'].includes(formMode.value));
|
||||
const canSubmit = computed(
|
||||
() => formMode.value === 'update' && formData.value?.status === MesDvRepairStatusEnum.PREPARE,
|
||||
);
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({
|
||||
create: '新增维修工单',
|
||||
update: '修改维修工单',
|
||||
confirm: '完成维修',
|
||||
finish: '验收维修',
|
||||
detail: '查看维修工单',
|
||||
})[formMode.value],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
formApi.setState({ schema: useFormSchema(formApi) });
|
||||
|
||||
/** 提交维修工单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
await doSubmit();
|
||||
}
|
||||
|
||||
/** 执行维修工单提交 */
|
||||
async function doSubmit() {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await updateRepair((await formApi.getValues()) as MesDvRepairApi.Repair);
|
||||
await submitRepair(formData.value!.id!);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success('提交成功');
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 完成维修 */
|
||||
async function handleConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid || !formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
const values = (await formApi.getValues()) as MesDvRepairApi.Repair;
|
||||
await doConfirm(values);
|
||||
}
|
||||
|
||||
/** 执行完成维修 */
|
||||
async function doConfirm(values: MesDvRepairApi.Repair) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await confirmRepair({
|
||||
id: formData.value!.id,
|
||||
finishDate: values.finishDate,
|
||||
});
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 验收维修 */
|
||||
async function handleFinish(result: number) {
|
||||
if (!formData.value?.id) {
|
||||
return;
|
||||
}
|
||||
await doFinish(result);
|
||||
}
|
||||
|
||||
/** 执行维修验收 */
|
||||
async function doFinish(result: number) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
await finishRepair(formData.value!.id!, result);
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
if (isDetail.value || formMode.value === 'confirm' || formMode.value === 'finish') {
|
||||
await modalApi.close();
|
||||
return;
|
||||
}
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
const data = (await formApi.getValues()) as MesDvRepairApi.Repair;
|
||||
try {
|
||||
if (formMode.value === 'create') {
|
||||
const id = await createRepair(data);
|
||||
formData.value = { ...data, id: id as number, status: MesDvRepairStatusEnum.PREPARE };
|
||||
await formApi.setFieldValue('id', id);
|
||||
formMode.value = 'update';
|
||||
} else {
|
||||
await updateRepair(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();
|
||||
const data = modalApi.getData<{ id?: number; type?: 'confirm' | 'create' | 'detail' | 'finish' | 'update' }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(isReadonly.value);
|
||||
modalApi.setState({ showConfirmButton: ['create', 'update'].includes(formMode.value) });
|
||||
if (!data?.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getRepair(data.id);
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-4/5">
|
||||
<Form class="mx-4" />
|
||||
<LineList v-if="formData?.id" :disabled="isReadonly" :repair-id="formData.id" />
|
||||
<template #prepend-footer>
|
||||
<div class="flex flex-auto items-center gap-2">
|
||||
<ElPopconfirm
|
||||
v-if="canSubmit"
|
||||
title="确认提交该维修工单?提交后将不能修改。"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">提交</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
<ElPopconfirm
|
||||
v-if="formMode === 'confirm'"
|
||||
title="确认完成维修?完成后进入待验收。"
|
||||
@confirm="handleConfirm"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">完成维修</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
<ElPopconfirm
|
||||
v-if="formMode === 'finish'"
|
||||
title="确认完成验收?"
|
||||
@confirm="handleFinish(MesDvRepairResultEnum.PASS)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton type="primary">验收通过</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
<ElPopconfirm
|
||||
v-if="formMode === 'finish'"
|
||||
title="确认完成验收?"
|
||||
@confirm="handleFinish(MesDvRepairResultEnum.FAIL)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton>不通过</ElButton>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvRepairLineApi } from '#/api/mes/dv/repair/line';
|
||||
|
||||
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 {
|
||||
createRepairLine,
|
||||
deleteRepairLine,
|
||||
getRepairLine,
|
||||
getRepairLinePage,
|
||||
updateRepairLine,
|
||||
} from '#/api/mes/dv/repair/line';
|
||||
import { $t } from '#/locales';
|
||||
import { DvSubjectSelect } from '#/views/mes/dv/subject/components';
|
||||
|
||||
const props = defineProps<{ disabled?: boolean; repairId: number }>();
|
||||
const formOpen = ref(false);
|
||||
const formLoading = ref(false);
|
||||
const lineFormType = ref<'create' | 'update'>('create');
|
||||
const formTitle = computed(() =>
|
||||
lineFormType.value === 'create' ? '添加维修项目' : '修改维修项目',
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 90,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'repairId',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'subjectId',
|
||||
label: '项目',
|
||||
component: DvSubjectSelect,
|
||||
componentProps: {
|
||||
placeholder: '请选择项目',
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'malfunction',
|
||||
label: '故障描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入故障描述',
|
||||
rows: 3,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'malfunctionUrl',
|
||||
label: '故障图片',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入故障图片 URL',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '维修描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入维修描述',
|
||||
rows: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'subjectName', title: '项目名称', minWidth: 150 },
|
||||
{ field: 'subjectContent', title: '项目内容', minWidth: 160 },
|
||||
{ field: 'subjectStandard', title: '项目标准', minWidth: 160 },
|
||||
{ field: 'malfunction', title: '故障描述', minWidth: 180 },
|
||||
{ field: 'malfunctionUrl', title: '故障图片', minWidth: 180 },
|
||||
{ field: 'description', title: '维修描述', minWidth: 180 },
|
||||
{
|
||||
title: '操作',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
visible: !props.disabled,
|
||||
},
|
||||
],
|
||||
height: 320,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) =>
|
||||
await getRepairLinePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
repairId: props.repairId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvRepairLineApi.RepairLine>,
|
||||
});
|
||||
|
||||
/** 打开维修明细表单 */
|
||||
async function openForm(type: 'create' | 'update', row?: MesDvRepairLineApi.RepairLine) {
|
||||
formOpen.value = true;
|
||||
lineFormType.value = type;
|
||||
await formApi.resetForm();
|
||||
await formApi.setValues(
|
||||
row
|
||||
? await getRepairLine(row.id!)
|
||||
: {
|
||||
repairId: props.repairId,
|
||||
malfunction: '',
|
||||
malfunctionUrl: '',
|
||||
description: '',
|
||||
remark: '',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 提交维修明细表单 */
|
||||
async function submitForm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
formLoading.value = true;
|
||||
try {
|
||||
const data = (await formApi.getValues()) as MesDvRepairLineApi.RepairLine;
|
||||
await (data.id ? updateRepairLine(data) : createRepairLine(data));
|
||||
formOpen.value = false;
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.query();
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除维修明细 */
|
||||
async function handleDelete(id: number) {
|
||||
await deleteRepairLine(id);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', ['维修项目']));
|
||||
await gridApi.query();
|
||||
}
|
||||
watch(
|
||||
() => props.repairId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
gridApi.query();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="mx-4 mt-4">
|
||||
<div v-if="!disabled" class="mb-3">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{ label: '添加维修项目', type: 'primary', onClick: openForm.bind(null, 'create') },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
<Grid table-title="维修项目明细">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: openForm.bind(null, 'update', row),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该维修项目吗?',
|
||||
confirm: handleDelete.bind(null, row.id!),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<ElDialog v-model="formOpen" :title="formTitle" width="620px">
|
||||
<Form class="mx-4" />
|
||||
<template #footer>
|
||||
<ElButton @click="formOpen = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="formLoading" @click="submitForm">确定</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
import { getSubjectSimpleList } from '#/api/mes/dv/subject';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
clearable?: boolean;
|
||||
disabled?: boolean;
|
||||
modelValue?: number;
|
||||
placeholder?: string;
|
||||
type?: number;
|
||||
}>(),
|
||||
{
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
modelValue: undefined,
|
||||
placeholder: '请选择项目',
|
||||
type: undefined,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
change: [row?: MesDvSubjectApi.Subject];
|
||||
'update:modelValue': [value?: number];
|
||||
}>();
|
||||
const list = ref<MesDvSubjectApi.Subject[]>([]); // 项目列表
|
||||
const filteredList = computed( // 筛选后的项目列表
|
||||
() => list.value.filter((item) => !props.type || item.type === props.type),
|
||||
);
|
||||
|
||||
/** 加载项目列表 */
|
||||
async function getList() {
|
||||
list.value = await getSubjectSimpleList();
|
||||
}
|
||||
|
||||
/** 处理选择变化 */
|
||||
function handleChange(value: number | string | undefined) {
|
||||
const subjectId = typeof value === 'number' ? value : undefined;
|
||||
emit('update:modelValue', subjectId);
|
||||
emit(
|
||||
'change',
|
||||
list.value.find((item) => item.id === subjectId),
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(getList);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElSelect
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:model-value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
class="w-full"
|
||||
filterable
|
||||
@change="handleChange"
|
||||
>
|
||||
<ElOption v-for="item in filteredList" :key="item.id" :label="item.name" :value="item.id!" />
|
||||
</ElSelect>
|
||||
</template>
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default as DvSubjectSelect } from './dv-subject-select.vue';
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { CommonStatusEnum, 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, MesDvSubjectTypeEnum } 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: {
|
||||
placeholder: '请输入项目编码',
|
||||
},
|
||||
rules: 'required',
|
||||
suffix: () =>
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'default',
|
||||
onClick: async () => {
|
||||
try {
|
||||
const code = await generateAutoCode(MesAutoCodeRuleCode.DV_SUBJECT_CODE);
|
||||
await formApi?.setFieldValue('code', code);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => '生成' },
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '项目名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入项目名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '项目类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
},
|
||||
rules: z.number().default(MesDvSubjectTypeEnum.CHECK),
|
||||
},
|
||||
{
|
||||
fieldName: 'content',
|
||||
label: '项目内容',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
placeholder: '请输入项目内容',
|
||||
rows: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'standard',
|
||||
label: '标准',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
placeholder: '请输入标准',
|
||||
rows: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
formItemClass: 'col-span-2',
|
||||
componentProps: {
|
||||
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: 'type',
|
||||
label: '项目类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.MES_DV_SUBJECT_TYPE, 'number'),
|
||||
placeholder: '请选择项目类型',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MesDvSubjectApi.Subject>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'code',
|
||||
title: '项目编码',
|
||||
minWidth: 140,
|
||||
slots: {
|
||||
default: 'code',
|
||||
},
|
||||
},
|
||||
{ field: 'name', title: '项目名称', minWidth: 140 },
|
||||
{
|
||||
field: 'type',
|
||||
title: '项目类型',
|
||||
width: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.MES_DV_SUBJECT_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'content', title: '项目内容', minWidth: 180 },
|
||||
{ field: 'standard', title: '标准', minWidth: 180 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'actions',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
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 { deleteSubject, exportSubject, getSubjectPage } from '#/api/mes/dv/subject';
|
||||
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: MesDvSubjectApi.Subject) {
|
||||
formModalApi.setData({ id: row.id, type: 'detail' }).open();
|
||||
}
|
||||
|
||||
/** 编辑点检保养项目 */
|
||||
function handleEdit(row: MesDvSubjectApi.Subject) {
|
||||
formModalApi.setData({ id: row.id, type: 'update' }).open();
|
||||
}
|
||||
|
||||
/** 删除点检保养项目 */
|
||||
async function handleDelete(row: MesDvSubjectApi.Subject) {
|
||||
const hideLoading = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]) });
|
||||
try {
|
||||
await deleteSubject(row.id!);
|
||||
ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出点检保养项目 */
|
||||
async function handleExport() {
|
||||
const data = await exportSubject(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) =>
|
||||
await getSubjectPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
}),
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MesDvSubjectApi.Subject>,
|
||||
});
|
||||
</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:dv-subject:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['mes:dv-subject: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:dv-subject:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'danger',
|
||||
link: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['mes:dv-subject:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<script lang="ts" setup>
|
||||
import type { MesDvSubjectApi } from '#/api/mes/dv/subject';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createSubject, getSubject, updateSubject } from '#/api/mes/dv/subject';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
type FormMode = 'create' | 'detail' | 'update';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formMode = ref<FormMode>('create'); // 表单模式
|
||||
const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式
|
||||
const getTitle = computed(
|
||||
() =>
|
||||
({ create: '新增点检保养项目', update: '修改点检保养项目', detail: '查看点检保养项目' })[
|
||||
formMode.value
|
||||
],
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-1',
|
||||
labelWidth: 110,
|
||||
},
|
||||
wrapperClass: 'grid-cols-2',
|
||||
layout: 'horizontal',
|
||||
schema: [],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
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 MesDvSubjectApi.Subject;
|
||||
try {
|
||||
await (data.id ? updateSubject(data) : createSubject(data));
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await formApi.resetForm();
|
||||
const data = modalApi.getData<{ id?: number; type?: FormMode }>();
|
||||
formMode.value = data?.type || 'create';
|
||||
formApi.setDisabled(formMode.value === 'detail');
|
||||
modalApi.setState({ showConfirmButton: formMode.value !== 'detail' });
|
||||
if (!data?.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
await formApi.setValues(await getSubject(data.id));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-2/5">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
Loading…
Reference in New Issue