mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-13 07:45:41 +08:00
feat(admin): 码牌预览二维码并精简编辑/新增流程
列表预览改为素二维码+扫码地址;编辑展示商户名称、隐藏关联应用;去掉单条新增入口。
This commit is contained in:
@@ -22,13 +22,6 @@ export const DeviceQrCodeApi = {
|
||||
return defHttp.get({ url: '/admin/device/qrcode/get', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增码牌(快捷绑定商户)
|
||||
*/
|
||||
add(data: DeviceQrCodeParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/qrcode/add', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量创建空白码牌
|
||||
*/
|
||||
@@ -77,6 +70,13 @@ export const DeviceQrCodeApi = {
|
||||
changeStatus(id: string, status: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/qrcode/change-status', params: { id, status } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取码牌扫码链接(完整 H5 地址)
|
||||
*/
|
||||
getCodeLink(code: string): Promise<Result<string>> {
|
||||
return defHttp.get({ url: '/admin/device/qrcode/get-code-link', params: { code } });
|
||||
},
|
||||
};
|
||||
|
||||
/** 支付码牌查询参数 */
|
||||
@@ -95,16 +95,14 @@ export interface DeviceQrCodeQuery {
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/** 支付码牌参数 */
|
||||
/** 支付码牌参数(编辑) */
|
||||
export interface DeviceQrCodeParam {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 码牌名称 */
|
||||
name?: string;
|
||||
/** 商户号 */
|
||||
/** 商户号(只读展示, 不提交归属变更) */
|
||||
mchNo?: string;
|
||||
/** 关联应用号(空=商户默认应用) */
|
||||
appId?: string;
|
||||
/** 金额类型 random/fixed */
|
||||
amountType?: string;
|
||||
/** 固定金额(分) */
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"viewCode": "View Code",
|
||||
"qrCodeTitle": "QR Code",
|
||||
"qrCodeTip": "Scan to enter the payment page",
|
||||
"copyLink": "Copy Link",
|
||||
"download": "Download QR Code",
|
||||
"amountLabel": "Amount"
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"viewCode": "查看码牌",
|
||||
"qrCodeTitle": "码牌二维码",
|
||||
"qrCodeTip": "扫码即可进入支付页面",
|
||||
"copyLink": "复制链接",
|
||||
"download": "下载二维码",
|
||||
"amountLabel": "金额"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { DeviceQrCodeApi, type DeviceQrCodeResult } from '#/api/payment/device/qrcode.api';
|
||||
import { BQuery, type QueryField } from '#/components/query';
|
||||
import { QrCode } from '#/components/qrcode';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
@@ -88,7 +89,7 @@
|
||||
|
||||
// 查看码牌弹窗
|
||||
const codeVisible = ref(false);
|
||||
const currentCode = ref('');
|
||||
const qrCodeUrl = ref('');
|
||||
|
||||
/**
|
||||
* 同步勾选行
|
||||
@@ -137,10 +138,6 @@
|
||||
queryPage();
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.show();
|
||||
}
|
||||
|
||||
function handleBatchCreate() {
|
||||
batchCreateRef.value?.show();
|
||||
}
|
||||
@@ -223,19 +220,27 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看码牌编码
|
||||
* 查看码牌二维码(拉取完整扫码链接后弹窗展示)
|
||||
*/
|
||||
function handleViewCode(row: DeviceQrCodeResult) {
|
||||
currentCode.value = row.code || '';
|
||||
codeVisible.value = true;
|
||||
if (!row.code) {
|
||||
return;
|
||||
}
|
||||
DeviceQrCodeApi.getCodeLink(row.code).then((res) => {
|
||||
qrCodeUrl.value = res.data || '';
|
||||
codeVisible.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制码牌编码
|
||||
* 复制码牌扫码链接
|
||||
*/
|
||||
async function handleCopyCode() {
|
||||
if (!qrCodeUrl.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(currentCode.value);
|
||||
await navigator.clipboard.writeText(qrCodeUrl.value);
|
||||
message.success($t('common.operationSuccess'));
|
||||
} catch {
|
||||
message.error($t('common.operationFail'));
|
||||
@@ -262,9 +267,6 @@
|
||||
@click="handleBatchCreate"
|
||||
>{{ $t('payment.device.qrcode.batchCreate') }}</a-button
|
||||
>
|
||||
<a-button v-if="hasPermission(PermCodes.Device.QrCode.MANAGE)" @click="handleAdd">{{
|
||||
$t('common.add')
|
||||
}}</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.QrCode.MANAGE)"
|
||||
:disabled="selectedRows.length === 0"
|
||||
@@ -402,7 +404,7 @@
|
||||
<DeviceQrCodeBatchCreate ref="batchCreateRef" @ok="queryPage" />
|
||||
<DeviceQrCodeBindMerchant ref="bindMerchantRef" @ok="queryPage" />
|
||||
|
||||
<!-- 查看码牌弹窗 -->
|
||||
<!-- 查看码牌弹窗: 素二维码 + 下方扫码地址 -->
|
||||
<a-modal
|
||||
v-model:open="codeVisible"
|
||||
:title="$t('payment.device.qrcode.qrCodeTitle')"
|
||||
@@ -412,9 +414,12 @@
|
||||
>
|
||||
<div class="code-modal">
|
||||
<p class="code-tip">{{ $t('payment.device.qrcode.qrCodeTip') }}</p>
|
||||
<div class="code-value">{{ currentCode }}</div>
|
||||
<a-button type="primary" block @click="handleCopyCode">
|
||||
{{ $t('common.copy') }}
|
||||
<div v-if="qrCodeUrl" class="qr-card">
|
||||
<QrCode :value="qrCodeUrl" :width="250" :margin="0" />
|
||||
</div>
|
||||
<div class="code-url">{{ qrCodeUrl }}</div>
|
||||
<a-button type="primary" block :disabled="!qrCodeUrl" @click="handleCopyCode">
|
||||
{{ $t('payment.device.qrcode.copyLink') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
@@ -427,7 +432,7 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 8px 0;
|
||||
padding: 8px 0 4px;
|
||||
}
|
||||
|
||||
.code-tip {
|
||||
@@ -436,14 +441,20 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.code-value {
|
||||
width: 100%;
|
||||
.qr-card {
|
||||
padding: 12px;
|
||||
background: var(--background-color);
|
||||
background: #fff;
|
||||
border: 1px solid var(--border-color, #e8e8e8);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.code-url {
|
||||
width: 100%;
|
||||
padding: 0 8px;
|
||||
color: var(--text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
font-size: 15px;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LabelValue } from '#/types/web';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DeviceQrCodeApi, type DeviceQrCodeParam, type DeviceQrCodeResult } from '#/api/payment/device/qrcode.api';
|
||||
import { MchAppInfoApi, type MchAppInfoResult } from '#/api/payment/merchant/mch-app-info.api';
|
||||
import { MerchantApi } from '#/api/payment/merchant/merchant.api';
|
||||
import { FormEditType } from '#/enums/formEditType';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
@@ -18,20 +14,13 @@
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const { visible, confirmLoading, title, initFormEditType, handleCancel, formEditType } = useFormEdit();
|
||||
const { visible, confirmLoading, title, initFormEditType, handleCancel } = useFormEdit();
|
||||
|
||||
// 是否编辑模式(编辑时商户/应用只读展示)
|
||||
const isEdit = computed(() => formEditType.value === FormEditType.Edit);
|
||||
|
||||
// 商户下拉选项
|
||||
const mchOptions = ref<LabelValue[]>([]);
|
||||
|
||||
// 应用下拉选项(根据商户联动)
|
||||
const appOptions = ref<MchAppInfoResult[]>([]);
|
||||
// 编辑态只读展示用商户名称(不参与提交)
|
||||
const editMchName = ref('');
|
||||
|
||||
const formState = ref<DeviceQrCodeParam>({
|
||||
name: '',
|
||||
mchNo: '',
|
||||
amountType: 'random',
|
||||
});
|
||||
|
||||
@@ -44,11 +33,6 @@
|
||||
name: [{ required: true, message: $t('payment.device.qrcode.validateName') }],
|
||||
amountType: [{ required: true, message: $t('payment.device.qrcode.validateAmountType') }],
|
||||
};
|
||||
// 新增时商户必填; 编辑不改归属
|
||||
if (!isEdit.value) {
|
||||
rules.mchNo = [{ required: true, message: $t('payment.device.qrcode.validateMchNo') }];
|
||||
}
|
||||
// 固定金额类型时金额必填
|
||||
if (isFixedAmount.value) {
|
||||
rules.fixedAmount = [
|
||||
{ required: true, message: $t('payment.device.qrcode.validateFixedAmount') },
|
||||
@@ -62,49 +46,19 @@
|
||||
return rules;
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载商户下拉
|
||||
*/
|
||||
async function loadMchOptions() {
|
||||
const { data } = await MerchantApi.dropdown();
|
||||
mchOptions.value = data || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 商户切换时重新加载应用列表并清空已选应用
|
||||
*/
|
||||
async function handleMchChange() {
|
||||
formState.value.appId = undefined;
|
||||
appOptions.value = [];
|
||||
if (formState.value.mchNo) {
|
||||
const { data } = await MchAppInfoApi.page({ mchNo: formState.value.mchNo, size: 999 });
|
||||
appOptions.value = data?.records || [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
function resetForm() {
|
||||
formState.value = {
|
||||
name: '',
|
||||
mchNo: '',
|
||||
amountType: 'random',
|
||||
fixedAmount: undefined,
|
||||
};
|
||||
appOptions.value = [];
|
||||
editMchName.value = '';
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增弹窗
|
||||
*/
|
||||
function show() {
|
||||
initFormEditType(FormEditType.Add);
|
||||
resetForm();
|
||||
loadMchOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑弹窗(归属只读)
|
||||
*/
|
||||
@@ -119,12 +73,12 @@
|
||||
id: row.id!,
|
||||
name: row.name,
|
||||
mchNo: row.mchNo,
|
||||
appId: row.appId,
|
||||
amountType: row.amountType,
|
||||
// 分转元展示
|
||||
fixedAmount: row.fixedAmount ? row.fixedAmount / 100 : undefined,
|
||||
remark: row.remark,
|
||||
};
|
||||
editMchName.value = row.mchName || '';
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
@@ -144,13 +98,16 @@
|
||||
try {
|
||||
// 金额元转分, 在构造时一次性赋值避免对对象立即修改
|
||||
const payload: DeviceQrCodeParam = {
|
||||
...formState.value,
|
||||
id: formState.value.id,
|
||||
name: formState.value.name,
|
||||
amountType: formState.value.amountType,
|
||||
fixedAmount:
|
||||
isFixedAmount.value && formState.value.fixedAmount
|
||||
? Math.round(formState.value.fixedAmount * 100)
|
||||
: undefined,
|
||||
remark: formState.value.remark,
|
||||
};
|
||||
await (formEditType.value === FormEditType.Edit ? DeviceQrCodeApi.update(payload) : DeviceQrCodeApi.add(payload));
|
||||
await DeviceQrCodeApi.update(payload);
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
@@ -160,7 +117,7 @@
|
||||
}
|
||||
|
||||
// import 放最后避免循环依赖(参照项目惯例)
|
||||
defineExpose({ show, showEdit });
|
||||
defineExpose({ showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -183,36 +140,14 @@
|
||||
:wrapper-col="{ span: 16 }"
|
||||
class="form-compact"
|
||||
>
|
||||
<!-- 新增: 可选商户/应用; 编辑: 只读展示归属 -->
|
||||
<a-form-item v-if="!isEdit" :label="$t('payment.device.qrcode.field.mchNo')" name="mchNo">
|
||||
<a-select
|
||||
v-model:value="formState.mchNo"
|
||||
:options="mchOptions"
|
||||
:placeholder="$t('payment.device.qrcode.pleaseSelectMch')"
|
||||
show-search
|
||||
option-filter-prop="label"
|
||||
@change="handleMchChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="!isEdit" :label="$t('payment.device.qrcode.field.appId')">
|
||||
<a-select
|
||||
v-model:value="formState.appId"
|
||||
:options="appOptions"
|
||||
:field-names="{ label: 'appName', value: 'appId' }"
|
||||
:placeholder="$t('payment.device.qrcode.pleaseSelectApp')"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isEdit" :label="$t('payment.device.qrcode.field.mchNo')">
|
||||
<a-tag v-if="formState.mchNo" color="blue">{{ formState.mchNo }}</a-tag>
|
||||
<!-- 归属只读 -->
|
||||
<a-form-item :label="$t('payment.device.qrcode.field.mchName')">
|
||||
<a-tag v-if="formState.mchNo" color="blue">{{ editMchName || formState.mchNo }}</a-tag>
|
||||
<a-tag v-else color="default">{{ $t('payment.device.qrcode.unbound') }}</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isEdit" :label="$t('payment.device.qrcode.field.appId')">
|
||||
<a-tag v-if="formState.appId" color="cyan">{{ formState.appId }}</a-tag>
|
||||
<span v-else-if="formState.mchNo" style="color: var(--text-color-placeholder)">
|
||||
{{ $t('payment.device.qrcode.defaultApp') }}
|
||||
</span>
|
||||
<span v-else style="color: var(--text-color-placeholder)">-</span>
|
||||
<a-form-item :label="$t('payment.device.qrcode.field.mchNo')">
|
||||
<a-tag v-if="formState.mchNo" color="blue">{{ formState.mchNo }}</a-tag>
|
||||
<a-tag v-else color="default">{{ $t('payment.device.qrcode.unbound') }}</a-tag>
|
||||
</a-form-item>
|
||||
<!-- 码牌名称 -->
|
||||
<a-form-item :label="$t('payment.device.qrcode.field.name')" name="name">
|
||||
|
||||
Reference in New Issue
Block a user