mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 10:18:43 +08:00 
			
		
		
		
	| @@ -7,7 +7,6 @@ export interface ChannelVO { | ||||
|   status: number | ||||
|   remark: string | ||||
|   feeRate: number | ||||
|   merchantId: number | ||||
|   appId: number | ||||
|   createTime: Date | ||||
| } | ||||
| @@ -18,13 +17,12 @@ export const getChannelPage = (params: PageParam) => { | ||||
| } | ||||
|  | ||||
| // 查询详情支付渠道 | ||||
| export const getChannel = (merchantId: number, appId: string, code: string) => { | ||||
| export const getChannel = (appId: string, code: string) => { | ||||
|   const params = { | ||||
|     merchantId: merchantId, | ||||
|     appId: appId, | ||||
|     code: code | ||||
|   } | ||||
|   return request.get({ url: '/pay/channel/get-channel', params: params }) | ||||
|   return request.get({ url: '/pay/channel/get', params: params }) | ||||
| } | ||||
|  | ||||
| // 新增支付渠道 | ||||
|   | ||||
| @@ -118,6 +118,10 @@ export const PayChannelEnum = { | ||||
|   ALIPAY_BAR: { | ||||
|     code: 'alipay_bar', | ||||
|     name: '支付宝条码支付' | ||||
|   }, | ||||
|   MOCK: { | ||||
|     code: 'mock', | ||||
|     name: '模拟支付' | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -126,7 +130,8 @@ export const PayChannelEnum = { | ||||
|  */ | ||||
| export const PayType = { | ||||
|   WECHAT: 'WECHAT', | ||||
|   ALIPAY: 'ALIPAY' | ||||
|   ALIPAY: 'ALIPAY', | ||||
|   MOCK: 'MOCK' | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -10,16 +10,6 @@ | ||||
|       <el-form-item label="应用名" prop="name"> | ||||
|         <el-input v-model="formData.name" placeholder="请输入应用名" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="所属商户" prop="merchantId"> | ||||
|         <el-select v-model="formData.merchantId" placeholder="请选择所属商户"> | ||||
|           <el-option | ||||
|             v-for="item in merchantList" | ||||
|             :key="item.id" | ||||
|             :label="item.name" | ||||
|             :value="item.id" | ||||
|           /> | ||||
|         </el-select> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="开启状态" prop="status"> | ||||
|         <el-radio-group v-model="formData.status"> | ||||
|           <el-radio | ||||
| @@ -47,10 +37,10 @@ | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import * as AppApi from '@/api/pay/app' | ||||
| import * as MerchantApi from '@/api/pay/merchant' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| 
 | ||||
| defineOptions({ name: 'PayAppForm' }) | ||||
| @@ -77,11 +67,9 @@ const formRules = reactive({ | ||||
|   name: [{ required: true, message: '应用名不能为空', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }], | ||||
|   payNotifyUrl: [{ required: true, message: '支付结果的回调地址不能为空', trigger: 'blur' }], | ||||
|   refundNotifyUrl: [{ required: true, message: '退款结果的回调地址不能为空', trigger: 'blur' }], | ||||
|   merchantId: [{ required: true, message: '商户编号不能为空', trigger: 'blur' }] | ||||
|   refundNotifyUrl: [{ required: true, message: '退款结果的回调地址不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| const merchantList = ref([]) // 商户列表 | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (type: string, id?: number) => { | ||||
| @@ -98,8 +86,6 @@ const open = async (type: string, id?: number) => { | ||||
|       formLoading.value = false | ||||
|     } | ||||
|   } | ||||
|   // 加载商户列表 | ||||
|   merchantList.value = await MerchantApi.getMerchantListByName() | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| @@ -137,8 +123,7 @@ const resetForm = () => { | ||||
|     status: CommonStatusEnum.ENABLE, | ||||
|     remark: undefined, | ||||
|     payNotifyUrl: undefined, | ||||
|     refundNotifyUrl: undefined, | ||||
|     merchantId: undefined | ||||
|     refundNotifyUrl: undefined | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
							
								
								
									
										315
									
								
								src/views/pay/app/components/alipayChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								src/views/pay/app/components/alipayChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <el-dialog | ||||
|       v-model="dialogVisible" | ||||
|       :title="title" | ||||
|       @closed="close" | ||||
|       append-to-body | ||||
|       destroy-on-close | ||||
|       width="830px" | ||||
|     > | ||||
|       <el-form | ||||
|         ref="formRef" | ||||
|         :model="formData" | ||||
|         :rules="rules" | ||||
|         label-width="100px" | ||||
|         v-loading="formLoading" | ||||
|       > | ||||
|         <el-form-item label-width="180px" label="渠道费率" prop="feeRate"> | ||||
|           <el-input v-model="formData.feeRate" placeholder="请输入渠道费率" clearable> | ||||
|             <template #append>%</template> | ||||
|           </el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="开放平台 APPID" prop="config.appId"> | ||||
|           <el-input v-model="formData.config.appId" placeholder="请输入开放平台 APPID" clearable /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="渠道状态" prop="status"> | ||||
|           <el-radio-group v-model="formData.status"> | ||||
|             <el-radio | ||||
|               v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|               :key="parseInt(dict.value)" | ||||
|               :label="parseInt(dict.value)" | ||||
|             > | ||||
|               {{ dict.label }} | ||||
|             </el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="网关地址" prop="config.serverUrl"> | ||||
|           <el-radio-group v-model="formData.config.serverUrl"> | ||||
|             <el-radio label="https://openapi.alipay.com/gateway.do">线上环境</el-radio> | ||||
|             <el-radio label="https://openapi-sandbox.dl.alipaydev.com/gateway.do" | ||||
|               >沙箱环境</el-radio | ||||
|             > | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="算法类型" prop="config.signType"> | ||||
|           <el-radio-group v-model="formData.config.signType"> | ||||
|             <el-radio key="RSA2" label="RSA2">RSA2</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="公钥类型" prop="config.mode"> | ||||
|           <el-radio-group v-model="formData.config.mode"> | ||||
|             <el-radio key="公钥模式" :label="1">公钥模式</el-radio> | ||||
|             <el-radio key="证书模式" :label="2">证书模式</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <div v-if="formData.config.mode === 1"> | ||||
|           <el-form-item label-width="180px" label="应用私钥" prop="config.privateKey"> | ||||
|             <el-input | ||||
|               type="textarea" | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               v-model="formData.config.privateKey" | ||||
|               placeholder="请输入应用私钥" | ||||
|               clearable | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label="支付宝公钥" prop="config.alipayPublicKey"> | ||||
|             <el-input | ||||
|               type="textarea" | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               v-model="formData.config.alipayPublicKey" | ||||
|               placeholder="请输入支付宝公钥" | ||||
|               clearable | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|         </div> | ||||
|         <div v-if="formData.config.mode === 2"> | ||||
|           <el-form-item label-width="180px" label="商户公钥应用证书" prop="config.appCertContent"> | ||||
|             <el-input | ||||
|               v-model="formData.config.appCertContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传商户公钥应用证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label=""> | ||||
|             <el-upload | ||||
|               action="" | ||||
|               ref="privateKeyContentFile" | ||||
|               :limit="1" | ||||
|               :accept="fileAccept" | ||||
|               :http-request="appCertUpload" | ||||
|               :before-upload="fileBeforeUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|           <el-form-item | ||||
|             label-width="180px" | ||||
|             label="支付宝公钥证书" | ||||
|             prop="config.alipayPublicCertContent" | ||||
|           > | ||||
|             <el-input | ||||
|               v-model="formData.config.alipayPublicCertContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传支付宝公钥证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label=""> | ||||
|             <el-upload | ||||
|               ref="privateCertContentFile" | ||||
|               action="" | ||||
|               :limit="1" | ||||
|               :accept="fileAccept" | ||||
|               :before-upload="fileBeforeUpload" | ||||
|               :http-request="alipayPublicCertUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label="根证书" prop="config.rootCertContent"> | ||||
|             <el-input | ||||
|               v-model="formData.config.rootCertContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传根证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label=""> | ||||
|             <el-upload | ||||
|               ref="privateCertContentFile" | ||||
|               :limit="1" | ||||
|               :accept="fileAccept" | ||||
|               action="" | ||||
|               :before-upload="fileBeforeUpload" | ||||
|               :http-request="rootCertUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|         </div> | ||||
|         <el-form-item label-width="180px" label="备注" prop="remark"> | ||||
|           <el-input v-model="formData.remark" :style="{ width: '100%' }" /> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <template #footer> | ||||
|         <el-button @click="close">取消</el-button> | ||||
|         <el-button type="primary" @click="submitForm">确定</el-button> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup name="AlipayChannelForm"> | ||||
| import { createChannel, getChannel, updateChannel } from '@/api/pay/channel' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
|  | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | ||||
| const emit = defineEmits(['success']) | ||||
|  | ||||
| const dialogVisible = ref(false) | ||||
| const formLoading = ref(false) | ||||
| const title = ref('') | ||||
| const formData = ref<any>({ | ||||
|   appId: '', | ||||
|   code: '', | ||||
|   status: undefined, | ||||
|   feeRate: undefined, | ||||
|   remark: '', | ||||
|   config: { | ||||
|     appId: '', | ||||
|     serverUrl: null, | ||||
|     signType: '', | ||||
|     mode: null, | ||||
|     privateKey: '', | ||||
|     alipayPublicKey: '', | ||||
|     appCertContent: '', | ||||
|     alipayPublicCertContent: '', | ||||
|     rootCertContent: '' | ||||
|   } | ||||
| }) | ||||
|  | ||||
| const rules = { | ||||
|   feeRate: [{ required: true, message: '请输入渠道费率', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '渠道状态不能为空', trigger: 'blur' }], | ||||
|   'config.appId': [{ required: true, message: '请输入开放平台上创建的应用的 ID', trigger: 'blur' }], | ||||
|   'config.serverUrl': [{ required: true, message: '请传入网关地址', trigger: 'blur' }], | ||||
|   'config.signType': [{ required: true, message: '请传入签名算法类型', trigger: 'blur' }], | ||||
|   'config.mode': [{ required: true, message: '公钥类型不能为空', trigger: 'blur' }], | ||||
|   'config.privateKey': [{ required: true, message: '请输入商户私钥', trigger: 'blur' }], | ||||
|   'config.alipayPublicKey': [ | ||||
|     { required: true, message: '请输入支付宝公钥字符串', trigger: 'blur' } | ||||
|   ], | ||||
|   'config.appCertContent': [{ required: true, message: '请上传商户公钥应用证书', trigger: 'blur' }], | ||||
|   'config.alipayPublicCertContent': [ | ||||
|     { required: true, message: '请上传支付宝公钥证书', trigger: 'blur' } | ||||
|   ], | ||||
|   'config.rootCertContent': [{ required: true, message: '请上传指定根证书', trigger: 'blur' }] | ||||
| } | ||||
|  | ||||
| const fileAccept = '.crt' | ||||
|  | ||||
| const formRef = ref() | ||||
|  | ||||
| const open = async (appId, code) => { | ||||
|   dialogVisible.value = true | ||||
|   formLoading.value = true | ||||
|   reset(appId, code) | ||||
|  | ||||
|   try { | ||||
|     const data = await getChannel(appId, code) | ||||
|     if (data && data.id) { | ||||
|       formData.value = data | ||||
|       formData.value.config = JSON.parse(data.config) | ||||
|     } | ||||
|     title.value = !formData.value.id ? '创建支付渠道' : '编辑支付渠道' | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| defineExpose({ open }) | ||||
|  | ||||
| const close = () => { | ||||
|   dialogVisible.value = false | ||||
|   reset(undefined, undefined) | ||||
| } | ||||
|  | ||||
| const submitForm = async () => { | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|  | ||||
|   const data: any = { ...formData.value } | ||||
|   data.config = JSON.stringify(formData.value.config) | ||||
|   if (!data.id) { | ||||
|     await createChannel(data) | ||||
|     message.success('新增成功') | ||||
|   } else { | ||||
|     await updateChannel(data) | ||||
|     message.success('修改成功') | ||||
|   } | ||||
|  | ||||
|   emit('success') | ||||
|   close() | ||||
| } | ||||
|  | ||||
| /** 重置表单 */ | ||||
| const reset = (appId, code) => { | ||||
|   formData.value = { | ||||
|     appId: appId, | ||||
|     code: code, | ||||
|     status: CommonStatusEnum.ENABLE, | ||||
|     remark: '', | ||||
|     feeRate: null, | ||||
|     config: { | ||||
|       appId: '', | ||||
|       serverUrl: null, | ||||
|       signType: 'RSA2', | ||||
|       mode: null, | ||||
|       privateKey: '', | ||||
|       alipayPublicKey: '', | ||||
|       appCertContent: '', | ||||
|       alipayPublicCertContent: '', | ||||
|       rootCertContent: '' | ||||
|     } | ||||
|   } | ||||
|   // formRef.value?.resetFields() | ||||
| } | ||||
|  | ||||
| const fileBeforeUpload = (file) => { | ||||
|   let format = '.' + file.name.split('.')[1] | ||||
|   if (format !== fileAccept) { | ||||
|     message.error(`请上传指定格式"${fileAccept}"文件`) | ||||
|     return false | ||||
|   } | ||||
|   let isRightSize = file.size / 1024 / 1024 < 2 | ||||
|   if (!isRightSize) { | ||||
|     message.error('文件大小超过 2MB') | ||||
|   } | ||||
|   return isRightSize | ||||
| } | ||||
|  | ||||
| const appCertUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.appCertContent = e.target.result | ||||
|   } | ||||
|   readFile.readAsText(event.file) | ||||
| } | ||||
|  | ||||
| const alipayPublicCertUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.alipayPublicCertContent = e.target.result | ||||
|   } | ||||
|   readFile.readAsText(event.file) | ||||
| } | ||||
|  | ||||
| const rootCertUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.rootCertContent = e.target.result | ||||
|   } | ||||
|   readFile.readAsText(event.file) | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										130
									
								
								src/views/pay/app/components/mockChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/views/pay/app/components/mockChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <el-dialog | ||||
|       v-model:visible="dialogVisible" | ||||
|       :title="title" | ||||
|       @closed="close" | ||||
|       append-to-body | ||||
|       destroy-on-close | ||||
|       width="800px" | ||||
|     > | ||||
|       <el-form | ||||
|         ref="formRef" | ||||
|         :model="formData" | ||||
|         :rules="rules" | ||||
|         label-width="100px" | ||||
|         v-loading="formLoading" | ||||
|       > | ||||
|         <el-form-item label-width="180px" label="渠道状态" prop="status"> | ||||
|           <el-radio-group v-model="formData.status"> | ||||
|             <el-radio | ||||
|               v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|               :key="parseInt(dict.value)" | ||||
|               :label="parseInt(dict.value)" | ||||
|             > | ||||
|               {{ dict.label }} | ||||
|             </el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="备注" prop="remark"> | ||||
|           <el-input v-model="formData.remark" :style="{ width: '100%' }" /> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <template #footer> | ||||
|         <el-button @click="close">取消</el-button> | ||||
|         <el-button type="primary" @click="submitForm">确定</el-button> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup name="MockChannelForm"> | ||||
| import { createChannel, getChannel, updateChannel } from '@/api/pay/channel' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
|  | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | ||||
| const dialogVisible = ref(false) | ||||
| const formLoading = ref(false) | ||||
| const title = ref('') | ||||
| const formData = ref<any>({ | ||||
|   appId: '', | ||||
|   code: '', | ||||
|   status: undefined, | ||||
|   feeRate: 0, | ||||
|   remark: '', | ||||
|   config: { | ||||
|     name: 'mock-conf' | ||||
|   } | ||||
| }) | ||||
|  | ||||
| const rules = { | ||||
|   status: [{ required: true, message: '渠道状态不能为空', trigger: 'blur' }] | ||||
| } | ||||
|  | ||||
| const formRef = ref() | ||||
|  | ||||
| const emit = defineEmits(['success']) | ||||
|  | ||||
| const open = async (appId, code) => { | ||||
|   dialogVisible.value = true | ||||
|   formLoading.value = true | ||||
|   reset(appId, code) | ||||
|  | ||||
|   try { | ||||
|     const data = await getChannel(appId, code) | ||||
|  | ||||
|     if (data && data.id) { | ||||
|       formData.value = data | ||||
|       formData.value.config = JSON.parse(data.config) | ||||
|     } | ||||
|     title.value = !formData.value.id ? '创建支付渠道' : '编辑支付渠道' | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| const close = () => { | ||||
|   dialogVisible.value = false | ||||
|   reset(undefined, undefined) | ||||
| } | ||||
|  | ||||
| const submitForm = async () => { | ||||
|   const valid = await formRef.value?.validate() | ||||
|   if (!valid) { | ||||
|     return | ||||
|   } | ||||
|   const data = { ...formData.value } | ||||
|   data.config = JSON.stringify(formData.value.config) | ||||
|   if (!data.id) { | ||||
|     createChannel(data).then(() => { | ||||
|       message.success('新增成功') | ||||
|       emit('success') | ||||
|       close() | ||||
|     }) | ||||
|   } else { | ||||
|     updateChannel(data).then(() => { | ||||
|       message.success('修改成功') | ||||
|       emit('success') | ||||
|       close() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** 重置表单 */ | ||||
| const reset = (appId, code) => { | ||||
|   formData.value = { | ||||
|     appId: appId, | ||||
|     code: code, | ||||
|     status: CommonStatusEnum.ENABLE, | ||||
|     remark: '', | ||||
|     feeRate: 0, | ||||
|     config: { | ||||
|       name: 'mock-conf' | ||||
|     } | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
|  | ||||
| defineExpose({ open }) | ||||
| </script> | ||||
							
								
								
									
										343
									
								
								src/views/pay/app/components/weixinChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/views/pay/app/components/weixinChannelForm.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <el-dialog | ||||
|       v-model="dialogVisible" | ||||
|       :title="title" | ||||
|       @close="close" | ||||
|       append-to-body | ||||
|       destroy-on-close | ||||
|       width="800px" | ||||
|     > | ||||
|       <el-form | ||||
|         ref="formRef" | ||||
|         :model="formData" | ||||
|         :rules="rules" | ||||
|         label-width="120px" | ||||
|         v-loading="formLoading" | ||||
|       > | ||||
|         <el-form-item label-width="180px" label="渠道费率" prop="feeRate"> | ||||
|           <el-input | ||||
|             v-model="formData.feeRate" | ||||
|             placeholder="请输入渠道费率" | ||||
|             clearable | ||||
|             :style="{ width: '100%' }" | ||||
|           > | ||||
|             <template #append>%</template> | ||||
|           </el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="公众号 APPID" prop="config.appId"> | ||||
|           <el-input | ||||
|             v-model="formData.config.appId" | ||||
|             placeholder="请输入公众号 APPID" | ||||
|             clearable | ||||
|             :style="{ width: '100%' }" | ||||
|           /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="商户号" prop="config.mchId"> | ||||
|           <el-input v-model="formData.config.mchId" :style="{ width: '100%' }" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="渠道状态" prop="status"> | ||||
|           <el-radio-group v-model="formData.status"> | ||||
|             <el-radio | ||||
|               v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|               :key="parseInt(dict.value)" | ||||
|               :label="parseInt(dict.value)" | ||||
|             > | ||||
|               {{ dict.label }} | ||||
|             </el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label-width="180px" label="API 版本" prop="config.apiVersion"> | ||||
|           <el-radio-group v-model="formData.config.apiVersion"> | ||||
|             <el-radio label="v2">v2</el-radio> | ||||
|             <el-radio label="v3">v3</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <div v-if="formData.config.apiVersion === 'v2'"> | ||||
|           <el-form-item label-width="180px" label="商户密钥" prop="config.mchKey"> | ||||
|             <el-input | ||||
|               v-model="formData.config.mchKey" | ||||
|               placeholder="请输入商户密钥" | ||||
|               clearable | ||||
|               :style="{ width: '100%' }" | ||||
|               type="textarea" | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item | ||||
|             label-width="180px" | ||||
|             label="apiclient_cert.p12 证书" | ||||
|             prop="config.keyContent" | ||||
|           > | ||||
|             <el-input | ||||
|               v-model="formData.config.keyContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传 apiclient_cert.p12 证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label=""> | ||||
|             <el-upload | ||||
|               :limit="1" | ||||
|               accept=".p12" | ||||
|               action="" | ||||
|               :before-upload="p12FileBeforeUpload" | ||||
|               :http-request="keyContentUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|         </div> | ||||
|         <div v-if="formData.config.apiVersion === 'v3'"> | ||||
|           <el-form-item label-width="180px" label="API V3 密钥" prop="config.apiV3Key"> | ||||
|             <el-input | ||||
|               v-model="formData.config.apiV3Key" | ||||
|               placeholder="请输入 API V3 密钥" | ||||
|               clearable | ||||
|               :style="{ width: '100%' }" | ||||
|               type="textarea" | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item | ||||
|             label-width="180px" | ||||
|             label="apiclient_key.pem 证书" | ||||
|             prop="config.privateKeyContent" | ||||
|           > | ||||
|             <el-input | ||||
|               v-model="formData.config.privateKeyContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传 apiclient_key.pem 证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label="" prop="privateKeyContentFile"> | ||||
|             <el-upload | ||||
|               ref="privateKeyContentFile" | ||||
|               :limit="1" | ||||
|               accept=".pem" | ||||
|               action="" | ||||
|               :before-upload="pemFileBeforeUpload" | ||||
|               :http-request="privateKeyContentUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|           <el-form-item | ||||
|             label-width="180px" | ||||
|             label="apiclient_cert.pem证书" | ||||
|             prop="config.privateCertContent" | ||||
|           > | ||||
|             <el-input | ||||
|               v-model="formData.config.privateCertContent" | ||||
|               type="textarea" | ||||
|               placeholder="请上传apiclient_cert.pem证书" | ||||
|               readonly | ||||
|               :autosize="{ minRows: 8, maxRows: 8 }" | ||||
|               :style="{ width: '100%' }" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label-width="180px" label="" prop="privateCertContentFile"> | ||||
|             <el-upload | ||||
|               ref="privateCertContentFile" | ||||
|               :limit="1" | ||||
|               accept=".pem" | ||||
|               action="" | ||||
|               :before-upload="pemFileBeforeUpload" | ||||
|               :http-request="privateCertContentUpload" | ||||
|             > | ||||
|               <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button> | ||||
|             </el-upload> | ||||
|           </el-form-item> | ||||
|         </div> | ||||
|         <el-form-item label-width="180px" label="备注" prop="remark"> | ||||
|           <el-input v-model="formData.remark" :style="{ width: '100%' }" /> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <template #footer> | ||||
|         <el-button @click="close">取消</el-button> | ||||
|         <el-button type="primary" @click="submitForm">确定</el-button> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup name="WeixinChannelForm"> | ||||
| import { createChannel, getChannel, updateChannel } from '@/api/pay/channel' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| import { DICT_TYPE, getDictOptions } from '@/utils/dict' | ||||
|  | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | ||||
| const dialogVisible = ref(false) | ||||
| const formLoading = ref(false) | ||||
| const title = ref('') | ||||
| const formData = ref<any>({ | ||||
|   appId: '', | ||||
|   code: '', | ||||
|   status: undefined, | ||||
|   feeRate: undefined, | ||||
|   remark: '', | ||||
|   config: { | ||||
|     appId: '', | ||||
|     mchId: '', | ||||
|     apiVersion: '', | ||||
|     mchKey: '', | ||||
|     keyContent: '', | ||||
|     privateKeyContent: '', | ||||
|     privateCertContent: '', | ||||
|     apiV3Key: '' | ||||
|   } | ||||
| }) | ||||
| const formRef = ref() | ||||
|  | ||||
| const emit = defineEmits(['success']) | ||||
|  | ||||
| const rules = { | ||||
|   feeRate: [{ required: true, message: '请输入渠道费率', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '渠道状态不能为空', trigger: 'blur' }], | ||||
|   'config.mchId': [{ required: true, message: '请传入商户号', trigger: 'blur' }], | ||||
|   'config.appId': [{ required: true, message: '请输入公众号APPID', trigger: 'blur' }], | ||||
|   'config.apiVersion': [{ required: true, message: 'API版本不能为空', trigger: 'blur' }], | ||||
|   'config.mchKey': [{ required: true, message: '请输入商户密钥', trigger: 'blur' }], | ||||
|   'config.keyContent': [ | ||||
|     { required: true, message: '请上传 apiclient_cert.p12 证书', trigger: 'blur' } | ||||
|   ], | ||||
|   'config.privateKeyContent': [ | ||||
|     { required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' } | ||||
|   ], | ||||
|   'config.privateCertContent': [ | ||||
|     { required: true, message: '请上传 apiclient_cert.pem证 书', trigger: 'blur' } | ||||
|   ], | ||||
|   'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }] | ||||
| } | ||||
|  | ||||
| const open = async (appId, code) => { | ||||
|   dialogVisible.value = true | ||||
|   formLoading.value = true | ||||
|   reset(appId, code) | ||||
|  | ||||
|   try { | ||||
|     const data = await getChannel(appId, code) | ||||
|     if (data && data.id) { | ||||
|       formData.value = data | ||||
|       formData.value.config = JSON.parse(data.config) | ||||
|     } | ||||
|     title.value = !formData.value.id ? '创建支付渠道' : '编辑支付渠道' | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| const close = () => { | ||||
|   dialogVisible.value = false | ||||
|   reset(undefined, undefined) | ||||
| } | ||||
|  | ||||
| const submitForm = async () => { | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) { | ||||
|     return | ||||
|   } | ||||
|   const data: any = { ...formData.value } | ||||
|   data.config = JSON.stringify(formData.value.config) | ||||
|   if (!data.id) { | ||||
|     createChannel(data).then(() => { | ||||
|       message.alertSuccess('新增成功') | ||||
|       emit('success') | ||||
|       close() | ||||
|     }) | ||||
|   } else { | ||||
|     updateChannel(data).then(() => { | ||||
|       message.alertSuccess('修改成功') | ||||
|       emit('success') | ||||
|       close() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** 重置表单 */ | ||||
| const reset = (appId, code) => { | ||||
|   formData.value = { | ||||
|     appId: appId, | ||||
|     code: code, | ||||
|     status: CommonStatusEnum.ENABLE, | ||||
|     feeRate: undefined, | ||||
|     remark: '', | ||||
|     config: { | ||||
|       appId: '', | ||||
|       mchId: '', | ||||
|       apiVersion: '', | ||||
|       mchKey: '', | ||||
|       keyContent: '', | ||||
|       privateKeyContent: '', | ||||
|       privateCertContent: '', | ||||
|       apiV3Key: '' | ||||
|     } | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * apiclient_cert.p12、apiclient_cert.pem、apiclient_key.pem 上传前的校验 | ||||
|  */ | ||||
| const fileBeforeUpload = (file, fileAccept) => { | ||||
|   let format = '.' + file.name.split('.')[1] | ||||
|   if (format !== fileAccept) { | ||||
|     debugger | ||||
|     message.error('请上传指定格式"' + fileAccept + '"文件') | ||||
|     return false | ||||
|   } | ||||
|   let isRightSize = file.size / 1024 / 1024 < 2 | ||||
|   if (!isRightSize) { | ||||
|     message.error('文件大小超过 2MB') | ||||
|   } | ||||
|   return isRightSize | ||||
| } | ||||
|  | ||||
| const p12FileBeforeUpload = (file) => { | ||||
|   fileBeforeUpload(file, '.p12') | ||||
| } | ||||
|  | ||||
| const pemFileBeforeUpload = (file) => { | ||||
|   fileBeforeUpload(file, '.pem') | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 读取 apiclient_key.pem 到 privateKeyContent 字段 | ||||
|  */ | ||||
| const privateKeyContentUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.privateKeyContent = e.target.result | ||||
|   } | ||||
|   readFile.readAsText(event.file) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 读取 apiclient_cert.pem 到 privateCertContent 字段 | ||||
|  */ | ||||
| const privateCertContentUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.privateCertContent = e.target.result | ||||
|   } | ||||
|   readFile.readAsText(event.file) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 读取 apiclient_cert.p12 到 keyContent 字段 | ||||
|  */ | ||||
| const keyContentUpload = (event) => { | ||||
|   const readFile = new FileReader() | ||||
|   readFile.onload = (e: any) => { | ||||
|     formData.value.config.keyContent = e.target.result.split(',')[1] | ||||
|   } | ||||
|   readFile.readAsDataURL(event.file) // 读成 base64 | ||||
| } | ||||
|  | ||||
| defineExpose({ open }) | ||||
| </script> | ||||
| @@ -17,15 +17,6 @@ | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="商户名称" prop="contactName"> | ||||
|         <el-input | ||||
|           v-model="queryParams.contactName" | ||||
|           placeholder="请输入商户名称" | ||||
|           clearable | ||||
|           @keyup.enter="handleQuery" | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="开启状态" prop="status"> | ||||
|         <el-select | ||||
|           v-model="queryParams.status" | ||||
| @@ -53,8 +44,8 @@ | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item> | ||||
|         <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />搜索</el-button> | ||||
|         <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button> | ||||
|         <el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" />搜索 </el-button> | ||||
|         <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" />重置 </el-button> | ||||
|         <el-button | ||||
|           type="primary" | ||||
|           plain | ||||
| @@ -83,19 +74,21 @@ | ||||
|       <el-table-column label="应用名" align="center" prop="name" /> | ||||
|       <el-table-column label="开启状态" align="center" prop="status"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> | ||||
|           <el-switch | ||||
|             v-model="scope.row.status" | ||||
|             :active-value="0" | ||||
|             :inactive-value="1" | ||||
|             @change="handleStatusChange(scope.row)" | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="商户名称" align="center" prop="payMerchant.name" /> | ||||
|       <el-table-column label="支付宝配置" align="center"> | ||||
|         <el-table-column :label="PayChannelEnum.ALIPAY_APP.name" align="center"> | ||||
|           <template #default="scope"> | ||||
|             <el-button | ||||
|               type="success" | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.ALIPAY_APP.code)" | ||||
|               @click=" | ||||
|                 handleUpdateChannel(scope.row, PayChannelEnum.ALIPAY_APP.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_APP.code, PayType.ALIPAY)" | ||||
|               circle | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
| @@ -104,9 +97,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click=" | ||||
|                 handleCreateChannel(scope.row, PayChannelEnum.ALIPAY_APP.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_APP.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -118,7 +109,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.ALIPAY_PC.code)" | ||||
|               @click="handleUpdateChannel(scope.row, PayChannelEnum.ALIPAY_PC.code, PayType.ALIPAY)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_PC.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -126,7 +117,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="handleCreateChannel(scope.row, PayChannelEnum.ALIPAY_PC.code, PayType.ALIPAY)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_PC.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -138,9 +129,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.ALIPAY_WAP.code)" | ||||
|               @click=" | ||||
|                 handleUpdateChannel(scope.row, PayChannelEnum.ALIPAY_WAP.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_WAP.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -148,9 +137,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click=" | ||||
|                 handleCreateChannel(scope.row, PayChannelEnum.ALIPAY_WAP.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_WAP.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -162,7 +149,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.ALIPAY_QR.code)" | ||||
|               @click="handleUpdateChannel(scope.row, PayChannelEnum.ALIPAY_QR.code, PayType.ALIPAY)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_QR.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -170,7 +157,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="handleCreateChannel(scope.row, PayChannelEnum.ALIPAY_QR.code, PayType.ALIPAY)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_QR.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -182,9 +169,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.ALIPAY_BAR.code)" | ||||
|               @click=" | ||||
|                 handleUpdateChannel(scope.row, PayChannelEnum.ALIPAY_BAR.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_BAR.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -192,9 +177,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click=" | ||||
|                 handleCreateChannel(scope.row, PayChannelEnum.ALIPAY_BAR.code, PayType.ALIPAY) | ||||
|               " | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.ALIPAY_BAR.code, PayType.ALIPAY)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -208,7 +191,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.WX_LITE.code)" | ||||
|               @click="handleUpdateChannel(scope.row, PayChannelEnum.WX_LITE.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_LITE.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -216,7 +199,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="handleCreateChannel(scope.row, PayChannelEnum.WX_LITE.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_LITE.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -228,7 +211,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.WX_PUB.code)" | ||||
|               @click="handleUpdateChannel(scope.row, PayChannelEnum.WX_PUB.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_PUB.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -236,7 +219,7 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="handleCreateChannel(scope.row, PayChannelEnum.WX_PUB.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_PUB.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
| @@ -248,7 +231,7 @@ | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.WX_APP.code)" | ||||
|               @click="handleUpdateChannel(scope.row, PayChannelEnum.WX_APP.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_APP.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:check" /> | ||||
|             </el-button> | ||||
| @@ -256,20 +239,33 @@ | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="handleCreateChannel(scope.row, PayChannelEnum.WX_APP.code, PayType.WECHAT)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.WX_APP.code, PayType.WECHAT)" | ||||
|             > | ||||
|               <Icon icon="ep:close" /> | ||||
|             </el-button> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="创建时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         width="180" | ||||
|         :formatter="dateFormatter" | ||||
|       /> | ||||
|       <el-table-column label="模拟支付配置" align="center"> | ||||
|         <el-table-column :label="PayChannelEnum.MOCK.name" align="center"> | ||||
|           <template #default="scope"> | ||||
|             <el-button | ||||
|               type="success" | ||||
|               circle | ||||
|               v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.MOCK.code)" | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)" | ||||
|               ><Icon icon="ep:check" | ||||
|             /></el-button> | ||||
|             <el-button | ||||
|               v-else | ||||
|               type="danger" | ||||
|               circle | ||||
|               @click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)" | ||||
|               ><Icon icon="ep:close" | ||||
|             /></el-button> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="操作" align="center" min-width="110" fixed="right"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
| @@ -302,20 +298,30 @@ | ||||
|  | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <AppForm ref="formRef" @success="getList" /> | ||||
|   <AlipayChannelForm ref="alipayFormRef" @success="getList" /> | ||||
|   <WeixinChannelForm ref="weixinFormRef" @success="getList" /> | ||||
|   <MockChannelForm ref="mockFormRef" @success="getList" /> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import download from '@/utils/download' | ||||
| import * as AppApi from '@/api/pay/app' | ||||
| import AppForm from '@/views/pay/app/AppForm.vue' | ||||
| import * as PayappApi from '@/api/pay/app' | ||||
| import AppForm from './components/AppForm.vue' | ||||
| import { PayChannelEnum, PayType } from '@/utils/constants' | ||||
| import AlipayChannelForm from './components/alipayChannelForm.vue' | ||||
| import WeixinChannelForm from './components/weixinChannelForm.vue' | ||||
| import MockChannelForm from './components/mockChannelForm.vue' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
|  | ||||
| defineOptions({ name: 'PayApp' }) | ||||
|  | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
|  | ||||
| const alipayFormRef = ref() | ||||
| const weixinFormRef = ref() | ||||
| const mockFormRef = ref() | ||||
|  | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| @@ -334,9 +340,6 @@ const queryFormRef = ref() // 搜索的表单 | ||||
| const exportLoading = ref(false) // 导出的加载中 | ||||
| const channelParam = reactive({ | ||||
|   loading: false, | ||||
|   edit: false, // 是否修改 | ||||
|   wechatOpen: false, // 微信是否显示 | ||||
|   aliPayOpen: false, // 支付宝是否显示 | ||||
|   appId: null, // 应用 ID | ||||
|   payCode: null, // 渠道编码 | ||||
|   // 商户对象 | ||||
| @@ -350,7 +353,7 @@ const channelParam = reactive({ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const data = await AppApi.getAppPage(queryParams) | ||||
|     const data = await PayappApi.getAppPage(queryParams) | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
| @@ -370,6 +373,20 @@ const resetQuery = () => { | ||||
|   handleQuery() | ||||
| } | ||||
|  | ||||
| // 用户状态修改 | ||||
| const handleStatusChange = async (row: any) => { | ||||
|   let text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用' | ||||
|  | ||||
|   try { | ||||
|     await message.confirm('确认要"' + text + '""' + row.name + '"应用吗?') | ||||
|     await PayappApi.changeAppStatus({ id: row.id, status: row.status }) | ||||
|     message.success(text + '成功') | ||||
|   } catch { | ||||
|     row.status = | ||||
|       row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
| @@ -382,7 +399,7 @@ const handleDelete = async (id: number) => { | ||||
|     // 删除的二次确认 | ||||
|     await message.delConfirm() | ||||
|     // 发起删除 | ||||
|     await AppApi.deleteApp(id) | ||||
|     await PayappApi.deleteApp(id) | ||||
|     message.success(t('common.delSuccess')) | ||||
|     // 刷新列表 | ||||
|     await getList() | ||||
| @@ -396,9 +413,8 @@ const handleExport = async () => { | ||||
|     await message.exportConfirm() | ||||
|     // 发起导出 | ||||
|     exportLoading.value = true | ||||
|     const data = await AppApi.exportApp(queryParams) | ||||
|     const data = await PayappApi.exportApp(queryParams) | ||||
|     download.excel(data, '支付应用信息.xls') | ||||
|   } catch { | ||||
|   } finally { | ||||
|     exportLoading.value = false | ||||
|   } | ||||
| @@ -417,46 +433,28 @@ const isChannelExists = (channels, channelCode) => { | ||||
|   return channels.indexOf(channelCode) !== -1 | ||||
| } | ||||
|  | ||||
| // TODO @芋艿:handleUpdateChannel 和 handleCreateChannel 合并,成为 openChannelForm | ||||
| /** | ||||
|  * 修改支付渠道信息 | ||||
|  * | ||||
|  * @param row 行记录 | ||||
|  * @param payCode 支付编码 | ||||
|  * @param type 支付类型 | ||||
|  */ | ||||
| const handleUpdateChannel = async (row, payCode, type) => { | ||||
|   // TODO @芋艿:表单未实现 | ||||
|   message.alert('待实现') | ||||
|   await settingChannelParam(row, payCode, type) | ||||
|   channelParam.edit = true | ||||
|   channelParam.loading = true | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 新增支付渠道信息 | ||||
|  */ | ||||
| const handleCreateChannel = async (row, payCode, type) => { | ||||
|   message.alert('待实现') | ||||
|   await settingChannelParam(row, payCode, type) | ||||
|   channelParam.edit = false | ||||
|   channelParam.loading = false | ||||
| } | ||||
|  | ||||
| const settingChannelParam = async (row, payCode, type) => { | ||||
|   if (type === PayType.WECHAT) { | ||||
|     channelParam.wechatOpen = true | ||||
|     channelParam.aliPayOpen = false | ||||
|   } | ||||
|   if (type === PayType.ALIPAY) { | ||||
|     channelParam.aliPayOpen = true | ||||
|     channelParam.wechatOpen = false | ||||
|   } | ||||
|   channelParam.edit = false | ||||
| const openChannelForm = async (row, payCode, type) => { | ||||
|   channelParam.loading = false | ||||
|   channelParam.appId = row.id | ||||
|   channelParam.payCode = payCode | ||||
|   channelParam.payMerchant = row.payMerchant | ||||
|  | ||||
|   switch (type) { | ||||
|     case PayType.ALIPAY: | ||||
|       alipayFormRef.value.open(row.id, payCode) | ||||
|       break | ||||
|  | ||||
|     case PayType.WECHAT: | ||||
|       weixinFormRef.value.open(row.id, payCode) | ||||
|       break | ||||
|  | ||||
|     case PayType.MOCK: | ||||
|       mockFormRef.value.open(row.id, payCode) | ||||
|       break | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** 初始化 **/ | ||||
|   | ||||
| @@ -325,7 +325,7 @@ const openDetail = (id: number) => { | ||||
| onMounted(async () => { | ||||
|   await getList() | ||||
|   // 加载商户列表 | ||||
|   merchantList.value = await MerchantApi.getMerchantListByName() | ||||
|   // merchantList.value = await MerchantApi.getMerchantListByName() | ||||
|   // 加载 App 列表 | ||||
|   // TODO 芋艿:候选少一个查询应用列表的接口 | ||||
|   // appList.value = await AppApi.getAppListByMerchantId() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 芋道源码
					芋道源码