mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-12 23:45:39 +08:00
feat(lakala): 拉卡拉服务商配置与通道商户管理(管理端)
- 新增拉卡拉服务商配置管理页(LakalaManage)与编辑页调整 - api 补充通道商户/支付配置接口, locale 中英文同步 - perm-codes 新增 payment:lakala:isv 权限码 - ProductDetailDispatch 分发 lakala_pay 至拉卡拉管理页
This commit is contained in:
@@ -3,13 +3,50 @@ import type { Result } from '#/types/web';
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 拉卡拉通道商户创建 API
|
||||
* 拉卡拉通道商户绑定 API
|
||||
*/
|
||||
export const ChannelMerchantLakalaApi = {
|
||||
export const LakalaChannelMerchantApi = {
|
||||
/**
|
||||
* 根据通道商户号查询拉卡拉通道商户配置
|
||||
*/
|
||||
findByChannelMchNo(channelMchNo: string): Promise<Result<LakalaIsvChannelMerchant>> {
|
||||
return defHttp.get({
|
||||
url: '/admin/lakala/isv-channel-merchant/find-by-channel-mch-no',
|
||||
params: { channelMchNo },
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 创建拉卡拉通道商户
|
||||
*/
|
||||
create(data: Record<string, any>): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/lakala/channel-merchant/create', data });
|
||||
create(data: LakalaIsvChannelMerchantCreateParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/lakala/isv-channel-merchant/create', data });
|
||||
},
|
||||
};
|
||||
|
||||
/** 拉卡拉通道商户绑定 */
|
||||
export interface LakalaIsvChannelMerchant {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 通道商户号 */
|
||||
channelMchNo?: string;
|
||||
/** 所属支付产品 */
|
||||
product?: string;
|
||||
/** 拉卡拉商户编号 */
|
||||
lakalaMchNo?: string;
|
||||
/** 终端号 */
|
||||
termNo?: string;
|
||||
}
|
||||
|
||||
/** 拉卡拉通道商户创建参数 */
|
||||
export interface LakalaIsvChannelMerchantCreateParam {
|
||||
/** 商户号 */
|
||||
mchNo: string;
|
||||
/** 通道商户名称 */
|
||||
channelMerchantName: string;
|
||||
/** 所属支付产品 */
|
||||
product: string;
|
||||
/** 拉卡拉商户编号 */
|
||||
lakalaMchNo: string;
|
||||
/** 终端号 */
|
||||
termNo?: string;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
import type { MchEntity, Result } from '#/types/web';
|
||||
import type { Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 拉卡拉产品支付配置 API
|
||||
* 拉卡拉服务商密钥配置 API
|
||||
*/
|
||||
export const LakalaPayConfigApi = {
|
||||
/**
|
||||
* 查询拉卡拉产品配置详情
|
||||
* 查询拉卡拉服务商密钥配置
|
||||
* 平台为唯一服务商,密钥全局唯一
|
||||
*/
|
||||
findConfig(isvNo: string, product: string, sandbox: boolean): Promise<Result<LakalaProductConfig>> {
|
||||
return defHttp.get({ url: '/admin/lakala/product-pay-config/find-config', params: { isvNo, product, sandbox } });
|
||||
findConfig(product: string): Promise<Result<LakalaIsvKeyConfig>> {
|
||||
return defHttp.get({ url: '/admin/lakala/isv-key-config/find-config', params: { product } });
|
||||
},
|
||||
/**
|
||||
* 保存拉卡拉产品配置
|
||||
* 保存拉卡拉服务商密钥配置
|
||||
*/
|
||||
saveConfig(data: LakalaProductConfig): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/lakala/product-pay-config/save-config', data });
|
||||
saveConfig(data: LakalaIsvKeyConfig): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/lakala/isv-key-config/save-config', data });
|
||||
},
|
||||
};
|
||||
|
||||
/** 拉卡拉产品支付配置 */
|
||||
export interface LakalaProductConfig extends MchEntity {
|
||||
/** 是否启用 */
|
||||
enable?: boolean;
|
||||
/** 沙箱环境 */
|
||||
sandbox?: boolean;
|
||||
/** 拉卡拉服务商密钥配置 */
|
||||
export interface LakalaIsvKeyConfig {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 产品编码 */
|
||||
product?: string;
|
||||
/** 拉卡拉应用编号 */
|
||||
lklAppId?: string;
|
||||
/** 商户证书序列号 */
|
||||
mchSerialNo?: string;
|
||||
/** 私钥 */
|
||||
/** 商户RSA私钥(PEM) */
|
||||
privateKey?: string;
|
||||
/** 公钥 */
|
||||
/** 拉卡拉RSA公钥证书(PEM) */
|
||||
publicKey?: string;
|
||||
/** sm4密钥 */
|
||||
/** SM4密钥(进件敏感字段加密用) */
|
||||
sm4Key?: string;
|
||||
/** 产品类型 */
|
||||
product?: string;
|
||||
/** 通道 */
|
||||
channel?: string;
|
||||
/** 是否沙箱环境 */
|
||||
sandbox?: boolean;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,11 @@ export const PermCodes = {
|
||||
VIEW: 'payment:wechat:isv:view',
|
||||
MANAGE: 'payment:wechat:isv:manage',
|
||||
},
|
||||
/** 拉卡拉服务商 menuCode=payment:lakala:isv */
|
||||
Lakala: {
|
||||
VIEW: 'payment:lakala:isv:view',
|
||||
MANAGE: 'payment:lakala:isv:manage',
|
||||
},
|
||||
/** 支付主数据 menuCode=payment:platform:* */
|
||||
Platform: {
|
||||
Product: {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"title": "Lakala ISV Configuration",
|
||||
"configTitle": "Lakala ISV Key Configuration",
|
||||
"manageTitle": "Lakala ISV Management",
|
||||
"basicConfig": "Basic Configuration",
|
||||
"keyConfig": "Key Configuration",
|
||||
"advancedConfig": "Advanced Configuration",
|
||||
"enable": "Enable",
|
||||
"lklAppId": "Lakala Application ID",
|
||||
"lklAppIdPlaceholder": "Please enter Lakala application ID",
|
||||
@@ -10,9 +13,15 @@
|
||||
"privateKey": "Private Key Certificate",
|
||||
"publicKey": "Public Key Certificate",
|
||||
"sm4Key": "SM4 Key",
|
||||
"sm4KeyPlaceholder": "Please enter SM4 key",
|
||||
"sm4KeyPlaceholder": "Please enter SM4 key (for onboarding sensitive fields, optional)",
|
||||
"uploadPublicKey": "Upload Public Key Certificate",
|
||||
"uploadPrivateKey": "Upload Private Key Certificate",
|
||||
"uploadKey": "Upload Private Key",
|
||||
"sandbox": "Sandbox Environment",
|
||||
"sandboxOn": "Sandbox",
|
||||
"sandboxOff": "Production",
|
||||
"cardMchKey": "ISV Key",
|
||||
"cardMchKeyDesc": "Configure Lakala ISV application ID, private/public key certificates",
|
||||
"validation": {
|
||||
"enable": "Please select whether to enable",
|
||||
"lklAppId": "Please enter Lakala application ID",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"title": "拉卡拉服务商配置",
|
||||
"configTitle": "拉卡拉服务商密钥配置",
|
||||
"manageTitle": "拉卡拉服务商管理",
|
||||
"basicConfig": "基础配置",
|
||||
"keyConfig": "密钥配置",
|
||||
"advancedConfig": "高级配置",
|
||||
"enable": "是否启用",
|
||||
"lklAppId": "拉卡拉应用编号",
|
||||
"lklAppIdPlaceholder": "请输入拉卡拉应用编号",
|
||||
@@ -10,9 +13,15 @@
|
||||
"privateKey": "私钥证书",
|
||||
"publicKey": "公钥证书",
|
||||
"sm4Key": "SM4密钥",
|
||||
"sm4KeyPlaceholder": "请输入SM4密钥",
|
||||
"sm4KeyPlaceholder": "请输入SM4密钥(进件敏感字段加密用, 选填)",
|
||||
"uploadPublicKey": "上传公钥证书",
|
||||
"uploadPrivateKey": "上传私钥证书",
|
||||
"uploadKey": "上传私钥",
|
||||
"sandbox": "沙箱环境",
|
||||
"sandboxOn": "沙箱",
|
||||
"sandboxOff": "生产",
|
||||
"cardMchKey": "服务商密钥",
|
||||
"cardMchKeyDesc": "配置拉卡拉服务商应用编号、私钥公钥证书",
|
||||
"validation": {
|
||||
"enable": "请选择是否启用",
|
||||
"lklAppId": "请输入拉卡拉应用编号",
|
||||
|
||||
@@ -5,11 +5,16 @@
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import { LakalaPayConfigApi, type LakalaProductConfig } from '#/api/payment/channel/lakala/pay-config.api';
|
||||
import { type IsvProductPayConfigResult } from '#/api/payment/config/pay-product-config.api';
|
||||
import {
|
||||
type LakalaIsvKeyConfig,
|
||||
LakalaPayConfigApi,
|
||||
} from '#/api/payment/channel/lakala/pay-config.api';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { ProductEnum } from '#/enums/payment/productEnum';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { readFileAsText } from '#/utils/file';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
import { readFileAsBase64 } from '#/utils/file';
|
||||
|
||||
defineOptions({ name: 'LakalaIsvConfigEdit' });
|
||||
|
||||
@@ -17,33 +22,27 @@
|
||||
(e: 'saved'): void;
|
||||
}>();
|
||||
|
||||
const { labelCol, wrapperCol, confirmLoading, visible, showable, handleCancel, diffForm } = useFormEdit();
|
||||
const { labelCol, wrapperCol, confirmLoading, visible, handleCancel, diffForm } = useFormEdit();
|
||||
|
||||
const { message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const isvNo = ref('');
|
||||
const productCode = ref('');
|
||||
const sandboxEnv = ref(false);
|
||||
const formRef = ref();
|
||||
const form = ref<LakalaProductConfig>({} as LakalaProductConfig);
|
||||
const form = ref<LakalaIsvKeyConfig>({} as LakalaIsvKeyConfig);
|
||||
let rawForm: Record<string, any> = {};
|
||||
|
||||
const drawerTitle = computed(() => {
|
||||
return $t('payment.channel.lakalaIsv.title');
|
||||
});
|
||||
const canEdit = computed(() => hasPermission(PermCodes.Payment.Lakala.MANAGE));
|
||||
|
||||
const rules = computed(() => ({
|
||||
enable: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.enable') }],
|
||||
const drawerTitle = $t('payment.channel.lakalaIsv.configTitle');
|
||||
|
||||
const rules = {
|
||||
lklAppId: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.lklAppId') }],
|
||||
mchSerialNo: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.mchSerialNo') }],
|
||||
privateKey: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.privateKey') }],
|
||||
publicKey: [{ required: true, message: $t('payment.channel.lakalaIsv.validation.publicKey') }],
|
||||
}));
|
||||
};
|
||||
|
||||
function init(no: string, _configInfo: IsvProductPayConfigResult, sandbox: boolean) {
|
||||
isvNo.value = no;
|
||||
productCode.value = _configInfo.product || '';
|
||||
sandboxEnv.value = sandbox;
|
||||
/** 打开抽屉并加载拉卡拉服务商密钥配置(平台为唯一服务商,密钥全局唯一) */
|
||||
function init() {
|
||||
visible.value = true;
|
||||
resetForm();
|
||||
loadConfig();
|
||||
@@ -51,16 +50,13 @@
|
||||
|
||||
function loadConfig() {
|
||||
confirmLoading.value = true;
|
||||
LakalaPayConfigApi.findConfig(isvNo.value, productCode.value, sandboxEnv.value)
|
||||
LakalaPayConfigApi.findConfig(ProductEnum.LAKALA_PAY)
|
||||
.then(({ data }) => {
|
||||
rawForm = { ...data };
|
||||
form.value = {
|
||||
isvNo: isvNo.value,
|
||||
product: productCode.value,
|
||||
channel: 'lakala',
|
||||
sandbox: sandboxEnv.value,
|
||||
product: ProductEnum.LAKALA_PAY,
|
||||
...data,
|
||||
} as LakalaProductConfig;
|
||||
} as LakalaIsvKeyConfig;
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
@@ -72,11 +68,17 @@
|
||||
confirmLoading.value = true;
|
||||
LakalaPayConfigApi.saveConfig({
|
||||
...form.value,
|
||||
...diffForm(rawForm, form.value, 'privateKey', 'publicKey', 'sm4Key'),
|
||||
isvNo: isvNo.value,
|
||||
product: productCode.value,
|
||||
channel: 'lakala',
|
||||
sandbox: sandboxEnv.value,
|
||||
...diffForm(
|
||||
rawForm,
|
||||
form.value,
|
||||
'lklAppId',
|
||||
'mchSerialNo',
|
||||
'privateKey',
|
||||
'publicKey',
|
||||
'sm4Key',
|
||||
'sandbox',
|
||||
),
|
||||
product: ProductEnum.LAKALA_PAY,
|
||||
})
|
||||
.then(() => {
|
||||
message.success($t('common.saveSuccess'));
|
||||
@@ -92,19 +94,13 @@
|
||||
function handleUpload(info: { file: File }, fieldName: string) {
|
||||
const file = info.file;
|
||||
if (!file) return;
|
||||
readFileAsText(file).then((content) => {
|
||||
readFileAsBase64(file).then((content) => {
|
||||
(form.value as Record<string, any>)[fieldName] = content;
|
||||
message.success($t('components.upload.uploadSuccess', { name: file.name }));
|
||||
formRef.value?.validateFields(fieldName);
|
||||
});
|
||||
}
|
||||
|
||||
function truncateContent(content: string, maxLength = 500): string {
|
||||
if (!content) return '';
|
||||
if (content.length <= maxLength) return content;
|
||||
return content.slice(0, Math.max(0, maxLength)) + '...';
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
nextTick(() => {
|
||||
formRef.value?.resetFields();
|
||||
@@ -121,6 +117,7 @@
|
||||
size="large"
|
||||
:styles="{ footer: { textAlign: 'right' } }"
|
||||
:mask-closable="false"
|
||||
destroy-on-hidden
|
||||
@close="handleCancel"
|
||||
>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
@@ -134,19 +131,10 @@
|
||||
>
|
||||
<a-divider orientation="left">{{ $t('payment.channel.lakalaIsv.basicConfig') }}</a-divider>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.enable')" name="enable">
|
||||
<a-switch
|
||||
v-model:checked="form.enable"
|
||||
:disabled="showable"
|
||||
:checked-children="$t('common.enable')"
|
||||
:un-checked-children="$t('common.disable')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.lklAppId')" name="lklAppId">
|
||||
<a-input
|
||||
v-model:value="form.lklAppId"
|
||||
:disabled="showable"
|
||||
:disabled="!canEdit"
|
||||
:placeholder="$t('payment.channel.lakalaIsv.lklAppIdPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -154,79 +142,86 @@
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.mchSerialNo')" name="mchSerialNo">
|
||||
<a-input
|
||||
v-model:value="form.mchSerialNo"
|
||||
:disabled="showable"
|
||||
:disabled="!canEdit"
|
||||
:placeholder="$t('payment.channel.lakalaIsv.mchSerialNoPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-divider orientation="left">{{ $t('payment.channel.lakalaIsv.keyConfig') }}</a-divider>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.privateKey')" name="privateKey">
|
||||
<a-upload
|
||||
v-if="!form.privateKey"
|
||||
:disabled="!canEdit"
|
||||
:multiple="false"
|
||||
:show-upload-list="false"
|
||||
accept=".pem,.key,.txt"
|
||||
:before-upload="() => false"
|
||||
@change="(info: any) => handleUpload(info, 'privateKey')"
|
||||
>
|
||||
<a-button :disabled="!canEdit">
|
||||
<template #icon><IconifyIcon icon="ant-design:upload-outlined" class="text-lg" /></template>
|
||||
{{ $t('payment.channel.lakalaIsv.uploadKey') }}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-input v-else value="private_key.pem" disabled>
|
||||
<template #suffix>
|
||||
<span v-if="canEdit" class="cursor-pointer text-gray-400" @click="form.privateKey = ''">
|
||||
<IconifyIcon icon="ant-design:close-circle-outlined" class="text-lg" />
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.publicKey')" name="publicKey">
|
||||
<a-upload
|
||||
v-if="!form.publicKey"
|
||||
:disabled="showable"
|
||||
:disabled="!canEdit"
|
||||
:multiple="false"
|
||||
:show-upload-list="false"
|
||||
accept=".cer"
|
||||
accept=".pem,.cer,.crt,.txt"
|
||||
:before-upload="() => false"
|
||||
@change="(info: any) => handleUpload(info, 'publicKey')"
|
||||
>
|
||||
<a-button :disabled="showable">
|
||||
<a-button :disabled="!canEdit">
|
||||
<template #icon><IconifyIcon icon="ant-design:upload-outlined" class="text-lg" /></template>
|
||||
{{ $t('payment.channel.lakalaIsv.uploadPublicKey') }}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-tooltip v-else :title="truncateContent(form.publicKey)" placement="top" :mouse-enter-delay="0.3">
|
||||
<a-input value="publicKey.cer" disabled>
|
||||
<template #suffix>
|
||||
<span v-if="!showable" class="cursor-pointer text-gray-400" @click="form.publicKey = ''">
|
||||
<IconifyIcon icon="ant-design:close-circle-outlined" class="text-lg" />
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-tooltip>
|
||||
<a-input v-else value="lakala_public_key.pem" disabled>
|
||||
<template #suffix>
|
||||
<span v-if="canEdit" class="cursor-pointer text-gray-400" @click="form.publicKey = ''">
|
||||
<IconifyIcon icon="ant-design:close-circle-outlined" class="text-lg" />
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.privateKey')" name="privateKey">
|
||||
<a-upload
|
||||
v-if="!form.privateKey"
|
||||
:disabled="showable"
|
||||
:multiple="false"
|
||||
:show-upload-list="false"
|
||||
accept=".pem"
|
||||
:before-upload="() => false"
|
||||
@change="(info: any) => handleUpload(info, 'privateKey')"
|
||||
>
|
||||
<a-button :disabled="showable">
|
||||
<template #icon><IconifyIcon icon="ant-design:upload-outlined" class="text-lg" /></template>
|
||||
{{ $t('payment.channel.lakalaIsv.uploadPrivateKey') }}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-tooltip v-else :title="truncateContent(form.privateKey)" placement="top" :mouse-enter-delay="0.3">
|
||||
<a-input value="private_key.pem" disabled>
|
||||
<template #suffix>
|
||||
<span v-if="!showable" class="cursor-pointer text-gray-400" @click="form.privateKey = ''">
|
||||
<IconifyIcon icon="ant-design:close-circle-outlined" class="text-lg" />
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
<a-divider orientation="left">{{ $t('payment.channel.lakalaIsv.advancedConfig') }}</a-divider>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.sm4Key')" name="sm4Key">
|
||||
<a-input
|
||||
v-model:value="form.sm4Key"
|
||||
:disabled="showable"
|
||||
:disabled="!canEdit"
|
||||
:placeholder="$t('payment.channel.lakalaIsv.sm4KeyPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="$t('payment.channel.lakalaIsv.sandbox')" name="sandbox">
|
||||
<a-switch
|
||||
v-model:checked="form.sandbox"
|
||||
:disabled="!canEdit"
|
||||
:checked-children="$t('payment.channel.lakalaIsv.sandboxOn')"
|
||||
:un-checked-children="$t('payment.channel.lakalaIsv.sandboxOff')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button @click="handleCancel">{{ $t('common.cancel') }}</a-button>
|
||||
<a-button v-if="!showable" type="primary" :loading="confirmLoading" @click="handleOk">
|
||||
<a-button v-if="canEdit" type="primary" :loading="confirmLoading" @click="handleOk">
|
||||
{{ $t('common.save') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import LakalaIsvConfigEdit from '#/views/payment/channel/lakala/config/LakalaIsvConfigEdit.vue';
|
||||
|
||||
defineOptions({ name: 'LakalaManage' });
|
||||
|
||||
const mchKeyEditRef = ref<InstanceType<typeof LakalaIsvConfigEdit> | null>(null);
|
||||
|
||||
/**
|
||||
* 功能卡片配置
|
||||
* 拉卡拉为聚合服务商模式, 密钥全局唯一, 不区分应用/能力绑定, 仅一个密钥配置入口
|
||||
*/
|
||||
const functionCards = computed(() => [
|
||||
{
|
||||
group: $t('payment.channel.lakalaIsv.manageTitle'),
|
||||
color: 'blue',
|
||||
cards: [
|
||||
{
|
||||
key: 'mchKey',
|
||||
title: $t('payment.channel.lakalaIsv.cardMchKey'),
|
||||
icon: 'ant-design:key-outlined',
|
||||
description: $t('payment.channel.lakalaIsv.cardMchKeyDesc'),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
function getIconBgClass(color: string) {
|
||||
const map: Record<string, string> = {
|
||||
green: 'bg-success/10 text-success',
|
||||
blue: 'bg-primary/10 text-primary',
|
||||
};
|
||||
return map[color] || 'bg-muted text-muted-foreground';
|
||||
}
|
||||
|
||||
function getGroupColorClass(color: string) {
|
||||
const map: Record<string, string> = {
|
||||
green: 'bg-emerald-500',
|
||||
blue: 'bg-blue-500',
|
||||
};
|
||||
return map[color] || 'bg-gray-500';
|
||||
}
|
||||
|
||||
/** 初始化(由分发页调用, 平台为唯一服务商, 无需服务商号) */
|
||||
function init() {
|
||||
}
|
||||
|
||||
function handleCardClick(card: { key: string }) {
|
||||
if (card.key === 'mchKey') {
|
||||
mchKeyEditRef.value?.init();
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-12 py-4">
|
||||
<div v-for="group in functionCards" :key="group.group">
|
||||
<div class="mb-6 flex items-center gap-3 px-2">
|
||||
<div class="h-6 w-1.5 rounded-full shadow-sm" :class="getGroupColorClass(group.color)"></div>
|
||||
<span class="text-xl font-extrabold tracking-tight text-foreground">{{ group.group }}</span>
|
||||
</div>
|
||||
<div class="card-grid">
|
||||
<a-card
|
||||
v-for="card in group.cards"
|
||||
:key="card.key"
|
||||
hoverable
|
||||
class="isv-card group relative overflow-hidden rounded-2xl border-none bg-card shadow-md transition-all duration-300 hover:-translate-y-1.5 hover:shadow-xl"
|
||||
:styles="{ body: { padding: '24px 20px' } }"
|
||||
@click="handleCardClick(card)"
|
||||
>
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<div
|
||||
class="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl shadow-sm transition-all duration-300 group-hover:scale-110 group-hover:shadow-md"
|
||||
:class="getIconBgClass(group.color)"
|
||||
>
|
||||
<IconifyIcon :icon="card.icon" class="h-7 w-7" />
|
||||
</div>
|
||||
<div
|
||||
class="mb-1.5 text-base font-bold text-foreground group-hover:text-primary transition-colors duration-300"
|
||||
>{{ card.title }}</div>
|
||||
<a-tooltip :title="card.description" placement="bottom">
|
||||
<div class="line-clamp-1 text-xs leading-relaxed text-muted-foreground">{{ card.description }}</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div
|
||||
class="absolute bottom-0 left-0 h-1.5 w-0 transition-all duration-300 group-hover:w-full"
|
||||
:class="getGroupColorClass(group.color)"
|
||||
></div>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
<LakalaIsvConfigEdit ref="mchKeyEditRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 220px);
|
||||
gap: 24px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.isv-card {
|
||||
max-height: 200px;
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,7 @@
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import AlipayIsvManage from '#/views/payment/channel/alipay/manage/AlipayIsvManage.vue';
|
||||
import LakalaManage from '#/views/payment/channel/lakala/manage/LakalaManage.vue';
|
||||
import WechatIsvManage from '#/views/payment/channel/wechat/manage/WechatIsvManage.vue';
|
||||
|
||||
defineOptions({ name: 'ProductDetailDispatch' });
|
||||
@@ -44,6 +45,10 @@
|
||||
currentComponent.value = markRaw(WechatIsvManage);
|
||||
break;
|
||||
}
|
||||
case 'lakala_pay': {
|
||||
currentComponent.value = markRaw(LakalaManage);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
currentComponent.value = null;
|
||||
break;
|
||||
@@ -62,6 +67,9 @@
|
||||
if (product.value === 'wechat_isv') {
|
||||
return $t('payment.constant.product.productName.wechatIsv');
|
||||
}
|
||||
if (product.value === 'lakala_pay') {
|
||||
return $t('payment.product.enum.lakalaPay');
|
||||
}
|
||||
if (product.value) {
|
||||
return $t('payment.constant.product.productName.unknown', { product: product.value });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user