liu
2026-10-23 79a901345ca8b7a3fdbce5c569dc3144db7c2205
feat(bpm): 审批统一走 BPM,补齐各业务审批详情组件

- 21 个业务列表页与 MES 项目详情页隐藏旧「审核 / 审核不通过」按钮,审批改在协同办公-待办任务中完成
- 新增 16 个审批详情组件 detail/approve/index.vue:
CRM 退费/销户,ERP 采购计划/申请/订单,HRM 转正/绩效考核,
MES 技术确认/设计项目,仓储设备入库/采购入库/杂项出入库/供应商退货/盘点结果,售后赔付
- 新增 HRM 转正 API

Co-Authored-By: Claude <noreply@anthropic.com>
已添加17个文件
已修改23个文件
2209 ■■■■■ 文件已修改
src/api/hrm/regularize/index.ts 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/aftersales/compensation/detail/approve/index.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/aftersales/compensation/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/cancellation/detail/approve/index.vue 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/cancellation/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/contract/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/invoice/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/refund/detail/approve/index.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/crm/refund/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/finance/payment/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/order/detail/approve/index.vue 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/order/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/plan/detail/approve/index.vue 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/plan/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/request/detail/approve/index.vue 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/erp/purchase/request/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/attendance/exception/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/leave/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/performance-appraise/detail/approve/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/performance-appraise/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/regularize/detail/approve/index.vue 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/resignation/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/detail/approve/index.vue 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/detail/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/project/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/tech-confirm/detail/approve/index.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/pd/tech-confirm/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/equipmentreceipt/detail/approve/index.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/equipmentreceipt/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/itemreceipt/detail/approve/index.vue 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/itemreceipt/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/miscissue/detail/approve/index.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/miscissue/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/miscreceipt/detail/approve/index.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/miscreceipt/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/returnvendor/detail/approve/index.vue 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/returnvendor/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/stocktaking/task/detail/approve/index.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/wls/stocktaking/task/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/regularize/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace HrmRegularizeApi {
  /** è½¬æ­£ç”³è¯· */
  export interface Regularize {
    id?: number; // ç”³è¯·ID
    no?: string; // ç”³è¯·å•号
    userId?: number; // ç”³è¯·äººID
    userName?: string; // ç”³è¯·äººå§“名
    deptId?: number; // éƒ¨é—¨ID
    deptName?: string; // éƒ¨é—¨åç§°
    postId?: number; // å²—位ID
    postName?: string; // å²—位名称
    employmentType?: number; // å½“前用工性质
    employmentTypeName?: string; // å½“前用工性质名称
    expectedDate?: string; // æ‹Ÿè½¬æ­£æ—¥æœŸ
    reason?: string; // è½¬æ­£åŽŸå› 
    status?: number; // å®¡æ‰¹çŠ¶æ€ï¼š0草稿 10审批中 20通过 30驳回 40已取消
    statusName?: string; // å®¡æ‰¹çŠ¶æ€åç§°
    reviewerId?: number; // å®¡æ ¸äººç¼–号
    reviewerName?: string; // å®¡æ ¸äººå§“名
    reviewTime?: string; // å®¡æ ¸æ—¶é—´
    reviewRemark?: string; // å®¡æ ¸æ„è§
    approveTime?: string; // å®¡æ‰¹å®Œæˆæ—¶é—´
    remark?: string; // å¤‡æ³¨
    createTime?: string; // åˆ›å»ºæ—¶é—´
  }
  export interface PageParams extends PageParam {
    no?: string;
    userId?: number;
    status?: number;
  }
}
/** æŸ¥è¯¢è½¬æ­£ç”³è¯·åˆ†é¡µ */
export function getRegularizePage(params: HrmRegularizeApi.PageParams) {
  return requestClient.get<PageResult<HrmRegularizeApi.Regularize>>(
    '/hrm/regularize/page',
    { params },
  );
}
/** æŸ¥è¯¢è½¬æ­£ç”³è¯·è¯¦æƒ… */
export function getRegularize(id: number) {
  return requestClient.get<HrmRegularizeApi.Regularize>(
    `/hrm/regularize/get?id=${id}`,
  );
}
/** æäº¤å®¡æ‰¹ï¼ˆè‰ç¨¿ / å®¡æ ¸ä¸é€šè¿‡ â†’ å®¡æ‰¹ä¸­ï¼‰ */
export function submitRegularize(id: number) {
  return requestClient.put('/hrm/regularize/submit', null, { params: { id } });
}
/** å®¡æ ¸è½¬æ­£ç”³è¯· */
export function auditRegularize(
  id: number,
  pass: boolean,
  reviewRemark?: string,
) {
  return requestClient.put('/hrm/regularize/audit', { id, pass, reviewRemark });
}
src/views/aftersales/compensation/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
<script lang="ts" setup>
import type { AfterSaleCompensationApi } from '#/api/aftersales/compensation';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getCompensation } from '#/api/aftersales/compensation';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'AfterSaleCompensationApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<AfterSaleCompensationApi.Compensation>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '赔付单号', field: 'compensationNo', render: textRender },
    {
      label: '赔付类型',
      field: 'compensationType',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.AFTER_SALE_COMPENSATION_TYPE, value: val }),
    },
    { label: '客户名称', field: 'customerName', render: textRender },
    {
      label: '赔付金额(元)',
      field: 'amount',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    { label: '赔付对象', field: 'payee', render: textRender },
    { label: '赔付原因', field: 'reason', render: textRender },
    { label: '关联售后工单', field: 'ticketId', render: textRender },
    { label: '关联销售订单', field: 'saleOrderId', render: textRender },
    { label: '审批状态', field: 'auditStatusName', render: textRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getCompensation(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- èµ”付基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">赔付基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/aftersales/compensation/index.vue
@@ -219,24 +219,6 @@
                row.auditStatus === AUDIT_STATUS.REJECT,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['aftersales:compensation:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['aftersales:compensation:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
src/views/crm/cancellation/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
<script lang="ts" setup>
import type { CrmCancellationApi } from '#/api/crm/cancellation';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getCancellation } from '#/api/crm/cancellation';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'CrmCancellationApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<CrmCancellationApi.Cancellation>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '销户编号', field: 'cancellationNo', render: textRender },
    { label: '客户名称', field: 'customerName', render: textRender },
    { label: '合同编号', field: 'contractNo', render: textRender },
    {
      label: '销户类型',
      field: 'cancellationType',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.CRM_CANCELLATION_TYPE, value: val }),
    },
    { label: '销户原因', field: 'cancelReason', render: textRender },
    {
      label: '预计销户日期',
      field: 'expectedCancelDate',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '实际销户日期',
      field: 'actualCancelDate',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '退费金额(元)',
      field: 'refundAmount',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    { label: '申请人', field: 'applyUserName', render: textRender },
    {
      label: '申请时间',
      field: 'applyTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '审批状态',
      field: 'auditStatus',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.CRM_AUDIT_STATUS, value: val }),
    },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getCancellation(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é”€æˆ·åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">销户基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/crm/cancellation/index.vue
@@ -214,24 +214,6 @@
              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['crm:cancellation:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['crm:cancellation:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
src/views/crm/contract/index.vue
@@ -346,24 +346,6 @@
              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['crm:contract:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['crm:contract:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '生成销售订单',
              type: 'link',
              auth: ['crm:contract:update'],
src/views/crm/invoice/index.vue
@@ -272,24 +272,6 @@
              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['crm:invoice:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['crm:invoice:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
src/views/crm/refund/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
<script lang="ts" setup>
import type { CrmRefundApi } from '#/api/crm/refund';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getRefund } from '#/api/crm/refund';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'CrmRefundApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<CrmRefundApi.Refund>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '退费编号', field: 'refundNo', render: textRender },
    { label: '客户名称', field: 'customerName', render: textRender },
    { label: '合同编号', field: 'contractNo', render: textRender },
    {
      label: '退费类型',
      field: 'refundType',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.CRM_REFUND_TYPE, value: val }),
    },
    {
      label: '退费金额(元)',
      field: 'refundAmount',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    { label: '退费原因', field: 'refundReason', render: textRender },
    { label: '申请人', field: 'applyUserName', render: textRender },
    {
      label: '申请时间',
      field: 'applyTime',
      render: (val) => formatDateTime(val) as string,
    },
    {
      label: '审批状态',
      field: 'auditStatus',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.CRM_AUDIT_STATUS, value: val }),
    },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getRefund(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é€€è´¹åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">退费基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/crm/refund/index.vue
@@ -212,24 +212,6 @@
              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['crm:refund:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['crm:refund:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
src/views/erp/finance/payment/index.vue
@@ -260,24 +260,6 @@
              onClick: handleSubmit.bind(null, row),
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:finance-payment:audit'],
              // ä»…「审批中 + å½“前登录用户是审批人」才显示,其他人看不到该按钮
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:finance-payment:audit'],
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '查看审批',
              type: 'link',
              icon: ACTION_ICON.VIEW,
src/views/erp/purchase/order/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,165 @@
<script lang="ts" setup>
import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order';
import type { DescriptionItemSchema } from '#/components/description';
import type { TableColumnsType } from 'ant-design-vue';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import {
  erpCountInputFormatter,
  erpPriceInputFormatter,
  formatDateTime,
} from '@vben/utils';
import { Spin, Table } from 'ant-design-vue';
import { getPurchaseOrder } from '#/api/erp/purchase/order';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'ErpPurchaseOrderApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<ErpPurchaseOrderApi.PurchaseOrder>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** è®¢å•明细列 */
const itemColumns: TableColumnsType = [
  { title: '序号', key: 'seq', width: 60, align: 'center' },
  { title: '产品名称', dataIndex: 'productName', width: 160 },
  { title: '单位', dataIndex: 'productUnitName', width: 90 },
  {
    title: '数量',
    dataIndex: 'count',
    width: 100,
    align: 'right',
    customRender: ({ text }) => erpCountInputFormatter(text) || '-',
  },
  {
    title: '单价(元)',
    dataIndex: 'productPrice',
    width: 120,
    align: 'right',
    customRender: ({ text }) => erpPriceInputFormatter(text) || '-',
  },
  {
    title: '总价(元)',
    dataIndex: 'totalPrice',
    width: 120,
    align: 'right',
    customRender: ({ text }) => erpPriceInputFormatter(text) || '-',
  },
  { title: '备注', dataIndex: 'remark', width: 140 },
];
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '采购订单号', field: 'no', render: textRender },
    { label: '供应商', field: 'supplierName', render: textRender },
    {
      label: '订单时间',
      field: 'orderTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.ERP_AUDIT_STATUS, value: val }),
    },
    {
      label: '合计数量',
      field: 'totalCount',
      render: (val) => erpCountInputFormatter(val) || '-',
    },
    {
      label: '合计金额(元)',
      field: 'totalPrice',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    {
      label: '优惠率(%)',
      field: 'discountPercent',
      render: (val) => (val === 0 || val ? String(val) : '-'),
    },
    {
      label: '定金金额(元)',
      field: 'depositPrice',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    { label: '产品名称', field: 'productNames', render: textRender },
    { label: '创建人', field: 'creatorName', render: textRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getPurchaseOrder(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é‡‡è´­è®¢å•基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购订单基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
        <!-- é‡‡è´­è®¢å•明细 -->
        <div
          v-if="formData?.items && formData.items.length > 0"
          class="rounded-lg border border-border bg-background p-4"
        >
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购订单明细</span>
          </div>
          <Table
            :columns="itemColumns"
            :data-source="formData.items"
            :pagination="false"
            bordered
            size="small"
          >
            <template #bodyCell="{ column, index }">
              <template v-if="column.key === 'seq'">{{ index + 1 }}</template>
            </template>
          </Table>
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/erp/purchase/order/index.vue
@@ -311,24 +311,6 @@
              onClick: handleSubmit.bind(null, row),
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:purchase-order:audit'],
              // ä»…「审批中 + å½“前登录用户是审批人」才显示,其他人看不到该按钮
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:purchase-order:audit'],
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '查看审批',
              type: 'link',
              icon: ACTION_ICON.VIEW,
src/views/erp/purchase/plan/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
<script lang="ts" setup>
import type { ErpPurchasePlanApi } from '#/api/erp/purchase/plan';
import type { DescriptionItemSchema } from '#/components/description';
import type { TableColumnsType } from 'ant-design-vue';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { erpCountInputFormatter, formatDateTime } from '@vben/utils';
import { Spin, Table } from 'ant-design-vue';
import { getPurchasePlan } from '#/api/erp/purchase/plan';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'ErpPurchasePlanApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<ErpPurchasePlanApi.PurchasePlan>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** è®¡åˆ’明细列 */
const itemColumns: TableColumnsType = [
  { title: '序号', key: 'seq', width: 60, align: 'center' },
  { title: '产品名称', dataIndex: 'productName', width: 160 },
  { title: '单位', dataIndex: 'productUnitName', width: 90 },
  {
    title: '计划采购数量',
    dataIndex: 'count',
    width: 120,
    align: 'right',
    customRender: ({ text }) => erpCountInputFormatter(text) || '-',
  },
  {
    title: '需求日期',
    dataIndex: 'demandTime',
    width: 160,
    customRender: ({ text }) => (text ? formatDateTime(text) : '-'),
  },
  { title: '备注', dataIndex: 'remark', width: 140 },
];
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '计划单号', field: 'no', render: textRender },
    { label: '计划名称', field: 'name', render: textRender },
    {
      label: '状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.ERP_AUDIT_STATUS, value: val }),
    },
    { label: '需求来源', field: 'demandSourceName', render: textRender },
    {
      label: '计划日期',
      field: 'planDate',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '编制人', field: 'planUserName', render: textRender },
    { label: '编制部门', field: 'planDeptName', render: textRender },
    { label: '供应商', field: 'supplierName', render: textRender },
    {
      label: '合计数量',
      field: 'totalCount',
      render: (val) => erpCountInputFormatter(val) || '-',
    },
    {
      label: '是否已生成采购申请',
      field: 'generatedFlag',
      render: (val) => (val ? '是' : '否'),
    },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    { label: '产品名称', field: 'productNames', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getPurchasePlan(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é‡‡è´­è®¡åˆ’基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购计划基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
        <!-- é‡‡è´­è®¡åˆ’明细 -->
        <div
          v-if="formData?.items && formData.items.length > 0"
          class="rounded-lg border border-border bg-background p-4"
        >
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购计划明细</span>
          </div>
          <Table
            :columns="itemColumns"
            :data-source="formData.items"
            :pagination="false"
            bordered
            size="small"
          >
            <template #bodyCell="{ column, index }">
              <template v-if="column.key === 'seq'">{{ index + 1 }}</template>
            </template>
          </Table>
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/erp/purchase/plan/index.vue
@@ -336,24 +336,6 @@
              onClick: handleSubmit.bind(null, row),
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:purchase-plan:audit'],
              // ä»…「审批中 + å½“前登录用户是审批人」才显示,其他人看不到该按钮
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['erp:purchase-plan:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçŽ°ï¼Œä»…å®¡æ‰¹äººå¯è§
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '生成申请',
              type: 'link',
              auth: ['erp:purchase-plan:generate-request'],
src/views/erp/purchase/request/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,161 @@
<script lang="ts" setup>
import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request';
import type { DescriptionItemSchema } from '#/components/description';
import type { TableColumnsType } from 'ant-design-vue';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import {
  erpCountInputFormatter,
  erpPriceInputFormatter,
  formatDateTime,
} from '@vben/utils';
import { Spin, Table } from 'ant-design-vue';
import { getPurchaseRequest } from '#/api/erp/purchase/request';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'ErpPurchaseRequestApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<ErpPurchaseRequestApi.PurchaseRequest>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** ç”³è¯·æ˜Žç»†åˆ— */
const itemColumns: TableColumnsType = [
  { title: '序号', key: 'seq', width: 60, align: 'center' },
  { title: '产品名称', dataIndex: 'productName', width: 160 },
  { title: '单位', dataIndex: 'productUnitName', width: 90 },
  {
    title: '数量',
    dataIndex: 'count',
    width: 100,
    align: 'right',
    customRender: ({ text }) => erpCountInputFormatter(text) || '-',
  },
  {
    title: '单价(元)',
    dataIndex: 'productPrice',
    width: 120,
    align: 'right',
    customRender: ({ text }) => erpPriceInputFormatter(text) || '-',
  },
  {
    title: '需求日期',
    dataIndex: 'demandTime',
    width: 160,
    customRender: ({ text }) => (text ? formatDateTime(text) : '-'),
  },
  { title: '备注', dataIndex: 'remark', width: 140 },
];
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '申请编号', field: 'no', render: textRender },
    { label: '申请人', field: 'requestUserName', render: textRender },
    { label: '申请部门', field: 'requestDeptName', render: textRender },
    {
      label: '申请时间',
      field: 'requestTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.ERP_AUDIT_STATUS, value: val }),
    },
    { label: '供应商', field: 'supplierName', render: textRender },
    {
      label: '总数量',
      field: 'totalCount',
      render: (val) => erpCountInputFormatter(val) || '-',
    },
    {
      label: '总金额(元)',
      field: 'totalPrice',
      render: (val) => erpPriceInputFormatter(val) || '-',
    },
    { label: '申请理由', field: 'requestReason', render: textRender },
    { label: '产品名称', field: 'productNames', render: textRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '生成采购订单',
      field: 'orderNo',
      render: (val) => (val ? String(val) : '-'),
    },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getPurchaseRequest(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é‡‡è´­ç”³è¯·åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购申请基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
        <!-- é‡‡è´­ç”³è¯·æ˜Žç»† -->
        <div
          v-if="formData?.items && formData.items.length > 0"
          class="rounded-lg border border-border bg-background p-4"
        >
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购申请明细</span>
          </div>
          <Table
            :columns="itemColumns"
            :data-source="formData.items"
            :pagination="false"
            bordered
            size="small"
          >
            <template #bodyCell="{ column, index }">
              <template v-if="column.key === 'seq'">{{ index + 1 }}</template>
            </template>
          </Table>
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/erp/purchase/request/index.vue
@@ -315,24 +315,6 @@
              onClick: handleSubmit.bind(null, row),
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:purchase-request:audit'],
              // ä»…「审批中 + å½“前登录用户是审批人」才显示,其他人看不到该按钮
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              icon: ACTION_ICON.AUDIT,
              auth: ['erp:purchase-request:audit'],
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '生成订单',
              type: 'link',
              auth: ['erp:purchase-request:generate-order'],
src/views/hrm/attendance/exception/index.vue
@@ -165,24 +165,6 @@
              // è‰ç¨¿(0) ä¸Ž å®¡æ ¸ä¸é€šè¿‡(30) å‡å¯æäº¤ï¼ŒåŽè€…需修改后重新提交
              ifShow: row.status === 0 || row.status === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['hrm:attendance-exception:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['hrm:attendance-exception:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
          ]"
        />
      </template>
src/views/hrm/leave/index.vue
@@ -222,24 +222,6 @@
              ifShow: row.status === 0 || row.status === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['hrm:leave:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['hrm:leave:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '撤销',
              type: 'link',
              auth: ['hrm:leave:cancel'],
src/views/hrm/performance-appraise/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
<script lang="ts" setup>
import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getAppraise } from '#/api/hrm/performance-appraise';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'HrmPerformanceAppraiseApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<HrmPerformanceAppraiseApi.Appraise>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '方案名称', field: 'name', render: textRender },
    {
      label: '考核周期类型',
      field: 'periodType',
      render: (val) =>
        h(DictTag, {
          type: DICT_TYPE.HRM_PERFORMANCE_PERIOD_TYPE,
          value: val,
        }),
    },
    { label: '考核周期', field: 'period', render: textRender },
    {
      label: '考核范围',
      field: 'scopeType',
      render: (val) =>
        h(DictTag, {
          type: DICT_TYPE.HRM_PERFORMANCE_SCOPE_TYPE,
          value: val,
        }),
    },
    { label: '方案状态', field: 'statusName', render: textRender },
    { label: '审批状态', field: 'auditStatusName', render: textRender },
    { label: '指标数', field: 'itemCount', render: textRender },
    { label: '已评分人数', field: 'resultCount', render: textRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getAppraise(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- ç»©æ•ˆè€ƒæ ¸æ–¹æ¡ˆåŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">绩效考核方案基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/hrm/performance-appraise/index.vue
@@ -311,24 +311,6 @@
              ifShow: row.auditStatus === AUDIT_STATUS.DRAFT || row.auditStatus === AUDIT_STATUS.REJECT,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['hrm:performance-appraise:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['hrm:performance-appraise:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              danger: true,
src/views/hrm/regularize/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
<script lang="ts" setup>
import type { HrmRegularizeApi } from '#/api/hrm/regularize';
import type { DescriptionItemSchema } from '#/components/description';
import { onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getRegularize } from '#/api/hrm/regularize';
import { useDescription } from '#/components/description';
defineOptions({ name: 'HrmRegularizeApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<HrmRegularizeApi.Regularize>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '申请单号', field: 'no', render: textRender },
    { label: '申请人', field: 'userName', render: textRender },
    { label: '部门', field: 'deptName', render: textRender },
    { label: '岗位', field: 'postName', render: textRender },
    { label: '当前用工性质', field: 'employmentTypeName', render: textRender },
    {
      label: '拟转正日期',
      field: 'expectedDate',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '转正原因', field: 'reason', render: textRender },
    { label: '审批状态', field: 'statusName', render: textRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    {
      label: '审批完成时间',
      field: 'approveTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => formatDateTime(val) as string,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getRegularize(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- è½¬æ­£ç”³è¯·åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">转正申请基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/hrm/resignation/index.vue
@@ -211,24 +211,6 @@
              ifShow: row.status === 0 || row.status === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['hrm:resignation:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['hrm:resignation:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '撤销',
              type: 'link',
              auth: ['hrm:resignation:cancel'],
src/views/hrm/transfer/index.vue
@@ -200,24 +200,6 @@
              ifShow: row.status === 0 || row.status === 30,
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['hrm:transfer:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审批人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['hrm:transfer:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '撤销',
              type: 'link',
              auth: ['hrm:transfer:cancel'],
src/views/mes/pd/project/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
<script lang="ts" setup>
import type { MesPdProjectApi } from '#/api/mes/pd/project';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getPdProject } from '#/api/mes/pd/project';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'MesPdProjectApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<MesPdProjectApi.Project>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '任务编码', field: 'taskCode', render: textRender },
    { label: '任务名称', field: 'taskName', render: textRender },
    { label: '产品编码', field: 'productCode', render: textRender },
    { label: '产品名称', field: 'productName', render: textRender },
    { label: '版本号', field: 'version', render: textRender },
    {
      label: '状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_PD_PROJECT_STATUS, value: val }),
    },
    { label: '主设计师', field: 'chiefDesignerNickname', render: textRender },
    { label: '计划完成时间', field: 'planFinishTime', render: timeRender },
    { label: '审核人', field: 'reviewerName', render: textRender },
    { label: '审核时间', field: 'reviewTime', render: timeRender },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getPdProject(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- è®¾è®¡é¡¹ç›®åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">设计项目基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/mes/pd/project/detail/index.vue
@@ -241,22 +241,6 @@
            },
          },
          {
            label: '审核',
            type: 'primary',
            icon: ACTION_ICON.AUDIT,
            // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
            ifShow: canAudit,
            onClick: () => handleAudit(true),
          },
          {
            label: '审核不通过',
            type: 'default',
            danger: true,
            // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
            ifShow: canAudit,
            onClick: () => handleAudit(false),
          },
          {
            label: '归档',
            type: 'default',
            ifShow: isPendingArchive,
src/views/mes/pd/project/index.vue
@@ -284,24 +284,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:pd-project:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:pd-project:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '归档',
              type: 'link',
              auth: ['mes:pd-project:archive'],
src/views/mes/pd/tech-confirm/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
<script lang="ts" setup>
import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getTechConfirm } from '#/api/mes/pd/tech-confirm';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'MesPdTechConfirmApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<MesPdTechConfirmApi.TechConfirm>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '确认函编号', field: 'confirmNo', render: textRender },
    { label: '客户名称', field: 'customerName', render: textRender },
    { label: '产品名称', field: 'productName', render: textRender },
    { label: '规格型号', field: 'specification', render: textRender },
    {
      label: '模板类型',
      field: 'templateType',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.PRODUCT_TECH_CONFIRM_TEMPLATE, value: val }),
    },
    {
      label: '状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.PRODUCT_TECH_CONFIRM_STATUS, value: val }),
    },
    { label: '编制人', field: 'drafterName', render: textRender },
    {
      label: '编制时间',
      field: 'draftTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核人', field: 'reviewerName', render: textRender },
    {
      label: '审核时间',
      field: 'reviewTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    { label: '客户确认人', field: 'customerConfirmUser', render: textRender },
    {
      label: '客户确认时间',
      field: 'customerConfirmTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => (val ? (formatDateTime(val) as string) : '-'),
    },
    {
      label: '技术参数',
      field: 'techParams',
      render: textRender,
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getTechConfirm(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- æŠ€æœ¯ç¡®è®¤å‡½åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">技术确认函基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/mes/pd/tech-confirm/index.vue
@@ -249,24 +249,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:pd-tech-confirm:audit'],
              // ä»…「待审核 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:pd-tech-confirm:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '客户确认',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
src/views/wls/equipmentreceipt/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
<script lang="ts" setup>
import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt';
import type { MesWmEquipmentReceiptLineApi } from '#/api/mes/wm/equipmentreceipt/line';
import type { DescriptionItemSchema } from '#/components/description';
import type { TableColumnsType } from 'ant-design-vue';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { erpCountInputFormatter, formatDateTime } from '@vben/utils';
import { Spin, Table, Tag } from 'ant-design-vue';
import { getEquipmentReceipt } from '#/api/mes/wm/equipmentreceipt';
import { getEquipmentReceiptLineListByReceiptId } from '#/api/mes/wm/equipmentreceipt/line';
import { useDescription } from '#/components/description';
import { EQUIPMENT_RECEIPT_STATUS_MAP } from '../../data';
defineOptions({ name: 'MesWmEquipmentReceiptApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<MesWmEquipmentReceiptApi.EquipmentReceipt>();
const lines = ref<MesWmEquipmentReceiptLineApi.EquipmentReceiptLine[]>([]);
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
/** æ˜Žç»†åˆ— */
const lineColumns: TableColumnsType = [
  { title: '序号', key: 'seq', width: 60, align: 'center' },
  { title: '设备编码', dataIndex: 'machineryCode', width: 140 },
  { title: '设备名称', dataIndex: 'machineryName', width: 160 },
  { title: '物料编码', dataIndex: 'itemCode', width: 120 },
  { title: '规格型号', dataIndex: 'specification', width: 140 },
  { title: '计量单位', dataIndex: 'unitMeasureName', width: 90 },
  {
    title: '入库数量',
    dataIndex: 'quantity',
    width: 110,
    align: 'right',
    customRender: ({ text }) => erpCountInputFormatter(text) || '-',
  },
  { title: '备注', dataIndex: 'remark', width: 140 },
];
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '单据编码', field: 'code', render: textRender },
    { label: '单据名称', field: 'name', render: textRender },
    { label: '入库日期', field: 'receiptDate', render: timeRender },
    { label: '入库来源', field: 'source', render: textRender },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(
          Tag,
          { color: EQUIPMENT_RECEIPT_STATUS_MAP[val as number]?.color },
          () =>
            EQUIPMENT_RECEIPT_STATUS_MAP[val as number]?.text ?? String(val),
        ),
    },
    { label: '审核人', field: 'reviewerName', render: textRender },
    { label: '审核时间', field: 'reviewTime', render: timeRender },
    { label: '审核意见', field: 'reviewRemark', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    const id = Number(props.id);
    formData.value = await getEquipmentReceipt(id);
    lines.value = await getEquipmentReceiptLineListByReceiptId(id);
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- è®¾å¤‡å…¥åº“基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">设备入库基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
        <!-- è®¾å¤‡å…¥åº“明细 -->
        <div
          v-if="lines.length > 0"
          class="rounded-lg border border-border bg-background p-4"
        >
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">设备入库明细</span>
          </div>
          <Table
            :columns="lineColumns"
            :data-source="lines"
            :pagination="false"
            bordered
            size="small"
          >
            <template #bodyCell="{ column, index }">
              <template v-if="column.key === 'seq'">{{ index + 1 }}</template>
            </template>
          </Table>
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/equipmentreceipt/index.vue
@@ -260,24 +260,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm:equipment-receipt:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm:equipment-receipt:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '执行入库',
              type: 'link',
              auth: ['mes:wm:equipment-receipt:finish'],
src/views/wls/itemreceipt/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
<script lang="ts" setup>
import type { WlsItemReceiptApi } from '#/api/wls/itemreceipt';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getItemReceipt } from '#/api/wls/itemreceipt';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'WlsItemReceiptApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<WlsItemReceiptApi.ItemReceipt>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '入库单编码', field: 'code', render: textRender },
    { label: '入库单名称', field: 'name', render: textRender },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_ITEM_RECEIPT_STATUS, value: val }),
    },
    { label: '入库日期', field: 'receiptDate', render: timeRender },
    { label: '采购订单号', field: 'purchaseOrderCode', render: textRender },
    { label: '供应商', field: 'vendorName', render: textRender },
    { label: '仓库', field: 'warehouseName', render: textRender },
    { label: '库区', field: 'locationName', render: textRender },
    { label: '库位', field: 'areaName', render: textRender },
    { label: '到货通知单', field: 'noticeCode', render: textRender },
    { label: '来料检验单', field: 'iqcCode', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getItemReceipt(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- é‡‡è´­å…¥åº“基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">采购入库基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/itemreceipt/index.vue
@@ -285,24 +285,6 @@
              onClick: handleStock.bind(null, row),
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm-item-receipt:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm-item-receipt:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '执行入库',
              type: 'link',
              auth: ['mes:wm-item-receipt:finish'],
src/views/wls/miscissue/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
<script lang="ts" setup>
import type { WlsMiscIssueApi } from '#/api/wls/miscissue';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getMiscIssue } from '#/api/wls/miscissue';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'WlsMiscIssueApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<WlsMiscIssueApi.MiscIssue>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '出库单编号', field: 'code', render: textRender },
    { label: '出库单名称', field: 'name', render: textRender },
    {
      label: '业务类型',
      field: 'type',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_MISC_ISSUE_TYPE, value: val }),
    },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_MISC_ISSUE_STATUS, value: val }),
    },
    { label: '出库日期', field: 'issueDate', render: timeRender },
    { label: '来源单据类型', field: 'sourceDocType', render: textRender },
    { label: '来源单据编码', field: 'sourceDocCode', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getMiscIssue(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- æ‚项出库基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">杂项出库基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/miscissue/index.vue
@@ -263,24 +263,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm-misc-issue:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm-misc-issue:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '执行出库',
              type: 'link',
              auth: ['mes:wm-misc-issue:finish'],
src/views/wls/miscreceipt/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
<script lang="ts" setup>
import type { WlsMiscReceiptApi } from '#/api/wls/miscreceipt';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getMiscReceipt } from '#/api/wls/miscreceipt';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'WlsMiscReceiptApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<WlsMiscReceiptApi.MiscReceipt>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '入库单编号', field: 'code', render: textRender },
    { label: '入库单名称', field: 'name', render: textRender },
    {
      label: '杂项类型',
      field: 'type',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_MISC_RECEIPT_TYPE, value: val }),
    },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_MISC_RECEIPT_STATUS, value: val }),
    },
    { label: '入库日期', field: 'receiptDate', render: timeRender },
    { label: '来源单据类型', field: 'sourceDocType', render: textRender },
    { label: '来源单据编码', field: 'sourceDocCode', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getMiscReceipt(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- æ‚项入库基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">杂项入库基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/miscreceipt/index.vue
@@ -263,24 +263,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm:misc-receipt:audit'],
              // ä»…「审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm:misc-receipt:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '执行入库',
              type: 'link',
              auth: ['mes:wm-misc-receipt:finish'],
src/views/wls/returnvendor/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
<script lang="ts" setup>
import type { WlsReturnVendorApi } from '#/api/wls/returnvendor';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getReturnVendor } from '#/api/wls/returnvendor';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'WlsReturnVendorApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<WlsReturnVendorApi.ReturnVendor>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '退货单编号', field: 'code', render: textRender },
    { label: '退货单名称', field: 'name', render: textRender },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_RETURN_VENDOR_STATUS, value: val }),
    },
    { label: '采购订单号', field: 'purchaseOrderCode', render: textRender },
    { label: '供应商编码', field: 'vendorCode', render: textRender },
    { label: '供应商名称', field: 'vendorName', render: textRender },
    { label: '退货日期', field: 'returnDate', render: timeRender },
    { label: '退货原因', field: 'returnReason', render: textRender },
    { label: '运单号', field: 'transportCode', render: textRender },
    { label: '联系电话', field: 'transportTelephone', render: textRender },
    { label: '创建时间', field: 'createTime', render: timeRender },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getReturnVendor(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- ä¾›åº”商退货基本信息 -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">供应商退货基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/returnvendor/index.vue
@@ -248,24 +248,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm-return-vendor:audit'],
              // ä»…「待拣货 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm-return-vendor:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: $t('common.edit'),
              type: 'link',
              icon: ACTION_ICON.EDIT,
src/views/wls/stocktaking/task/detail/approve/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
<script lang="ts" setup>
import type { WlsStockTakingTaskApi } from '#/api/wls/stocktaking/task';
import type { DescriptionItemSchema } from '#/components/description';
import { h, onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { formatDateTime } from '@vben/utils';
import { Spin } from 'ant-design-vue';
import { getStockTaking } from '#/api/wls/stocktaking/task';
import { DictTag } from '#/components/dict-tag';
import { useDescription } from '#/components/description';
defineOptions({ name: 'WlsStockTakingTaskApproveDetail' });
const props = defineProps<{ id: string }>();
const loading = ref(false);
const formData = ref<WlsStockTakingTaskApi.StockTakingTask>();
/** æ–‡æœ¬å­—段空值占位 */
function textRender(val: unknown) {
  return val === 0 || val ? String(val) : '-';
}
/** æ—¶é—´å­—段空值占位 */
function timeRender(val: unknown) {
  return val ? (formatDateTime(val as number) as string) : '-';
}
const [Descriptions] = useDescription({
  bordered: true,
  column: 2,
  schema: [
    { label: '任务编码', field: 'code', render: textRender },
    { label: '任务名称', field: 'name', render: textRender },
    {
      label: '盘点类型',
      field: 'type',
      render: (val) =>
        h(DictTag, { type: DICT_TYPE.MES_WM_STOCK_TAKING_TYPE, value: val }),
    },
    {
      label: '单据状态',
      field: 'status',
      render: (val) =>
        h(DictTag, {
          type: DICT_TYPE.MES_WM_STOCK_TAKING_TASK_STATUS,
          value: val,
        }),
    },
    {
      label: '盘点日期',
      field: 'takingDate',
      render: (val) => (val ? (formatDateTime(val as string) as string) : '-'),
    },
    { label: '盘点人', field: 'userNickname', render: textRender },
    { label: '盘点方案编码', field: 'planCode', render: textRender },
    { label: '盘点方案名称', field: 'planName', render: textRender },
    {
      label: '是否盲盘',
      field: 'blindFlag',
      render: (val) => (val ? '是' : '否'),
    },
    {
      label: '是否冻结库存',
      field: 'frozen',
      render: (val) => (val ? '是' : '否'),
    },
    { label: '开始时间', field: 'startTime', render: timeRender },
    { label: '结束时间', field: 'endTime', render: timeRender },
    {
      label: '创建时间',
      field: 'createTime',
      render: (val) => (val ? (formatDateTime(val as string) as string) : '-'),
    },
    { label: '备注', field: 'remark', render: textRender },
  ] as DescriptionItemSchema[],
});
onMounted(async () => {
  try {
    loading.value = true;
    formData.value = await getStockTaking(Number(props.id));
  } finally {
    loading.value = false;
  }
});
</script>
<template>
  <ContentWrap class="m-2">
    <Spin :spinning="loading" tip="加载中...">
      <div class="space-y-4">
        <!-- ç›˜ç‚¹ä»»åŠ¡åŸºæœ¬ä¿¡æ¯ -->
        <div class="rounded-lg border border-border bg-background p-4">
          <div class="mb-4 flex items-center gap-2">
            <span class="inline-block h-4 w-1 rounded bg-primary" />
            <span class="text-base font-semibold">盘点任务基本信息</span>
          </div>
          <Descriptions :data="formData" />
        </div>
      </div>
    </Spin>
  </ContentWrap>
</template>
src/views/wls/stocktaking/task/index.vue
@@ -320,24 +320,6 @@
              },
            },
            {
              label: '审核',
              type: 'link',
              icon: ACTION_ICON.AUDIT,
              auth: ['mes:wm-stock-taking-task:audit'],
              // ä»…「结果审批中 + å½“前登录用户在审批配置的审核人名单内」才显示
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, true),
            },
            {
              label: '审核不通过',
              type: 'link',
              danger: true,
              auth: ['mes:wm-stock-taking-task:audit'],
              // åŒä¸Šï¼Œä¸Žã€Œå®¡æ ¸ã€æˆå¯¹å‡ºçް
              ifShow: canAudit(row),
              onClick: handleAudit.bind(null, row, false),
            },
            {
              label: '取消',
              type: 'link',
              danger: true,