zhengyiming
2 天以前 da9363290e99b960805b7dfdefef201f89d1dde0
fix: 奖励金审核
4个文件已添加
5个文件已修改
678 ■■■■■ 已修改文件
src/constants/reward.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/ParkBountyApply.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/User.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/typings.d.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Reward/RewardApplyTradeCheck.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Reward/components/RewardApplyTradeCheckDialog.vue 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Reward/constants/columns.ts 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Reward/constants/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/reward.ts
@@ -177,3 +177,31 @@
  [BillStatusEnum.NeedUpload]: '待完善',
  [BillStatusEnum.HasUpload]: '已上传',
};
export enum EnumParkBountyTradeDetailAuditStatus {
  /// 待审核
  Wait = 10,
  /// 审核通过
  Pass = 20,
  /// 驳回
  Reject = 30,
}
export const EnumParkBountyTradeDetailAuditStatusText = {
  [EnumParkBountyTradeDetailAuditStatus.Wait]: '待审核',
  [EnumParkBountyTradeDetailAuditStatus.Pass]: '审核通过',
  [EnumParkBountyTradeDetailAuditStatus.Reject]: '驳回',
};
export const EnumParkBountyTradeDetailAuditStatusTextForAdudit = {
  [EnumParkBountyTradeDetailAuditStatus.Pass]: '通过',
  [EnumParkBountyTradeDetailAuditStatus.Reject]: '驳回',
};
export const EnumParkBountyTradeDetailAuditStatusTag = {
  [EnumParkBountyTradeDetailAuditStatus.Wait]: 'warning',
  [EnumParkBountyTradeDetailAuditStatus.Pass]: 'success',
  [EnumParkBountyTradeDetailAuditStatus.Reject]: 'danger',
};
src/router/index.ts
@@ -234,6 +234,19 @@
          icon: 'home',
        },
      },
      {
        path: '/RewardApplyTradeCheck',
        name: 'RewardApplyTradeCheck',
        hidden: false,
        alwaysShow: true,
        component: () => import('@/views/Reward/RewardApplyTradeCheck.vue'),
        meta: {
          rank: 10034,
          title: '出账审批',
          // rootMenu: true,
          icon: 'home',
        },
      },
    ],
  },
  // {
src/services/api/ParkBountyApply.ts
@@ -17,6 +17,21 @@
  });
}
/** 奖励金-出账 POST /api/ParkBountyApply/AuditParkBountyApplyTrade */
export async function auditParkBountyApplyTrade(
  body: API.AuditParkBountyTradeInput,
  options?: API.RequestConfig
) {
  return request<number>('/api/ParkBountyApply/AuditParkBountyApplyTrade', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** 撤回申报 GET /api/ParkBountyApply/CancelParkBountyApply */
export async function cancelParkBountyApply(
  // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
src/services/api/User.ts
@@ -306,6 +306,20 @@
  });
}
/** 获取江佑保用户信息 GET /api/User/GetJybUserInfo/${param0} */
export async function getJybUserInfoId(
  // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
  params: API.APIgetJybUserInfoIdParams,
  options?: API.RequestConfig
) {
  const { id: param0, ...queryParams } = params;
  return request<API.JybUserInfoDto>(`/api/User/GetJybUserInfo/${param0}`, {
    method: 'GET',
    params: { ...queryParams },
    ...(options || {}),
  });
}
/** 获取互相关注分页列表 POST /api/User/GetMutualFollowUserPage */
export async function getMutualFollowUserPage(body: API.PageInput, options?: API.RequestConfig) {
  return request<API.FollowUserListOutputPageOutput>('/api/User/GetMutualFollowUserPage', {
src/services/api/typings.d.ts
@@ -1535,6 +1535,10 @@
    suppierType?: number;
  }
  interface APIgetJybUserInfoIdParams {
    id: string;
  }
  interface APIgetLgGigWorkUserDetailParams {
    id?: string;
  }
@@ -2368,6 +2372,16 @@
    auditNote?: string;
    /** 审核人 */
    checkUserId?: string;
  }
  interface AuditParkBountyTradeInput {
    /** 出账记录Id */
    id?: string;
    auditStatus?: EnumParkBountyTradeDetailAuditStatus;
    /** 审核备注 */
    auditRemark?: string;
    /** 审核凭证 */
    payAuditFileUrl?: string;
  }
  interface AuditWalletBatchTransferDetailInput {
@@ -5904,6 +5918,8 @@
    configuration?: Record<string, any>;
  }
  type EnumParkBountyTradeDetailAuditStatus = 10 | 20 | 30;
  interface ExportBountyApplyData {
    /** 企业名称 */
    enterpriseName: string;
@@ -8429,11 +8445,26 @@
    /** 出账结束日期 */
    transferTimeEnd?: string;
    enterpriseId?: string;
    auditStatus?: EnumParkBountyTradeDetailAuditStatus;
  }
  interface GetParkBountyTradeDetailOutput {
    enterpriseId?: string;
    /** 账号 */
    userName?: string;
    /** 企业名称 */
    enterpriseName?: string;
    /** 电话 */
    contactPhone?: string;
    /** 社会统一信用代码 */
    societyCreditCode?: string;
    authType?: EnterpriseTypeEnum;
    /** 申请园区 */
    parkName?: string;
    /** 申请园区类型 */
    parkTypeName?: string;
    id?: string;
    /** 最近出账金额 */
    /** 出账金额 */
    tradeAmount?: number;
    /** 出账凭证 */
    payFileUrl?: string;
@@ -8445,6 +8476,13 @@
    /** 奖励金余额 */
    remianAmount?: number;
    financeType?: FinanceTypeEnum;
    auditStatus?: EnumParkBountyTradeDetailAuditStatus;
    /** 审核备注 */
    auditRemark?: string;
    /** 审核时间 */
    auditTime?: string;
    /** 审核凭证 */
    payAuditFileUrl?: string;
  }
  interface GetParkBountyTradeDetailOutputPageOutput {
@@ -13003,6 +13041,28 @@
    properties?: Record<string, any>;
  }
  interface JybUserInfoDto {
    id?: string;
    /** 企业名称 */
    enterpriseName?: string;
    /** 联系人 */
    contact?: string;
    /** 联系电话 */
    contactPhone?: string;
    /** 统一社会信用代码 */
    societyCreditCode?: string;
    /** 详细地址 */
    addressDetail?: string;
    /** 用户手机号 */
    phoneNumber?: string;
    /** 银行总行名称 */
    bankName?: string;
    /** 银行卡号 */
    bankCardNumber?: string;
    /** 绑定邮箱地址 */
    bindEmailAddress?: string;
  }
  interface KeyInput {
    id?: string;
  }
src/views/Reward/RewardApplyTradeCheck.vue
New file
@@ -0,0 +1,228 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableQueryFilterBar @on-reset="reset">
        <template #query>
          <QueryFilterItem tip-content="申请出账日期">
            <FieldDatePicker
              v-model="extraParamState.creationTime"
              type="daterange"
              range-separator="~"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              clearable
              @change="getList()"
            ></FieldDatePicker>
          </QueryFilterItem>
          <QueryFilterItem>
            <SearchInput
              v-model="extraParamState.searchKeyWord"
              style="width: 200px"
              placeholder="企业名称/信用代码"
              @on-click-search="getList"
            >
            </SearchInput>
          </QueryFilterItem>
        </template>
      </ProTableQueryFilterBar>
      <ProTableV2
        v-bind="proTableProps"
        :columns="RewardApplyTradeCheckColumns"
        :operationBtns="operationBtns"
      >
      </ProTableV2>
      <RewardApplyTradeCheckDialog v-bind="dialogProps"></RewardApplyTradeCheckDialog>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import {
  ProTableQueryFilterBar,
  OperationBtnType,
  ProTableV2,
  SearchInput,
  LoadingLayout,
  AppContainer,
  QueryFilterItem,
  useTable,
  FieldDatePicker,
  useFormDialog,
  UploadUserFile,
  defineOperationBtns,
} from '@bole-core/components';
import { Message, OrderInputType } from '@bole-core/core';
import * as parkBountyApplyServices from '@/services/api/ParkBountyApply';
import {
  EnterpriseType,
  EnterpriseTypeText,
  EnumParkBountyTradeDetailAuditStatus,
  EnumParkBountyTradeDetailAuditStatusTag,
  EnumParkBountyTradeDetailAuditStatusText,
} from '@/constants';
import { ModelValueType } from 'element-plus';
import RewardApplyTradeCheckDialog from './components/RewardApplyTradeCheckDialog.vue';
import { convertApi2FormUrlOnlyOne, format } from '@/utils';
import { RewardApplyTradeCheckColumns } from './constants';
defineOptions({
  name: 'RewardApplyTradeCheck',
});
const operationBtns = defineOperationBtns([
  {
    data: {
      enCode: 'detailBtn',
      name: '详情',
    },
    emits: {
      onClick: (role) => openDialog(role, true),
    },
    extraProps: {
      hide: (row: API.GetParkBountyTradeDetailOutput) =>
        row.auditStatus === EnumParkBountyTradeDetailAuditStatus.Wait,
    },
  },
  {
    data: {
      enCode: 'checkBtn',
      name: '审批',
    },
    emits: {
      onClick: (row) => openDialog(row),
    },
    extraProps: {
      hide: (row: API.GetParkBountyTradeDetailOutput) =>
        row.auditStatus !== EnumParkBountyTradeDetailAuditStatus.Wait,
    },
  },
]);
const BaseState = {
  loading: true,
};
const state = reactive({ ...BaseState });
onMounted(async () => {
  await getList();
  state.loading = false;
});
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
  reset,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.GetParkBountyTradeDetailByIdInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
        searchKeyWord: extraParamState.searchKeyWord,
        // transferTimeBegin: format(extraParamState.transferTime?.[0] ?? '', 'YYYY-MM-DD 00:00:00'),
        // transferTimeEnd: format(extraParamState.transferTime?.[1] ?? '', 'YYYY-MM-DD 23:59:59'),
        creationTimeBegin: format(extraParamState.creationTime?.[0] ?? '', 'YYYY-MM-DD 00:00:00'),
        creationTimeEnd: format(extraParamState.creationTime?.[1] ?? '', 'YYYY-MM-DD 23:59:59'),
        // "enterpriseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        // "auditStatus": 10
      };
      let res = await parkBountyApplyServices.getParkBountyTradeDetailList(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {}
  },
  {
    defaultExtraParams: {
      searchKeyWord: '',
      creationTime: [] as unknown as ModelValueType,
      orderInput: [{ property: 'tradeTime', order: OrderInputType.Desc }],
    },
    columnsRenderProps: {
      authType: { type: 'enum', valueEnum: EnterpriseTypeText },
      tradeTime: { type: 'date', format: 'YYYY-MM-DD HH:mm:ss' },
      tradeAmount: { type: 'money' },
      remianAmount: { type: 'money' },
      auditStatus: {
        type: 'tag',
        valueEnum: EnumParkBountyTradeDetailAuditStatusText,
        tagTypeEnum: EnumParkBountyTradeDetailAuditStatusTag,
      },
    },
  }
);
function openDialog(row: API.GetParkBountyTradeDetailOutput, isCheck = false) {
  handleAdd({
    id: row.id,
    isCheck,
    auditStatus:
      row.auditStatus === EnumParkBountyTradeDetailAuditStatus.Reject
        ? EnumParkBountyTradeDetailAuditStatus.Reject
        : EnumParkBountyTradeDetailAuditStatus.Pass,
    auditRemark: row.auditRemark,
    payAuditFileUrl: convertApi2FormUrlOnlyOne(row.payAuditFileUrl),
    userName: row.userName,
    enterpriseName: row.enterpriseName,
    societyCreditCode: row.societyCreditCode,
    contactPhone: row.contactPhone,
    authType: row.authType,
    parkName: row.parkName,
    parkTypeName: row.parkTypeName,
    tradeAmount: row.tradeAmount,
    remianAmount: row.remianAmount,
    tradeTime: row.tradeTime,
    auditTime: row.auditTime,
    payRemark: row.payRemark,
    payFileUrl: convertApi2FormUrlOnlyOne(row.payFileUrl),
  });
}
const { dialogProps, handleAdd, handleEdit, editForm } = useFormDialog({
  onConfirm: handleAddOrEdit,
  defaultFormParams: {
    id: '',
    auditStatus: '' as any as EnumParkBountyTradeDetailAuditStatus,
    auditRemark: '',
    payAuditFileUrl: [] as UploadUserFile[],
    isCheck: false,
    userName: '',
    enterpriseName: '',
    societyCreditCode: '',
    contactPhone: '',
    authType: EnterpriseType.HREnterprise,
    parkName: '',
    parkTypeName: '',
    tradeAmount: '' as any as number,
    remianAmount: '' as any as number,
    tradeTime: '',
    auditTime: '',
    payRemark: '',
    payFileUrl: [] as UploadUserFile[],
  },
});
async function handleAddOrEdit() {
  try {
    let params: API.AuditParkBountyTradeInput = {
      id: editForm.id,
      auditStatus: editForm.auditStatus,
      /** 审核备注 */
      auditRemark: editForm.auditRemark,
      /** 审核凭证 */
      payAuditFileUrl: editForm.payAuditFileUrl?.[0]?.path ?? '',
    };
    let res = await parkBountyApplyServices.auditParkBountyApplyTrade(params);
    if (res) {
      Message.successMessage('操作成功');
      getList(paginationState.pageIndex);
    }
  } catch (error) {}
}
</script>
src/views/Reward/components/RewardApplyTradeCheckDialog.vue
New file
@@ -0,0 +1,236 @@
<template>
  <ProDialog title="出账审批" v-model="visible" @close="onDialogClose" destroy-on-close draggable>
    <PortraitTableWithAttachment v-bind="portraitTableWithAttachmentProps" />
    <ProForm
      :model="form"
      ref="dialogForm"
      label-width="90px"
      style="margin-top: 20px"
      :is-read="form.isCheck"
    >
      <ProFormCol>
        <ProFormColItem :span="12">
          <ProFormItemV2
            label="审核:"
            prop="auditStatus"
            :check-rules="[{ message: '请选择审核状态' }]"
          >
            <ProFormRadio
              v-model="form.auditStatus"
              :value-enum="EnumParkBountyTradeDetailAuditStatusTextForAdudit"
            />
          </ProFormItemV2>
        </ProFormColItem>
      </ProFormCol>
      <ProFormCol v-if="form.isCheck">
        <ProFormColItem :span="12">
          <ProFormItemV2 label="审核日期:" prop="auditTime">
            <ProFormDatePicker v-model="form.auditTime" type="date" format="YYYY-MM-DD HH:mm" />
          </ProFormItemV2>
        </ProFormColItem>
      </ProFormCol>
      <ProFormCol>
        <ProFormColItem :span="12">
          <ProFormItemV2
            label="上传凭证:"
            prop="payAuditFileUrl"
            :check-rules="[
              {
                message: '请上传凭证',
                type: 'upload',
              },
            ]"
          >
            <ProFormUpload
              v-model:file-url="form.payAuditFileUrl"
              :limit="1"
              :showTip="false"
              :limitFileSize="50"
            ></ProFormUpload>
          </ProFormItemV2>
        </ProFormColItem>
      </ProFormCol>
      <ProFormCol>
        <ProFormColItem>
          <ProFormItemV2
            label="审核理由:"
            prop="auditRemark"
            :required="form.auditStatus === EnumParkBountyTradeDetailAuditStatus.Reject"
            :check-rules="[
              {
                message: '请输入审核理由',
                validator: (rule, value, callback) => {
                  if (!value && form.auditStatus === EnumParkBountyTradeDetailAuditStatus.Reject) {
                    callback(new Error('请输入驳回理由'));
                  }
                  callback();
                },
              },
            ]"
          >
            <ProFormTextArea
              v-model="form.auditRemark"
              placeholder="请输入"
              show-word-limit
              :maxlength="150"
            ></ProFormTextArea>
          </ProFormItemV2>
        </ProFormColItem>
      </ProFormCol>
    </ProForm>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="emit('onCancel')">取 消</el-button>
        <el-button type="primary" @click="handleConfirm">确 定</el-button>
      </span>
    </template>
  </ProDialog>
</template>
<script setup lang="ts">
import { FormInstance } from 'element-plus';
import {
  ProDialog,
  ProForm,
  ProFormItemV2,
  ProFormTextArea,
  ProFormCol,
  ProFormColItem,
  ProFormRadio,
  ProFormUpload,
  ProFormDatePicker,
  UploadUserFile,
} from '@bole-core/components';
import * as parkBountyApplyServices from '@/services/api/ParkBountyApply';
import { usePortraitTableWithAttachment } from '@/hooks';
import { convertApi2FormUrl, convertApi2FormUrlOnlyOne } from '@/utils';
import { useQuery } from '@tanstack/vue-query';
import {
  EnumParkBountyTradeDetailAuditStatus,
  EnumParkBountyTradeDetailAuditStatusTextForAdudit,
  EnterpriseType,
  EnterpriseTypeText,
} from '@/constants';
defineOptions({
  name: 'WithdrawalApprovalAuditDialog',
});
// type Props = {};
// const props = withDefaults(defineProps<Props>(), {});
const visible = defineModel({ type: Boolean });
type Form = {
  title?: string;
  id: string;
  auditStatus: EnumParkBountyTradeDetailAuditStatus;
  auditRemark: string;
  payAuditFileUrl: UploadUserFile[];
  isCheck: boolean;
  userName: string;
  enterpriseName: string;
  societyCreditCode: string;
  contactPhone: string;
  authType: EnterpriseType;
  parkName: string;
  parkTypeName: string;
  tradeAmount: number;
  remianAmount: number;
  tradeTime: string;
  auditTime: string;
  payRemark: string;
  payFileUrl: UploadUserFile[];
};
const form = defineModel<Form>('form');
const emit = defineEmits<{
  (e: 'onConfirm'): void;
  (e: 'onCancel'): void;
}>();
const { portraitTableWithAttachmentProps } = usePortraitTableWithAttachment({
  data: form,
  annexList: computed(() => form.value?.payFileUrl),
  columns: [
    {
      label: '开户账号',
      key: 'userName',
      formatter: () => '1511 2001 2920 0156 069',
    },
    {
      label: '进账单位',
      key: 'enterpriseName',
      formatter: () => '太平财产保险有限公司抚州中心支公司',
    },
    {
      label: '开户名称',
      key: 'societyCreditCode',
      formatter: () => '太平财产保险有限公司抚州中心支公司',
    },
    {
      label: '开户银行',
      key: 'contactPhone',
      formatter: () => '中国工商银行股份有限公司抚州赣东支行',
    },
    // {
    //   label: '企业类型',
    //   key: 'authType',
    //   type: 'enum',
    //   valueEnum: EnterpriseTypeText,
    // },
    // {
    //   label: '所属园区',
    //   key: 'parkName',
    // },
    // {
    //   label: '园区类型',
    //   key: 'parkTypeName',
    // },
    // {
    //   label: '消费类型',
    //   key: 'payRemark',
    // },
    {
      label: '申请出账金额',
      key: 'tradeAmount',
      type: 'money',
    },
    {
      label: '申请出账时间',
      key: 'tradeTime',
      type: 'date',
    },
    {
      label: '资金余额',
      key: 'remianAmount',
      type: 'money',
    },
  ],
});
const dialogForm = ref<FormInstance>();
function onDialogClose() {
  if (!dialogForm.value) return;
  dialogForm.value.resetFields();
}
function handleConfirm() {
  if (!dialogForm.value) return;
  if (form.value?.isCheck) {
    emit('onCancel');
    return;
  }
  dialogForm.value.validate((valid) => {
    if (valid) {
      emit('onConfirm');
    } else {
      return;
    }
  });
}
</script>
src/views/Reward/constants/columns.ts
New file
@@ -0,0 +1,81 @@
import { defineColumns } from '@bole-core/components';
export const RewardApplyTradeCheckColumns = defineColumns([
  {
    id: '1',
    enCode: 'userName',
    name: '帐号',
    width: 160,
  },
  {
    id: '2',
    enCode: 'enterpriseName',
    name: '企业名称',
    width: 160,
  },
  {
    id: '3',
    enCode: 'societyCreditCode',
    name: '信用代码',
    width: 160,
  },
  {
    id: '4',
    enCode: 'contactPhone',
    name: '电话',
    width: 160,
  },
  {
    id: '5',
    enCode: 'authType',
    name: '企业类型',
    width: 160,
  },
  {
    id: '6',
    enCode: 'parkName',
    name: '所属园区',
    width: 160,
  },
  {
    id: '7',
    enCode: 'parkTypeName',
    name: '园区类型',
  },
  {
    id: '22',
    enCode: 'auditStatus',
    name: '审核状态',
    width: 160,
  },
  {
    id: '21',
    enCode: 'payRemark',
    name: '消费类型',
    width: 160,
  },
  {
    id: '10',
    enCode: 'tradeAmount',
    name: '申请出账金额',
    width: 160,
  },
  {
    id: '11',
    enCode: 'tradeTime',
    name: '申请出账时间',
    width: 160,
  },
  // {
  //   id: '12',
  //   enCode: 'payFileUrl',
  //   name: '出账凭证',
  //   width: 160,
  // },
  {
    id: '9',
    enCode: 'remianAmount',
    name: '资金余额',
    width: 160,
  },
]);
src/views/Reward/constants/index.ts
New file
@@ -0,0 +1 @@
export * from './columns';