mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-11 15:15:34 +08:00
refactor(admin): 开源版移除云音箱/云打印/厂商设备管理
硬件对接归商业版; 保留码牌管理与相关权限。
This commit is contained in:
@@ -1,123 +0,0 @@
|
||||
import type { BaseEntity, PageResult, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 云打印设备 API
|
||||
*/
|
||||
export const DevicePrinterApi = {
|
||||
/**
|
||||
* 分页查询云打印设备
|
||||
*/
|
||||
page(
|
||||
params: DevicePrinterQuery & { current?: number; size?: number },
|
||||
): Promise<Result<PageResult<DevicePrinterResult>>> {
|
||||
return defHttp.get({ url: '/admin/device/printer/page', params });
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据 id 查询云打印设备
|
||||
*/
|
||||
get(id: string): Promise<Result<DevicePrinterResult>> {
|
||||
return defHttp.get({ url: '/admin/device/printer/get', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增云打印设备
|
||||
*/
|
||||
add(data: DevicePrinterParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/printer/add', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改云打印设备
|
||||
*/
|
||||
update(data: DevicePrinterParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/printer/update', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除云打印设备
|
||||
*/
|
||||
delete(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/printer/delete', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 绑定云打印设备(首期仅更新本地状态, 真实设备对接由独立服务完成)
|
||||
*/
|
||||
bind(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/printer/bind', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 解绑云打印设备
|
||||
*/
|
||||
unbind(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/printer/unbind', params: { id } });
|
||||
},
|
||||
};
|
||||
|
||||
/** 云打印设备查询参数 */
|
||||
export interface DevicePrinterQuery {
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备状态 unbound/online/offline/fault */
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/** 云打印设备参数 */
|
||||
export interface DevicePrinterParam {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 厂商配置ID */
|
||||
vendorConfigId?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备IMEI */
|
||||
imei?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 云打印设备结果 */
|
||||
export interface DevicePrinterResult extends BaseEntity {
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 厂商配置ID */
|
||||
vendorConfigId?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备IMEI */
|
||||
imei?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 设备状态 */
|
||||
status?: string;
|
||||
/** 绑定时间 */
|
||||
bindTime?: string;
|
||||
/** 最后在线时间 */
|
||||
lastOnlineTime?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
import type { BaseEntity, PageResult, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 云音箱设备 API
|
||||
*/
|
||||
export const DeviceSpeakerApi = {
|
||||
/**
|
||||
* 分页查询云音箱设备
|
||||
*/
|
||||
page(
|
||||
params: DeviceSpeakerQuery & { current?: number; size?: number },
|
||||
): Promise<Result<PageResult<DeviceSpeakerResult>>> {
|
||||
return defHttp.get({ url: '/admin/device/speaker/page', params });
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据 id 查询云音箱设备
|
||||
*/
|
||||
get(id: string): Promise<Result<DeviceSpeakerResult>> {
|
||||
return defHttp.get({ url: '/admin/device/speaker/get', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增云音箱设备
|
||||
*/
|
||||
add(data: DeviceSpeakerParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/speaker/add', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改云音箱设备
|
||||
*/
|
||||
update(data: DeviceSpeakerParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/speaker/update', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除云音箱设备
|
||||
*/
|
||||
delete(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/speaker/delete', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 绑定云音箱设备(首期仅更新本地状态, 真实设备对接由独立服务完成)
|
||||
*/
|
||||
bind(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/speaker/bind', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 解绑云音箱设备
|
||||
*/
|
||||
unbind(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/speaker/unbind', params: { id } });
|
||||
},
|
||||
};
|
||||
|
||||
/** 云音箱设备查询参数 */
|
||||
export interface DeviceSpeakerQuery {
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备状态 unbound/online/offline/fault */
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/** 云音箱设备参数 */
|
||||
export interface DeviceSpeakerParam {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 厂商配置ID */
|
||||
vendorConfigId?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备IMEI */
|
||||
imei?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 云音箱设备结果 */
|
||||
export interface DeviceSpeakerResult extends BaseEntity {
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 厂商配置ID */
|
||||
vendorConfigId?: string;
|
||||
/** 设备序列号 */
|
||||
deviceSn?: string;
|
||||
/** 设备IMEI */
|
||||
imei?: string;
|
||||
/** 厂商门店ID */
|
||||
shopId?: string;
|
||||
/** 设备名称 */
|
||||
deviceName?: string;
|
||||
/** 设备状态 */
|
||||
status?: string;
|
||||
/** 绑定时间 */
|
||||
bindTime?: string;
|
||||
/** 最后在线时间 */
|
||||
lastOnlineTime?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import type { BaseEntity, PageResult, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 设备厂商配置 API
|
||||
*/
|
||||
export const DeviceVendorConfigApi = {
|
||||
/**
|
||||
* 分页查询厂商配置
|
||||
*/
|
||||
page(
|
||||
params: DeviceVendorConfigQuery & { current?: number; size?: number },
|
||||
): Promise<Result<PageResult<DeviceVendorConfigResult>>> {
|
||||
return defHttp.get({ url: '/admin/device/vendor-config/page', params });
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据 id 查询厂商配置
|
||||
*/
|
||||
get(id: string): Promise<Result<DeviceVendorConfigResult>> {
|
||||
return defHttp.get({ url: '/admin/device/vendor-config/get', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增厂商配置
|
||||
*/
|
||||
add(data: DeviceVendorConfigParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/vendor-config/add', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改厂商配置
|
||||
*/
|
||||
update(data: DeviceVendorConfigParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/vendor-config/update', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除厂商配置
|
||||
*/
|
||||
delete(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/vendor-config/delete', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询指定设备类型和厂商的启用配置列表(设备下拉用)
|
||||
*/
|
||||
listEnabledByVendor(
|
||||
deviceType: string,
|
||||
vendorCode: string,
|
||||
): Promise<Result<DeviceVendorConfigResult[]>> {
|
||||
return defHttp.get({
|
||||
url: '/admin/device/vendor-config/list-enabled-by-vendor',
|
||||
params: { deviceType, vendorCode },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/** 厂商配置查询参数 */
|
||||
export interface DeviceVendorConfigQuery {
|
||||
/** 设备类型(顶层厂商配置页可不传, 按厂商聚合查询) */
|
||||
deviceType?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 配置名称 */
|
||||
configName?: string;
|
||||
/** 是否启用 */
|
||||
enable?: boolean;
|
||||
}
|
||||
|
||||
/** 厂商配置参数 */
|
||||
export interface DeviceVendorConfigParam {
|
||||
/** 主键 */
|
||||
id?: string;
|
||||
/** 设备类型 */
|
||||
deviceType?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 配置名称 */
|
||||
configName?: string;
|
||||
/** 厂商应用ID */
|
||||
appId?: string;
|
||||
/** 厂商应用密钥 */
|
||||
appSecret?: string;
|
||||
/** 是否启用 */
|
||||
enable?: boolean;
|
||||
/** 扩展参数(JSON) */
|
||||
extParam?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 厂商配置结果 */
|
||||
export interface DeviceVendorConfigResult extends BaseEntity {
|
||||
/** 设备类型 */
|
||||
deviceType?: string;
|
||||
/** 厂商代码 */
|
||||
vendorCode?: string;
|
||||
/** 配置名称 */
|
||||
configName?: string;
|
||||
/** 厂商应用ID */
|
||||
appId?: string;
|
||||
/** 厂商应用密钥(脱敏) */
|
||||
appSecret?: string;
|
||||
/** 是否启用 */
|
||||
enable?: boolean;
|
||||
/** 扩展参数(JSON) */
|
||||
extParam?: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"><circle cx="32" cy="32" r="30" fill="#52c41a"/><text x="32" y="32" font-size="28" font-weight="bold" fill="#fff" text-anchor="middle" dominant-baseline="central" font-family="sans-serif">博</text></svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1 +0,0 @@
|
||||
<svg t="1782370182964" class="icon" viewBox="0 0 5152 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6267" width="200" height="200"><path d="M1897.987261 0v704.407643c0 71.745223-52.178344 130.44586-123.923567 130.44586h-247.847133c-65.22293 0-123.923567-58.700637-123.923567-130.44586V0h-247.847134v769.630573c0 143.490446 110.878981 254.369427 247.847134 254.369427h482.649681c136.968153 0 247.847134-117.401274 247.847134-254.369427V0h-234.802548zM737.019108 417.426752H352.203822c-58.700637 0-104.356688-52.178344-104.356688-110.878981 0-58.700637 45.656051-110.878981 104.356688-110.878981H952.254777V0H247.847134C110.878981 0 0 117.401274 0 254.369427v97.834395c0 143.490446 110.878981 254.369427 247.847134 254.369426h384.815286c58.700637 0 104.356688 52.178344 104.356688 110.878981s-45.656051 110.878981-104.356688 110.878981H32.611465v189.146497h710.929936c136.968153 0 247.847134-117.401274 247.847134-254.369427v-97.834395c-13.044586-136.968153-117.401274-247.847134-254.369427-247.847133zM3065.477707 0h-482.649682c-136.968153 0-247.847134 117.401274-247.847133 254.369427v769.630573h247.847133V319.592357c0-71.745223 52.178344-130.44586 123.923567-130.44586h247.847134c65.22293 0 123.923567 58.700637 123.923567 130.44586v704.407643H3326.369427V254.369427C3313.324841 117.401274 3202.44586 0 3065.477707 0zM4904.764331 0H5152.611465v1024h-247.847134zM3985.121019 384.815287h247.847134v639.184713h-247.847134z" fill="#FF6000" p-id="6268"></path><path d="M4474.292994 0H3743.796178c-136.968153 0-247.847134 117.401274-247.847133 254.369427v769.630573h247.847133V319.592357c0-71.745223 52.178344-130.44586 123.923567-130.44586h489.171975c65.22293 0 123.923567 58.700637 123.923567 130.44586v704.407643h247.847133V254.369427C4722.140127 117.401274 4611.261146 0 4474.292994 0z" fill="#FF6000" p-id="6269"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"><circle cx="32" cy="32" r="30" fill="#1677ff"/><text x="32" y="32" font-size="28" font-weight="bold" fill="#fff" text-anchor="middle" dominant-baseline="central" font-family="sans-serif">智</text></svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1,65 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { vendorLogoMap } from '#/enums/payment/deviceEnum';
|
||||
|
||||
defineOptions({ name: 'VendorLogo' });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
vendor: string;
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 32,
|
||||
},
|
||||
);
|
||||
|
||||
/** 厂商 SVG logo 的 URL */
|
||||
const logoSrc = computed(() => {
|
||||
const fileName = vendorLogoMap[props.vendor];
|
||||
if (!fileName) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return new URL(`/src/assets/device-vendor/${fileName}.svg`, import.meta.url).href;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="vendor-logo" :style="{ width: `${size}px`, height: `${size}px` }">
|
||||
<img v-if="logoSrc" :src="logoSrc" :width="size" :height="size" :alt="vendor" class="logo-img" />
|
||||
<span
|
||||
v-else
|
||||
class="fallback-icon"
|
||||
:style="{ width: `${size}px`, height: `${size}px`, fontSize: `${Math.max(size! * 0.5, 14)}px` }"
|
||||
>
|
||||
🔈
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vendor-logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.fallback-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -211,28 +211,13 @@ export const PermCodes = {
|
||||
},
|
||||
},
|
||||
|
||||
/** 设备管理(独立命名空间) */
|
||||
/** 设备管理(独立命名空间;开源仅码牌,硬件对接归商业版) */
|
||||
Device: {
|
||||
/** 云音箱 menuCode=device:speaker */
|
||||
Speaker: {
|
||||
VIEW: 'device:speaker:view',
|
||||
MANAGE: 'device:speaker:manage',
|
||||
},
|
||||
/** 码牌 menuCode=device:qrcode */
|
||||
QrCode: {
|
||||
VIEW: 'device:qrcode:view',
|
||||
MANAGE: 'device:qrcode:manage',
|
||||
},
|
||||
/** 云打印 menuCode=device:printer */
|
||||
Printer: {
|
||||
VIEW: 'device:printer:view',
|
||||
MANAGE: 'device:printer:manage',
|
||||
},
|
||||
/** 厂商配置 menuCode=device:vendor_config */
|
||||
VendorConfig: {
|
||||
VIEW: 'device:vendor_config:view',
|
||||
MANAGE: 'device:vendor_config:manage',
|
||||
},
|
||||
},
|
||||
|
||||
/** 系统域 */
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* 设备类型枚举
|
||||
*/
|
||||
export enum DeviceType {
|
||||
/** 云音箱 */
|
||||
SPEAKER = 'speaker',
|
||||
/** 云打印 */
|
||||
PRINTER = 'printer',
|
||||
/** 码牌 */
|
||||
QRCODE = 'qrcode',
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备厂商枚举
|
||||
*/
|
||||
export enum DeviceVendor {
|
||||
/** 商米 */
|
||||
SUNMI = 'sunmi',
|
||||
/** 智谷联 */
|
||||
ZHIGULIAN = 'zhigulian',
|
||||
/** 博实结 */
|
||||
BOSHIJIE = 'boshijie',
|
||||
}
|
||||
|
||||
/**
|
||||
* 厂商国际化 Key 映射
|
||||
*/
|
||||
export const vendorI18nMap: Record<string, string> = {
|
||||
[DeviceVendor.SUNMI]: 'payment.device.vendor.vendorSunmi',
|
||||
[DeviceVendor.ZHIGULIAN]: 'payment.device.vendor.vendorZhigulian',
|
||||
[DeviceVendor.BOSHIJIE]: 'payment.device.vendor.vendorBoshijie',
|
||||
};
|
||||
|
||||
/**
|
||||
* 厂商 Logo 映射(SVG 文件名, 对应 src/assets/device-vendor/ 下的文件)
|
||||
*/
|
||||
export const vendorLogoMap: Record<string, string> = {
|
||||
[DeviceVendor.SUNMI]: 'sunmi',
|
||||
[DeviceVendor.ZHIGULIAN]: 'zhigulian',
|
||||
[DeviceVendor.BOSHIJIE]: 'boshijie',
|
||||
};
|
||||
|
||||
/**
|
||||
* 设备类型 → 支持的厂商列表
|
||||
*
|
||||
* 每种设备类型支持的厂商不同, 在此维护映射关系。
|
||||
*/
|
||||
export const deviceVendorMap: Record<string, DeviceVendor[]> = {
|
||||
[DeviceType.SPEAKER]: [DeviceVendor.SUNMI, DeviceVendor.ZHIGULIAN, DeviceVendor.BOSHIJIE],
|
||||
[DeviceType.PRINTER]: [DeviceVendor.SUNMI],
|
||||
};
|
||||
|
||||
/**
|
||||
* 设备类型国际化 Key 映射(用于厂商配置页设备类型列展示)
|
||||
*/
|
||||
export const deviceTypeI18nMap: Record<string, string> = {
|
||||
[DeviceType.SPEAKER]: 'payment.device.vendor.deviceTypeLabel.speaker',
|
||||
[DeviceType.PRINTER]: 'payment.device.vendor.deviceTypeLabel.printer',
|
||||
[DeviceType.QRCODE]: 'payment.device.vendor.deviceTypeLabel.qrcode',
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据厂商代码反查其支持的设备类型列表(基于 deviceVendorMap 反向查询)
|
||||
*/
|
||||
export function getDeviceTypesByVendor(vendorCode: string): DeviceType[] {
|
||||
return (Object.keys(deviceVendorMap) as DeviceType[])
|
||||
.filter((dt) => deviceVendorMap[dt]?.includes(vendorCode as DeviceVendor));
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"title": "Cloud Printer Device",
|
||||
"field": {
|
||||
"mchNo": "Merchant No.",
|
||||
"vendorCode": "Vendor",
|
||||
"vendorConfigId": "Vendor Config",
|
||||
"deviceSn": "Device Serial No.",
|
||||
"imei": "Device IMEI",
|
||||
"shopId": "Vendor Shop ID",
|
||||
"deviceName": "Device Name",
|
||||
"status": "Status",
|
||||
"bindTime": "Bind Time",
|
||||
"lastOnlineTime": "Last Online Time",
|
||||
"remark": "Remark",
|
||||
"createTime": "Create Time"
|
||||
},
|
||||
"status": {
|
||||
"unbound": "Unbound",
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"fault": "Fault"
|
||||
},
|
||||
"validateMchNo": "Please enter merchant no.",
|
||||
"validateDeviceSn": "Please enter device serial no.",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"confirmBind": "Are you sure to bind this device?",
|
||||
"confirmUnbind": "Are you sure to unbind this device? Printing will stop after unbind.",
|
||||
"confirmDelete": "Are you sure to delete this device?",
|
||||
"validateVendorCode": "Please select a vendor",
|
||||
"validateVendorConfig": "Please select a vendor config",
|
||||
"pleaseSelectVendor": "Please select a vendor",
|
||||
"pleaseSelectVendorConfig": "Please select a config",
|
||||
"unassigned": "Unassigned"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"title": "Cloud Speaker Device",
|
||||
"field": {
|
||||
"mchNo": "Merchant No.",
|
||||
"vendorCode": "Vendor",
|
||||
"vendorConfigId": "Vendor Config",
|
||||
"deviceSn": "Device Serial No.",
|
||||
"imei": "Device IMEI",
|
||||
"shopId": "Vendor Shop ID",
|
||||
"deviceName": "Device Name",
|
||||
"status": "Status",
|
||||
"bindTime": "Bind Time",
|
||||
"lastOnlineTime": "Last Online Time",
|
||||
"remark": "Remark",
|
||||
"createTime": "Create Time"
|
||||
},
|
||||
"status": {
|
||||
"unbound": "Unbound",
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"fault": "Fault"
|
||||
},
|
||||
"validateMchNo": "Please enter merchant no.",
|
||||
"validateDeviceSn": "Please enter device serial no.",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"confirmBind": "Are you sure to bind this device?",
|
||||
"confirmUnbind": "Are you sure to unbind this device? Broadcast will stop after unbind.",
|
||||
"confirmDelete": "Are you sure to delete this device?",
|
||||
"validateVendorCode": "Please select a vendor",
|
||||
"validateVendorConfig": "Please select a vendor config",
|
||||
"pleaseSelectVendor": "Please select a vendor",
|
||||
"pleaseSelectVendorConfig": "Please select a config",
|
||||
"unassigned": "Unassigned"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"title": "Device Vendor Management",
|
||||
"vendorSunmi": "SUNMI",
|
||||
"vendorZhigulian": "Zhigulian",
|
||||
"vendorBoshijie": "Boshijie",
|
||||
"field": {
|
||||
"deviceType": "Device Type",
|
||||
"vendorCode": "Vendor",
|
||||
"configName": "Config Name",
|
||||
"appId": "App ID",
|
||||
"appSecret": "App Secret",
|
||||
"enable": "Enabled",
|
||||
"extParam": "Ext Params",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At"
|
||||
},
|
||||
"action": {
|
||||
"addConfig": "Add Config",
|
||||
"editConfig": "Edit Config"
|
||||
},
|
||||
"placeholder": {
|
||||
"configName": "Please enter config name",
|
||||
"appId": "Please enter App ID",
|
||||
"appSecret": "Please enter App Secret",
|
||||
"extParam": "Please enter ext params (JSON)"
|
||||
},
|
||||
"hint": {
|
||||
"appSecretKeepEmpty": "Leave empty to keep unchanged",
|
||||
"clickToManage": "Click to manage vendor configs"
|
||||
},
|
||||
"enable": "Enabled",
|
||||
"disable": "Disabled",
|
||||
"confirmDelete": "Are you sure to delete this vendor config?",
|
||||
"validateConfigName": "Please enter config name",
|
||||
"validateAppId": "Please enter App ID",
|
||||
"validateAppSecret": "Please enter App Secret",
|
||||
"deviceTypeLabel": {
|
||||
"speaker": "Cloud Speaker",
|
||||
"printer": "Cloud Printer",
|
||||
"qrcode": "QR Code"
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"title": "云打印设备",
|
||||
"field": {
|
||||
"mchNo": "商户号",
|
||||
"vendorCode": "厂商",
|
||||
"vendorConfigId": "厂商配置",
|
||||
"deviceSn": "设备序列号",
|
||||
"imei": "设备IMEI",
|
||||
"shopId": "厂商门店ID",
|
||||
"deviceName": "设备名称",
|
||||
"status": "设备状态",
|
||||
"bindTime": "绑定时间",
|
||||
"lastOnlineTime": "最后在线时间",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间"
|
||||
},
|
||||
"status": {
|
||||
"unbound": "未绑定",
|
||||
"online": "在线",
|
||||
"offline": "离线",
|
||||
"fault": "故障"
|
||||
},
|
||||
"validateMchNo": "请输入商户号",
|
||||
"validateDeviceSn": "请输入设备序列号",
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"confirmBind": "确定要绑定该设备吗?",
|
||||
"confirmUnbind": "确定要解绑该设备吗?解绑后将停止打印。",
|
||||
"confirmDelete": "确定要删除该设备吗?",
|
||||
"validateVendorCode": "请选择厂商",
|
||||
"validateVendorConfig": "请选择厂商配置",
|
||||
"pleaseSelectVendor": "请选择厂商",
|
||||
"pleaseSelectVendorConfig": "请选择配置",
|
||||
"unassigned": "未分配"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"title": "云音箱设备",
|
||||
"field": {
|
||||
"mchNo": "商户号",
|
||||
"vendorCode": "厂商",
|
||||
"vendorConfigId": "厂商配置",
|
||||
"deviceSn": "设备序列号",
|
||||
"imei": "设备IMEI",
|
||||
"shopId": "厂商门店ID",
|
||||
"deviceName": "设备名称",
|
||||
"status": "设备状态",
|
||||
"bindTime": "绑定时间",
|
||||
"lastOnlineTime": "最后在线时间",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间"
|
||||
},
|
||||
"status": {
|
||||
"unbound": "未绑定",
|
||||
"online": "在线",
|
||||
"offline": "离线",
|
||||
"fault": "故障"
|
||||
},
|
||||
"validateMchNo": "请输入商户号",
|
||||
"validateDeviceSn": "请输入设备序列号",
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"confirmBind": "确定要绑定该设备吗?",
|
||||
"confirmUnbind": "确定要解绑该设备吗?解绑后将停止播报。",
|
||||
"confirmDelete": "确定要删除该设备吗?",
|
||||
"validateVendorCode": "请选择厂商",
|
||||
"validateVendorConfig": "请选择厂商配置",
|
||||
"pleaseSelectVendor": "请选择厂商",
|
||||
"pleaseSelectVendorConfig": "请选择配置",
|
||||
"unassigned": "未分配"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"title": "设备厂商管理",
|
||||
"vendorSunmi": "商米",
|
||||
"vendorZhigulian": "智谷联",
|
||||
"vendorBoshijie": "博实结",
|
||||
"field": {
|
||||
"deviceType": "设备类型",
|
||||
"vendorCode": "厂商",
|
||||
"configName": "配置名称",
|
||||
"appId": "应用ID",
|
||||
"appSecret": "应用密钥",
|
||||
"enable": "是否启用",
|
||||
"extParam": "扩展参数",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间"
|
||||
},
|
||||
"action": {
|
||||
"addConfig": "新增配置",
|
||||
"editConfig": "编辑配置"
|
||||
},
|
||||
"placeholder": {
|
||||
"configName": "请输入配置名称",
|
||||
"appId": "请输入应用ID",
|
||||
"appSecret": "请输入应用密钥",
|
||||
"extParam": "请输入扩展参数(JSON格式)"
|
||||
},
|
||||
"hint": {
|
||||
"appSecretKeepEmpty": "留空不修改",
|
||||
"clickToManage": "点击管理厂商配置"
|
||||
},
|
||||
"enable": "启用",
|
||||
"disable": "停用",
|
||||
"confirmDelete": "确定要删除该厂商配置吗?",
|
||||
"validateConfigName": "请输入配置名称",
|
||||
"validateAppId": "请输入应用ID",
|
||||
"validateAppSecret": "请输入应用密钥",
|
||||
"deviceTypeLabel": {
|
||||
"speaker": "云音箱",
|
||||
"printer": "云打印",
|
||||
"qrcode": "码牌"
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DevicePrinterApi, type DevicePrinterParam, type DevicePrinterResult } from '#/api/payment/device/printer.api';
|
||||
import { DeviceVendorConfigApi, type DeviceVendorConfigResult } from '#/api/payment/device/vendor-config.api';
|
||||
import { DeviceType, deviceVendorMap, vendorI18nMap } from '#/enums/payment/deviceEnum';
|
||||
import { FormEditType } from '#/enums/formEditType';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
|
||||
const emit = defineEmits(['ok']);
|
||||
|
||||
const { message } = useMessage();
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const { visible, confirmLoading, title, initFormEditType, handleCancel, formEditType } = useFormEdit();
|
||||
|
||||
// 厂商选项(从设备类型→厂商映射获取)
|
||||
const vendorOptions = computed(() => {
|
||||
const vendorList = deviceVendorMap[DeviceType.PRINTER] || [];
|
||||
return vendorList.map((v) => ({
|
||||
label: $t(vendorI18nMap[v] || v),
|
||||
value: v,
|
||||
}));
|
||||
});
|
||||
|
||||
// 厂商配置选项(根据厂商动态加载)
|
||||
const configOptions = ref<DeviceVendorConfigResult[]>([]);
|
||||
|
||||
const formState = ref<DevicePrinterParam>({
|
||||
vendorCode: '',
|
||||
deviceSn: '',
|
||||
deviceName: '',
|
||||
});
|
||||
|
||||
const formRules = computed(() => ({
|
||||
vendorCode: [{ required: true, message: $t('payment.device.printer.validateVendorCode') }],
|
||||
vendorConfigId: [{ required: true, message: $t('payment.device.printer.validateVendorConfig') }],
|
||||
deviceSn: [{ required: true, message: $t('payment.device.printer.validateDeviceSn') }],
|
||||
}));
|
||||
|
||||
/**
|
||||
* 加载厂商配置选项
|
||||
*/
|
||||
async function loadConfigOptions(vendorCode?: string) {
|
||||
if (!vendorCode) {
|
||||
configOptions.value = [];
|
||||
return;
|
||||
}
|
||||
const { data } = await DeviceVendorConfigApi.listEnabledByVendor(DeviceType.PRINTER, vendorCode);
|
||||
configOptions.value = data || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 厂商切换时清空配置并重新加载
|
||||
*/
|
||||
function handleVendorChange() {
|
||||
formState.value.vendorConfigId = undefined;
|
||||
loadConfigOptions(formState.value.vendorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
function resetForm() {
|
||||
formState.value = {
|
||||
vendorCode: '',
|
||||
deviceSn: '',
|
||||
deviceName: '',
|
||||
};
|
||||
configOptions.value = [];
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增弹窗
|
||||
*/
|
||||
function show() {
|
||||
initFormEditType(FormEditType.Add);
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑弹窗
|
||||
*/
|
||||
async function showEdit(record: DevicePrinterResult) {
|
||||
initFormEditType(FormEditType.Edit);
|
||||
resetForm();
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const { data } = await DevicePrinterApi.get(record.id!);
|
||||
const row = data || record;
|
||||
formState.value = {
|
||||
id: row.id!,
|
||||
mchNo: row.mchNo,
|
||||
vendorCode: row.vendorCode,
|
||||
vendorConfigId: row.vendorConfigId,
|
||||
deviceSn: row.deviceSn,
|
||||
imei: row.imei,
|
||||
shopId: row.shopId,
|
||||
deviceName: row.deviceName,
|
||||
remark: row.remark,
|
||||
};
|
||||
// 加载已有厂商的配置列表
|
||||
if (row.vendorCode) {
|
||||
await loadConfigOptions(row.vendorCode);
|
||||
}
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
async function handleOk() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
// 校验失败:表单已显示错误提示
|
||||
return;
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const payload: DevicePrinterParam = { ...formState.value };
|
||||
await (formEditType.value === FormEditType.Edit
|
||||
? DevicePrinterApi.update(payload)
|
||||
: DevicePrinterApi.add(payload));
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ show, showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
:width="640"
|
||||
:confirm-loading="confirmLoading"
|
||||
:destroy-on-hidden="true"
|
||||
:mask-closable="false"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
class="form-compact"
|
||||
>
|
||||
<!-- 厂商 -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.vendorCode')" name="vendorCode">
|
||||
<a-select
|
||||
v-model:value="formState.vendorCode"
|
||||
:options="vendorOptions"
|
||||
:placeholder="$t('payment.device.printer.pleaseSelectVendor')"
|
||||
@change="handleVendorChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 厂商配置 -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.vendorConfigId')" name="vendorConfigId">
|
||||
<a-select
|
||||
v-model:value="formState.vendorConfigId"
|
||||
:options="configOptions"
|
||||
:field-names="{ label: 'configName', value: 'id' }"
|
||||
:placeholder="$t('payment.device.printer.pleaseSelectVendorConfig')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 设备序列号 -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.deviceSn')" name="deviceSn">
|
||||
<a-input v-model:value="formState.deviceSn" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 设备名称 -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.deviceName')">
|
||||
<a-input v-model:value="formState.deviceName" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 设备IMEI -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.imei')">
|
||||
<a-input v-model:value="formState.imei" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 备注 -->
|
||||
<a-form-item :label="$t('payment.device.printer.field.remark')">
|
||||
<a-textarea v-model:value="formState.remark" :rows="2" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -1,295 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DevicePrinterApi, type DevicePrinterResult } from '#/api/payment/device/printer.api';
|
||||
import { DeviceType, deviceVendorMap, vendorI18nMap } from '#/enums/payment/deviceEnum';
|
||||
import { BQuery, type QueryField } from '#/components/query';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
import DevicePrinterEdit from './DevicePrinterEdit.vue';
|
||||
|
||||
defineOptions({ name: 'DevicePrinterList' });
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const loading = ref(false);
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
const editRef = ref<InstanceType<typeof DevicePrinterEdit>>();
|
||||
|
||||
const queryForm = ref<Record<string, any>>({});
|
||||
|
||||
// 厂商选项(查询用)
|
||||
const vendorOptions = computed(() => {
|
||||
const vendorList = deviceVendorMap[DeviceType.PRINTER] || [];
|
||||
return vendorList.map((v) => ({
|
||||
label: $t(vendorI18nMap[v] || v),
|
||||
value: v,
|
||||
}));
|
||||
});
|
||||
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
type: 'string',
|
||||
field: 'mchNo',
|
||||
name: $t('payment.device.printer.field.mchNo'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'vendorCode',
|
||||
name: $t('payment.device.printer.field.vendorCode'),
|
||||
placeholder: $t('common.pleaseSelect'),
|
||||
selectList: vendorOptions.value,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'deviceSn',
|
||||
name: $t('payment.device.printer.field.deviceSn'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'deviceName',
|
||||
name: $t('payment.device.printer.field.deviceName'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'status',
|
||||
name: $t('payment.device.printer.field.status'),
|
||||
placeholder: $t('common.pleaseSelect'),
|
||||
selectList: [
|
||||
{ label: $t('payment.device.printer.status.unbound'), value: 'unbound' },
|
||||
{ label: $t('payment.device.printer.status.online'), value: 'online' },
|
||||
{ label: $t('payment.device.printer.status.offline'), value: 'offline' },
|
||||
{ label: $t('payment.device.printer.status.fault'), value: 'fault' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const tableData = ref<DevicePrinterResult[]>([]);
|
||||
|
||||
/**
|
||||
* 分页查询云打印设备列表
|
||||
*/
|
||||
function queryPage() {
|
||||
loading.value = true;
|
||||
DevicePrinterApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
...queryForm.value,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data.records || [];
|
||||
pageConfig.value.total = Number(res.data.total) || 0;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
queryForm.value = {};
|
||||
pageConfig.value.currentPage = 1;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
function handlePageChange({ currentPage, pageSize }: any) {
|
||||
pageConfig.value.currentPage = currentPage;
|
||||
pageConfig.value.pageSize = pageSize;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
queryPage();
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.show();
|
||||
}
|
||||
|
||||
function handleEdit(row: DevicePrinterResult) {
|
||||
editRef.value?.showEdit(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
*/
|
||||
function handleDelete(row: DevicePrinterResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.printer.confirmDelete'),
|
||||
onOk() {
|
||||
return DevicePrinterApi.delete(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定设备(首期仅更新本地状态)
|
||||
*/
|
||||
function handleBind(row: DevicePrinterResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.printer.confirmBind'),
|
||||
onOk() {
|
||||
return DevicePrinterApi.bind(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑设备
|
||||
*/
|
||||
function handleUnbind(row: DevicePrinterResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.printer.confirmUnbind'),
|
||||
onOk() {
|
||||
return DevicePrinterApi.unbind(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="m-3 p-3 bg-background rounded-lg list-page-compact">
|
||||
<a-card>
|
||||
<BQuery :fields="queryFields" :query-params="queryForm" @query="queryPage" @reset="resetQuery" />
|
||||
</a-card>
|
||||
|
||||
<div class="mt-4">
|
||||
<a-card>
|
||||
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: queryPage }">
|
||||
<template #buttons>
|
||||
<a-space>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Printer.MANAGE)"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>{{ $t('common.add') }}</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" :loading="loading">
|
||||
<vxe-column type="seq" :title="$t('common.seq')" width="60" align="center" />
|
||||
<vxe-column field="deviceSn" :title="$t('payment.device.printer.field.deviceSn')" :min-width="180" />
|
||||
<vxe-column field="deviceName" :title="$t('payment.device.printer.field.deviceName')" :min-width="140" />
|
||||
<vxe-column field="mchNo" :title="$t('payment.device.printer.field.mchNo')" :min-width="140">
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.mchNo" color="blue">{{ row.mchNo }}</a-tag>
|
||||
<span v-else style="color: var(--text-color-placeholder)">{{ $t('payment.device.printer.unassigned') }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="vendorCode" :title="$t('payment.device.printer.field.vendorCode')" :min-width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.vendorCode ? $t(vendorI18nMap[row.vendorCode] || row.vendorCode) : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="status"
|
||||
:title="$t('payment.device.printer.field.status')"
|
||||
:min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.status === 'online'" color="green">{{
|
||||
$t('payment.device.printer.status.online')
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="row.status === 'offline'" color="default">{{
|
||||
$t('payment.device.printer.status.offline')
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="row.status === 'fault'" color="red">{{
|
||||
$t('payment.device.printer.status.fault')
|
||||
}}</a-tag>
|
||||
<a-tag v-else color="blue">{{ $t('payment.device.printer.status.unbound') }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="bindTime"
|
||||
:title="$t('payment.device.printer.field.bindTime')"
|
||||
:min-width="180"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
:title="$t('payment.device.printer.field.createTime')"
|
||||
:min-width="180"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column fixed="right" width="200" :show-overflow="false" :title="$t('common.operation')">
|
||||
<template #default="{ row }">
|
||||
<a-space :size="2">
|
||||
<template #separator>
|
||||
<a-divider type="vertical" />
|
||||
</template>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Printer.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
>{{ $t('common.edit') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Printer.MANAGE) && row.status !== 'online'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleBind(row)"
|
||||
>{{ $t('payment.device.printer.bind') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-else-if="hasPermission(PermCodes.Device.Printer.MANAGE) && row.status === 'online'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleUnbind(row)"
|
||||
>{{ $t('payment.device.printer.unbind') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Printer.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleDelete(row)"
|
||||
>{{ $t('common.delete') }}</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<vxe-pager
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
:current-page="pageConfig.currentPage"
|
||||
:page-size="pageConfig.pageSize"
|
||||
:total="pageConfig.total"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<DevicePrinterEdit ref="editRef" @ok="queryPage" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,200 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DeviceSpeakerApi, type DeviceSpeakerParam, type DeviceSpeakerResult } from '#/api/payment/device/speaker.api';
|
||||
import { DeviceVendorConfigApi, type DeviceVendorConfigResult } from '#/api/payment/device/vendor-config.api';
|
||||
import { DeviceType, deviceVendorMap, vendorI18nMap } from '#/enums/payment/deviceEnum';
|
||||
import { FormEditType } from '#/enums/formEditType';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
|
||||
const emit = defineEmits(['ok']);
|
||||
|
||||
const { message } = useMessage();
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const { visible, confirmLoading, title, initFormEditType, handleCancel, formEditType } = useFormEdit();
|
||||
|
||||
// 厂商选项(从设备类型→厂商映射获取)
|
||||
const vendorOptions = computed(() => {
|
||||
const vendorList = deviceVendorMap[DeviceType.SPEAKER] || [];
|
||||
return vendorList.map((v) => ({
|
||||
label: $t(vendorI18nMap[v] || v),
|
||||
value: v,
|
||||
}));
|
||||
});
|
||||
|
||||
// 厂商配置选项(根据厂商动态加载)
|
||||
const configOptions = ref<DeviceVendorConfigResult[]>([]);
|
||||
|
||||
const formState = ref<DeviceSpeakerParam>({
|
||||
vendorCode: '',
|
||||
deviceSn: '',
|
||||
deviceName: '',
|
||||
});
|
||||
|
||||
const formRules = computed(() => ({
|
||||
vendorCode: [{ required: true, message: $t('payment.device.speaker.validateVendorCode') }],
|
||||
vendorConfigId: [{ required: true, message: $t('payment.device.speaker.validateVendorConfig') }],
|
||||
deviceSn: [{ required: true, message: $t('payment.device.speaker.validateDeviceSn') }],
|
||||
}));
|
||||
|
||||
/**
|
||||
* 加载厂商配置选项
|
||||
*/
|
||||
async function loadConfigOptions(vendorCode?: string) {
|
||||
if (!vendorCode) {
|
||||
configOptions.value = [];
|
||||
return;
|
||||
}
|
||||
const { data } = await DeviceVendorConfigApi.listEnabledByVendor(DeviceType.SPEAKER, vendorCode);
|
||||
configOptions.value = data || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 厂商切换时清空配置并重新加载
|
||||
*/
|
||||
function handleVendorChange() {
|
||||
formState.value.vendorConfigId = undefined;
|
||||
loadConfigOptions(formState.value.vendorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
function resetForm() {
|
||||
formState.value = {
|
||||
vendorCode: '',
|
||||
deviceSn: '',
|
||||
deviceName: '',
|
||||
};
|
||||
configOptions.value = [];
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增弹窗
|
||||
*/
|
||||
function show() {
|
||||
initFormEditType(FormEditType.Add);
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑弹窗
|
||||
*/
|
||||
async function showEdit(record: DeviceSpeakerResult) {
|
||||
initFormEditType(FormEditType.Edit);
|
||||
resetForm();
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const { data } = await DeviceSpeakerApi.get(record.id!);
|
||||
const row = data || record;
|
||||
formState.value = {
|
||||
id: row.id!,
|
||||
mchNo: row.mchNo,
|
||||
vendorCode: row.vendorCode,
|
||||
vendorConfigId: row.vendorConfigId,
|
||||
deviceSn: row.deviceSn,
|
||||
imei: row.imei,
|
||||
shopId: row.shopId,
|
||||
deviceName: row.deviceName,
|
||||
remark: row.remark,
|
||||
};
|
||||
// 加载已有厂商的配置列表
|
||||
if (row.vendorCode) {
|
||||
await loadConfigOptions(row.vendorCode);
|
||||
}
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
async function handleOk() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
// 校验失败:表单已显示错误提示
|
||||
return;
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const payload: DeviceSpeakerParam = { ...formState.value };
|
||||
await (formEditType.value === FormEditType.Edit
|
||||
? DeviceSpeakerApi.update(payload)
|
||||
: DeviceSpeakerApi.add(payload));
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ show, showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
:width="640"
|
||||
:confirm-loading="confirmLoading"
|
||||
:destroy-on-hidden="true"
|
||||
:mask-closable="false"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
class="form-compact"
|
||||
>
|
||||
<!-- 厂商 -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.vendorCode')" name="vendorCode">
|
||||
<a-select
|
||||
v-model:value="formState.vendorCode"
|
||||
:options="vendorOptions"
|
||||
:placeholder="$t('payment.device.speaker.pleaseSelectVendor')"
|
||||
@change="handleVendorChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 厂商配置 -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.vendorConfigId')" name="vendorConfigId">
|
||||
<a-select
|
||||
v-model:value="formState.vendorConfigId"
|
||||
:options="configOptions"
|
||||
:field-names="{ label: 'configName', value: 'id' }"
|
||||
:placeholder="$t('payment.device.speaker.pleaseSelectVendorConfig')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 设备序列号 -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.deviceSn')" name="deviceSn">
|
||||
<a-input v-model:value="formState.deviceSn" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 设备名称 -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.deviceName')">
|
||||
<a-input v-model:value="formState.deviceName" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 设备IMEI -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.imei')">
|
||||
<a-input v-model:value="formState.imei" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<!-- 备注 -->
|
||||
<a-form-item :label="$t('payment.device.speaker.field.remark')">
|
||||
<a-textarea v-model:value="formState.remark" :rows="2" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -1,295 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DeviceSpeakerApi, type DeviceSpeakerResult } from '#/api/payment/device/speaker.api';
|
||||
import { DeviceType, deviceVendorMap, vendorI18nMap } from '#/enums/payment/deviceEnum';
|
||||
import { BQuery, type QueryField } from '#/components/query';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
import DeviceSpeakerEdit from './DeviceSpeakerEdit.vue';
|
||||
|
||||
defineOptions({ name: 'DeviceSpeakerList' });
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const loading = ref(false);
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
const editRef = ref<InstanceType<typeof DeviceSpeakerEdit>>();
|
||||
|
||||
const queryForm = ref<Record<string, any>>({});
|
||||
|
||||
// 厂商选项(查询用)
|
||||
const vendorOptions = computed(() => {
|
||||
const vendorList = deviceVendorMap[DeviceType.SPEAKER] || [];
|
||||
return vendorList.map((v) => ({
|
||||
label: $t(vendorI18nMap[v] || v),
|
||||
value: v,
|
||||
}));
|
||||
});
|
||||
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
type: 'string',
|
||||
field: 'mchNo',
|
||||
name: $t('payment.device.speaker.field.mchNo'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'vendorCode',
|
||||
name: $t('payment.device.speaker.field.vendorCode'),
|
||||
placeholder: $t('common.pleaseSelect'),
|
||||
selectList: vendorOptions.value,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'deviceSn',
|
||||
name: $t('payment.device.speaker.field.deviceSn'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'deviceName',
|
||||
name: $t('payment.device.speaker.field.deviceName'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'status',
|
||||
name: $t('payment.device.speaker.field.status'),
|
||||
placeholder: $t('common.pleaseSelect'),
|
||||
selectList: [
|
||||
{ label: $t('payment.device.speaker.status.unbound'), value: 'unbound' },
|
||||
{ label: $t('payment.device.speaker.status.online'), value: 'online' },
|
||||
{ label: $t('payment.device.speaker.status.offline'), value: 'offline' },
|
||||
{ label: $t('payment.device.speaker.status.fault'), value: 'fault' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const tableData = ref<DeviceSpeakerResult[]>([]);
|
||||
|
||||
/**
|
||||
* 分页查询云音箱设备列表
|
||||
*/
|
||||
function queryPage() {
|
||||
loading.value = true;
|
||||
DeviceSpeakerApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
...queryForm.value,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data.records || [];
|
||||
pageConfig.value.total = Number(res.data.total) || 0;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
queryForm.value = {};
|
||||
pageConfig.value.currentPage = 1;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
function handlePageChange({ currentPage, pageSize }: any) {
|
||||
pageConfig.value.currentPage = currentPage;
|
||||
pageConfig.value.pageSize = pageSize;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
queryPage();
|
||||
});
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.show();
|
||||
}
|
||||
|
||||
function handleEdit(row: DeviceSpeakerResult) {
|
||||
editRef.value?.showEdit(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
*/
|
||||
function handleDelete(row: DeviceSpeakerResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.speaker.confirmDelete'),
|
||||
onOk() {
|
||||
return DeviceSpeakerApi.delete(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定设备(首期仅更新本地状态)
|
||||
*/
|
||||
function handleBind(row: DeviceSpeakerResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.speaker.confirmBind'),
|
||||
onOk() {
|
||||
return DeviceSpeakerApi.bind(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑设备
|
||||
*/
|
||||
function handleUnbind(row: DeviceSpeakerResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.speaker.confirmUnbind'),
|
||||
onOk() {
|
||||
return DeviceSpeakerApi.unbind(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="m-3 p-3 bg-background rounded-lg list-page-compact">
|
||||
<a-card>
|
||||
<BQuery :fields="queryFields" :query-params="queryForm" @query="queryPage" @reset="resetQuery" />
|
||||
</a-card>
|
||||
|
||||
<div class="mt-4">
|
||||
<a-card>
|
||||
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: queryPage }">
|
||||
<template #buttons>
|
||||
<a-space>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Speaker.MANAGE)"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>{{ $t('common.add') }}</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" :loading="loading">
|
||||
<vxe-column type="seq" :title="$t('common.seq')" width="60" align="center" />
|
||||
<vxe-column field="deviceSn" :title="$t('payment.device.speaker.field.deviceSn')" :min-width="180" />
|
||||
<vxe-column field="deviceName" :title="$t('payment.device.speaker.field.deviceName')" :min-width="140" />
|
||||
<vxe-column field="mchNo" :title="$t('payment.device.speaker.field.mchNo')" :min-width="140">
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.mchNo" color="blue">{{ row.mchNo }}</a-tag>
|
||||
<span v-else style="color: var(--text-color-placeholder)">{{ $t('payment.device.speaker.unassigned') }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="vendorCode" :title="$t('payment.device.speaker.field.vendorCode')" :min-width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.vendorCode ? $t(vendorI18nMap[row.vendorCode] || row.vendorCode) : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="status"
|
||||
:title="$t('payment.device.speaker.field.status')"
|
||||
:min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.status === 'online'" color="green">{{
|
||||
$t('payment.device.speaker.status.online')
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="row.status === 'offline'" color="default">{{
|
||||
$t('payment.device.speaker.status.offline')
|
||||
}}</a-tag>
|
||||
<a-tag v-else-if="row.status === 'fault'" color="red">{{
|
||||
$t('payment.device.speaker.status.fault')
|
||||
}}</a-tag>
|
||||
<a-tag v-else color="blue">{{ $t('payment.device.speaker.status.unbound') }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="bindTime"
|
||||
:title="$t('payment.device.speaker.field.bindTime')"
|
||||
:min-width="180"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
:title="$t('payment.device.speaker.field.createTime')"
|
||||
:min-width="180"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column fixed="right" width="200" :show-overflow="false" :title="$t('common.operation')">
|
||||
<template #default="{ row }">
|
||||
<a-space :size="2">
|
||||
<template #separator>
|
||||
<a-divider type="vertical" />
|
||||
</template>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Speaker.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
>{{ $t('common.edit') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Speaker.MANAGE) && row.status !== 'online'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleBind(row)"
|
||||
>{{ $t('payment.device.speaker.bind') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-else-if="hasPermission(PermCodes.Device.Speaker.MANAGE) && row.status === 'online'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleUnbind(row)"
|
||||
>{{ $t('payment.device.speaker.unbind') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.Speaker.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleDelete(row)"
|
||||
>{{ $t('common.delete') }}</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<vxe-pager
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
:current-page="pageConfig.currentPage"
|
||||
:page-size="pageConfig.pageSize"
|
||||
:total="pageConfig.total"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<DeviceSpeakerEdit ref="editRef" @ok="queryPage" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,205 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { DeviceVendorConfigApi, type DeviceVendorConfigParam, type DeviceVendorConfigResult } from '#/api/payment/device/vendor-config.api';
|
||||
import { deviceTypeI18nMap, getDeviceTypesByVendor } from '#/enums/payment/deviceEnum';
|
||||
import { FormEditType } from '#/enums/formEditType';
|
||||
import { useFormEdit } from '#/hooks/useFormEdit';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
|
||||
defineOptions({ name: 'VendorConfigEdit' });
|
||||
|
||||
const emit = defineEmits(['ok']);
|
||||
|
||||
const { message } = useMessage();
|
||||
const formRef = ref();
|
||||
|
||||
const { visible, confirmLoading, title, initFormEditType, handleCancel, formEditType, diffForm } = useFormEdit();
|
||||
|
||||
// 厂商代码(由父组件传入, 同一抽屉内固定)
|
||||
const vendorCode = ref('');
|
||||
|
||||
// 设备类型选项(收敛到该厂商支持的设备类型, 由 deviceVendorMap 反查)
|
||||
const deviceTypeOptions = computed(() =>
|
||||
getDeviceTypesByVendor(vendorCode.value).map((dt) => ({
|
||||
label: $t(deviceTypeI18nMap[dt] || dt),
|
||||
value: dt,
|
||||
})),
|
||||
);
|
||||
|
||||
const formState = ref<DeviceVendorConfigParam>({
|
||||
deviceType: '',
|
||||
configName: '',
|
||||
appId: '',
|
||||
enable: true,
|
||||
});
|
||||
|
||||
// 原始脱敏数据(来自后端), 用于 diffForm 比对敏感字段是否被修改
|
||||
const originalForm = ref<DeviceVendorConfigResult>({});
|
||||
|
||||
const formRules = computed(() => ({
|
||||
deviceType: [{ required: true, message: $t('common.pleaseSelect') }],
|
||||
configName: [{ required: true, message: $t('payment.device.vendor.validateConfigName') }],
|
||||
appId: [{ required: true, message: $t('payment.device.vendor.validateAppId') }],
|
||||
appSecret: [{ required: true, message: $t('payment.device.vendor.validateAppSecret') }],
|
||||
enable: [{ required: true, type: 'boolean', message: $t('common.pleaseSelect') }],
|
||||
}));
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
function resetForm() {
|
||||
formState.value = {
|
||||
deviceType: '',
|
||||
configName: '',
|
||||
appId: '',
|
||||
appSecret: '',
|
||||
enable: true,
|
||||
extParam: '',
|
||||
remark: '',
|
||||
};
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增弹窗
|
||||
*/
|
||||
function show(vc: string) {
|
||||
vendorCode.value = vc;
|
||||
initFormEditType(FormEditType.Add);
|
||||
title.value = $t('payment.device.vendor.action.addConfig');
|
||||
resetForm();
|
||||
// 首次新增无原值, originalForm 置空以便 diffForm 正确识别用户输入的新值
|
||||
originalForm.value = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑弹窗
|
||||
*/
|
||||
async function showEdit(vc: string, record: DeviceVendorConfigResult) {
|
||||
vendorCode.value = vc;
|
||||
initFormEditType(FormEditType.Edit);
|
||||
title.value = $t('payment.device.vendor.action.editConfig');
|
||||
resetForm();
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const { data } = await DeviceVendorConfigApi.get(record.id!);
|
||||
const row = data || record;
|
||||
// 保存原始脱敏数据用于 diffForm 比对
|
||||
originalForm.value = { ...row };
|
||||
formState.value = {
|
||||
id: row.id ?? undefined,
|
||||
deviceType: row.deviceType,
|
||||
configName: row.configName,
|
||||
appId: row.appId,
|
||||
// 预填脱敏值, 未修改时 diffForm 返回 undefined(后端跳过更新)
|
||||
appSecret: row.appSecret,
|
||||
enable: row.enable,
|
||||
extParam: row.extParam,
|
||||
remark: row.remark,
|
||||
};
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
async function handleOk() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
// 校验失败:表单已显示错误提示
|
||||
return;
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
// 敏感字段(appSecret)用 diffForm 比对: 未修改返回 undefined(后端跳过), 修改则返回新值
|
||||
const sensitiveData = diffForm(originalForm.value, formState.value, 'appSecret');
|
||||
const payload = {
|
||||
...formState.value,
|
||||
...sensitiveData,
|
||||
vendorCode: vendorCode.value,
|
||||
} as DeviceVendorConfigParam;
|
||||
await (formEditType.value === FormEditType.Edit
|
||||
? DeviceVendorConfigApi.update(payload)
|
||||
: DeviceVendorConfigApi.add(payload));
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ show, showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
:width="600"
|
||||
:confirm-loading="confirmLoading"
|
||||
:destroy-on-hidden="true"
|
||||
:mask-closable="false"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
class="form-compact"
|
||||
>
|
||||
<!-- 设备类型 -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.deviceType')" name="deviceType">
|
||||
<a-select
|
||||
v-model:value="formState.deviceType"
|
||||
:options="deviceTypeOptions"
|
||||
:placeholder="$t('common.pleaseSelect')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 配置名称 -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.configName')" name="configName">
|
||||
<a-input
|
||||
v-model:value="formState.configName"
|
||||
:placeholder="$t('payment.device.vendor.placeholder.configName')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 应用ID -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.appId')" name="appId">
|
||||
<a-input
|
||||
v-model:value="formState.appId"
|
||||
:placeholder="$t('payment.device.vendor.placeholder.appId')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 应用密钥 -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.appSecret')" name="appSecret">
|
||||
<a-input
|
||||
v-model:value="formState.appSecret"
|
||||
:placeholder="$t('payment.device.vendor.placeholder.appSecret')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 是否启用 -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.enable')" name="enable">
|
||||
<a-switch v-model:checked="formState.enable" />
|
||||
</a-form-item>
|
||||
<!-- 备注 -->
|
||||
<a-form-item :label="$t('payment.device.vendor.field.remark')">
|
||||
<a-textarea
|
||||
v-model:value="formState.remark"
|
||||
:rows="2"
|
||||
:placeholder="$t('common.pleaseInput')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -1,300 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import VendorLogo from '#/components/device-vendor/VendorLogo.vue';
|
||||
import { DeviceVendorConfigApi, type DeviceVendorConfigResult } from '#/api/payment/device/vendor-config.api';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { deviceTypeI18nMap, deviceVendorMap, vendorI18nMap } from '#/enums/payment/deviceEnum';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
import VendorConfigEdit from './VendorConfigEdit.vue';
|
||||
|
||||
defineOptions({ name: 'VendorManage' });
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
// 厂商卡片数据(平台级通用: 所有至少被一种设备类型支持的厂商并集)
|
||||
const vendors = computed(() => {
|
||||
const set = new Set<string>();
|
||||
Object.values(deviceVendorMap).forEach((list) => list.forEach((v) => set.add(v)));
|
||||
return [...set].map((v) => ({
|
||||
key: v,
|
||||
// 厂商名称
|
||||
title: $t(vendorI18nMap[v] || v),
|
||||
vendorCode: v,
|
||||
}));
|
||||
});
|
||||
|
||||
// 抽屉
|
||||
const drawerVisible = ref(false);
|
||||
const loading = ref(false);
|
||||
const currentVendor = ref('');
|
||||
|
||||
// 配置列表
|
||||
const tableData = ref<DeviceVendorConfigResult[]>([]);
|
||||
const editRef = ref<InstanceType<typeof VendorConfigEdit>>();
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
|
||||
// 分页
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* 卡片点击 - 打开抽屉加载该厂商在各设备类型的配置列表
|
||||
*/
|
||||
function handleCardClick(vendorCode: string) {
|
||||
currentVendor.value = vendorCode;
|
||||
drawerVisible.value = true;
|
||||
pageConfig.value.currentPage = 1;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询厂商配置列表(跨设备类型, 按 vendorCode 聚合)
|
||||
*/
|
||||
function queryPage() {
|
||||
loading.value = true;
|
||||
DeviceVendorConfigApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
vendorCode: currentVendor.value,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data?.records || [];
|
||||
pageConfig.value.total = Number(res.data?.total) || 0;
|
||||
loading.value = false;
|
||||
nextTick(() => {
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页变化
|
||||
*/
|
||||
function handlePageChange({ currentPage, pageSize }: any) {
|
||||
pageConfig.value.currentPage = currentPage;
|
||||
pageConfig.value.pageSize = pageSize;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.show(currentVendor.value);
|
||||
}
|
||||
|
||||
function handleEdit(row: DeviceVendorConfigResult) {
|
||||
editRef.value?.showEdit(currentVendor.value, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置
|
||||
*/
|
||||
function handleDelete(row: DeviceVendorConfigResult) {
|
||||
confirm({
|
||||
content: $t('payment.device.vendor.confirmDelete'),
|
||||
onOk() {
|
||||
return DeviceVendorConfigApi.delete(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭抽屉
|
||||
*/
|
||||
function handleDrawerClose() {
|
||||
drawerVisible.value = false;
|
||||
currentVendor.value = '';
|
||||
tableData.value = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="m-4">
|
||||
<a-card variant="borderless" class="rounded-xl shadow-sm">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-lg font-bold text-foreground">{{ $t('payment.device.vendor.title') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 厂商卡片网格 -->
|
||||
<div class="card-grid">
|
||||
<a-card
|
||||
v-for="card in vendors"
|
||||
:key="card.key"
|
||||
class="vendor-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: 0, display: 'flex', flexDirection: 'column', height: '100%' } }"
|
||||
>
|
||||
<!-- 主体内容 -->
|
||||
<div class="flex-1 flex flex-col items-center justify-center pt-6 pb-3">
|
||||
<div class="mb-3 transform transition-transform duration-300 group-hover:scale-110">
|
||||
<VendorLogo :vendor="card.vendorCode" :size="52" />
|
||||
</div>
|
||||
<div class="text-center font-bold text-foreground text-[15px] px-4">
|
||||
{{ card.title }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部配置条 -->
|
||||
<div class="flex border-t border-border h-10 bg-muted/50">
|
||||
<div class="config-slot" @click="handleCardClick(card.vendorCode)">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<IconifyIcon icon="ant-design:setting-filled" class="text-blue-500/80 text-sm" />
|
||||
<span class="text-[10px] font-bold text-muted-foreground uppercase">
|
||||
{{ $t('payment.device.vendor.title') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 配置列表抽屉 -->
|
||||
<a-drawer
|
||||
v-model:open="drawerVisible"
|
||||
:size="1080"
|
||||
@close="handleDrawerClose"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<VendorLogo :vendor="currentVendor" :size="24" />
|
||||
<span>{{ $t('payment.device.vendor.title') }}</span>
|
||||
<span class="text-muted-foreground">·</span>
|
||||
<span class="text-muted-foreground">{{ $t(vendorI18nMap[currentVendor] || currentVendor) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<a-spin :spinning="loading">
|
||||
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: queryPage }">
|
||||
<template #buttons>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.VendorConfig.MANAGE)"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
{{ $t('payment.device.vendor.action.addConfig') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" size="small">
|
||||
<vxe-column type="seq" :title="$t('common.seq')" width="60" align="center" />
|
||||
<vxe-column
|
||||
field="deviceType"
|
||||
:title="$t('payment.device.vendor.field.deviceType')"
|
||||
:min-width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ row.deviceType ? $t(deviceTypeI18nMap[row.deviceType] || row.deviceType) : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="configName"
|
||||
:title="$t('payment.device.vendor.field.configName')"
|
||||
:min-width="140"
|
||||
/>
|
||||
<vxe-column
|
||||
field="appId"
|
||||
:title="$t('payment.device.vendor.field.appId')"
|
||||
:min-width="160"
|
||||
/>
|
||||
<vxe-column
|
||||
:title="$t('payment.device.vendor.field.enable')"
|
||||
width="80"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.enable" color="green">{{ $t('payment.device.vendor.enable') }}</a-tag>
|
||||
<a-tag v-else>{{ $t('payment.device.vendor.disable') }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
:title="$t('payment.device.vendor.field.createTime')"
|
||||
:min-width="160"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column :title="$t('common.operation')" width="120" fixed="right" :show-overflow="false">
|
||||
<template #default="{ row }">
|
||||
<a-space :size="2">
|
||||
<template #separator>
|
||||
<a-divider type="vertical" />
|
||||
</template>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.VendorConfig.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
>{{ $t('common.edit') }}</a-button
|
||||
>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Device.VendorConfig.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleDelete(row)"
|
||||
>{{ $t('common.delete') }}</a-button
|
||||
>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<vxe-pager
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
:current-page="pageConfig.currentPage"
|
||||
:page-size="pageConfig.pageSize"
|
||||
:total="pageConfig.total"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</a-spin>
|
||||
<template #footer>
|
||||
<a-button @click="handleDrawerClose">{{ $t('common.close') }}</a-button>
|
||||
</template>
|
||||
</a-drawer>
|
||||
|
||||
<VendorConfigEdit ref="editRef" @ok="queryPage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.vendor-card {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.config-slot {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.config-slot:hover {
|
||||
background-color: hsl(var(--background));
|
||||
box-shadow: inset 0 -2px 0 0 hsl(var(--primary));
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user