feat: 新增 支付管理 - 应用信息模块
							parent
							
								
									e0080bb3e0
								
							
						
					
					
						commit
						4562d204e0
					
				|  | @ -3,12 +3,11 @@ import type { | ||||||
|   UploadFile, |   UploadFile, | ||||||
|   UploadProgressEvent, |   UploadProgressEvent, | ||||||
|   UploadRequestOptions, |   UploadRequestOptions, | ||||||
|  |   UploadUserFile, | ||||||
| } from 'element-plus'; | } from 'element-plus'; | ||||||
| 
 | 
 | ||||||
| import type { AxiosResponse } from '@vben/request'; | import type { AxiosResponse } from '@vben/request'; | ||||||
| 
 | 
 | ||||||
| import type { CustomUploadFile } from './typing'; |  | ||||||
| 
 |  | ||||||
| import type { AxiosProgressEvent } from '#/api/infra/file'; | import type { AxiosProgressEvent } from '#/api/infra/file'; | ||||||
| 
 | 
 | ||||||
| import { ref, toRefs, watch } from 'vue'; | import { ref, toRefs, watch } from 'vue'; | ||||||
|  | @ -63,7 +62,7 @@ const props = withDefaults( | ||||||
|     showDescription: false, |     showDescription: false, | ||||||
|   }, |   }, | ||||||
| ); | ); | ||||||
| const emit = defineEmits(['change', 'update:value', 'delete']); | const emit = defineEmits(['change', 'update:value', 'delete', 'returnText']); | ||||||
| const { accept, helpText, maxNumber, maxSize } = toRefs(props); | const { accept, helpText, maxNumber, maxSize } = toRefs(props); | ||||||
| const isInnerOperate = ref<boolean>(false); | const isInnerOperate = ref<boolean>(false); | ||||||
| const { getStringAccept } = useUploadType({ | const { getStringAccept } = useUploadType({ | ||||||
|  | @ -73,7 +72,7 @@ const { getStringAccept } = useUploadType({ | ||||||
|   maxSizeRef: maxSize, |   maxSizeRef: maxSize, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const fileList = ref<CustomUploadFile[]>([]); | const fileList = ref<UploadUserFile[]>([]); | ||||||
| const isLtMsg = ref<boolean>(true); // 文件大小错误提示 | const isLtMsg = ref<boolean>(true); // 文件大小错误提示 | ||||||
| const isActMsg = ref<boolean>(true); // 文件类型错误提示 | const isActMsg = ref<boolean>(true); // 文件类型错误提示 | ||||||
| const isFirstRender = ref<boolean>(true); // 是否第一次渲染 | const isFirstRender = ref<boolean>(true); // 是否第一次渲染 | ||||||
|  | @ -100,7 +99,7 @@ watch( | ||||||
|               name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)), |               name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)), | ||||||
|               status: UploadResultStatus.DONE, |               status: UploadResultStatus.DONE, | ||||||
|               url: item, |               url: item, | ||||||
|             } as CustomUploadFile; |             } as UploadUserFile; | ||||||
|           } else if (item && isObject(item)) { |           } else if (item && isObject(item)) { | ||||||
|             const file = item as Record<string, any>; |             const file = item as Record<string, any>; | ||||||
|             return { |             return { | ||||||
|  | @ -111,11 +110,11 @@ watch( | ||||||
|               response: file.response, |               response: file.response, | ||||||
|               percentage: file.percentage, |               percentage: file.percentage, | ||||||
|               size: file.size, |               size: file.size, | ||||||
|             } as CustomUploadFile; |             } as UploadUserFile; | ||||||
|           } |           } | ||||||
|           return null; |           return null; | ||||||
|         }) |         }) | ||||||
|         .filter(Boolean) as CustomUploadFile[]; |         .filter(Boolean) as UploadUserFile[]; | ||||||
|     } |     } | ||||||
|     if (!isFirstRender.value) { |     if (!isFirstRender.value) { | ||||||
|       emit('change', value); |       emit('change', value); | ||||||
|  | @ -141,6 +140,8 @@ const handleRemove = async (file: UploadFile) => { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const beforeUpload = async (file: File) => { | const beforeUpload = async (file: File) => { | ||||||
|  |   const fileContent = await file.text(); | ||||||
|  |   emit('returnText', fileContent); | ||||||
|   const { maxSize, accept } = props; |   const { maxSize, accept } = props; | ||||||
|   const isAct = checkFileType(file, accept); |   const isAct = checkFileType(file, accept); | ||||||
|   if (!isAct) { |   if (!isAct) { | ||||||
|  | @ -175,17 +176,17 @@ async function customRequest(options: UploadRequestOptions) { | ||||||
|         total: e.total || 0, |         total: e.total || 0, | ||||||
|         loaded: e.loaded || 0, |         loaded: e.loaded || 0, | ||||||
|         lengthComputable: true, |         lengthComputable: true, | ||||||
|         target: e.target as EventTarget, |         target: e.event.target as EventTarget, | ||||||
|         bubbles: false, |         bubbles: false, | ||||||
|         cancelBubble: false, |         cancelBubble: false, | ||||||
|         cancelable: false, |         cancelable: false, | ||||||
|         composed: false, |         composed: false, | ||||||
|         currentTarget: e.target as EventTarget, |         currentTarget: e.event.target as EventTarget, | ||||||
|         defaultPrevented: false, |         defaultPrevented: false, | ||||||
|         eventPhase: 0, |         eventPhase: 0, | ||||||
|         isTrusted: true, |         isTrusted: true, | ||||||
|         returnValue: true, |         returnValue: true, | ||||||
|         srcElement: e.target as EventTarget, |         srcElement: e.event.target as EventTarget, | ||||||
|         timeStamp: Date.now(), |         timeStamp: Date.now(), | ||||||
|         type: 'progress', |         type: 'progress', | ||||||
|         composedPath: () => [], |         composedPath: () => [], | ||||||
|  | @ -193,6 +194,10 @@ async function customRequest(options: UploadRequestOptions) { | ||||||
|         preventDefault: () => {}, |         preventDefault: () => {}, | ||||||
|         stopImmediatePropagation: () => {}, |         stopImmediatePropagation: () => {}, | ||||||
|         stopPropagation: () => {}, |         stopPropagation: () => {}, | ||||||
|  |         NONE: 0, | ||||||
|  |         CAPTURING_PHASE: 1, | ||||||
|  |         AT_TARGET: 2, | ||||||
|  |         BUBBLING_PHASE: 3, | ||||||
|       }; |       }; | ||||||
|       options.onProgress!(progressEvent); |       options.onProgress!(progressEvent); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -1,2 +1,3 @@ | ||||||
| export { default as FileUpload } from './file-upload.vue'; | export { default as FileUpload } from './file-upload.vue'; | ||||||
| export { default as ImageUpload } from './image-upload.vue'; | export { default as ImageUpload } from './image-upload.vue'; | ||||||
|  | export { default as InputUpload } from './input-upload.vue'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,75 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  | // TODO @xingyu:这个组件,只有 pay 在用,和现有的 file-upload 和 image-upload 有点不一致。是不是可以考虑移除,只在 pay 那搞个复用的组件; | ||||||
|  | import type { InputProps } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import type { FileUploadProps } from './typing'; | ||||||
|  | 
 | ||||||
|  | import { computed } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVModel } from '@vueuse/core'; | ||||||
|  | import { ElCol, ElInput, ElRow } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import FileUpload from './file-upload.vue'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  |   defaultValue?: number | string; | ||||||
|  |   fileUploadProps?: FileUploadProps; | ||||||
|  |   inputProps?: InputProps; | ||||||
|  |   inputType?: 'input' | 'textarea'; | ||||||
|  |   modelValue?: number | string; | ||||||
|  |   textareaProps?: InputProps; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const emits = defineEmits<{ | ||||||
|  |   (e: 'change', payload: number | string): void; | ||||||
|  |   (e: 'update:value', payload: number | string): void; | ||||||
|  |   (e: 'update:modelValue', payload: number | string): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const modelValue = useVModel(props, 'modelValue', emits, { | ||||||
|  |   defaultValue: props.defaultValue, | ||||||
|  |   passive: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function handleReturnText(text: string) { | ||||||
|  |   modelValue.value = text; | ||||||
|  |   emits('change', modelValue.value); | ||||||
|  |   emits('update:value', modelValue.value); | ||||||
|  |   emits('update:modelValue', modelValue.value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const inputProps = computed(() => { | ||||||
|  |   return { | ||||||
|  |     ...props.inputProps, | ||||||
|  |     value: modelValue.value, | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const textareaProps = computed(() => { | ||||||
|  |   return { | ||||||
|  |     ...props.textareaProps, | ||||||
|  |     value: modelValue.value, | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const fileUploadProps = computed(() => { | ||||||
|  |   return { | ||||||
|  |     ...props.fileUploadProps, | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <ElRow> | ||||||
|  |     <ElCol :span="18"> | ||||||
|  |       <ElInput v-if="inputType === 'input'" v-bind="inputProps" /> | ||||||
|  |       <ElInput v-else :row="4" type="textarea" v-bind="textareaProps" /> | ||||||
|  |     </ElCol> | ||||||
|  |     <ElCol :span="6"> | ||||||
|  |       <FileUpload | ||||||
|  |         class="ml-4" | ||||||
|  |         v-bind="fileUploadProps" | ||||||
|  |         @return-text="handleReturnText" | ||||||
|  |       /> | ||||||
|  |     </ElCol> | ||||||
|  |   </ElRow> | ||||||
|  | </template> | ||||||
|  | @ -1,11 +1,17 @@ | ||||||
| import type { UploadStatus } from 'element-plus'; | import type { AxiosResponse } from '@vben/request'; | ||||||
|  | 
 | ||||||
|  | import type { AxiosProgressEvent } from '#/api/infra/file'; | ||||||
| 
 | 
 | ||||||
| export type UploadListType = 'picture' | 'picture-card' | 'text'; | export type UploadListType = 'picture' | 'picture-card' | 'text'; | ||||||
| 
 | 
 | ||||||
| export type UploadStatus = 'error' | 'removed' | 'success' | 'uploading'; | export type UploadStatus = | ||||||
|  |   | 'error' | ||||||
|  |   | 'fail' | ||||||
|  |   | 'removed' | ||||||
|  |   | 'success' | ||||||
|  |   | 'uploading'; | ||||||
| 
 | 
 | ||||||
| export enum UploadResultStatus { | export enum UploadResultStatus { | ||||||
|   DONE = 'success', |  | ||||||
|   ERROR = 'error', |   ERROR = 'error', | ||||||
|   REMOVED = 'removed', |   REMOVED = 'removed', | ||||||
|   SUCCESS = 'success', |   SUCCESS = 'success', | ||||||
|  | @ -27,11 +33,11 @@ export function convertToUploadStatus( | ||||||
|   status: UploadResultStatus, |   status: UploadResultStatus, | ||||||
| ): UploadStatus { | ): UploadStatus { | ||||||
|   switch (status) { |   switch (status) { | ||||||
|     case UploadResultStatus.DONE: { |     case UploadResultStatus.SUCCESS: { | ||||||
|       return 'success'; |       return 'success'; | ||||||
|     } |     } | ||||||
|     case UploadResultStatus.ERROR: { |     case UploadResultStatus.ERROR: { | ||||||
|       return 'error'; |       return 'fail'; | ||||||
|     } |     } | ||||||
|     case UploadResultStatus.REMOVED: { |     case UploadResultStatus.REMOVED: { | ||||||
|       return 'removed'; |       return 'removed'; | ||||||
|  | @ -44,3 +50,28 @@ export function convertToUploadStatus( | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export interface FileUploadProps { | ||||||
|  |   // 根据后缀,或者其他
 | ||||||
|  |   accept?: string[]; | ||||||
|  |   api?: ( | ||||||
|  |     file: File, | ||||||
|  |     onUploadProgress?: AxiosProgressEvent, | ||||||
|  |   ) => Promise<AxiosResponse<any>>; | ||||||
|  |   // 上传的目录
 | ||||||
|  |   directory?: string; | ||||||
|  |   disabled?: boolean; | ||||||
|  |   helpText?: string; | ||||||
|  |   listType?: UploadListType; | ||||||
|  |   // 最大数量的文件,Infinity不限制
 | ||||||
|  |   maxNumber?: number; | ||||||
|  |   // 文件最大多少MB
 | ||||||
|  |   maxSize?: number; | ||||||
|  |   // 是否支持多选
 | ||||||
|  |   multiple?: boolean; | ||||||
|  |   // support xxx.xxx.xx
 | ||||||
|  |   resultField?: string; | ||||||
|  |   // 是否显示下面的描述
 | ||||||
|  |   showDescription?: boolean; | ||||||
|  |   value?: string | string[]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,242 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { PayAppApi } from '#/api/pay/app'; | ||||||
|  | 
 | ||||||
|  | import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       component: 'Input', | ||||||
|  |       fieldName: 'name', | ||||||
|  |       label: '应用名', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入应用名', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'Select', | ||||||
|  |       fieldName: 'status', | ||||||
|  |       label: '开启状态', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请选择开启状态', | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '创建时间', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: ['开始日期', '结束日期'], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function useGridColumns<T = PayAppApi.App>( | ||||||
|  |   onStatusChange?: ( | ||||||
|  |     newStatus: number, | ||||||
|  |     row: T, | ||||||
|  |   ) => PromiseLike<boolean | undefined>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { type: 'checkbox', width: 60 }, | ||||||
|  |     { | ||||||
|  |       title: '应用标识', | ||||||
|  |       field: 'appKey', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '应用名', | ||||||
|  |       field: 'name', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'status', | ||||||
|  |       title: '状态', | ||||||
|  |       align: 'center', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { beforeChange: onStatusChange }, | ||||||
|  |         name: 'CellSwitch', | ||||||
|  |         props: { | ||||||
|  |           checkedValue: CommonStatusEnum.ENABLE, | ||||||
|  |           unCheckedValue: CommonStatusEnum.DISABLE, | ||||||
|  |           inlinePrompt: true, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '支付宝配置', | ||||||
|  |       children: [ | ||||||
|  |         { | ||||||
|  |           title: 'APP 支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'alipayAppConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'PC 网站支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'alipayPCConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'WAP 网站支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'alipayWAPConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: '扫码支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'alipayQrConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: '条码支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'alipayBarConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '微信配置', | ||||||
|  |       children: [ | ||||||
|  |         { | ||||||
|  |           title: '小程序支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxLiteConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'JSAPI 支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxPubConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'APP 支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxAppConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'Native 支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxNativeConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: 'WAP 网站支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxWapConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           title: '条码支付', | ||||||
|  |           slots: { | ||||||
|  |             default: 'wxBarConfig', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '钱包支付配置', | ||||||
|  |       field: 'walletConfig', | ||||||
|  |       slots: { | ||||||
|  |         default: 'walletConfig', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '模拟支付配置', | ||||||
|  |       field: 'mockConfig', | ||||||
|  |       slots: { | ||||||
|  |         default: 'mockConfig', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: '操作', | ||||||
|  |       width: 130, | ||||||
|  |       fixed: 'right', | ||||||
|  |       slots: { default: 'actions' }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 新增/修改的表单 */ | ||||||
|  | export function useFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       label: '应用编号', | ||||||
|  |       fieldName: 'id', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         show: () => false, | ||||||
|  |         triggerFields: [''], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '应用名', | ||||||
|  |       fieldName: 'name', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入应用名', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '应用标识', | ||||||
|  |       fieldName: 'appKey', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入应用标识', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '开启状态', | ||||||
|  |       fieldName: 'status', | ||||||
|  |       component: 'RadioGroup', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '支付结果的回调地址', | ||||||
|  |       fieldName: 'orderNotifyUrl', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入支付结果的回调地址', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '退款结果的回调地址', | ||||||
|  |       fieldName: 'refundNotifyUrl', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入支付结果的回调地址', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '转账结果的回调地址', | ||||||
|  |       fieldName: 'transferNotifyUrl', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入转账结果的回调地址', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: '备注', | ||||||
|  |       fieldName: 'remark', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         rows: 3, | ||||||
|  |         placeholder: '请输入备注', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,527 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { PayAppApi } from '#/api/pay/app/index'; | ||||||
|  | 
 | ||||||
|  | import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { ElLoading, ElMessage } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { changeAppStatus, deleteApp, getAppPage } from '#/api/pay/app/index'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | import { CommonStatusEnum, PayChannelEnum } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import appFrom from './modules/app-form.vue'; | ||||||
|  | import channelFrom from './modules/channel-form.vue'; | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [AppFormModal, appFormModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: appFrom, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [ChannelFormModal, channelFormModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: channelFrom, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function handleCreate() { | ||||||
|  |   appFormModalApi.setData(null).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleEdit(row: Required<PayAppApi.App>) { | ||||||
|  |   appFormModalApi.setData({ id: row.id }).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function handleDelete(row: Required<PayAppApi.App>) { | ||||||
|  |   const loadingInstance = ElLoading.service({ | ||||||
|  |     text: $t('ui.actionMessage.deleting', [row.name]), | ||||||
|  |     fullscreen: true, | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await deleteApp(row.id as number); | ||||||
|  |     ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.name])); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     loadingInstance.close(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 更新状态 */ | ||||||
|  | async function handleStatusChange( | ||||||
|  |   newStatus: number, | ||||||
|  |   row: PayAppApi.App, | ||||||
|  | ): Promise<boolean | undefined> { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     const text = newStatus === CommonStatusEnum.ENABLE ? '启用' : '停用'; | ||||||
|  |     confirm({ | ||||||
|  |       content: `确认要${text + row.name}应用吗?`, | ||||||
|  |     }) | ||||||
|  |       .then(async () => { | ||||||
|  |         // 更新状态 | ||||||
|  |         const res = await changeAppStatus({ | ||||||
|  |           id: row.id as number, | ||||||
|  |           status: newStatus, | ||||||
|  |         }); | ||||||
|  |         if (res) { | ||||||
|  |           // 提示并返回成功 | ||||||
|  |           ElMessage.success(`${text}成功`); | ||||||
|  |           resolve(true); | ||||||
|  |         } else { | ||||||
|  |           reject(new Error('更新失败')); | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       .catch(() => { | ||||||
|  |         reject(new Error('取消操作')); | ||||||
|  |       }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 根据渠道编码判断渠道列表中是否存在 | ||||||
|  |  * | ||||||
|  |  * @param channels 渠道列表 | ||||||
|  |  * @param channelCode 渠道编码 | ||||||
|  |  */ | ||||||
|  | function isChannelExists(channels: string[], channelCode: string) { | ||||||
|  |   if (!channels) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return channels.includes(channelCode); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function openChannelForm(row: PayAppApi.App, payCode: string) { | ||||||
|  |   channelFormModalApi.setData({ id: row.id, payCode }).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(handleStatusChange), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getAppPage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<PayAppApi.App>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <template #doc> | ||||||
|  |       <DocAlert title="支付功能开启" url="https://doc.iocoder.cn/pay/build/" /> | ||||||
|  |     </template> | ||||||
|  | 
 | ||||||
|  |     <AppFormModal @success="onRefresh" /> | ||||||
|  |     <ChannelFormModal @success="onRefresh" /> | ||||||
|  | 
 | ||||||
|  |     <Grid> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               label: $t('ui.actionTitle.create', ['应用']), | ||||||
|  |               type: 'primary', | ||||||
|  |               icon: ACTION_ICON.ADD, | ||||||
|  |               auth: ['pay:app:create'], | ||||||
|  |               onClick: handleCreate, | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #actions="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               label: $t('common.edit'), | ||||||
|  |               link: true, | ||||||
|  |               type: 'primary', | ||||||
|  |               icon: ACTION_ICON.EDIT, | ||||||
|  |               auth: ['pay:app:update'], | ||||||
|  |               onClick: handleEdit.bind(null, row), | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               label: $t('common.delete'), | ||||||
|  |               link: true, | ||||||
|  |               type: 'danger', | ||||||
|  |               icon: ACTION_ICON.DELETE, | ||||||
|  |               auth: ['pay:app:delete'], | ||||||
|  |               popConfirm: { | ||||||
|  |                 title: $t('ui.actionMessage.deleteConfirm', [row.name]), | ||||||
|  |                 confirm: handleDelete.bind(null, row), | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #alipayAppConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #alipayPCConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_PC.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.ALIPAY_PC.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #alipayWAPConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_WAP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.ALIPAY_WAP.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #alipayQrConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_QR.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.ALIPAY_QR.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #alipayBarConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_BAR.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.ALIPAY_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.ALIPAY_BAR.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxLiteConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_LITE.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_LITE.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_LITE.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxPubConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_PUB.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_PUB.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_PUB.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxAppConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_APP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_APP.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxNativeConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_NATIVE.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_NATIVE.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_NATIVE.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxWapConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_WAP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_WAP.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_WAP.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #wxBarConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_BAR.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WX_BAR.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WX_BAR.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #walletConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WALLET.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists( | ||||||
|  |                 row.channelCodes, | ||||||
|  |                 PayChannelEnum.WALLET.code, | ||||||
|  |               ) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.WALLET.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |       <template #mockConfig="{ row }"> | ||||||
|  |         <TableAction | ||||||
|  |           :actions="[ | ||||||
|  |             { | ||||||
|  |               size: 'small', | ||||||
|  |               icon: isChannelExists(row.channelCodes, PayChannelEnum.MOCK.code) | ||||||
|  |                 ? 'lucide:check' | ||||||
|  |                 : 'lucide:x', | ||||||
|  |               type: !isChannelExists(row.channelCodes, PayChannelEnum.MOCK.code) | ||||||
|  |                 ? 'danger' | ||||||
|  |                 : 'primary', | ||||||
|  |               circle: true, | ||||||
|  |               onClick: openChannelForm.bind( | ||||||
|  |                 null, | ||||||
|  |                 row, | ||||||
|  |                 PayChannelEnum.MOCK.code, | ||||||
|  |               ), | ||||||
|  |             }, | ||||||
|  |           ]" | ||||||
|  |         /> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,83 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { PayAppApi } from '#/api/pay/app'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { $t } from '@vben/locales'; | ||||||
|  | 
 | ||||||
|  | import { ElMessage } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { createApp, getApp, updateApp } from '#/api/pay/app'; | ||||||
|  | 
 | ||||||
|  | import { useFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const formData = ref<PayAppApi.App>(); | ||||||
|  | const title = computed(() => { | ||||||
|  |   return formData.value?.id | ||||||
|  |     ? $t('ui.actionTitle.edit', '应用') | ||||||
|  |     : $t('ui.actionTitle.create', '应用'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   commonConfig: { | ||||||
|  |     componentProps: { | ||||||
|  |       class: 'w-full', | ||||||
|  |     }, | ||||||
|  |     formItemClass: 'col-span-2', | ||||||
|  |     labelWidth: 160, | ||||||
|  |   }, | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: useFormSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 提交表单 | ||||||
|  |     const data = (await formApi.getValues()) as PayAppApi.App; | ||||||
|  |     try { | ||||||
|  |       await (formData.value?.id ? updateApp(data) : createApp(data)); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       ElMessage.success($t('ui.actionMessage.operationSuccess')); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.unlock(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   onOpenChange: async (isOpen) => { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       formData.value = undefined; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const { id } = modalApi.getData() as { | ||||||
|  |       id?: number; | ||||||
|  |     }; | ||||||
|  |     if (!id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       formData.value = await getApp(id); | ||||||
|  |       // 设置到 values | ||||||
|  |       await formApi.setValues(formData.value); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.unlock(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Modal :close-on-click-modal="false" :title="title" class="w-2/5"> | ||||||
|  |     <Form /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,167 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { PayChannelApi } from '#/api/pay/channel'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { $t } from '@vben/locales'; | ||||||
|  | 
 | ||||||
|  | import { ElMessage } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { createChannel, getChannel, updateChannel } from '#/api/pay/channel'; | ||||||
|  | import { CommonStatusEnum } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | import { channelSchema } from './data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const formData = ref<any>(); | ||||||
|  | const formType = ref<string>(''); | ||||||
|  | const title = computed(() => { | ||||||
|  |   return formData.value?.id === 0 | ||||||
|  |     ? $t('ui.actionTitle.create', '应用') | ||||||
|  |     : $t('ui.actionTitle.edit', '应用'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   commonConfig: { | ||||||
|  |     componentProps: { | ||||||
|  |       class: 'w-full', | ||||||
|  |     }, | ||||||
|  |     formItemClass: 'col-span-2', | ||||||
|  |     labelWidth: 160, | ||||||
|  |   }, | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   showDefaultActions: false, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 提交表单 | ||||||
|  |     const data = (await formApi.getValues()) as PayChannelApi.Channel; | ||||||
|  |     // 只保留表单中实际存在的字段,且值不为 undefined | ||||||
|  |     const data2 = Object.fromEntries( | ||||||
|  |       Object.entries(data).filter(([key, value]) => { | ||||||
|  |         // 检查字段是否在表单中存在,且值不为 undefined | ||||||
|  |         return key in data && value !== undefined; | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |     const data3 = { ...formData.value, ...data2 }; | ||||||
|  |     data3.config = JSON.stringify(data3.config); | ||||||
|  |     try { | ||||||
|  |       await (data3.id ? updateChannel(data3) : createChannel(data3)); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       ElMessage.success($t('ui.actionMessage.operationSuccess')); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.unlock(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   onOpenChange: async (isOpen) => { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       formData.value = undefined; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const { id, payCode } = modalApi.getData() as { | ||||||
|  |       id?: number; | ||||||
|  |       payCode?: string; | ||||||
|  |     }; | ||||||
|  |     if (!id || !payCode) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     formType.value = payCode; | ||||||
|  |     if (payCode.includes('alipay_')) { | ||||||
|  |       formData.value = { | ||||||
|  |         appId: id, | ||||||
|  |         code: payCode, | ||||||
|  |         status: CommonStatusEnum.ENABLE, | ||||||
|  |         remark: '', | ||||||
|  |         feeRate: null, | ||||||
|  |         config: { | ||||||
|  |           appId: '', | ||||||
|  |           serverUrl: null, | ||||||
|  |           signType: 'RSA2', | ||||||
|  |           mode: null, | ||||||
|  |           privateKey: '', | ||||||
|  |           alipayPublicKey: '', | ||||||
|  |           appCertContent: '', | ||||||
|  |           alipayPublicCertContent: '', | ||||||
|  |           rootCertContent: '', | ||||||
|  |           encryptType: '', | ||||||
|  |           encryptKey: '', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     } else if (payCode.includes('mock')) { | ||||||
|  |       formData.value = { | ||||||
|  |         appId: id, | ||||||
|  |         code: payCode, | ||||||
|  |         status: CommonStatusEnum.ENABLE, | ||||||
|  |         remark: '', | ||||||
|  |         feeRate: 0, | ||||||
|  |         config: { | ||||||
|  |           name: 'mock-conf', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     } else if (payCode.includes('wallet')) { | ||||||
|  |       formData.value = { | ||||||
|  |         appId: id, | ||||||
|  |         code: payCode, | ||||||
|  |         status: CommonStatusEnum.ENABLE, | ||||||
|  |         remark: '', | ||||||
|  |         feeRate: 0, | ||||||
|  |         config: { | ||||||
|  |           name: 'mock-conf', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     } else if (payCode.includes('wx')) { | ||||||
|  |       formData.value = { | ||||||
|  |         appId: id, | ||||||
|  |         code: payCode, | ||||||
|  |         status: CommonStatusEnum.ENABLE, | ||||||
|  |         feeRate: undefined, | ||||||
|  |         remark: '', | ||||||
|  |         config: { | ||||||
|  |           appId: '', | ||||||
|  |           mchId: '', | ||||||
|  |           apiVersion: '', | ||||||
|  |           mchKey: '', | ||||||
|  |           keyContent: '', | ||||||
|  |           privateKeyContent: '', | ||||||
|  |           certSerialNo: '', | ||||||
|  |           apiV3Key: '', | ||||||
|  |           publicKeyContent: '', | ||||||
|  |           publicKeyId: '', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       const res = await getChannel(id, payCode); | ||||||
|  |       formData.value = { | ||||||
|  |         ...res, | ||||||
|  |         config: { | ||||||
|  |           ...JSON.parse(res.config), | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |       // 设置到 values | ||||||
|  |       await formApi.setValues(formData.value); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.unlock(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :close-on-click-modal="false" :title="title" class="w-2/5"> | ||||||
|  |     <Form :schema="channelSchema(formType)" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,491 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | 
 | ||||||
|  | import { h } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { InputUpload } from '#/components/upload'; | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | export function channelSchema(formType: string): VbenFormSchema[] { | ||||||
|  |   if (formType.includes('alipay_')) { | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: '应用编号', | ||||||
|  |         fieldName: 'appId', | ||||||
|  |         component: 'Input', | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道费率', | ||||||
|  |         fieldName: 'feeRate', | ||||||
|  |         component: 'InputNumber', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入渠道费率', | ||||||
|  |           addonAfter: '%', | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '开放平台 APPID', | ||||||
|  |         fieldName: 'config.appId', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入开放平台 APPID', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道状态', | ||||||
|  |         fieldName: 'status', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '网关地址', | ||||||
|  |         fieldName: 'config.serverUrl', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: [ | ||||||
|  |             { | ||||||
|  |               value: 'https://openapi.alipay.com/gateway.do', | ||||||
|  |               label: '线上环境', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               value: 'https://openapi-sandbox.dl.alipaydev.com/gateway.do', | ||||||
|  |               label: '沙箱环境', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '算法类型', | ||||||
|  |         fieldName: 'config.signType', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: [ | ||||||
|  |             { | ||||||
|  |               value: 'RSA2', | ||||||
|  |               label: 'RSA2', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |         defaultValue: 'RSA2', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '公钥类型', | ||||||
|  |         fieldName: 'config.mode', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: [ | ||||||
|  |             { | ||||||
|  |               value: 1, | ||||||
|  |               label: '公钥模式', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               value: 2, | ||||||
|  |               label: '证书模式', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '应用私钥', | ||||||
|  |         fieldName: 'config.privateKey', | ||||||
|  |         component: 'Textarea', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入应用私钥', | ||||||
|  |           rows: 8, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '支付宝公钥', | ||||||
|  |         fieldName: 'config.alipayPublicKey', | ||||||
|  |         component: 'Textarea', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入支付宝公钥', | ||||||
|  |           rows: 8, | ||||||
|  |         }, | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.mode === 1; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '商户公钥应用证书', | ||||||
|  |         fieldName: 'config.appCertContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { rows: 8, placeholder: '请上传商户公钥应用证书' }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['crt'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.mode === 2; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '支付宝公钥证书', | ||||||
|  |         fieldName: 'config.alipayPublicCertContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { rows: 8, placeholder: '请上传支付宝公钥证书' }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['crt'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.mode === 2; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '根证书', | ||||||
|  |         fieldName: 'config.rootCertContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { rows: 8, placeholder: '请上传根证书' }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['crt'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.mode === 2; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '接口内容加密方式', | ||||||
|  |         fieldName: 'config.encryptType', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: [ | ||||||
|  |             { | ||||||
|  |               value: 'NONE', | ||||||
|  |               label: '无加密', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               value: 'AES', | ||||||
|  |               label: 'AES', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |         defaultValue: 'NONE', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '接口内容加密密钥', | ||||||
|  |         fieldName: 'config.encryptKey', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.encryptType === 'AES'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.encryptType', 'encryptType', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '备注', | ||||||
|  |         fieldName: 'remark', | ||||||
|  |         component: 'Input', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入备注', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |   } else if (formType.includes('mock') || formType.includes('wallet')) { | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: '应用编号', | ||||||
|  |         fieldName: 'appId', | ||||||
|  |         component: 'Input', | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道状态', | ||||||
|  |         fieldName: 'status', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道编码', | ||||||
|  |         fieldName: 'code', | ||||||
|  |         component: 'Input', | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道费率', | ||||||
|  |         fieldName: 'feeRate', | ||||||
|  |         component: 'InputNumber', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入渠道费率', | ||||||
|  |           addonAfter: '%', | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '备注', | ||||||
|  |         fieldName: 'remark', | ||||||
|  |         component: 'Input', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入备注', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |   } else if (formType.includes('wx')) { | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: '应用编号', | ||||||
|  |         fieldName: 'appId', | ||||||
|  |         component: 'Input', | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道编码', | ||||||
|  |         fieldName: 'code', | ||||||
|  |         component: 'Input', | ||||||
|  |         dependencies: { | ||||||
|  |           show: () => false, | ||||||
|  |           triggerFields: [''], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道费率', | ||||||
|  |         fieldName: 'feeRate', | ||||||
|  |         component: 'InputNumber', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入渠道费率', | ||||||
|  |           addonAfter: '%', | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '微信 APPID', | ||||||
|  |         fieldName: 'config.appId', | ||||||
|  |         help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/extend/merchant_appid/mapay_platform/account_manage]查看 APPID', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入微信 APPID', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '商户号', | ||||||
|  |         fieldName: 'config.mchId', | ||||||
|  |         help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/extend/pay_setting]查看商户号', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入商户号', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '渠道状态', | ||||||
|  |         fieldName: 'status', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         }, | ||||||
|  |         defaultValue: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'API 版本', | ||||||
|  |         fieldName: 'config.apiVersion', | ||||||
|  |         component: 'RadioGroup', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           options: [ | ||||||
|  |             { | ||||||
|  |               label: 'v2', | ||||||
|  |               value: 'v2', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               label: 'v3', | ||||||
|  |               value: 'v3', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '商户密钥', | ||||||
|  |         fieldName: 'config.mchKey', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入商户密钥', | ||||||
|  |         }, | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v2'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'apiclient_cert.p12 证书', | ||||||
|  |         fieldName: 'config.keyContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { | ||||||
|  |             rows: 8, | ||||||
|  |             placeholder: '请上传 apiclient_cert.p12 证书', | ||||||
|  |           }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['p12'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v2'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'API V3 密钥', | ||||||
|  |         fieldName: 'config.apiV3Key', | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入 API V3 密钥', | ||||||
|  |         }, | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v3'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'apiclient_key.pem 证书', | ||||||
|  |         fieldName: 'config.privateKeyContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { | ||||||
|  |             rows: 8, | ||||||
|  |             placeholder: '请上传 apiclient_key.pem 证书', | ||||||
|  |           }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['pem'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v3'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '证书序列号', | ||||||
|  |         fieldName: 'config.certSerialNo', | ||||||
|  |         component: 'Input', | ||||||
|  |         help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage]查看证书序列号', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入证书序列号', | ||||||
|  |         }, | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v3'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'public_key.pem 证书', | ||||||
|  |         fieldName: 'config.publicKeyContent', | ||||||
|  |         component: h(InputUpload, { | ||||||
|  |           inputType: 'textarea', | ||||||
|  |           textareaProps: { | ||||||
|  |             rows: 8, | ||||||
|  |             placeholder: '请上传 public_key.pem 证书', | ||||||
|  |           }, | ||||||
|  |           fileUploadProps: { | ||||||
|  |             accept: ['pem'], | ||||||
|  |           }, | ||||||
|  |         }), | ||||||
|  |         rules: 'required', | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v3'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '公钥 ID', | ||||||
|  |         fieldName: 'config.publicKeyId', | ||||||
|  |         component: 'Input', | ||||||
|  |         help: '微信支付公钥产品简介及使用说明[https://pay.weixin.qq.com/doc/v3/merchant/4012153196]', | ||||||
|  |         rules: 'required', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入公钥 ID', | ||||||
|  |         }, | ||||||
|  |         dependencies: { | ||||||
|  |           show(values) { | ||||||
|  |             return values?.config?.apiVersion === 'v3'; | ||||||
|  |           }, | ||||||
|  |           triggerFields: ['config.mode', 'mode', 'config'], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: '备注', | ||||||
|  |         fieldName: 'remark', | ||||||
|  |         component: 'Input', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入备注', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |   } else { | ||||||
|  |     return []; | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	 吃货
						吃货