feat 云闪付配置界面, 修复useUpload根路径拼接问题

This commit is contained in:
bootx
2024-03-10 22:51:26 +08:00
parent 966b876ecc
commit b952affe49
7 changed files with 347 additions and 4 deletions

View File

@@ -2,7 +2,7 @@ import { useUserStoreWithOut } from '/@/store/modules/user'
import { computed } from 'vue'
import { getAppEnvConfig } from '/@/utils/env'
const useUserStore = useUserStoreWithOut()
const { VITE_GLOB_API_URL } = getAppEnvConfig()
const { VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX } = getAppEnvConfig()
export function useUpload(uploadUrl: string) {
/**
@@ -18,7 +18,7 @@ export function useUpload(uploadUrl: string) {
* 上传地址
*/
const uploadAction = computed(() => {
return VITE_GLOB_API_URL + uploadUrl
return VITE_GLOB_API_URL + VITE_GLOB_API_URL_PREFIX + uploadUrl
})
return {
tokenHeader,

View File

@@ -1,6 +1,7 @@
<template>
<alipay-config-edit ref="alipay" @ok="ok" />
<wechat-pay-config-edit ref="wechat" @ok="ok" />
<union-pay-config-edit ref="union" @ok="ok" />
<wallet-config-edit ref="wallet" @ok="ok" />
<voucher-config-edit ref="voucher" @ok="ok" />
<cash-config-edit ref="cash" @ok="ok" />
@@ -15,11 +16,13 @@
import WalletConfigEdit from '/@/views/payment/channel/wallet/config/WalletConfigEdit.vue'
import VoucherConfigEdit from '/@/views/payment/channel/voucher/config/VoucherConfigEdit.vue'
import CashConfigEdit from '/@/views/payment/channel/cash/config/CashConfigEdit.vue'
import UnionPayConfigEdit from '/@/views/payment/channel/union/config/UnionPayConfigEdit.vue'
const { createMessage } = useMessage()
let alipay = $ref<any>()
let wechat = $ref<any>()
let union = $ref<any>()
let wallet = $ref<any>()
let voucher = $ref<any>()
let cash = $ref<any>()
@@ -38,6 +41,10 @@
wechat.init(record)
break
}
case payChannelEnum.UNION_PAY: {
union.init(record)
break
}
case payChannelEnum.WALLET: {
wallet.init(record)
break

View File

@@ -0,0 +1,63 @@
import { defHttp } from '/@/utils/http/axios'
import { Result } from '/#/axios'
import { BaseEntity, KeyValue } from '/#/web'
/**
* 获取单条
*/
export function getConfig() {
return defHttp.get<Result<UnionPayConfig>>({
url: '/union/pay/config/getConfig',
})
}
/**
* 更新
*/
export function update(obj: UnionPayConfig) {
return defHttp.post({
url: '/union/pay/config/update',
data: obj,
})
}
/**
* 获取云闪付支持支付方式
*/
export function findPayWayList() {
return defHttp.get<Result<KeyValue[]>>({
url: '/union/pay/config/findPayWays',
})
}
/**
* 微信支付配置
*/
export interface UnionPayConfig extends BaseEntity {
// 商户号
machId?: string
// 商户收款账号
seller?: string
// 是否启用
enable: boolean
// 签名类型
signType?: string
// 是否为证书签名
certSign?: boolean
// 应用私钥证书
keyPrivateCert?: string | null
// 私钥证书对应的密码
keyPrivateCertPwd?: string | null
// 中级证书
acpMiddleCert?: string | null
// 根证书
acpRootCert?: string | null
// 异步通知页面路径
notifyUrl?: string
// 页面跳转同步通知页面路径
returnUrl?: string
// 是否沙箱环境
sandbox?: boolean
// 支持的支付类型
payWays?: string[]
}

View File

@@ -0,0 +1,273 @@
<template>
<basic-drawer
showFooter
v-bind="$attrs"
width="60%"
title="云闪付支付配置"
:visible="visible"
:maskClosable="false"
@close="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="主键" name="id" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="商户号" name="machId">
<a-input v-model:value="form.machId" :disabled="showable" placeholder="请输入商户号" />
</a-form-item>
<a-form-item label="是否启用" name="enable">
<a-switch checked-children="启用" un-checked-children="停用" v-model:checked="form.enable" />
</a-form-item>
<a-form-item label="签名类型" name="signType">
<a-select
allowClear
:disabled="showable"
:options="signTypeList"
v-model:value="form.signType"
style="width: 100%"
placeholder="选择支付方式"
/>
</a-form-item>
<a-form-item name="keyPrivateCert" label="应用私钥证书">
<a-upload
v-if="!form.keyPrivateCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'keyPrivateCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_enc.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon @click="form.keyPrivateCert = ''" icon="ant-design:close-circle-outlined" :size="20" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="私钥证书密码" name="keyPrivateCertPwd">
<a-input allow-clear v-model:value="form.keyPrivateCertPwd" :disabled="showable" placeholder="请输入私钥证书密码" />
</a-form-item>
<a-form-item name="acpMiddleCert" label="中级证书">
<a-upload
v-if="!form.acpMiddleCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'acpMiddleCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_middle.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon @click="form.acpMiddleCert = ''" icon="ant-design:close-circle-outlined" :size="20" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item name="acpRootCert" label="根证书">
<a-upload
v-if="!form.acpRootCert"
:disabled="showable"
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
@change="(info) => handleChange(info, 'acpRootCert')"
>
<a-button type="primary" preIcon="carbon:cloud-upload"> 证书上传 </a-button>
</a-upload>
<a-input v-else defaultValue="acp_root.cer" disabled>
<template #addonAfter v-if="!showable">
<a-tooltip>
<template #title> 删除上传的证书文件 </template>
<icon @click="form.acpRootCert = ''" icon="ant-design:close-circle-outlined" :size="20" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item name="notifyUrl">
<template #label>
<basic-title helpMessage="此处为本网关接收通知的地址, 而不是客户系统接收通知所需的地址"> 异步通知地址 </basic-title>
</template>
<a-input v-model:value="form.notifyUrl" :disabled="showable" placeholder="请输入服务器异步通知地址" />
</a-form-item>
<a-form-item name="returnUrl">
<template #label>
<basic-title helpMessage="此处为本网关接收通知的地址, 而不是客户系统接收通知所需的地址"> 同步通知地址 </basic-title>
</template>
<a-input v-model:value="form.returnUrl" :disabled="showable" placeholder="请输入页面跳转同步通知地址" />
</a-form-item>
<a-form-item label="支持支付方式" name="payWays">
<a-select
allowClear
mode="multiple"
:disabled="showable"
:options="payWayList"
v-model:value="form.payWays"
style="width: 100%"
placeholder="选择支付方式"
/>
</a-form-item>
<a-form-item label="测试环境" name="sandbox">
<a-switch checked-children="" un-checked-children="" v-model:checked="form.sandbox" />
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea v-model:value="form.remark" :disabled="showable" placeholder="请输入备注" />
</a-form-item>
</a-form>
</a-spin>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button v-if="!showable" key="forward" :loading="confirmLoading" type="primary" @click="handleOk">保存</a-button>
</a-space>
</template>
</basic-drawer>
</template>
<script lang="ts" setup>
import { computed, nextTick } from 'vue'
import { $ref } from 'vue/macros'
import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { BasicDrawer } from '/@/components/Drawer'
import Icon from '/@/components/Icon/src/Icon.vue'
import { useUpload } from '/@/hooks/bootx/useUpload'
import { useMessage } from '/@/hooks/web/useMessage'
import { LabeledValue } from 'ant-design-vue/lib/select'
import BasicTitle from '/@/components/Basic/src/BasicTitle.vue'
import { getConfig, update, findPayWayList, UnionPayConfig } from './UnionPayConfig.api'
import { useDict } from '/@/hooks/bootx/useDict'
const { handleCancel, search, diffForm, labelCol, wrapperCol, modalWidth, title, confirmLoading, visible, editable, showable } =
useFormEdit()
// 文件上传
const { tokenHeader, uploadAction } = useUpload('/union/pay/config/toBase64')
const { createMessage } = useMessage()
const { dictDropDown } = useDict()
// 表单
const formRef = $ref<FormInstance>()
let rawForm: any
let form = $ref<UnionPayConfig>({
id: null,
seller: '',
enable: false,
notifyUrl: '',
returnUrl: '',
payWays: [],
sandbox: false,
})
// 校验
const rules = computed(() => {
return {
machId: [{ required: true, message: '请输入商户号' }],
wxAppId: [{ required: true, message: '请输入应用编号' }],
// certSign: [{ required: true, message: '请选择是否为证书签名' }],
signType: [{ required: true, message: '请选择签名类型' }],
keyPrivateCert: [{ required: true, message: '请上传应用私钥证书' }],
keyPrivateCertPwd: [{ required: true, message: '请输入应用私钥证书密码' }],
acpMiddleCert: [{ required: true, message: '请上传中级证书' }],
acpRootCert: [{ required: true, message: '请上传根证书' }],
enable: [{ required: true, message: '请选择是否启用' }],
notifyUrl: [{ required: true, message: '请输入异步通知页面地址' }],
returnUrl: [{ required: true, message: '请输入同步通知页面地址' }],
apiVersion: [{ required: true, message: '请选择支付API版本' }],
apiKeyV2: [{ required: true, message: '请输入V2秘钥' }],
sandbox: [{ required: true, message: '请选择是否为沙箱环境' }],
payWays: [{ required: true, message: '请选择支持的支付类型' }],
} as Record<string, Rule[]>
})
let payWayList = $ref<LabeledValue[]>([])
let signTypeList = $ref<LabeledValue[]>([])
// 事件
const emits = defineEmits(['ok'])
/**
* 入口
*/
function init() {
visible.value = true
resetForm()
getInfo()
}
// 获取信息
async function getInfo() {
confirmLoading.value = true
findPayWayList().then(({ data }) => {
payWayList = data
})
getConfig().then(({ data }) => {
rawForm = { ...data }
form = data
confirmLoading.value = false
})
signTypeList = await dictDropDown('UnionPaySignType')
}
// 保存
function handleOk() {
formRef?.validate().then(async () => {
confirmLoading.value = true
await update({
...form,
...diffForm(rawForm, form, 'keyPrivateCert', 'keyPrivateCertPwd', 'acpMiddleCert', 'acpRootCert'),
}).finally(() => {
confirmLoading.value = false
})
handleCancel()
createMessage.success('保存成功')
emits('ok')
})
}
// 重置表单
function resetForm() {
nextTick(() => {
formRef?.resetFields()
})
}
/**
* 文件上传
*/
function handleChange(info, fieldName) {
// 上传完毕
if (info.file.status === 'done') {
const res = info.file.response
if (!res.code) {
form[fieldName] = res.data
createMessage.success(`${info.file.name} 上传成功!`)
} else {
createMessage.error(`${res.msg}`)
}
} else if (info.file.status === 'error') {
createMessage.error('上传失败')
}
}
defineExpose({
init,
})
</script>
<style lang="less" scoped></style>

View File

@@ -23,7 +23,7 @@
<a-input v-model:value="form.appSecret" :disabled="showable" placeholder="APPID对应的接口密码用于获取接口调用凭证时使用" />
</a-form-item>
<a-form-item label="是否启用" name="enable">
<a-switch v-model:checked="form.enable" />
<a-switch checked-children="启用" un-checked-children="停用" v-model:checked="form.enable" />
</a-form-item>
<a-form-item name="notifyUrl">
<template #label>

View File

@@ -78,7 +78,7 @@
<script lang="ts" setup>
import { computed, onMounted } from 'vue'
import { $ref } from 'vue/macros'
import { page, resetRefund, syncById } from "./RefundOrder.api";
import { page, resetRefund, syncById } from './RefundOrder.api'
import useTablePage from '/@/hooks/bootx/useTablePage'
import RefundOrderInfo from './RefundOrderInfo.vue'
import { VxeTable, VxeTableInstance, VxeToolbarInstance } from 'vxe-table'