huminmin
2026-08-13 404ca7b6abb46b93b9446510b7ca78d3d4710a15
Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
已修改2个文件
已添加12个文件
1699 ■■■■■ 文件已修改
dist.zip 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/dv/metering/index.ts 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/mes/dv/meteringcheck/index.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/packages/constants/src/biz-mes-enum.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/packages/constants/src/dict-enum.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/components/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/components/select-dialog.vue 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/components/select.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/data.ts 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/index.vue 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/metering/modules/form.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/meteringcheck/data.ts 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/meteringcheck/index.vue 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/dv/meteringcheck/modules/form.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist.zip
Binary files differ
src/api/mes/dv/metering/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesDvMeteringApi {
  /** MES è®¡é‡å™¨å…· */
  export interface Metering {
    id?: number; // ä¸»é”®ID
    code?: string; // è®¡é‡å™¨å…·ç¼–码
    name?: string; // è®¡é‡å™¨å…·åç§°
    specification?: string; // è§„格型号
    brand?: string; // å“ç‰Œ/生产厂家
    category?: number; // å™¨å…·ç±»åˆ«ï¼ˆå­—典)
    precision?: string; // ç²¾åº¦
    range?: string; // é‡ç¨‹
    deptId?: number; // ä½¿ç”¨éƒ¨é—¨ç¼–号
    deptName?: string; // ä½¿ç”¨éƒ¨é—¨åç§°
    chargeUserId?: number; // è´£ä»»äººç¼–号
    chargeUserNickname?: string; // è´£ä»»äººæ˜µç§°
    checkCycleCount?: number; // æ ¡å‡†å‘¨æœŸï¼ˆæœˆï¼‰
    useDate?: string; // æŠ•入使用日期
    lastCheckDate?: string; // ä¸Šæ¬¡æ£€æµ‹æ—¥æœŸ
    nextCheckDate?: string; // ä¸‹æ¬¡æ£€æµ‹æ—¥æœŸï¼ˆåˆ°æœŸæ—¥ï¼‰
    status?: number; // çŠ¶æ€ï¼ˆå­—å…¸ï¼‰
    expireStatus?: number; // åˆ°æœŸçŠ¶æ€ï¼š1 å·²è¿‡æœŸ / 2 å³å°†åˆ°æœŸ / 3 æ­£å¸¸
    expireDays?: number; // è·åˆ°æœŸå¤©æ•°
    remark?: string; // å¤‡æ³¨
    createTime?: Date; // åˆ›å»ºæ—¶é—´
  }
}
/** æŸ¥è¯¢è®¡é‡å™¨å…·åˆ†é¡µ */
export function getMeteringPage(params: PageParam) {
  return requestClient.get<PageResult<MesDvMeteringApi.Metering>>(
    '/mes/dv/metering/page',
    { params },
  );
}
/** æŸ¥è¯¢è®¡é‡å™¨å…·è¯¦æƒ… */
export function getMetering(id: number) {
  return requestClient.get<MesDvMeteringApi.Metering>(
    `/mes/dv/metering/get?id=${id}`,
  );
}
/** æ–°å¢žè®¡é‡å™¨å…· */
export function createMetering(data: MesDvMeteringApi.Metering) {
  return requestClient.post<number>('/mes/dv/metering/create', data);
}
/** ä¿®æ”¹è®¡é‡å™¨å…· */
export function updateMetering(data: MesDvMeteringApi.Metering) {
  return requestClient.put('/mes/dv/metering/update', data);
}
/** åˆ é™¤è®¡é‡å™¨å…· */
export function deleteMetering(id: number) {
  return requestClient.delete(`/mes/dv/metering/delete?id=${id}`);
}
/** å¯¼å‡ºè®¡é‡å™¨å…· */
export function exportMetering(params: any) {
  return requestClient.download('/mes/dv/metering/export-excel', { params });
}
/** æŸ¥è¯¢è®¡é‡å™¨å…·ç²¾ç®€åˆ—表(下拉选项) */
export function getMeteringSimpleList() {
  return requestClient.get<MesDvMeteringApi.Metering[]>(
    '/mes/dv/metering/simple-list',
  );
}
src/api/mes/dv/meteringcheck/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesDvMeteringCheckApi {
  /** MES è®¡é‡æ£€æµ‹è®°å½• */
  export interface MeteringCheck {
    id?: number; // ä¸»é”®ID
    meteringId?: number; // è®¡é‡å™¨å…·ID
    meteringCode?: string; // è®¡é‡å™¨å…·ç¼–码
    meteringName?: string; // è®¡é‡å™¨å…·åç§°
    checkCode?: string; // æ£€æµ‹ç¼–号
    checkDate?: string; // æ£€æµ‹æ—¥æœŸ
    checkOrg?: string; // æ£€æµ‹æœºæž„
    checkResult?: number; // æ£€æµ‹ç»“论(字典)
    certNo?: string; // è¯ä¹¦ç¼–号
    checkUserId?: number; // æ£€æµ‹äººç¼–号
    checkUserNickname?: string; // æ£€æµ‹äººæ˜µç§°
    nextCheckDate?: string; // ä¸‹æ¬¡æ£€æµ‹æ—¥æœŸ
    remark?: string; // å¤‡æ³¨
    createTime?: Date; // åˆ›å»ºæ—¶é—´
  }
}
/** æŸ¥è¯¢è®¡é‡æ£€æµ‹è®°å½•分页 */
export function getMeteringCheckPage(params: PageParam) {
  return requestClient.get<PageResult<MesDvMeteringCheckApi.MeteringCheck>>(
    '/mes/dv/metering-check/page',
    { params },
  );
}
/** æŸ¥è¯¢è®¡é‡æ£€æµ‹è®°å½•详情 */
export function getMeteringCheck(id: number) {
  return requestClient.get<MesDvMeteringCheckApi.MeteringCheck>(
    `/mes/dv/metering-check/get?id=${id}`,
  );
}
/** æ–°å¢žè®¡é‡æ£€æµ‹è®°å½• */
export function createMeteringCheck(data: MesDvMeteringCheckApi.MeteringCheck) {
  return requestClient.post<number>('/mes/dv/metering-check/create', data);
}
/** ä¿®æ”¹è®¡é‡æ£€æµ‹è®°å½• */
export function updateMeteringCheck(data: MesDvMeteringCheckApi.MeteringCheck) {
  return requestClient.put('/mes/dv/metering-check/update', data);
}
/** åˆ é™¤è®¡é‡æ£€æµ‹è®°å½• */
export function deleteMeteringCheck(id: number) {
  return requestClient.delete(`/mes/dv/metering-check/delete?id=${id}`);
}
/** å¯¼å‡ºè®¡é‡æ£€æµ‹è®°å½• */
export function exportMeteringCheck(params: any) {
  return requestClient.download('/mes/dv/metering-check/export-excel', {
    params,
  });
}
src/packages/constants/src/biz-mes-enum.ts
@@ -104,6 +104,26 @@
  ABNORMAL: 2,
} as const;
/** MES è®¡é‡å™¨å…·çŠ¶æ€æžšä¸¾ */
export const MesDvMeteringStatusEnum = {
  IN_USE: 1, // åœ¨ç”¨
  STOPPED: 2, // åœç”¨
  SCRAPPED: 3, // æŠ¥åºŸ
} as const;
/** MES è®¡é‡æ£€æµ‹ç»“论枚举 */
export const MesDvMeteringCheckResultEnum = {
  QUALIFIED: 1, // åˆæ ¼
  UNQUALIFIED: 2, // ä¸åˆæ ¼
} as const;
/** MES è®¡é‡å™¨å…·åˆ°æœŸçŠ¶æ€æžšä¸¾ */
export const MesDvMeteringExpireStatusEnum = {
  OVERDUE: 1, // å·²è¿‡æœŸ
  WITHIN: 2, // å³å°†åˆ°æœŸ
  NORMAL: 3, // æ­£å¸¸
} as const;
/** MES ç»´ä¿®å·¥å•状态枚举 */
export const MesDvRepairStatusEnum = {
  PREPARE: MesOrderStatusConstants.DRAFT,
src/packages/constants/src/dict-enum.ts
@@ -234,6 +234,9 @@
  MES_DV_REPAIR_RESULT: 'mes_dv_repair_result', // MES ç»´ä¿®ç»“æžœ
  MES_DV_CHECK_RECORD_STATUS: 'mes_dv_check_record_status', // MES ç‚¹æ£€è®°å½•状态
  MES_DV_CHECK_RESULT: 'mes_dv_check_result', // MES ç‚¹æ£€ç»“æžœ
  MES_DV_METERING_CATEGORY: 'mes_dv_metering_category', // MES è®¡é‡å™¨å…·ç±»åˆ«
  MES_DV_METERING_STATUS: 'mes_dv_metering_status', // MES è®¡é‡å™¨å…·çŠ¶æ€
  MES_DV_METERING_CHECK_RESULT: 'mes_dv_metering_check_result', // MES è®¡é‡æ£€æµ‹ç»“论
  MES_PRO_LINK_TYPE: 'mes_pro_link_type', // MES å·¥åºå…³ç³»ç±»åž‹
  MES_PRO_WORK_ORDER_STATUS: 'mes_pro_work_order_status', // MES ç”Ÿäº§å·¥å•状态
  MES_PRO_WORK_ORDER_TYPE: 'mes_pro_work_order_type', // MES å·¥å•类型
src/views/mes/dv/metering/components/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2 @@
export { default as DvMeteringSelectDialog } from './select-dialog.vue';
export { default as DvMeteringSelect } from './select.vue';
src/views/mes/dv/metering/components/select-dialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,213 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMeteringApi } from '#/api/mes/dv/metering';
import { nextTick, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getMeteringPage } from '#/api/mes/dv/metering';
import {
  useMeteringSelectGridColumns,
  useMeteringSelectGridFormSchema,
} from '../data';
defineOptions({ name: 'DvMeteringSelectDialog' });
const emit = defineEmits<{
  selected: [rows: MesDvMeteringApi.Metering[]];
}>();
const open = ref(false); // å¼¹çª—是否打开
const multiple = ref(false); // æ˜¯å¦å¤šé€‰
const selectedRows = ref<MesDvMeteringApi.Metering[]>([]); // å·²é€‰å™¨å…·åˆ—表
const preSelectedIds = ref<number[]>([]); // é¢„选器具编号列表
/** èŽ·å–å½“å‰è¡¨æ ¼æ•°æ® */
function getTableRows() {
  return gridApi.grid.getTableData().fullData as MesDvMeteringApi.Metering[];
}
/** èŽ·å–å¤šé€‰è®°å½•ï¼ŒåŒ…å« VXE reserve è·¨é¡µè®°å½• */
function getMultipleSelectedRows() {
  const selectedMap = new Map<number, MesDvMeteringApi.Metering>();
  const records = [
    ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
    ...(gridApi.grid.getCheckboxRecords?.() ?? []),
  ] as MesDvMeteringApi.Metering[];
  records.forEach((row) => {
    const rowId = row.id;
    if (rowId !== undefined && rowId !== null) {
      selectedMap.set(rowId, row);
    }
  });
  return [...selectedMap.values()];
}
/** å¤„理勾选变化 */
function handleCheckboxSelectChange() {
  selectedRows.value = getMultipleSelectedRows();
}
/** å¤„理单选变化 */
function handleRadioChange(row: MesDvMeteringApi.Metering) {
  selectedRows.value = [row];
}
/** å¤šé€‰æ¨¡å¼ä¸‹åˆ‡æ¢è¡Œå‹¾é€‰ */
async function toggleMultipleRow(row: MesDvMeteringApi.Metering) {
  const selected = gridApi.grid.isCheckedByCheckboxRow(row);
  await gridApi.grid.setCheckboxRow(row, !selected);
  selectedRows.value = getMultipleSelectedRows();
}
/** å¤„理行双击 */
async function handleCellDblclick({
  row,
}: {
  row: MesDvMeteringApi.Metering;
}) {
  if (multiple.value) {
    await toggleMultipleRow(row);
    return;
  }
  selectedRows.value = [row];
  await gridApi.grid.setRadioRow(row);
  handleConfirm();
}
/** å›žæ˜¾é¢„选器具 */
async function applyPreSelection() {
  if (preSelectedIds.value.length === 0) {
    return;
  }
  const rows = getTableRows();
  for (const row of rows) {
    if (
      row.id === undefined ||
      row.id === null ||
      !preSelectedIds.value.includes(row.id)
    ) {
      continue;
    }
    if (multiple.value) {
      await gridApi.grid.setCheckboxRow(row, true);
    } else {
      await gridApi.grid.setRadioRow(row);
      selectedRows.value = [row];
      return;
    }
  }
  if (multiple.value) {
    selectedRows.value = getMultipleSelectedRows();
  }
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useMeteringSelectGridFormSchema(),
  },
  gridOptions: {
    columns: useMeteringSelectGridColumns(false),
    height: 480,
    keepSource: true,
    checkboxConfig: {
      highlight: true,
      range: true,
      reserve: true,
    },
    radioConfig: {
      highlight: true,
      trigger: 'row',
    },
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          return await getMeteringPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          });
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesDvMeteringApi.Metering>,
  gridEvents: {
    cellDblclick: handleCellDblclick,
    checkboxAll: handleCheckboxSelectChange,
    checkboxChange: handleCheckboxSelectChange,
    radioChange: ({ row }: { row: MesDvMeteringApi.Metering }) => {
      handleRadioChange(row);
    },
  },
});
/** é‡ç½®æŸ¥è¯¢å’Œé€‰æ‹©çŠ¶æ€ */
async function resetQueryState() {
  selectedRows.value = [];
  await gridApi.grid.clearCheckboxRow();
  await gridApi.grid.clearCheckboxReserve();
  await gridApi.grid.clearRadioRow();
  await gridApi.formApi.resetForm();
}
/** æ‰“开计量器具选择弹窗 */
async function openModal(
  selectedIds?: number[],
  options?: { multiple?: boolean },
) {
  open.value = true;
  multiple.value = options?.multiple ?? false;
  preSelectedIds.value = selectedIds || [];
  await nextTick();
  gridApi.setGridOptions({
    columns: useMeteringSelectGridColumns(multiple.value),
  });
  await resetQueryState();
  await gridApi.query();
  await nextTick();
  await applyPreSelection();
}
/** å…³é—­è®¡é‡å™¨å…·é€‰æ‹©å¼¹çª— */
async function closeModal() {
  open.value = false;
  await resetQueryState();
}
/** ç¡®è®¤é€‰æ‹©è®¡é‡å™¨å…· */
function handleConfirm() {
  const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
  if (rows.length === 0) {
    message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
    return;
  }
  emit('selected', multiple.value ? rows : [rows[0]!]);
  open.value = false;
}
defineExpose({ open: openModal });
</script>
<template>
  <Modal
    v-model:open="open"
    title="计量器具选择"
    width="70%"
    :destroy-on-close="true"
    @ok="handleConfirm"
    @cancel="closeModal"
  >
    <Grid table-title="计量器具列表" />
  </Modal>
</template>
src/views/mes/dv/metering/components/select.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
<script lang="ts" setup>
import type { MesDvMeteringApi } from '#/api/mes/dv/metering';
import { computed, ref, useAttrs, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { Input, Tooltip } from 'ant-design-vue';
import { getMetering } from '#/api/mes/dv/metering';
import DvMeteringSelectDialog from './select-dialog.vue';
defineOptions({ name: 'DvMeteringSelect', inheritAttrs: false });
const props = withDefaults(
  defineProps<{
    allowClear?: boolean;
    disabled?: boolean;
    modelValue?: number;
    placeholder?: string;
  }>(),
  {
    allowClear: true,
    disabled: false,
    modelValue: undefined,
    placeholder: '请选择计量器具',
  },
);
const emit = defineEmits<{
  change: [item: MesDvMeteringApi.Metering | undefined];
  'update:modelValue': [value: number | undefined];
}>();
const attrs = useAttrs(); // é€ä¼ å±žæ€§
const dialogRef = ref<InstanceType<typeof DvMeteringSelectDialog>>(); // è®¡é‡å™¨å…·é€‰æ‹©å¼¹çª—
const hovering = ref(false); // æ˜¯å¦æ‚¬åœ
const selectedItem = ref<MesDvMeteringApi.Metering>(); // å½“前选中的计量器具
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // é€‰æ‹©å™¨å±•示名称
const showClear = computed(
  () =>
    props.allowClear &&
    !props.disabled &&
    hovering.value &&
    props.modelValue !== null,
);
/** æ ¹æ®å™¨å…·ç¼–号回显选择器 */
async function resolveItemById(id: number | undefined) {
  if (!id) {
    selectedItem.value = undefined;
    return;
  }
  if (selectedItem.value?.id === id) {
    return;
  }
  selectedItem.value = await getMetering(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]
  ) as number[];
  dialogRef.value?.open(selectedIds, { multiple: false });
}
/** å›žå¡«é€‰ä¸­çš„计量器具 */
function handleSelected(rows: MesDvMeteringApi.Metering[]) {
  const item = rows[0];
  if (!item) {
    return;
  }
  selectedItem.value = item;
  emit('update:modelValue', item.id);
  emit('change', item);
}
</script>
<template>
  <div
    v-bind="attrs"
    class="w-full"
    :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
    @click="handleClick"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
      <template #title>
        <div v-if="selectedItem" class="leading-6">
          <div>器具编码:{{ selectedItem.code || '-' }}</div>
          <div>器具名称:{{ selectedItem.name || '-' }}</div>
          <div v-if="selectedItem.specification">
            è§„格型号:{{ selectedItem.specification }}
          </div>
          <div v-if="selectedItem.deptName">
            ä½¿ç”¨éƒ¨é—¨ï¼š{{ selectedItem.deptName }}
          </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>
  <DvMeteringSelectDialog ref="dialogRef" @selected="handleSelected" />
</template>
src/views/mes/dv/metering/data.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,437 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMeteringApi } from '#/api/mes/dv/metering';
import {
  DICT_TYPE,
  MesDvMeteringExpireStatusEnum,
  MesDvMeteringStatusEnum,
} from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils';
import { z } from '#/adapter/form';
import { getSimpleDeptList } from '#/api/system/dept';
import { getSimpleUserList } from '#/api/system/user';
/** è¡¨å•类型 */
export type FormType = 'create' | 'detail' | 'update';
/** åˆ°æœŸçŠ¶æ€å±•ç¤ºé…ç½® */
export const METERING_EXPIRE_STATUS_OPTIONS: {
  color: string;
  label: string;
  value: number;
}[] = [
  {
    label: '已过期',
    value: MesDvMeteringExpireStatusEnum.OVERDUE,
    color: 'red',
  },
  {
    label: '即将到期',
    value: MesDvMeteringExpireStatusEnum.WITHIN,
    color: 'orange',
  },
  {
    label: '正常',
    value: MesDvMeteringExpireStatusEnum.NORMAL,
    color: 'green',
  },
];
/** æ–°å¢ž/修改计量器具的表单 */
export function useFormSchema(formType: FormType): 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',
    },
    {
      fieldName: 'name',
      label: '器具名称',
      component: 'Input',
      componentProps: {
        placeholder: '请输入器具名称',
      },
      rules: 'required',
    },
    {
      fieldName: 'category',
      label: '器具类别',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_CATEGORY, 'number'),
        placeholder: '请选择器具类别',
      },
      help: '如卡尺、压力表、温度计等,不同类别有常见的校准周期',
      rules: 'selectRequired',
    },
    {
      fieldName: 'specification',
      label: '规格型号',
      component: 'Input',
      componentProps: {
        placeholder: '请输入规格型号',
      },
    },
    {
      fieldName: 'brand',
      label: '品牌/生产厂家',
      component: 'Input',
      componentProps: {
        placeholder: '请输入品牌/生产厂家',
      },
    },
    {
      fieldName: 'precision',
      label: '精度',
      component: 'Input',
      componentProps: {
        placeholder: '请输入精度',
      },
      help: '测量精度,如 0.01mm',
    },
    {
      fieldName: 'range',
      label: '量程',
      component: 'Input',
      componentProps: {
        placeholder: '请输入量程',
      },
      help: '测量范围,如 0~150mm',
    },
    {
      fieldName: 'deptId',
      label: '使用部门',
      component: 'ApiTreeSelect',
      componentProps: {
        api: async () => handleTree(await getSimpleDeptList()),
        childrenField: 'children',
        labelField: 'name',
        placeholder: '请选择使用部门',
        valueField: 'id',
      },
    },
    {
      fieldName: 'chargeUserId',
      label: '责任人',
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        api: getSimpleUserList,
        labelField: 'nickname',
        placeholder: '请选择责任人',
        valueField: 'id',
      },
      help: '该器具的维护责任人,到期提醒的站内信会发给他',
    },
    {
      fieldName: 'checkCycleCount',
      label: '校准周期',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        min: 1,
        precision: 0,
        placeholder: '请输入校准周期',
        suffix: '月',
      },
      help: '器具每隔 N ä¸ªæœˆé¡»æ ¡å‡†/检定一次;新增检测记录时不填下次检测日期,将按「检测日期 + æ ¡å‡†å‘¨æœŸã€è‡ªåŠ¨è®¡ç®—',
      rules: 'required',
    },
    {
      fieldName: 'useDate',
      label: '投入使用日期',
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        format: 'YYYY-MM-DD',
        placeholder: '请选择投入使用日期',
        valueFormat: 'YYYY-MM-DD',
      },
      help: '器具开始投入使用的日期(仅作记录)',
    },
    {
      fieldName: 'lastCheckDate',
      label: '上次检测日期',
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        disabled: true,
        format: 'YYYY-MM-DD',
        placeholder: '上次检测日期',
        valueFormat: 'YYYY-MM-DD',
      },
      help: '由最新一条检测记录自动回写,不可手动修改',
      dependencies: {
        triggerFields: ['id'],
        show: () => formType === 'detail',
      },
    },
    {
      fieldName: 'nextCheckDate',
      label: '下次检测日期',
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        disabled: true,
        format: 'YYYY-MM-DD',
        placeholder: '下次检测日期',
        valueFormat: 'YYYY-MM-DD',
      },
      help: '到期日,由检测记录自动计算回写;到期前 N å¤©ï¼ˆåŸºç¡€è®¾æ–½é…ç½®ï¼‰å°†æé†’责任人',
      dependencies: {
        triggerFields: ['id'],
        show: () => formType === 'detail',
      },
    },
    {
      fieldName: 'status',
      label: '器具状态',
      component: 'RadioGroup',
      componentProps: {
        buttonStyle: 'solid',
        optionType: 'button',
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_STATUS, 'number'),
      },
      help: '在用/停用/报废',
      rules: z.number().default(MesDvMeteringStatusEnum.IN_USE),
    },
    {
      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: 'category',
      label: '器具类别',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_CATEGORY, 'number'),
        placeholder: '请选择器具类别',
      },
    },
    {
      fieldName: 'deptId',
      label: '使用部门',
      component: 'ApiTreeSelect',
      componentProps: {
        allowClear: true,
        api: async () => handleTree(await getSimpleDeptList()),
        childrenField: 'children',
        labelField: 'name',
        placeholder: '请选择使用部门',
        valueField: 'id',
      },
    },
    {
      fieldName: 'chargeUserId',
      label: '责任人',
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        api: getSimpleUserList,
        labelField: 'nickname',
        placeholder: '请选择责任人',
        valueField: 'id',
      },
    },
    {
      fieldName: 'status',
      label: '器具状态',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_STATUS, 'number'),
        placeholder: '请选择器具状态',
      },
    },
    {
      fieldName: 'expireStatus',
      label: '到期状态',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: METERING_EXPIRE_STATUS_OPTIONS,
        placeholder: '请选择到期状态',
      },
      help: '下次检测日期距今天 â‰¤ æé†’天数(基础设施配置 mes.dv-metering.remind-days)即视为「即将到期」',
    },
  ];
}
/** åˆ—表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesDvMeteringApi.Metering>['columns'] {
  return [
    {
      field: 'code',
      title: '器具编码',
      minWidth: 150,
      slots: {
        default: 'code',
      },
    },
    { field: 'name', title: '器具名称', minWidth: 150 },
    {
      field: 'category',
      title: '器具类别',
      width: 110,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_DV_METERING_CATEGORY },
      },
    },
    { field: 'specification', title: '规格型号', minWidth: 130 },
    { field: 'brand', title: '品牌/生产厂家', minWidth: 130 },
    { field: 'deptName', title: '使用部门', minWidth: 130 },
    { field: 'chargeUserNickname', title: '责任人', minWidth: 100 },
    {
      field: 'expireStatus',
      title: '到期状态',
      width: 110,
      slots: {
        default: 'expireStatus',
      },
    },
    {
      field: 'nextCheckDate',
      title: '下次检测日期',
      width: 130,
      formatter: 'formatDate',
    },
    {
      field: 'status',
      title: '器具状态',
      width: 100,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_DV_METERING_STATUS },
      },
    },
    {
      field: 'createTime',
      title: '创建时间',
      width: 180,
      formatter: 'formatDateTime',
    },
    {
      title: '操作',
      width: 210,
      fixed: 'right',
      slots: {
        default: 'actions',
      },
    },
  ];
}
/** è®¡é‡å™¨å…·é€‰æ‹©å¼¹çª—的搜索表单 */
export function useMeteringSelectGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'code',
      label: '器具编码',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入器具编码',
      },
    },
    {
      fieldName: 'name',
      label: '器具名称',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入器具名称',
      },
    },
  ];
}
/** è®¡é‡å™¨å…·é€‰æ‹©å¼¹çª—的字段 */
export function useMeteringSelectGridColumns(
  multiple = false,
): VxeTableGridOptions<MesDvMeteringApi.Metering>['columns'] {
  return [
    { type: multiple ? 'checkbox' : 'radio', width: 50 },
    { field: 'code', title: '器具编码', width: 140 },
    { field: 'name', title: '器具名称', minWidth: 140 },
    {
      field: 'category',
      title: '器具类别',
      width: 100,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_DV_METERING_CATEGORY },
      },
    },
    { field: 'specification', title: '规格型号', minWidth: 120 },
    { field: 'deptName', title: '使用部门', width: 120 },
    { field: 'chargeUserNickname', title: '责任人', width: 100 },
    {
      field: 'nextCheckDate',
      title: '下次检测日期',
      width: 120,
      formatter: 'formatDate',
    },
    {
      field: 'status',
      title: '器具状态',
      width: 90,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_DV_METERING_STATUS },
      },
    },
  ];
}
src/views/mes/dv/metering/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,176 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMeteringApi } from '#/api/mes/dv/metering';
import { Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message, Tag } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  deleteMetering,
  exportMetering,
  getMeteringPage,
} from '#/api/mes/dv/metering';
import { $t } from '#/locales';
import {
  METERING_EXPIRE_STATUS_OPTIONS,
  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({ formType: 'create' }).open();
}
/** æŸ¥çœ‹è®¡é‡å™¨å…· */
function handleDetail(row: MesDvMeteringApi.Metering) {
  formModalApi.setData({ id: row.id, formType: 'detail' }).open();
}
/** ç¼–辑计量器具 */
function handleEdit(row: MesDvMeteringApi.Metering) {
  formModalApi.setData({ id: row.id, formType: 'update' }).open();
}
/** åˆ é™¤è®¡é‡å™¨å…· */
async function handleDelete(row: MesDvMeteringApi.Metering) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.name]),
    duration: 0,
  });
  try {
    await deleteMetering(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºè®¡é‡å™¨å…· */
async function handleExport() {
  const data = await exportMetering(await gridApi.formApi.getValues());
  downloadFileFromBlobPart({ fileName: '计量器具.xls', source: data });
}
/** åˆ°æœŸçŠ¶æ€å±•ç¤º */
function getExpireStatusConfig(status?: number) {
  return METERING_EXPIRE_STATUS_OPTIONS.find((item) => item.value === status);
}
const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useGridFormSchema(),
  },
  gridOptions: {
    columns: useGridColumns(),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) =>
          await getMeteringPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          }),
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesDvMeteringApi.Metering>,
});
</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-metering:create'],
              onClick: handleCreate,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:dv-metering:export'],
              onClick: handleExport,
            },
          ]"
        />
      </template>
      <template #code="{ row }">
        <Button type="link" @click="handleDetail(row)">
          {{ row.code }}
        </Button>
      </template>
      <template #expireStatus="{ row }">
        <Tag
          v-if="getExpireStatusConfig(row.expireStatus)"
          :color="getExpireStatusConfig(row.expireStatus)!.color"
        >
          {{ getExpireStatusConfig(row.expireStatus)!.label }}
        </Tag>
        <span v-else>-</span>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
            {
              label: $t('common.edit'),
              type: 'link',
              icon: ACTION_ICON.EDIT,
              auth: ['mes:dv-metering:update'],
              onClick: handleEdit.bind(null, row),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
              icon: ACTION_ICON.DELETE,
              auth: ['mes:dv-metering: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-metering:query'],
              onClick: handleDetail.bind(null, row),
            },
          ]"
        />
      </template>
    </Grid>
  </Page>
</template>
src/views/mes/dv/metering/modules/form.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesDvMeteringApi } from '#/api/mes/dv/metering';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
  createMetering,
  getMetering,
  updateMetering,
} from '#/api/mes/dv/metering';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesDvMeteringApi.Metering>();
const isDetail = computed(() => formType.value === 'detail');
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看计量器具';
  }
  return formType.value === 'update' ? '修改计量器具' : '新增计量器具';
});
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  wrapperClass: 'grid-cols-3',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
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 MesDvMeteringApi.Metering;
    try {
      if (formType.value === 'create') {
        await createMetering(data);
      } else {
        await updateMetering(data);
      }
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
      await modalApi.close();
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(formType.value) });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getMetering(data.id);
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
</script>
<template>
  <Modal :title="getTitle" class="w-3/5">
    <Form class="mx-4" />
  </Modal>
</template>
src/views/mes/dv/meteringcheck/data.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,218 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMeteringCheckApi } from '#/api/mes/dv/meteringcheck';
import { markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { getSimpleUserList } from '#/api/system/user';
import { DvMeteringSelect } from '#/views/mes/dv/metering/components';
/** è¡¨å•类型 */
export type FormType = 'create' | 'detail' | 'update';
/** æ–°å¢ž/修改计量检测记录的表单 */
export function useFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'meteringId',
      label: '计量器具',
      component: markRaw(DvMeteringSelect),
      componentProps: {
        placeholder: '请选择计量器具',
      },
      help: '选择器具后,其校准周期将用于自动计算下次检测日期',
      rules: 'selectRequired',
    },
    {
      fieldName: 'checkCode',
      label: '检测编号',
      component: 'Input',
      componentProps: {
        placeholder: '请输入检测编号',
      },
      rules: 'required',
    },
    {
      fieldName: 'checkDate',
      label: '检测日期',
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        format: 'YYYY-MM-DD',
        placeholder: '请选择检测日期',
        valueFormat: 'YYYY-MM-DD',
      },
      rules: 'required',
    },
    {
      fieldName: 'checkOrg',
      label: '检测机构',
      component: 'Input',
      componentProps: {
        placeholder: '请输入检测机构',
      },
    },
    {
      fieldName: 'checkResult',
      label: '检测结论',
      component: 'RadioGroup',
      componentProps: {
        buttonStyle: 'solid',
        optionType: 'button',
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_CHECK_RESULT, 'number'),
      },
      rules: 'required',
    },
    {
      fieldName: 'certNo',
      label: '证书编号',
      component: 'Input',
      componentProps: {
        placeholder: '请输入证书编号',
      },
    },
    {
      fieldName: 'checkUserId',
      label: '检测人',
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        api: getSimpleUserList,
        labelField: 'nickname',
        placeholder: '请选择检测人',
        valueField: 'id',
      },
    },
    {
      fieldName: 'nextCheckDate',
      label: '下次检测日期',
      component: 'DatePicker',
      componentProps: {
        class: '!w-full',
        format: 'YYYY-MM-DD',
        placeholder: '请选择下次检测日期',
        valueFormat: 'YYYY-MM-DD',
      },
      help: '到期日,建议不填:将按「检测日期 + æ ¡å‡†å‘¨æœŸã€è‡ªåŠ¨è®¡ç®—å¹¶å›žå†™åˆ°å™¨å…·ï¼›ä¹Ÿå¯æ‰‹åŠ¨æŒ‡å®š',
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
      formItemClass: 'col-span-3',
      componentProps: {
        placeholder: '请输入备注',
        rows: 3,
      },
    },
  ];
}
/** åˆ—表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      fieldName: 'meteringId',
      label: '计量器具',
      component: markRaw(DvMeteringSelect),
      componentProps: {
        allowClear: true,
        placeholder: '请选择计量器具',
      },
    },
    {
      fieldName: 'checkCode',
      label: '检测编号',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入检测编号',
      },
    },
    {
      fieldName: 'checkResult',
      label: '检测结论',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.MES_DV_METERING_CHECK_RESULT, 'number'),
        placeholder: '请选择检测结论',
      },
    },
    {
      fieldName: 'checkDate',
      label: '检测日期',
      component: 'RangePicker',
      componentProps: {
        allowClear: true,
        format: 'YYYY-MM-DD',
        valueFormat: 'YYYY-MM-DD',
      },
    },
  ];
}
/** åˆ—表的字段 */
export function useGridColumns(): VxeTableGridOptions<MesDvMeteringCheckApi.MeteringCheck>['columns'] {
  return [
    {
      field: 'meteringCode',
      title: '器具编码',
      minWidth: 140,
      slots: {
        default: 'meteringCode',
      },
    },
    { field: 'meteringName', title: '器具名称', minWidth: 150 },
    { field: 'checkCode', title: '检测编号', minWidth: 140 },
    {
      field: 'checkDate',
      title: '检测日期',
      width: 120,
      formatter: 'formatDate',
    },
    { field: 'checkOrg', title: '检测机构', minWidth: 140 },
    {
      field: 'checkResult',
      title: '检测结论',
      width: 100,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.MES_DV_METERING_CHECK_RESULT },
      },
    },
    { field: 'certNo', title: '证书编号', minWidth: 130 },
    { field: 'checkUserNickname', title: '检测人', minWidth: 100 },
    {
      field: 'nextCheckDate',
      title: '下次检测日期',
      width: 130,
      formatter: 'formatDate',
    },
    {
      field: 'createTime',
      title: '创建时间',
      width: 180,
      formatter: 'formatDateTime',
    },
    {
      title: '操作',
      width: 200,
      fixed: 'right',
      slots: {
        default: 'actions',
      },
    },
  ];
}
src/views/mes/dv/meteringcheck/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,158 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMeteringCheckApi } from '#/api/mes/dv/meteringcheck';
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 {
  deleteMeteringCheck,
  exportMeteringCheck,
  getMeteringCheckPage,
} from '#/api/mes/dv/meteringcheck';
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({ formType: 'create' }).open();
}
/** æŸ¥çœ‹è®¡é‡æ£€æµ‹è®°å½• */
function handleDetail(row: MesDvMeteringCheckApi.MeteringCheck) {
  formModalApi.setData({ id: row.id, formType: 'detail' }).open();
}
/** ç¼–辑计量检测记录 */
function handleEdit(row: MesDvMeteringCheckApi.MeteringCheck) {
  formModalApi.setData({ id: row.id, formType: 'update' }).open();
}
/** åˆ é™¤è®¡é‡æ£€æµ‹è®°å½• */
async function handleDelete(row: MesDvMeteringCheckApi.MeteringCheck) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.checkCode]),
    duration: 0,
  });
  try {
    await deleteMeteringCheck(row.id!);
    message.success($t('ui.actionMessage.deleteSuccess', [row.checkCode]));
    handleRefresh();
  } finally {
    hideLoading();
  }
}
/** å¯¼å‡ºè®¡é‡æ£€æµ‹è®°å½• */
async function handleExport() {
  const data = await exportMeteringCheck(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 getMeteringCheckPage({
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            ...formValues,
          }),
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<MesDvMeteringCheckApi.MeteringCheck>,
});
</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-metering-check:create'],
              onClick: handleCreate,
            },
            {
              label: $t('ui.actionTitle.export'),
              type: 'primary',
              icon: ACTION_ICON.DOWNLOAD,
              auth: ['mes:dv-metering-check:export'],
              onClick: handleExport,
            },
          ]"
        />
      </template>
      <template #meteringCode="{ row }">
        <Button type="link" @click="handleDetail(row)">
          {{ row.meteringCode }}
        </Button>
      </template>
      <template #actions="{ row }">
        <TableAction
          :actions="[
            {
              label: $t('common.edit'),
              type: 'link',
              icon: ACTION_ICON.EDIT,
              auth: ['mes:dv-metering-check:update'],
              onClick: handleEdit.bind(null, row),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
              icon: ACTION_ICON.DELETE,
              auth: ['mes:dv-metering-check:delete'],
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm', [row.checkCode]),
                confirm: handleDelete.bind(null, row),
              },
            },
            {
              label: $t('common.detail'),
              type: 'link',
              icon: ACTION_ICON.VIEW,
              auth: ['mes:dv-metering-check:query'],
              onClick: handleDetail.bind(null, row),
            },
          ]"
        />
      </template>
    </Grid>
  </Page>
</template>
src/views/mes/dv/meteringcheck/modules/form.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesDvMeteringCheckApi } from '#/api/mes/dv/meteringcheck';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
  createMeteringCheck,
  getMeteringCheck,
  updateMeteringCheck,
} from '#/api/mes/dv/meteringcheck';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const formData = ref<MesDvMeteringCheckApi.MeteringCheck>();
const isDetail = computed(() => formType.value === 'detail');
const getTitle = computed(() => {
  if (formType.value === 'detail') {
    return '查看计量检测记录';
  }
  return formType.value === 'update' ? '修改计量检测记录' : '新增计量检测记录';
});
const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
    },
    formItemClass: 'col-span-1',
    labelWidth: 110,
  },
  wrapperClass: 'grid-cols-3',
  layout: 'horizontal',
  schema: [],
  showDefaultActions: false,
});
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 MesDvMeteringCheckApi.MeteringCheck;
    try {
      if (formType.value === 'create') {
        await createMeteringCheck(data);
      } else {
        await updateMeteringCheck(data);
      }
      emit('success');
      message.success($t('ui.actionMessage.operationSuccess'));
      await modalApi.close();
    } finally {
      modalApi.unlock();
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      formData.value = undefined;
      return;
    }
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema() });
    formApi.setDisabled(formType.value === 'detail');
    modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
    if (!data?.id) {
      return;
    }
    modalApi.lock();
    try {
      formData.value = await getMeteringCheck(data.id);
      await formApi.setValues(formData.value);
    } finally {
      modalApi.unlock();
    }
  },
});
</script>
<template>
  <Modal :title="getTitle" class="w-3/5">
    <Form class="mx-4" />
  </Modal>
</template>