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