feat: 支付宝商户应用管理页面及国际化

This commit is contained in:
DaxPay Dev
2026-06-14 10:35:25 +08:00
parent d944d9fb7b
commit d2aa6397b1
24 changed files with 415 additions and 110 deletions

View File

@@ -59,6 +59,17 @@ export const AlipayMchAppApi = {
saveKeyConfig(data: AlipayMchAppKeyConfig): Promise<Result<void>> {
return defHttp.post({ url: '/admin/alipay/mch-app/save-key-config', data });
},
/** 查询应用授权认证配置 */
findAuthConfigByAppId(appId: string): Promise<Result<AlipayMchAppAuthConfig>> {
return defHttp.get({
url: '/admin/alipay/mch-app/find-auth-config-by-app-id',
params: { appId },
});
},
/** 保存应用授权认证配置 */
saveAuthConfig(data: AlipayMchAppAuthConfig): Promise<Result<void>> {
return defHttp.post({ url: '/admin/alipay/mch-app/save-auth-config', data });
},
};
/** 支付宝通道商户应用 */
@@ -94,3 +105,17 @@ export interface AlipayMchAppKeyConfig {
/** AES 通信密钥 */
secretKey?: string;
}
/** 支付宝通道商户应用授权认证配置 */
export interface AlipayMchAppAuthConfig {
/** 应用 ID */
appId?: string;
/** 商户号 */
mchNo?: string;
/** 通道商户号 */
channelMchNo?: string;
/** 用户标识类型 */
userIdType?: string;
/** 授权回调地址 */
authCallbackUrl?: string;
}

View File

@@ -15,10 +15,6 @@
"payment": {
"title": "Payment Management",
"platform": "Platform Management",
"isvList": "ISV List",
"isvManage": "ISV Management",
"isvInfoManage": "ISV Info",
"isvEntityManage": "Entity Info",
"merchantList": "Merchant List",
"merchantManage": "Merchant Management",
"merchantChannelMerchant": "Merchant Channel Merchant",

View File

@@ -18,5 +18,6 @@
"aliAppIdPrefix": "App ID: {appId}",
"detailTitle": "{name} - App Management",
"tabBasicInfo": "Basic Info",
"tabKeyConfig": "Key Config"
"tabKeyConfig": "Key Config",
"tabAuthConfig": "Authorization"
}

View File

@@ -2,5 +2,7 @@
"title": "Alipay Direct Management",
"groupApp": "App Management",
"cardMchApp": "Alipay Channel Merchant Apps",
"cardMchAppDesc": "Manage Alipay channel merchant applications"
"cardMchAppDesc": "Manage Alipay channel merchant applications",
"cardAddMchApp": "Add Alipay App",
"cardAddMchAppDesc": "Create a new Alipay channel merchant app"
}

View File

@@ -1,5 +1,4 @@
{
"missingIsvNo": "Missing ISV number. Open from the ISV list",
"missingMchNo": "Missing merchant number. Open from the merchant list",
"missingAppContext": "Open channel routing from App Management",
"productDetail": {

View File

@@ -15,10 +15,6 @@
"payment": {
"title": "支付管理",
"platform": "平台管理",
"isvList": "服务商列表",
"isvManage": "服务商管理",
"isvInfoManage": "服务商信息",
"isvEntityManage": "主体信息",
"merchantList": "商户列表",
"merchantManage": "商户管理",
"merchantChannelMerchant": "商户通道商户",

View File

@@ -18,5 +18,6 @@
"aliAppIdPrefix": "AppId{appId}",
"detailTitle": "{name} - 应用管理",
"tabBasicInfo": "基础信息",
"tabKeyConfig": "密钥配置"
"tabKeyConfig": "密钥配置",
"tabAuthConfig": "授权认证"
}

View File

@@ -2,5 +2,7 @@
"title": "支付宝直连管理",
"groupApp": "应用管理",
"cardMchApp": "支付宝通道商户应用",
"cardMchAppDesc": "管理支付宝通道商户应用"
"cardMchAppDesc": "管理支付宝通道商户应用",
"cardAddMchApp": "添加支付宝应用",
"cardAddMchAppDesc": "创建新的支付宝通道商户应用"
}

View File

@@ -1,5 +1,4 @@
{
"missingIsvNo": "缺少服务商编号,请从服务商列表进入",
"missingMchNo": "缺少商户编号,请从商户列表进入",
"missingAppContext": "请从应用管理进入通道路由配置",
"productDetail": {

View File

@@ -1,13 +1,13 @@
<script lang="ts" setup>
import { computed, nextTick, ref } from 'vue';
import { useRouter } from 'vue-router';
import { $t } from '@vben/locales';
import { IconifyIcon } from '@vben-core/icons';
import { ChannelMerchantAlipayApi } from '#/api/payment/channelMerchant.api';
import ChannelLogo from '#/components/channel/ChannelLogo.vue';
import { channelI18nMap, channelNameMap } from '#/enums/payment';
import { ProductEnum } from '#/enums/payment/productEnum';
import { channelI18nMap, channelNameMap, productI18nMap, productNameMap } from '#/enums/payment';
import { useMessage } from '#/hooks/useMessage';
defineOptions({ name: 'AlipayDirectMchCreateConfig' });
@@ -17,7 +17,6 @@ const emit = defineEmits<{
(e: 'close'): void;
}>();
const router = useRouter();
const { message } = useMessage();
const mchNo = ref('');
@@ -30,6 +29,7 @@ const form = ref({
});
const visible = ref(false);
const createSuccess = ref(false);
const submitLoading = ref(false);
const channelDisplayName = computed(() => {
@@ -42,6 +42,17 @@ const channelDisplayName = computed(() => {
return channelNameMap[channel] || channel;
});
/** 支付产品展示名称,区分服务商/直连等模式 */
const productDisplayName = computed(() => {
const product = productCode.value;
if (!product) return '-';
const i18nKey = productI18nMap[product];
if (i18nKey) {
return $t(i18nKey);
}
return productNameMap[product] || product;
});
const rules = computed(() => ({
channelMerchantName: [{ required: true, message: $t('payment.merchant.channelMerchant.channelMerchantNameRequired') }],
alipayUserId: [{ required: true, message: $t('payment.channel.alipay.validation.alipayUserIdRequired') }],
@@ -52,6 +63,7 @@ function init(no: string, product: string, channel: string) {
productCode.value = product;
channelCode.value = channel;
visible.value = true;
createSuccess.value = false;
resetForm();
}
@@ -68,16 +80,9 @@ function handleSubmit() {
channel: 'alipay',
...form.value,
})
.then(({ data }) => {
.then(() => {
createSuccess.value = true;
message.success($t('payment.merchant.channelMerchant.createSuccess'));
router.push({
path: '/payment/merchant/channel-merchant/detail',
query: {
mchNo: mchNo.value,
id: data ? String(data) : '',
product: productCode.value || ProductEnum.ALIPAY,
},
});
})
.finally(() => {
submitLoading.value = false;
@@ -100,44 +105,61 @@ defineExpose({ init });
<template>
<div v-if="visible">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
:validate-trigger="['blur', 'change']"
>
<!-- 国际化支付产品 -->
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ channelDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->
<a-form-item :label="$t('payment.merchant.channelMerchant.channelMerchantName')" name="channelMerchantName">
<a-input
v-model:value="form.channelMerchantName"
:placeholder="$t('payment.merchant.channelMerchant.pleaseInputName')"
/>
</a-form-item>
<!-- 国际化支付宝商家用户 IDPID -->
<a-form-item :label="$t('payment.channel.alipay.alipayUserId')" name="alipayUserId">
<a-input
v-model:value="form.alipayUserId"
:placeholder="$t('payment.channel.alipay.validation.alipayUserIdRequired')"
/>
</a-form-item>
<div v-if="!createSuccess">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
:validate-trigger="['blur', 'change']"
>
<!-- 国际化支付产品 -->
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ productDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->
<a-form-item :label="$t('payment.merchant.channelMerchant.channelMerchantName')" name="channelMerchantName">
<a-input
v-model:value="form.channelMerchantName"
:placeholder="$t('payment.merchant.channelMerchant.pleaseInputName')"
/>
</a-form-item>
<!-- 国际化支付宝商家用户 IDPID -->
<a-form-item :label="$t('payment.channel.alipay.alipayUserId')" name="alipayUserId">
<a-input
v-model:value="form.alipayUserId"
:placeholder="$t('payment.channel.alipay.validation.alipayUserIdRequired')"
/>
</a-form-item>
<div class="flex justify-center gap-4 mt-8 pt-6 border-t border-border">
<a-button @click="handlePrev">
{{ $t('payment.merchant.channelMerchant.prevStep') }}
</a-button>
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
{{ $t('payment.merchant.channelMerchant.create') }}
</a-button>
<div class="flex justify-center gap-4 mt-8 pt-6 border-t border-border">
<a-button @click="handlePrev">
{{ $t('payment.merchant.channelMerchant.prevStep') }}
</a-button>
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
{{ $t('payment.merchant.channelMerchant.create') }}
</a-button>
</div>
</a-form>
</div>
<div v-else class="flex flex-col items-center justify-center py-12">
<div class="w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center mb-6">
<IconifyIcon icon="ant-design:check-circle-filled" class="text-4xl text-green-500" />
</div>
</a-form>
<div class="text-xl font-bold text-foreground mb-2">{{
$t('payment.merchant.channelMerchant.createSuccess')
}}</div>
<div class="text-sm text-muted-foreground mb-8">{{
$t('payment.merchant.channelMerchant.createSuccessDesc')
}}</div>
<a-button type="primary" @click="emit('close')">
{{ $t('payment.merchant.channelMerchant.closePage') }}
</a-button>
</div>
</div>
</template>

View File

@@ -8,7 +8,7 @@ import { IconifyIcon } from '@vben-core/icons';
import { type AlipayIsvApp, AlipayIsvAppApi } from '#/api/payment/alipayIsvApp.api';
import { ChannelMerchantAlipayApi } from '#/api/payment/channelMerchant.api';
import ChannelLogo from '#/components/channel/ChannelLogo.vue';
import { channelI18nMap, channelNameMap } from '#/enums/payment';
import { channelI18nMap, channelNameMap, productI18nMap, productNameMap } from '#/enums/payment';
import { useMessage } from '#/hooks/useMessage';
defineOptions({ name: 'AlipayMchCreateConfig' });
@@ -21,7 +21,6 @@ const emit = defineEmits<{
const { message } = useMessage();
const mchNo = ref('');
const isvNo = ref('');
const productCode = ref('');
const channelCode = ref('');
const formRef = ref();
@@ -49,6 +48,17 @@ const channelDisplayName = computed(() => {
return channelNameMap[channel] || channel;
});
/** 支付产品展示名称,区分服务商/直连等模式 */
const productDisplayName = computed(() => {
const product = productCode.value;
if (!product) return '-';
const i18nKey = productI18nMap[product];
if (i18nKey) {
return $t(i18nKey);
}
return productNameMap[product] || product;
});
const rules = computed(() => ({
channelMerchantName: [{ required: true, message: $t('payment.merchant.channelMerchant.channelMerchantNameRequired') }],
isvAppId: [{ required: true, message: $t('payment.merchant.channelMerchant.alipayIsvAppRequired') }],
@@ -64,13 +74,10 @@ const isvAppOptions = computed(() =>
})),
);
/** 加载支付宝服务商应用下拉列表(开源版全局共享,无服务商号隔离) */
function loadIsvAppList() {
if (!isvNo.value) {
isvAppList.value = [];
return;
}
isvAppLoading.value = true;
AlipayIsvAppApi.listByIsvNo(isvNo.value)
AlipayIsvAppApi.listAll()
.then(({ data }) => {
isvAppList.value = data || [];
})
@@ -79,9 +86,8 @@ function loadIsvAppList() {
});
}
function init(no: string, product: string, channel: string, isv: string) {
function init(no: string, product: string, channel: string) {
mchNo.value = no;
isvNo.value = isv;
productCode.value = product;
channelCode.value = channel;
visible.value = true;
@@ -163,7 +169,7 @@ defineExpose({ init, validate, getData, submit });
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ channelDisplayName }}</span>
<span class="text-foreground">{{ productDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->

View File

@@ -9,6 +9,7 @@
import type { ChannelMerchantResult } from '#/api/payment/channelMerchant.api';
import AlipayChannelMerchantBasicInfo from './AlipayChannelMerchantBasicInfo.vue';
import AlipayMchAppEdit from './app/AlipayMchAppEdit.vue';
defineOptions({ name: 'AlipayMchManage' });
@@ -18,6 +19,7 @@
const channelMchNo = ref('');
const channelMerchant = ref<ChannelMerchantResult>({});
const basicInfoRef = ref<InstanceType<typeof AlipayChannelMerchantBasicInfo>>();
const mchAppEditRef = ref<InstanceType<typeof AlipayMchAppEdit>>();
/** 功能卡片配置 */
const functionCards = computed(() => [
@@ -40,6 +42,14 @@
group: $t('payment.channel.alipayMchManage.groupApp'),
color: 'blue',
cards: [
{
key: 'addMchApp',
// 国际化:添加支付宝应用
title: $t('payment.channel.alipayMchManage.cardAddMchApp'),
icon: 'ant-design:plus-circle-outlined',
// 国际化:创建新的支付宝通道商户应用
description: $t('payment.channel.alipayMchManage.cardAddMchAppDesc'),
},
{
key: 'mchApp',
// 国际化:通道商户应用
@@ -78,21 +88,33 @@
channelMerchant.value = summary;
}
/** 添加应用成功,跳转到应用管理页 */
function handleAddAppOk() {
router.push({
path: '/payment/merchant/channel-merchant/alipay-app-manage',
query: {
mchNo: mchNo.value,
channelMchNo: channelMchNo.value,
},
});
}
/** 卡片点击 */
function handleCardClick(card: { key: string; route?: string }) {
if (card.key === 'basicInfo') {
basicInfoRef.value?.open();
return;
}
if (card.key === 'addMchApp') {
mchAppEditRef.value?.show(mchNo.value, channelMchNo.value);
return;
}
if (card.route) {
router.push({
path: card.route,
query: {
mchNo: mchNo.value,
channelMchNo: channelMchNo.value,
channelMerchantName: channelMerchant.value.channelMerchantName || '',
channelMerchantId: channelMerchant.value.id || '',
product: channelMerchant.value.product || '',
},
});
}
@@ -154,6 +176,7 @@
:channel-mch-no="channelMchNo"
:channel-merchant="channelMerchant"
/>
<AlipayMchAppEdit ref="mchAppEditRef" @ok="handleAddAppOk" />
</div>
</template>

View File

@@ -5,6 +5,7 @@
import { AlipayMchAppApi, type AlipayMchApp } from '#/api/payment/alipayMchApp.api';
import AlipayMchAppAuthConfig from './tabs/AlipayMchAppAuthConfig.vue';
import AlipayMchAppBasicInfo from './tabs/AlipayMchAppBasicInfo.vue';
import AlipayMchAppKeyConfig from './tabs/AlipayMchAppKeyConfig.vue';
@@ -77,6 +78,13 @@
:ali-app-id="appDetail.aliAppId"
/>
</a-tab-pane>
<a-tab-pane key="auth" :tab="$t('payment.channel.alipayMchApp.tabAuthConfig')">
<AlipayMchAppAuthConfig
:app-id="appDetail.id!"
:mch-no="mchNo"
:channel-mch-no="channelMchNo"
/>
</a-tab-pane>
</a-tabs>
</a-spin>
</a-modal>

View File

@@ -82,18 +82,21 @@
channelMerchantName.value = normalizeRouteQueryValue(route.query.channelMerchantName);
}
/** 返回通道商户详情 */
/** 返回通道商户详情或列表 */
function handleBack() {
const id = normalizeRouteQueryValue(route.query.channelMerchantId);
const product = normalizeRouteQueryValue(route.query.product) || ProductEnum.ALIPAY;
router.push({
path: '/payment/merchant/channel-merchant/detail',
query: {
mchNo: mchNo.value,
id,
product,
},
});
if (id) {
const product = normalizeRouteQueryValue(route.query.product) || ProductEnum.ALIPAY;
router.push({
path: '/payment/merchant/channel-merchant/detail',
query: { mchNo: mchNo.value, id, product },
});
} else {
router.push({
path: '/payment/merchant/channel-merchant',
query: { mchNo: mchNo.value },
});
}
}
function handleAdd() {

View File

@@ -0,0 +1,190 @@
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { $t } from '@vben/locales';
import { AlipayMchAppApi, type AlipayMchAppAuthConfig } from '#/api/payment/alipayMchApp.api';
import { PermCodes } from '#/constants/perm-codes';
import { useFormEdit } from '#/hooks/useFormEdit';
import { useMessage } from '#/hooks/useMessage';
import { usePermission } from '#/hooks/usePermission';
const props = defineProps<{
appId?: string;
mchNo?: string;
channelMchNo?: string;
}>();
const { labelCol, wrapperCol } = useFormEdit();
const { confirm, message } = useMessage();
const { hasPermission } = usePermission();
const loading = ref(false);
const saving = ref(false);
// 编辑模式
const isEditing = ref(false);
const formRef = ref();
const formState = ref<AlipayMchAppAuthConfig>({
userIdType: 'openid',
});
const canEdit = computed(() => hasPermission(PermCodes.Payment.ChannelMerchant.EDIT));
/** 表单校验规则 */
const formRules = computed(() => ({
userIdType: [{ required: true, message: $t('payment.channel.alipayIsv.validation.userIdType') }],
authCallbackUrl: [
{ required: true, message: $t('payment.channel.alipayIsv.validation.authCallbackUrl') },
],
}));
/**
* 加载授权认证配置
*/
function loadConfig() {
if (!props.appId) return;
loading.value = true;
AlipayMchAppApi.findAuthConfigByAppId(props.appId)
.then(({ data }) => {
formState.value = {
userIdType: 'openid',
...data,
appId: props.appId,
mchNo: props.mchNo,
channelMchNo: props.channelMchNo,
};
})
.finally(() => {
loading.value = false;
});
}
/**
* 进入编辑模式
*/
function handleEdit() {
isEditing.value = true;
}
/**
* 取消编辑
*/
function handleCancel() {
confirm({
title: $t('common.confirm'),
content: $t('common.confirmCancelContent'),
okText: $t('common.okText'),
cancelText: $t('common.cancelText'),
onOk() {
loadConfig();
isEditing.value = false;
},
});
}
/**
* 保存配置
*/
async function handleSave() {
await formRef.value?.validate();
confirm({
title: $t('common.confirm'),
content: $t('common.confirmSaveContent'),
okText: $t('common.okText'),
cancelText: $t('common.cancelText'),
onOk() {
saving.value = true;
const submitData: AlipayMchAppAuthConfig = {
...formState.value,
appId: props.appId,
mchNo: props.mchNo,
channelMchNo: props.channelMchNo,
};
return AlipayMchAppApi.saveAuthConfig(submitData)
.then(() => {
message.success($t('common.saveSuccess'));
isEditing.value = false;
loadConfig();
})
.finally(() => {
saving.value = false;
});
},
});
}
watch(
() => props.appId,
(appId) => {
if (appId) {
isEditing.value = false;
loadConfig();
}
},
{ immediate: true },
);
</script>
<template>
<div class="auth-config-panel">
<a-card variant="borderless" class="rounded-xl shadow-sm">
<template #title>
<!-- 国际化授权认证配置 -->
<span class="text-base font-semibold text-foreground">
{{ $t('payment.channel.alipayIsv.authConfigTitle') }}
</span>
</template>
<template #extra>
<template v-if="canEdit && !isEditing">
<!-- 国际化编辑 -->
<a-button type="primary" @click="handleEdit">{{ $t('common.edit') }}</a-button>
</template>
<template v-else-if="canEdit && isEditing">
<a-space>
<a-button @click="handleCancel">{{ $t('common.cancelText') }}</a-button>
<a-button type="primary" :loading="saving" @click="handleSave">
{{ $t('common.save') }}
</a-button>
</a-space>
</template>
</template>
<a-spin :spinning="loading">
<a-form
ref="formRef"
:model="formState"
:rules="formRules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
:validate-trigger="['blur', 'change']"
class="form-compact max-w-3xl"
>
<!-- 用户标识类型 -->
<a-form-item :label="$t('payment.channel.alipayIsv.userIdType')" name="userIdType">
<a-radio-group v-model:value="formState.userIdType" :disabled="!isEditing">
<a-radio value="openid">{{ $t('payment.channel.alipayIsv.userIdTypeOpenid') }}</a-radio>
<a-radio value="userid">{{ $t('payment.channel.alipayIsv.userIdTypeUserid') }}</a-radio>
<a-radio value="openid_userid">{{ $t('payment.channel.alipayIsv.userIdTypeBoth') }}</a-radio>
</a-radio-group>
</a-form-item>
<!-- 授权回调地址 -->
<a-form-item :label="$t('payment.channel.alipayIsv.authCallbackUrl')" name="authCallbackUrl">
<a-input
v-model:value="formState.authCallbackUrl"
:placeholder="$t('payment.channel.alipayIsv.authCallbackUrlPlaceholder')"
:disabled="!isEditing"
allow-clear
/>
</a-form-item>
</a-form>
</a-spin>
</a-card>
</div>
</template>
<style scoped>
.auth-config-panel {
padding: 4px 0;
}
</style>

View File

@@ -7,7 +7,7 @@ import { IconifyIcon } from '@vben-core/icons';
import { ChannelMerchantDouyinApi } from '#/api/payment/channelMerchant.api';
import ChannelLogo from '#/components/channel/ChannelLogo.vue';
import { channelI18nMap, channelNameMap } from '#/enums/payment';
import { channelI18nMap, channelNameMap, productI18nMap, productNameMap } from '#/enums/payment';
import { useMessage } from '#/hooks/useMessage';
defineOptions({ name: 'DouyinMchCreateConfig' });
@@ -44,6 +44,17 @@ const channelDisplayName = computed(() => {
return channelNameMap[channel] || channel;
});
/** 支付产品展示名称,区分服务商/直连等模式 */
const productDisplayName = computed(() => {
const product = productCode.value;
if (!product) return '-';
const i18nKey = productI18nMap[product];
if (i18nKey) {
return $t(i18nKey);
}
return productNameMap[product] || product;
});
const rules = computed(() => ({
channelMerchantName: [{ required: true, message: $t('payment.merchant.channelMerchant.channelMerchantNameRequired') }],
douyinMchId: [{ required: true, message: $t('payment.channel.douyin.validation.douyinMchIdRequired') }],
@@ -113,7 +124,7 @@ defineExpose({ init });
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ channelDisplayName }}</span>
<span class="text-foreground">{{ productDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->

View File

@@ -7,7 +7,7 @@ import { IconifyIcon } from '@vben-core/icons';
import { ChannelMerchantLakalaApi } from '#/api/payment/channelMerchant.api';
import ChannelLogo from '#/components/channel/ChannelLogo.vue';
import { channelI18nMap, channelNameMap } from '#/enums/payment';
import { channelI18nMap, channelNameMap, productI18nMap, productNameMap } from '#/enums/payment';
import { useMessage } from '#/hooks/useMessage';
defineOptions({ name: 'LakalaMchCreateConfig' });
@@ -42,6 +42,17 @@ const channelDisplayName = computed(() => {
return channelNameMap[channel] || channel;
});
/** 支付产品展示名称,区分服务商/直连等模式 */
const productDisplayName = computed(() => {
const product = productCode.value;
if (!product) return '-';
const i18nKey = productI18nMap[product];
if (i18nKey) {
return $t(i18nKey);
}
return productNameMap[product] || product;
});
const rules = computed(() => ({
channelMerchantName: [{ required: true, message: $t('payment.merchant.channelMerchant.channelMerchantNameRequired') }],
termNo: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.termNo') }],
@@ -123,7 +134,7 @@ defineExpose({ init, validate, getData, submit });
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ channelDisplayName }}</span>
<span class="text-foreground">{{ productDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->

View File

@@ -7,7 +7,7 @@ import { IconifyIcon } from '@vben-core/icons';
import { ChannelMerchantWechatApi } from '#/api/payment/channelMerchant.api';
import ChannelLogo from '#/components/channel/ChannelLogo.vue';
import { channelI18nMap, channelNameMap } from '#/enums/payment';
import { channelI18nMap, channelNameMap, productI18nMap, productNameMap } from '#/enums/payment';
import { useMessage } from '#/hooks/useMessage';
defineOptions({ name: 'WechatMchCreateConfig' });
@@ -42,6 +42,17 @@ const channelDisplayName = computed(() => {
return channelNameMap[channel] || channel;
});
/** 支付产品展示名称,区分服务商/直连等模式 */
const productDisplayName = computed(() => {
const product = productCode.value;
if (!product) return '-';
const i18nKey = productI18nMap[product];
if (i18nKey) {
return $t(i18nKey);
}
return productNameMap[product] || product;
});
/** 是否为微信直连产品 */
const isDirectProduct = computed(() => productCode.value === 'wechat_pay');
@@ -145,7 +156,7 @@ defineExpose({ init, validate, getData, submit });
<a-form-item :label="$t('payment.merchant.channelMerchant.product')">
<div class="flex items-center gap-2 h-8">
<ChannelLogo v-if="channelCode" :channel="channelCode" :size="24" />
<span class="text-foreground">{{ channelDisplayName }}</span>
<span class="text-foreground">{{ productDisplayName }}</span>
</div>
</a-form-item>
<!-- 国际化商户名称 -->

View File

@@ -89,9 +89,6 @@
query: {
mchNo: mchNo.value,
channelMchNo: channelMchNo.value,
channelMerchantName: channelMerchant.value.channelMerchantName || '',
channelMerchantId: channelMerchant.value.id || '',
product: channelMerchant.value.product || '',
},
});
return;

View File

@@ -83,17 +83,21 @@
channelMerchantName.value = normalizeRouteQueryValue(route.query.channelMerchantName);
}
/** 返回通道商户详情或列表 */
function handleBack() {
const id = normalizeRouteQueryValue(route.query.channelMerchantId);
const product = normalizeRouteQueryValue(route.query.product) || ProductEnum.WECHAT_ISV;
router.push({
path: '/payment/merchant/channel-merchant/detail',
query: {
mchNo: mchNo.value,
id,
product,
},
});
if (id) {
const product = normalizeRouteQueryValue(route.query.product) || ProductEnum.WECHAT_ISV;
router.push({
path: '/payment/merchant/channel-merchant/detail',
query: { mchNo: mchNo.value, id, product },
});
} else {
router.push({
path: '/payment/merchant/channel-merchant',
query: { mchNo: mchNo.value },
});
}
}
function handleAdd() {

View File

@@ -126,7 +126,6 @@
selectedProduct.value!.code!,
mchNo.value,
selectedProduct.value!.channel || '',
'',
selectedProduct.value!.name || getProductName(selectedProduct.value!.code || ''),
);
});

View File

@@ -39,12 +39,12 @@ function isUmsProduct(product: string) {
/**
* 初始化对应通道的配置组件
*/
function init(product: string, mchNo: string, channel: string, isvNo = '', productName = '') {
function init(product: string, mchNo: string, channel: string, productName = '') {
currentProduct.value = { product, channel };
nextTick(() => {
switch (product) {
case ProductEnum.ALIPAY_ISV: {
alipayRef.value?.init(mchNo, product, channel, isvNo);
alipayRef.value?.init(mchNo, product, channel);
break;
}
case ProductEnum.ALIPAY: {

View File

@@ -240,7 +240,7 @@
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: loadList }">
<template #buttons>
<a-button v-if="hasPermission(PermCodes.Payment.ChannelMerchant.ADD)" type="primary" @click="handleCreate">
<a-button v-if="hasPermission(PermCodes.Payment.ChannelMerchant.EDIT)" type="primary" @click="handleCreate">
<template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
{{ $t('payment.merchant.channelMerchant.create') }}
</a-button>

View File

@@ -128,7 +128,6 @@
<vxe-column field="mchNo" :title="$t('payment.merchant.base.field.mchNo')" :min-width="180" />
<vxe-column field="mchName" :title="$t('payment.merchant.base.field.mchName')" :min-width="200" />
<vxe-column field="mchShortName" :title="$t('payment.merchant.base.field.mchShortName')" :min-width="150" />
<vxe-column field="isvName" :title="$t('payment.merchant.base.field.isvName')" :min-width="150" />
<vxe-column
field="subjectType"
:title="$t('payment.merchant.base.field.subjectType')"