docs(mes): 登记 MES-B033~B036 修复并更新 review 索引
- INDEX.md: R014、R026、R031、R039 转 fixed,分别关联 MES-B033~B036 - bug_done.md: 追加 B033(client 销售记录详情入口)、B034(salesnotice 选择弹窗筛选)、B035(高基数选择器恢复分页弹窗)、B036(RQC 保存后 留在弹窗编辑子表)完整修复记录 - bug_rejected.md: 同步前序 B029 归档调整 剩余 open: R006、R010;disputed: R008、R009。pull/351/MERGE
parent
a188c8245d
commit
2fc7390091
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as DvCheckPlanSelectDialog } from './select-dialog.vue';
|
||||||
export { default as DvCheckPlanSelect } from './select.vue';
|
export { default as DvCheckPlanSelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
|
||||||
|
|
||||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||||
|
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { Select } from 'ant-design-vue';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
|
import { Input, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
defineOptions({ name: 'DvCheckPlanSelect' });
|
import { getCheckPlan } from '#/api/mes/dv/checkplan';
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
|
||||||
|
import DvCheckPlanSelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DvCheckPlanSelect', inheritAttrs: false });
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
@ -24,51 +28,125 @@ const props = withDefaults(
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
modelValue: undefined,
|
modelValue: undefined,
|
||||||
placeholder: '请选择计划',
|
placeholder: '请选择保养方案',
|
||||||
status: undefined,
|
status: undefined,
|
||||||
type: undefined,
|
type: undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesDvCheckPlanApi.CheckPlan];
|
change: [item: MesDvCheckPlanApi.CheckPlan | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesDvCheckPlanApi.CheckPlan[]>([]); // 点检计划列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof DvCheckPlanSelectDialog>>(); // 方案选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesDvCheckPlanApi.CheckPlan>(); // 当前选中方案
|
||||||
|
|
||||||
/** 加载点检计划列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
const data = await getCheckPlanPage({
|
() =>
|
||||||
pageNo: 1,
|
props.allowClear &&
|
||||||
pageSize: 100,
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据方案编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getCheckPlan(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => {
|
||||||
|
resolveItemById(value);
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选方案 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开方案选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.ant-input-suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, {
|
||||||
|
multiple: false,
|
||||||
status: props.status,
|
status: props.status,
|
||||||
type: props.type,
|
type: props.type,
|
||||||
});
|
});
|
||||||
list.value = data.list || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理点检计划选择变化 */
|
/** 回填选中的方案 */
|
||||||
function handleChange(value: SelectValue) {
|
function handleSelected(rows: MesDvCheckPlanApi.CheckPlan[]) {
|
||||||
const planId = typeof value === 'number' ? value : undefined;
|
const item = rows[0];
|
||||||
emit('update:modelValue', planId);
|
if (!item) {
|
||||||
emit('change', list.value.find((item) => item.id === planId));
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => [props.status, props.type], getList);
|
|
||||||
|
|
||||||
onMounted(getList);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Select
|
<div
|
||||||
:allow-clear="allowClear"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:field-names="{ label: 'name', value: 'id' }"
|
|
||||||
:options="list"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:value="modelValue"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
option-filter-prop="name"
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
show-search
|
@click="handleClick"
|
||||||
@change="handleChange"
|
@mouseenter="hovering = true"
|
||||||
/>
|
@mouseleave="hovering = false"
|
||||||
|
>
|
||||||
|
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
|
||||||
|
<template #title>
|
||||||
|
<div v-if="selectedItem" class="leading-6">
|
||||||
|
<div>编码:{{ selectedItem.code || '-' }}</div>
|
||||||
|
<div>名称:{{ selectedItem.name || '-' }}</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
频度:{{ selectedItem.cycleCount ?? '-' }}
|
||||||
|
<DictTag
|
||||||
|
class="ml-1"
|
||||||
|
:type="DICT_TYPE.MES_DV_CYCLE_TYPE"
|
||||||
|
:value="selectedItem.cycleType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Input
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="displayLabel"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<IconifyIcon
|
||||||
|
class="size-4"
|
||||||
|
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<DvCheckPlanSelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -245,3 +245,68 @@ export function useGridColumns(): VxeTableGridOptions<MesDvCheckPlanApi.CheckPla
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 点检方案选择弹窗的搜索表单 */
|
||||||
|
export function useCheckPlanSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '计划编号',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入计划编号',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '计划名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入计划名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点检方案选择弹窗的字段 */
|
||||||
|
export function useCheckPlanSelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '计划编码', minWidth: 180 },
|
||||||
|
{ 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: 120, formatter: 'formatDate' },
|
||||||
|
{ field: 'endDate', title: '结束日期', width: 120, formatter: 'formatDate' },
|
||||||
|
{ field: 'cycleCount', title: '频率', width: 100 },
|
||||||
|
{
|
||||||
|
field: 'cycleType',
|
||||||
|
title: '周期类型',
|
||||||
|
width: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_CHECK_PLAN_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as DvMachinerySelectDialog } from './select-dialog.vue';
|
||||||
export { default as DvMachinerySelect } from './select.vue';
|
export { default as DvMachinerySelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
|
||||||
|
|
||||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { Select } from 'ant-design-vue';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
|
import { Input, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
defineOptions({ name: 'DvMachinerySelect' });
|
import { getMachinery } from '#/api/mes/dv/machinery';
|
||||||
|
|
||||||
withDefaults(
|
import DvMachinerySelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DvMachinerySelect', inheritAttrs: false });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
allowClear?: boolean;
|
allowClear?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
@ -26,37 +28,111 @@ withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesDvMachineryApi.Machinery];
|
change: [item: MesDvMachineryApi.Machinery | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesDvMachineryApi.Machinery[]>([]); // 设备列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof DvMachinerySelectDialog>>(); // 设备选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesDvMachineryApi.Machinery>(); // 当前选中设备
|
||||||
|
|
||||||
/** 加载设备列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
list.value = await getMachinerySimpleList();
|
() =>
|
||||||
|
props.allowClear &&
|
||||||
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据设备编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getMachinery(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理设备选择变化 */
|
watch(
|
||||||
function handleChange(value: SelectValue) {
|
() => props.modelValue,
|
||||||
const machineryId = typeof value === 'number' ? value : undefined;
|
(value) => {
|
||||||
emit('update:modelValue', machineryId);
|
resolveItemById(value);
|
||||||
emit('change', list.value.find((item) => item.id === machineryId));
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选设备 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(getList);
|
/** 打开设备选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.ant-input-suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回填选中的设备 */
|
||||||
|
function handleSelected(rows: MesDvMachineryApi.Machinery[]) {
|
||||||
|
const item = rows[0];
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Select
|
<div
|
||||||
:allow-clear="allowClear"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:field-names="{ label: 'name', value: 'id' }"
|
|
||||||
:options="list"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:value="modelValue"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
option-filter-prop="name"
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
show-search
|
@click="handleClick"
|
||||||
@change="handleChange"
|
@mouseenter="hovering = true"
|
||||||
/>
|
@mouseleave="hovering = false"
|
||||||
|
>
|
||||||
|
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
|
||||||
|
<template #title>
|
||||||
|
<div v-if="selectedItem" class="leading-6">
|
||||||
|
<div>设备编码:{{ selectedItem.code || '-' }}</div>
|
||||||
|
<div>设备名称:{{ selectedItem.name || '-' }}</div>
|
||||||
|
<div v-if="selectedItem.brand">品牌:{{ selectedItem.brand }}</div>
|
||||||
|
<div v-if="selectedItem.specification">
|
||||||
|
规格型号:{{ selectedItem.specification }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Input
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="displayLabel"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<IconifyIcon
|
||||||
|
class="size-4"
|
||||||
|
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<DvMachinerySelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -276,3 +276,65 @@ export function useImportFormSchema(): VbenFormSchema[] {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 设备选择弹窗的搜索表单 */
|
||||||
|
export function useMachinerySelectGridFormSchema(): 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: '请选择所属车间',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设备选择弹窗的字段 */
|
||||||
|
export function useMachinerySelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesDvMachineryApi.Machinery>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '设备编码', width: 120 },
|
||||||
|
{ field: 'name', title: '设备名称', minWidth: 120 },
|
||||||
|
{ field: 'brand', title: '品牌', minWidth: 120 },
|
||||||
|
{ field: 'specification', title: '规格型号', minWidth: 120 },
|
||||||
|
{ field: 'workshopName', title: '所属车间', width: 120 },
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '设备状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_MACHINERY_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 160,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,31 @@
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales';
|
import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getProductSalesPage } from '#/api/mes/wm/productsales';
|
import { getProductSalesPage } from '#/api/mes/wm/productsales';
|
||||||
|
import ProductSalesForm from '#/views/mes/wm/productsales/modules/form.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
clientId: number;
|
clientId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const [DetailModal, detailModalApi] = useVbenModal({
|
||||||
|
connectedComponent: ProductSalesForm,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 查看销售出库单详情 */
|
||||||
|
function handleViewSales(row: MesWmProductSalesApi.ProductSales) {
|
||||||
|
if (row.id) {
|
||||||
|
detailModalApi.setData({ formType: 'detail', id: row.id }).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [Grid] = useVbenVxeGrid({
|
const [Grid] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: [
|
columns: [
|
||||||
|
|
@ -18,6 +34,7 @@ const [Grid] = useVbenVxeGrid({
|
||||||
field: 'code',
|
field: 'code',
|
||||||
title: '出库单编号',
|
title: '出库单编号',
|
||||||
minWidth: 160,
|
minWidth: 160,
|
||||||
|
slots: { default: 'code' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
|
|
@ -70,5 +87,12 @@ const [Grid] = useVbenVxeGrid({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Grid table-title="销售记录" />
|
<DetailModal />
|
||||||
|
<Grid table-title="销售记录">
|
||||||
|
<template #code="{ row }">
|
||||||
|
<Button type="link" @click="handleViewSales(row)">
|
||||||
|
{{ row.code }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 关闭并提示
|
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
await modalApi.close();
|
|
||||||
emit('success');
|
emit('success');
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
|
@ -160,7 +158,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
formType.value = data.formType;
|
formType.value = data.formType;
|
||||||
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
|
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
|
||||||
formApi.setDisabled(formType.value === 'detail');
|
formApi.setDisabled(formType.value === 'detail');
|
||||||
modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
|
modalApi.setState({
|
||||||
|
confirmText: formType.value === 'detail' ? undefined : '保存',
|
||||||
|
showConfirmButton: formType.value !== 'detail',
|
||||||
|
});
|
||||||
if (data?.id) {
|
if (data?.id) {
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as TmToolSelectDialog } from './select-dialog.vue';
|
||||||
export { default as TmToolSelect } from './select.vue';
|
export { default as TmToolSelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,141 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
|
||||||
|
|
||||||
import type { MesTmToolApi } from '#/api/mes/tm/tool';
|
import type { MesTmToolApi } from '#/api/mes/tm/tool';
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { Select } from 'ant-design-vue';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { getToolSimpleList } from '#/api/mes/tm/tool';
|
import { Input, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
withDefaults(
|
import { getTool } from '#/api/mes/tm/tool';
|
||||||
|
|
||||||
|
import TmToolSelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'TmToolSelect', inheritAttrs: false });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
allowClear?: boolean;
|
allowClear?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
modelValue?: number;
|
modelValue?: number;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{ allowClear: true, disabled: false, modelValue: undefined, placeholder: '请选择工具' },
|
{
|
||||||
|
allowClear: true,
|
||||||
|
disabled: false,
|
||||||
|
modelValue: undefined,
|
||||||
|
placeholder: '请选择工具',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesTmToolApi.Tool];
|
change: [item: MesTmToolApi.Tool | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesTmToolApi.Tool[]>([]); // 工具列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof TmToolSelectDialog>>(); // 工具选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesTmToolApi.Tool>(); // 当前选中工具
|
||||||
|
|
||||||
/** 加载工具列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
list.value = await getToolSimpleList();
|
() =>
|
||||||
|
props.allowClear &&
|
||||||
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据工具编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getTool(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理工具选择变化 */
|
watch(
|
||||||
function handleChange(value: SelectValue) {
|
() => props.modelValue,
|
||||||
const toolId = typeof value === 'number' ? value : undefined;
|
(value) => {
|
||||||
emit('update:modelValue', toolId);
|
resolveItemById(value);
|
||||||
emit('change', list.value.find((item) => item.id === toolId));
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选工具 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(getList);
|
/** 打开工具选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.ant-input-suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回填选中的工具 */
|
||||||
|
function handleSelected(rows: MesTmToolApi.Tool[]) {
|
||||||
|
const item = rows[0];
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Select
|
<div
|
||||||
:allow-clear="allowClear"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:field-names="{ label: 'name', value: 'id' }"
|
|
||||||
:options="list"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:value="modelValue"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
option-filter-prop="name"
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
show-search
|
@click="handleClick"
|
||||||
@change="handleChange"
|
@mouseenter="hovering = true"
|
||||||
/>
|
@mouseleave="hovering = false"
|
||||||
|
>
|
||||||
|
<Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
|
||||||
|
<template #title>
|
||||||
|
<div v-if="selectedItem" class="leading-6">
|
||||||
|
<div>工具编码:{{ selectedItem.code || '-' }}</div>
|
||||||
|
<div>工具名称:{{ selectedItem.name || '-' }}</div>
|
||||||
|
<div v-if="selectedItem.brand">品牌:{{ selectedItem.brand }}</div>
|
||||||
|
<div v-if="selectedItem.specification">
|
||||||
|
型号规格:{{ selectedItem.specification }}
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedItem.toolTypeName">
|
||||||
|
工具类型:{{ selectedItem.toolTypeName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Input
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="displayLabel"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<IconifyIcon
|
||||||
|
class="size-4"
|
||||||
|
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<TmToolSelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -325,3 +325,85 @@ export function useGridColumns(): VxeTableGridOptions<MesTmToolApi.Tool>['column
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 工具选择弹窗的搜索表单 */
|
||||||
|
export function useToolSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '工具编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入工具编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '工具名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入工具名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'toolTypeId',
|
||||||
|
label: '工具类型',
|
||||||
|
component: markRaw(TmToolTypeSelect),
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择工具类型',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'brand',
|
||||||
|
label: '品牌',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
placeholder: '请输入品牌',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_TM_TOOL_STATUS, 'number'),
|
||||||
|
placeholder: '请选择状态',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 工具选择弹窗的字段 */
|
||||||
|
export function useToolSelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesTmToolApi.Tool>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '工具编码', width: 120 },
|
||||||
|
{ field: 'name', title: '工具名称', minWidth: 120 },
|
||||||
|
{ field: 'brand', title: '品牌', minWidth: 100 },
|
||||||
|
{ field: 'specification', title: '型号规格', minWidth: 100 },
|
||||||
|
{ field: 'toolTypeName', title: '工具类型', width: 120 },
|
||||||
|
{ field: 'quantity', title: '库存数量', width: 100 },
|
||||||
|
{ field: 'availableQuantity', title: '可用数量', width: 100 },
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_TM_TOOL_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,16 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
|
import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
|
||||||
|
|
||||||
import { nextTick, ref } from 'vue';
|
import { markRaw, nextTick, ref } from 'vue';
|
||||||
|
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getSalesNoticePage } from '#/api/mes/wm/salesnotice';
|
import { getSalesNoticePage } from '#/api/mes/wm/salesnotice';
|
||||||
|
import { MdClientSelect } from '#/views/mes/md/client/components';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
selected: [rows: MesWmSalesNoticeApi.SalesNotice[]];
|
selected: [rows: MesWmSalesNoticeApi.SalesNotice[]];
|
||||||
|
|
@ -23,8 +25,8 @@ const syncingSingleSelection = ref(false); // 是否同步单选勾选状态
|
||||||
const selectedRows = ref<MesWmSalesNoticeApi.SalesNotice[]>([]); // 已选通知单列表
|
const selectedRows = ref<MesWmSalesNoticeApi.SalesNotice[]>([]); // 已选通知单列表
|
||||||
const preSelectedIds = ref<number[]>([]); // 预选通知单编号列表
|
const preSelectedIds = ref<number[]>([]); // 预选通知单编号列表
|
||||||
|
|
||||||
/** 搜索表单 */
|
/** 搜索表单:未固定状态时展示状态下拉,固定状态时隐藏 */
|
||||||
function useSearchSchema(): VbenFormSchema[] {
|
function useSearchSchema(hasFixedStatus: boolean): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
|
|
@ -53,6 +55,31 @@ function useSearchSchema(): VbenFormSchema[] {
|
||||||
placeholder: '请输入销售订单编号',
|
placeholder: '请输入销售订单编号',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'clientId',
|
||||||
|
label: '客户',
|
||||||
|
component: markRaw(MdClientSelect),
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择客户',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(hasFixedStatus
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '单据状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getDictOptions(
|
||||||
|
DICT_TYPE.MES_WM_SALES_NOTICE_STATUS,
|
||||||
|
'number',
|
||||||
|
),
|
||||||
|
placeholder: '请选择单据状态',
|
||||||
|
},
|
||||||
|
} as VbenFormSchema,
|
||||||
|
]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +192,7 @@ function applyPreSelection() {
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useSearchSchema(),
|
schema: useSearchSchema(false),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(),
|
columns: useGridColumns(),
|
||||||
|
|
@ -182,8 +209,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
return await getSalesNoticePage({
|
return await getSalesNoticePage({
|
||||||
pageNo: page.currentPage,
|
pageNo: page.currentPage,
|
||||||
pageSize: page.pageSize,
|
pageSize: page.pageSize,
|
||||||
status: fixedStatus.value,
|
|
||||||
...formValues,
|
...formValues,
|
||||||
|
// 固定状态优先,未固定时用搜索表单里的状态
|
||||||
|
status: fixedStatus.value ?? formValues.status,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -219,6 +247,10 @@ async function openModal(
|
||||||
multiple.value = options?.multiple ?? false;
|
multiple.value = options?.multiple ?? false;
|
||||||
fixedStatus.value = options?.status;
|
fixedStatus.value = options?.status;
|
||||||
preSelectedIds.value = selectedIds || [];
|
preSelectedIds.value = selectedIds || [];
|
||||||
|
// 固定状态时隐藏状态搜索项,未固定时展示
|
||||||
|
gridApi.formApi.setState({
|
||||||
|
schema: useSearchSchema(fixedStatus.value != null),
|
||||||
|
});
|
||||||
await nextTick();
|
await nextTick();
|
||||||
await resetQueryState();
|
await resetQueryState();
|
||||||
await gridApi.query();
|
await gridApi.query();
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as DvCheckPlanSelectDialog } from './select-dialog.vue';
|
||||||
export { default as DvCheckPlanSelect } from './select.vue';
|
export { default as DvCheckPlanSelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
import type { MesDvCheckPlanApi } from '#/api/mes/dv/checkplan';
|
||||||
|
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { ElOption, ElSelect } from 'element-plus';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { CircleX, Search } from '@vben/icons';
|
||||||
|
|
||||||
import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
|
import { ElInput, ElTooltip } from 'element-plus';
|
||||||
|
|
||||||
defineOptions({ name: 'DvCheckPlanSelect' });
|
import { getCheckPlan } from '#/api/mes/dv/checkplan';
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
|
||||||
|
import DvCheckPlanSelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DvCheckPlanSelect', inheritAttrs: false });
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
@ -22,55 +28,123 @@ const props = withDefaults(
|
||||||
clearable: true,
|
clearable: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
modelValue: undefined,
|
modelValue: undefined,
|
||||||
placeholder: '请选择计划',
|
placeholder: '请选择保养方案',
|
||||||
status: undefined,
|
status: undefined,
|
||||||
type: undefined,
|
type: undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesDvCheckPlanApi.CheckPlan];
|
change: [item: MesDvCheckPlanApi.CheckPlan | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesDvCheckPlanApi.CheckPlan[]>([]); // 点检计划列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof DvCheckPlanSelectDialog>>(); // 方案选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesDvCheckPlanApi.CheckPlan>(); // 当前选中方案
|
||||||
|
|
||||||
/** 加载点检计划列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
const data = await getCheckPlanPage({
|
() =>
|
||||||
pageNo: 1,
|
props.clearable &&
|
||||||
pageSize: 100,
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据方案编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getCheckPlan(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => {
|
||||||
|
resolveItemById(value);
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选方案 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开方案选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.el-input__suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, {
|
||||||
|
multiple: false,
|
||||||
status: props.status,
|
status: props.status,
|
||||||
type: props.type,
|
type: props.type,
|
||||||
});
|
});
|
||||||
list.value = data.list || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理点检计划选择变化 */
|
/** 回填选中的方案 */
|
||||||
function handleChange(value: number | string | undefined) {
|
function handleSelected(rows: MesDvCheckPlanApi.CheckPlan[]) {
|
||||||
const planId = typeof value === 'number' ? value : undefined;
|
const item = rows[0];
|
||||||
emit('update:modelValue', planId);
|
if (!item) {
|
||||||
emit('change', list.value.find((item) => item.id === planId));
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => [props.status, props.type], getList);
|
|
||||||
|
|
||||||
onMounted(getList);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElSelect
|
<div
|
||||||
:clearable="clearable"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
filterable
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
@change="handleChange"
|
@click="handleClick"
|
||||||
|
@mouseenter="hovering = true"
|
||||||
|
@mouseleave="hovering = false"
|
||||||
>
|
>
|
||||||
<ElOption
|
<ElTooltip :disabled="!selectedItem" placement="top" :show-after="500">
|
||||||
v-for="item in list"
|
<template #content>
|
||||||
:key="item.id"
|
<div v-if="selectedItem" class="leading-6">
|
||||||
:label="item.name"
|
<div>编码:{{ selectedItem.code || '-' }}</div>
|
||||||
:value="item.id!"
|
<div>名称:{{ selectedItem.name || '-' }}</div>
|
||||||
/>
|
<div class="flex items-center">
|
||||||
</ElSelect>
|
频度:{{ selectedItem.cycleCount ?? '-' }}
|
||||||
|
<DictTag
|
||||||
|
class="ml-1"
|
||||||
|
:type="DICT_TYPE.MES_DV_CYCLE_TYPE"
|
||||||
|
:value="selectedItem.cycleType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ElInput
|
||||||
|
:disabled="disabled"
|
||||||
|
:model-value="displayLabel"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<CircleX v-if="showClear" class="size-4" />
|
||||||
|
<Search v-else class="size-4" />
|
||||||
|
</template>
|
||||||
|
</ElInput>
|
||||||
|
</ElTooltip>
|
||||||
|
</div>
|
||||||
|
<DvCheckPlanSelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -245,3 +245,68 @@ export function useGridColumns(): VxeTableGridOptions<MesDvCheckPlanApi.CheckPla
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 点检方案选择弹窗的搜索表单 */
|
||||||
|
export function useCheckPlanSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '计划编号',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入计划编号',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '计划名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入计划名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点检方案选择弹窗的字段 */
|
||||||
|
export function useCheckPlanSelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesDvCheckPlanApi.CheckPlan>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '计划编码', minWidth: 180 },
|
||||||
|
{ 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: 120, formatter: 'formatDate' },
|
||||||
|
{ field: 'endDate', title: '结束日期', width: 120, formatter: 'formatDate' },
|
||||||
|
{ field: 'cycleCount', title: '频率', width: 100 },
|
||||||
|
{
|
||||||
|
field: 'cycleType',
|
||||||
|
title: '周期类型',
|
||||||
|
width: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_CYCLE_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_CHECK_PLAN_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as DvMachinerySelectDialog } from './select-dialog.vue';
|
||||||
export { default as DvMachinerySelect } from './select.vue';
|
export { default as DvMachinerySelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
import type { MesDvMachineryApi } from '#/api/mes/dv/machinery';
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { ElOption, ElSelect } from 'element-plus';
|
import { CircleX, Search } from '@vben/icons';
|
||||||
|
|
||||||
import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
|
import { ElInput, ElTooltip } from 'element-plus';
|
||||||
|
|
||||||
defineOptions({ name: 'DvMachinerySelect' });
|
import { getMachinery } from '#/api/mes/dv/machinery';
|
||||||
|
|
||||||
withDefaults(
|
import DvMachinerySelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'DvMachinerySelect', inheritAttrs: false });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
@ -24,41 +28,109 @@ withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesDvMachineryApi.Machinery];
|
change: [item: MesDvMachineryApi.Machinery | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesDvMachineryApi.Machinery[]>([]); // 设备列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof DvMachinerySelectDialog>>(); // 设备选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesDvMachineryApi.Machinery>(); // 当前选中设备
|
||||||
|
|
||||||
/** 加载设备列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
list.value = await getMachinerySimpleList();
|
() =>
|
||||||
|
props.clearable &&
|
||||||
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据设备编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getMachinery(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理设备选择变化 */
|
watch(
|
||||||
function handleChange(value: number | string | undefined) {
|
() => props.modelValue,
|
||||||
const machineryId = typeof value === 'number' ? value : undefined;
|
(value) => {
|
||||||
emit('update:modelValue', machineryId);
|
resolveItemById(value);
|
||||||
emit('change', list.value.find((item) => item.id === machineryId));
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选设备 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(getList);
|
/** 打开设备选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.el-input__suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回填选中的设备 */
|
||||||
|
function handleSelected(rows: MesDvMachineryApi.Machinery[]) {
|
||||||
|
const item = rows[0];
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElSelect
|
<div
|
||||||
:clearable="clearable"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
filterable
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
@change="handleChange"
|
@click="handleClick"
|
||||||
|
@mouseenter="hovering = true"
|
||||||
|
@mouseleave="hovering = false"
|
||||||
>
|
>
|
||||||
<ElOption
|
<ElTooltip :disabled="!selectedItem" placement="top" :show-after="500">
|
||||||
v-for="item in list"
|
<template #content>
|
||||||
:key="item.id"
|
<div v-if="selectedItem" class="leading-6">
|
||||||
:label="item.name"
|
<div>设备编码:{{ selectedItem.code || '-' }}</div>
|
||||||
:value="item.id!"
|
<div>设备名称:{{ selectedItem.name || '-' }}</div>
|
||||||
/>
|
<div v-if="selectedItem.brand">品牌:{{ selectedItem.brand }}</div>
|
||||||
</ElSelect>
|
<div v-if="selectedItem.specification">
|
||||||
|
规格型号:{{ selectedItem.specification }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ElInput
|
||||||
|
:disabled="disabled"
|
||||||
|
:model-value="displayLabel"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<CircleX v-if="showClear" class="size-4" />
|
||||||
|
<Search v-else class="size-4" />
|
||||||
|
</template>
|
||||||
|
</ElInput>
|
||||||
|
</ElTooltip>
|
||||||
|
</div>
|
||||||
|
<DvMachinerySelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -273,3 +273,65 @@ export function useImportFormSchema(): VbenFormSchema[] {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 设备选择弹窗的搜索表单 */
|
||||||
|
export function useMachinerySelectGridFormSchema(): 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: '请选择所属车间',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设备选择弹窗的字段 */
|
||||||
|
export function useMachinerySelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesDvMachineryApi.Machinery>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '设备编码', width: 120 },
|
||||||
|
{ field: 'name', title: '设备名称', minWidth: 120 },
|
||||||
|
{ field: 'brand', title: '品牌', minWidth: 120 },
|
||||||
|
{ field: 'specification', title: '规格型号', minWidth: 120 },
|
||||||
|
{ field: 'workshopName', title: '所属车间', width: 120 },
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '设备状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_DV_MACHINERY_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 160,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,31 @@
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales';
|
import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
|
||||||
|
import { ElButton } from 'element-plus';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getProductSalesPage } from '#/api/mes/wm/productsales';
|
import { getProductSalesPage } from '#/api/mes/wm/productsales';
|
||||||
|
import ProductSalesForm from '#/views/mes/wm/productsales/modules/form.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
clientId: number;
|
clientId: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const [DetailModal, detailModalApi] = useVbenModal({
|
||||||
|
connectedComponent: ProductSalesForm,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 查看销售出库单详情 */
|
||||||
|
function handleViewSales(row: MesWmProductSalesApi.ProductSales) {
|
||||||
|
if (row.id) {
|
||||||
|
detailModalApi.setData({ formType: 'detail', id: row.id }).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [Grid] = useVbenVxeGrid({
|
const [Grid] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: [
|
columns: [
|
||||||
|
|
@ -18,6 +34,7 @@ const [Grid] = useVbenVxeGrid({
|
||||||
field: 'code',
|
field: 'code',
|
||||||
title: '出库单编号',
|
title: '出库单编号',
|
||||||
minWidth: 160,
|
minWidth: 160,
|
||||||
|
slots: { default: 'code' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
|
|
@ -70,5 +87,12 @@ const [Grid] = useVbenVxeGrid({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Grid table-title="销售记录" />
|
<DetailModal />
|
||||||
|
<Grid table-title="销售记录">
|
||||||
|
<template #code="{ row }">
|
||||||
|
<ElButton link type="primary" @click="handleViewSales(row)">
|
||||||
|
{{ row.code }}
|
||||||
|
</ElButton>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -143,9 +143,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 关闭并提示
|
|
||||||
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
ElMessage.success($t('ui.actionMessage.operationSuccess'));
|
||||||
await modalApi.close();
|
|
||||||
emit('success');
|
emit('success');
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
|
@ -167,7 +165,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
formType.value = data.formType;
|
formType.value = data.formType;
|
||||||
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
|
formApi.setState({ schema: useFormSchema(formType.value, formApi) });
|
||||||
formApi.setDisabled(formType.value === 'detail');
|
formApi.setDisabled(formType.value === 'detail');
|
||||||
modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
|
modalApi.setState({
|
||||||
|
confirmText: formType.value === 'detail' ? undefined : '保存',
|
||||||
|
showConfirmButton: formType.value !== 'detail',
|
||||||
|
});
|
||||||
if (data?.id) {
|
if (data?.id) {
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as TmToolSelectDialog } from './select-dialog.vue';
|
||||||
export { default as TmToolSelect } from './select.vue';
|
export { default as TmToolSelect } from './select.vue';
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,139 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { MesTmToolApi } from '#/api/mes/tm/tool';
|
import type { MesTmToolApi } from '#/api/mes/tm/tool';
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, ref, useAttrs, watch } from 'vue';
|
||||||
|
|
||||||
import { ElOption, ElSelect } from 'element-plus';
|
import { CircleX, Search } from '@vben/icons';
|
||||||
|
|
||||||
import { getToolSimpleList } from '#/api/mes/tm/tool';
|
import { ElInput, ElTooltip } from 'element-plus';
|
||||||
|
|
||||||
withDefaults(
|
import { getTool } from '#/api/mes/tm/tool';
|
||||||
|
|
||||||
|
import TmToolSelectDialog from './select-dialog.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'TmToolSelect', inheritAttrs: false });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
modelValue?: number;
|
modelValue?: number;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{ clearable: true, disabled: false, modelValue: undefined, placeholder: '请选择工具' },
|
{
|
||||||
|
clearable: true,
|
||||||
|
disabled: false,
|
||||||
|
modelValue: undefined,
|
||||||
|
placeholder: '请选择工具',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [row?: MesTmToolApi.Tool];
|
change: [item: MesTmToolApi.Tool | undefined];
|
||||||
'update:modelValue': [value?: number];
|
'update:modelValue': [value: number | undefined];
|
||||||
}>();
|
}>();
|
||||||
const list = ref<MesTmToolApi.Tool[]>([]); // 工具列表
|
const attrs = useAttrs(); // 透传属性
|
||||||
|
const dialogRef = ref<InstanceType<typeof TmToolSelectDialog>>(); // 工具选择弹窗
|
||||||
|
const hovering = ref(false); // 是否悬停
|
||||||
|
const selectedItem = ref<MesTmToolApi.Tool>(); // 当前选中工具
|
||||||
|
|
||||||
/** 加载工具列表 */
|
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
|
||||||
async function getList() {
|
const showClear = computed(
|
||||||
list.value = await getToolSimpleList();
|
() =>
|
||||||
|
props.clearable &&
|
||||||
|
!props.disabled &&
|
||||||
|
hovering.value &&
|
||||||
|
props.modelValue != null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 根据工具编号回显选择器 */
|
||||||
|
async function resolveItemById(id: number | undefined) {
|
||||||
|
if (id == null) {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedItem.value?.id === id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = await getTool(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理工具选择变化 */
|
watch(
|
||||||
function handleChange(value: number | string | undefined) {
|
() => props.modelValue,
|
||||||
const toolId = typeof value === 'number' ? value : undefined;
|
(value) => {
|
||||||
emit('update:modelValue', toolId);
|
resolveItemById(value);
|
||||||
emit('change', list.value.find((item) => item.id === toolId));
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 清空已选工具 */
|
||||||
|
function clearSelected() {
|
||||||
|
selectedItem.value = undefined;
|
||||||
|
emit('update:modelValue', undefined);
|
||||||
|
emit('change', undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(getList);
|
/** 打开工具选择弹窗 */
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (showClear.value && target.closest('.el-input__suffix')) {
|
||||||
|
event.stopPropagation();
|
||||||
|
clearSelected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedIds = props.modelValue == null ? [] : [props.modelValue];
|
||||||
|
dialogRef.value?.open(selectedIds, { multiple: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回填选中的工具 */
|
||||||
|
function handleSelected(rows: MesTmToolApi.Tool[]) {
|
||||||
|
const item = rows[0];
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItem.value = item;
|
||||||
|
emit('update:modelValue', item.id);
|
||||||
|
emit('change', item);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElSelect
|
<div
|
||||||
:clearable="clearable"
|
v-bind="attrs"
|
||||||
:disabled="disabled"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
filterable
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
@change="handleChange"
|
@click="handleClick"
|
||||||
|
@mouseenter="hovering = true"
|
||||||
|
@mouseleave="hovering = false"
|
||||||
>
|
>
|
||||||
<ElOption v-for="item in list" :key="item.id" :label="item.name" :value="item.id!" />
|
<ElTooltip :disabled="!selectedItem" placement="top" :show-after="500">
|
||||||
</ElSelect>
|
<template #content>
|
||||||
|
<div v-if="selectedItem" class="leading-6">
|
||||||
|
<div>工具编码:{{ selectedItem.code || '-' }}</div>
|
||||||
|
<div>工具名称:{{ selectedItem.name || '-' }}</div>
|
||||||
|
<div v-if="selectedItem.brand">品牌:{{ selectedItem.brand }}</div>
|
||||||
|
<div v-if="selectedItem.specification">
|
||||||
|
型号规格:{{ selectedItem.specification }}
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedItem.toolTypeName">
|
||||||
|
工具类型:{{ selectedItem.toolTypeName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ElInput
|
||||||
|
:disabled="disabled"
|
||||||
|
:model-value="displayLabel"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<CircleX v-if="showClear" class="size-4" />
|
||||||
|
<Search v-else class="size-4" />
|
||||||
|
</template>
|
||||||
|
</ElInput>
|
||||||
|
</ElTooltip>
|
||||||
|
</div>
|
||||||
|
<TmToolSelectDialog ref="dialogRef" @selected="handleSelected" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -328,3 +328,85 @@ export function useGridColumns(): VxeTableGridOptions<MesTmToolApi.Tool>['column
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 工具选择弹窗的搜索表单 */
|
||||||
|
export function useToolSelectGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'code',
|
||||||
|
label: '工具编码',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入工具编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '工具名称',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入工具名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'toolTypeId',
|
||||||
|
label: '工具类型',
|
||||||
|
component: markRaw(TmToolTypeSelect),
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择工具类型',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'brand',
|
||||||
|
label: '品牌',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请输入品牌',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.MES_TM_TOOL_STATUS, 'number'),
|
||||||
|
placeholder: '请选择状态',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 工具选择弹窗的字段 */
|
||||||
|
export function useToolSelectGridColumns(
|
||||||
|
multiple = false,
|
||||||
|
): VxeTableGridOptions<MesTmToolApi.Tool>['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
|
||||||
|
{ field: 'code', title: '工具编码', width: 120 },
|
||||||
|
{ field: 'name', title: '工具名称', minWidth: 120 },
|
||||||
|
{ field: 'brand', title: '品牌', minWidth: 100 },
|
||||||
|
{ field: 'specification', title: '型号规格', minWidth: 100 },
|
||||||
|
{ field: 'toolTypeName', title: '工具类型', width: 120 },
|
||||||
|
{ field: 'quantity', title: '库存数量', width: 100 },
|
||||||
|
{ field: 'availableQuantity', title: '可用数量', width: 100 },
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '状态',
|
||||||
|
width: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.MES_TM_TOOL_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
width: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,16 @@ import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
|
import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
|
||||||
|
|
||||||
import { nextTick, ref } from 'vue';
|
import { markRaw, nextTick, ref } from 'vue';
|
||||||
|
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
import { ElButton, ElDialog, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getSalesNoticePage } from '#/api/mes/wm/salesnotice';
|
import { getSalesNoticePage } from '#/api/mes/wm/salesnotice';
|
||||||
|
import { MdClientSelect } from '#/views/mes/md/client/components';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
selected: [rows: MesWmSalesNoticeApi.SalesNotice[]];
|
selected: [rows: MesWmSalesNoticeApi.SalesNotice[]];
|
||||||
|
|
@ -23,8 +25,8 @@ const syncingSingleSelection = ref(false); // 是否同步单选勾选状态
|
||||||
const selectedRows = ref<MesWmSalesNoticeApi.SalesNotice[]>([]); // 已选通知单列表
|
const selectedRows = ref<MesWmSalesNoticeApi.SalesNotice[]>([]); // 已选通知单列表
|
||||||
const preSelectedIds = ref<number[]>([]); // 预选通知单编号列表
|
const preSelectedIds = ref<number[]>([]); // 预选通知单编号列表
|
||||||
|
|
||||||
/** 搜索表单 */
|
/** 搜索表单:未固定状态时展示状态下拉,固定状态时隐藏 */
|
||||||
function useSearchSchema(): VbenFormSchema[] {
|
function useSearchSchema(hasFixedStatus: boolean): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
fieldName: 'code',
|
fieldName: 'code',
|
||||||
|
|
@ -53,6 +55,31 @@ function useSearchSchema(): VbenFormSchema[] {
|
||||||
placeholder: '请输入销售订单编号',
|
placeholder: '请输入销售订单编号',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'clientId',
|
||||||
|
label: '客户',
|
||||||
|
component: markRaw(MdClientSelect),
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择客户',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(hasFixedStatus
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '单据状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
options: getDictOptions(
|
||||||
|
DICT_TYPE.MES_WM_SALES_NOTICE_STATUS,
|
||||||
|
'number',
|
||||||
|
),
|
||||||
|
placeholder: '请选择单据状态',
|
||||||
|
},
|
||||||
|
} as VbenFormSchema,
|
||||||
|
]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +192,7 @@ function applyPreSelection() {
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useSearchSchema(),
|
schema: useSearchSchema(false),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(),
|
columns: useGridColumns(),
|
||||||
|
|
@ -182,8 +209,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
return await getSalesNoticePage({
|
return await getSalesNoticePage({
|
||||||
pageNo: page.currentPage,
|
pageNo: page.currentPage,
|
||||||
pageSize: page.pageSize,
|
pageSize: page.pageSize,
|
||||||
status: fixedStatus.value,
|
|
||||||
...formValues,
|
...formValues,
|
||||||
|
// 固定状态优先,未固定时用搜索表单里的状态
|
||||||
|
status: fixedStatus.value ?? formValues.status,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -219,6 +247,10 @@ async function openModal(
|
||||||
multiple.value = options?.multiple ?? false;
|
multiple.value = options?.multiple ?? false;
|
||||||
fixedStatus.value = options?.status;
|
fixedStatus.value = options?.status;
|
||||||
preSelectedIds.value = selectedIds || [];
|
preSelectedIds.value = selectedIds || [];
|
||||||
|
// 固定状态时隐藏状态搜索项,未固定时展示
|
||||||
|
gridApi.formApi.setState({
|
||||||
|
schema: useSearchSchema(fixedStatus.value != null),
|
||||||
|
});
|
||||||
await nextTick();
|
await nextTick();
|
||||||
await resetQueryState();
|
await resetQueryState();
|
||||||
await gridApi.query();
|
await gridApi.query();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue