mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-11 23:25:33 +08:00
feat(admin): 终端管理页面与菜单迁移至商户工作台
- 系统终端页面移至 merchant/terminal/(照搬门店模式: 强制 mchNo 路由参数+返回按钮+商户名) - 通道终端面板(device/terminal/channel)嵌入通道商户详情抽屉, 复用 channel:merchant 权限 - 商户工作台基础管理组新增系统终端卡片入口 - 权限码 device:terminal:system → merchant:terminal(perm-codes.ts) - menu-titles/workbench 10语种同步(cardTerminal + menu.payment.merchant.terminal)
This commit is contained in:
174
apps/daxpay-admin/src/api/payment/device/terminal.api.ts
Normal file
174
apps/daxpay-admin/src/api/payment/device/terminal.api.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import type { BaseEntity, PageResult, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 系统终端 API
|
||||
*/
|
||||
export const TerminalDeviceApi = {
|
||||
/** 分页 */
|
||||
page(
|
||||
params: TerminalDeviceQuery & { current?: number; size?: number },
|
||||
): Promise<Result<PageResult<TerminalDeviceResult>>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/system/page', params });
|
||||
},
|
||||
/** 详情 */
|
||||
get(id: string): Promise<Result<TerminalDeviceResult>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/system/get', params: { id } });
|
||||
},
|
||||
/** 按商户列表 */
|
||||
listByMchNo(mchNo: string): Promise<Result<TerminalDeviceResult[]>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/system/list-by-mch-no', params: { mchNo } });
|
||||
},
|
||||
/** 新增 */
|
||||
add(data: TerminalDeviceParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/system/add', data });
|
||||
},
|
||||
/** 修改 */
|
||||
update(data: TerminalDeviceParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/system/update', data });
|
||||
},
|
||||
/** 删除 */
|
||||
delete(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/system/delete', params: { id } });
|
||||
},
|
||||
/** 绑定通道终端 */
|
||||
bind(data: TerminalChannelBindParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/system/bind', data });
|
||||
},
|
||||
/** 解绑通道终端 */
|
||||
unbind(data: TerminalChannelBindParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/system/unbind', data });
|
||||
},
|
||||
/** 已绑定通道终端 */
|
||||
boundChannelList(terminalNo: string): Promise<Result<ChannelTerminalResult[]>> {
|
||||
return defHttp.get({
|
||||
url: '/admin/device/terminal/system/bound-channel-list',
|
||||
params: { terminalNo },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 通道终端 API
|
||||
*/
|
||||
export const ChannelTerminalApi = {
|
||||
/** 分页 */
|
||||
page(
|
||||
params: ChannelTerminalQuery & { current?: number; size?: number },
|
||||
): Promise<Result<PageResult<ChannelTerminalResult>>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/channel/page', params });
|
||||
},
|
||||
/** 详情 */
|
||||
get(id: string): Promise<Result<ChannelTerminalResult>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/channel/get', params: { id } });
|
||||
},
|
||||
/** 按商户列表 */
|
||||
listByMchNo(mchNo: string): Promise<Result<ChannelTerminalResult[]>> {
|
||||
return defHttp.get({ url: '/admin/device/terminal/channel/list-by-mch-no', params: { mchNo } });
|
||||
},
|
||||
/** 新增 */
|
||||
add(data: ChannelTerminalParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/channel/add', data });
|
||||
},
|
||||
/** 修改 */
|
||||
update(data: ChannelTerminalParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/channel/update', data });
|
||||
},
|
||||
/** 删除 */
|
||||
delete(id: string): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/channel/delete', params: { id } });
|
||||
},
|
||||
/** 绑定系统终端 */
|
||||
bind(data: TerminalChannelBindParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/channel/bind', data });
|
||||
},
|
||||
/** 解绑系统终端 */
|
||||
unbind(data: TerminalChannelBindParam): Promise<Result<void>> {
|
||||
return defHttp.post({ url: '/admin/device/terminal/channel/unbind', data });
|
||||
},
|
||||
/** 已绑定系统终端 */
|
||||
boundSystemList(channelTerminalId: string): Promise<Result<TerminalDeviceResult[]>> {
|
||||
return defHttp.get({
|
||||
url: '/admin/device/terminal/channel/bound-system-list',
|
||||
params: { channelTerminalId },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/** 系统终端查询 */
|
||||
export interface TerminalDeviceQuery {
|
||||
mchNo?: string;
|
||||
terminalNo?: string;
|
||||
name?: string;
|
||||
storeNo?: string;
|
||||
enable?: boolean;
|
||||
}
|
||||
|
||||
/** 系统终端参数 */
|
||||
export interface TerminalDeviceParam {
|
||||
id?: string;
|
||||
mchNo?: string;
|
||||
name?: string;
|
||||
storeNo?: string;
|
||||
enable?: boolean;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 系统终端结果 */
|
||||
export interface TerminalDeviceResult extends BaseEntity {
|
||||
terminalNo?: string;
|
||||
name?: string;
|
||||
mchNo?: string;
|
||||
mchName?: string;
|
||||
storeNo?: string;
|
||||
storeName?: string;
|
||||
enable?: boolean;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 通道终端查询 */
|
||||
export interface ChannelTerminalQuery {
|
||||
mchNo?: string;
|
||||
channelMchNo?: string;
|
||||
channel?: string;
|
||||
product?: string;
|
||||
type?: string;
|
||||
name?: string;
|
||||
outTerminalNo?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/** 通道终端参数 */
|
||||
export interface ChannelTerminalParam {
|
||||
id?: string;
|
||||
mchNo?: string;
|
||||
channelMchNo?: string;
|
||||
type?: string;
|
||||
name?: string;
|
||||
outTerminalNo?: string;
|
||||
status?: string;
|
||||
errorMsg?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 通道终端结果 */
|
||||
export interface ChannelTerminalResult extends BaseEntity {
|
||||
channelMchNo?: string;
|
||||
product?: string;
|
||||
channel?: string;
|
||||
type?: string;
|
||||
name?: string;
|
||||
outTerminalNo?: string;
|
||||
status?: string;
|
||||
errorMsg?: string;
|
||||
remark?: string;
|
||||
mchNo?: string;
|
||||
mchName?: string;
|
||||
}
|
||||
|
||||
/** 绑定参数 */
|
||||
export interface TerminalChannelBindParam {
|
||||
systemTerminalNo: string;
|
||||
channelTerminalId: string;
|
||||
}
|
||||
@@ -85,6 +85,11 @@ export const PermCodes = {
|
||||
VIEW: 'merchant:store:view',
|
||||
MANAGE: 'merchant:store:manage',
|
||||
},
|
||||
/** 系统终端 menuCode=merchant:terminal */
|
||||
Terminal: {
|
||||
VIEW: 'merchant:terminal:view',
|
||||
MANAGE: 'merchant:terminal:manage',
|
||||
},
|
||||
/** 预留 */
|
||||
User: {
|
||||
VIEW: 'merchant:user:view',
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "System Terminals",
|
||||
"channelTitle": "Channel Terminals",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"bindManage": "Bindings",
|
||||
"confirmDelete": "Delete this terminal?",
|
||||
"confirmUnbind": "Remove this binding?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "Merchant User",
|
||||
"cardUserDesc": "User account management and permission assignment",
|
||||
"cardStore": "Store Management",
|
||||
"cardStoreDesc": "Physical store profiles and location management"
|
||||
"cardStoreDesc": "Physical store profiles and location management",
|
||||
"cardTerminal": "System Terminals",
|
||||
"cardTerminalDesc": "Merchant logical terminal codes and channel terminal binding management"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "System Terminals",
|
||||
"channelTitle": "Channel Terminals",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"bindManage": "Bindings",
|
||||
"confirmDelete": "Delete this terminal?",
|
||||
"confirmUnbind": "Remove this binding?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "Pengguna Pedagang",
|
||||
"cardUserDesc": "Manajemen akun pengguna dan penetapan izin",
|
||||
"cardStore": "Manajemen Toko",
|
||||
"cardStoreDesc": "Profil toko fisik dan manajemen lokasi"
|
||||
"cardStoreDesc": "Profil toko fisik dan manajemen lokasi",
|
||||
"cardTerminal": "Terminal Sistem",
|
||||
"cardTerminalDesc": "Kelola kode terminal logis pedagang dan pengikatan terminal saluran"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "システム端末",
|
||||
"channelTitle": "チャネル端末",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "追加",
|
||||
"edit": "編集",
|
||||
"bind": "紐付け",
|
||||
"unbind": "解除",
|
||||
"bindManage": "紐付け管理",
|
||||
"confirmDelete": "この端末を削除しますか?",
|
||||
"confirmUnbind": "紐付けを解除しますか?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "販売者ユーザー",
|
||||
"cardUserDesc": "ユーザーアカウントの管理と権限の割り当て",
|
||||
"cardStore": "店舗管理",
|
||||
"cardStoreDesc": "実店舗のプロファイルとロケーション管理"
|
||||
"cardStoreDesc": "実店舗のプロファイルとロケーション管理",
|
||||
"cardTerminal": "システム端末",
|
||||
"cardTerminalDesc": "マーチャント論理端末とチャネル端末のバインド管理"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "시스템 단말",
|
||||
"channelTitle": "채널 단말",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "추가",
|
||||
"edit": "편집",
|
||||
"bind": "연결",
|
||||
"unbind": "해제",
|
||||
"bindManage": "연결 관리",
|
||||
"confirmDelete": "이 단말을 삭제하시겠습니까?",
|
||||
"confirmUnbind": "연결을 해제하시겠습니까?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "판매자 사용자",
|
||||
"cardUserDesc": "사용자 계정 관리 및 권한 할당",
|
||||
"cardStore": "매장관리",
|
||||
"cardStoreDesc": "실제 매장 프로필 및 위치 관리"
|
||||
"cardStoreDesc": "실제 매장 프로필 및 위치 관리",
|
||||
"cardTerminal": "시스템 단말",
|
||||
"cardTerminalDesc": "머천트 논리 단말 및 채널 단말 바인딩 관리"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "System Terminals",
|
||||
"channelTitle": "Channel Terminals",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"bindManage": "Bindings",
|
||||
"confirmDelete": "Delete this terminal?",
|
||||
"confirmUnbind": "Remove this binding?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "Pengguna Saudagar",
|
||||
"cardUserDesc": "Pengurusan akaun pengguna dan tugasan kebenaran",
|
||||
"cardStore": "Pengurusan Stor",
|
||||
"cardStoreDesc": "Profil kedai fizikal dan pengurusan lokasi"
|
||||
"cardStoreDesc": "Profil kedai fizikal dan pengurusan lokasi",
|
||||
"cardTerminal": "Terminal Sistem",
|
||||
"cardTerminalDesc": "Urus kod terminal logik pedagang dan pengikatan terminal saluran"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "System Terminals",
|
||||
"channelTitle": "Channel Terminals",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"bindManage": "Bindings",
|
||||
"confirmDelete": "Delete this terminal?",
|
||||
"confirmUnbind": "Remove this binding?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "ผู้ใช้ร้านค้า",
|
||||
"cardUserDesc": "การจัดการบัญชีผู้ใช้และการมอบหมายสิทธิ์",
|
||||
"cardStore": "การจัดการร้านค้า",
|
||||
"cardStoreDesc": "โปรไฟล์ร้านค้าทางกายภาพและการจัดการสถานที่ตั้ง"
|
||||
"cardStoreDesc": "โปรไฟล์ร้านค้าทางกายภาพและการจัดการสถานที่ตั้ง",
|
||||
"cardTerminal": "เทอร์มินัลระบบ",
|
||||
"cardTerminalDesc": "จัดการรหัสเทอร์มินัลตรรกะของผู้ค้าและการผูกเทอร์มินัลช่องทาง"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "System Terminals",
|
||||
"channelTitle": "Channel Terminals",
|
||||
"field": {
|
||||
"terminalNo": "Terminal No.",
|
||||
"name": "Name",
|
||||
"mchNo": "Merchant No.",
|
||||
"merchant": "Merchant",
|
||||
"store": "Store",
|
||||
"storeNo": "Store No.",
|
||||
"enable": "Enabled",
|
||||
"remark": "Remark",
|
||||
"createTime": "Created At",
|
||||
"channelMchNo": "Channel Merchant No.",
|
||||
"channel": "Channel",
|
||||
"product": "Product",
|
||||
"type": "Report Type",
|
||||
"outTerminalNo": "Channel Terminal No.",
|
||||
"status": "Status",
|
||||
"errorMsg": "Error Message"
|
||||
},
|
||||
"enableYes": "Enabled",
|
||||
"enableNo": "Disabled",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"bindManage": "Bindings",
|
||||
"confirmDelete": "Delete this terminal?",
|
||||
"confirmUnbind": "Remove this binding?",
|
||||
"validateName": "Please enter name",
|
||||
"validateMchNo": "Please select merchant",
|
||||
"validateChannelMchNo": "Please select channel merchant",
|
||||
"validateType": "Please select report type",
|
||||
"validateEnable": "Please select status",
|
||||
"pleaseSelectMch": "Select merchant",
|
||||
"pleaseSelectStore": "Select store (optional)",
|
||||
"pleaseSelectChannelMch": "Select channel merchant",
|
||||
"pleaseSelectTerminal": "Select system terminal",
|
||||
"pleaseSelectChannelTerminal": "Select channel terminal",
|
||||
"boundChannel": "Bound channel terminals",
|
||||
"boundSystem": "Bound system terminals",
|
||||
"noBound": "No bindings",
|
||||
"addBind": "Add binding"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "Người dùng thương gia",
|
||||
"cardUserDesc": "Quản lý tài khoản người dùng và phân quyền",
|
||||
"cardStore": "Quản lý cửa hàng",
|
||||
"cardStoreDesc": "Hồ sơ cửa hàng vật lý và quản lý vị trí"
|
||||
"cardStoreDesc": "Hồ sơ cửa hàng vật lý và quản lý vị trí",
|
||||
"cardTerminal": "Thiết bị hệ thống",
|
||||
"cardTerminalDesc": "Quản lý mã thiết bị logic của thương gia và liên kết thiết bị kênh"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "系统终端",
|
||||
"channelTitle": "通道终端",
|
||||
"field": {
|
||||
"terminalNo": "终端编码",
|
||||
"name": "终端名称",
|
||||
"mchNo": "商户号",
|
||||
"merchant": "商户",
|
||||
"store": "门店",
|
||||
"storeNo": "门店号",
|
||||
"enable": "启用",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间",
|
||||
"channelMchNo": "通道商户号",
|
||||
"channel": "通道",
|
||||
"product": "支付产品",
|
||||
"type": "报送类型",
|
||||
"outTerminalNo": "通道终端号",
|
||||
"status": "登记状态",
|
||||
"errorMsg": "错误信息"
|
||||
},
|
||||
"enableYes": "启用",
|
||||
"enableNo": "停用",
|
||||
"add": "新增",
|
||||
"edit": "编辑",
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"bindManage": "绑定管理",
|
||||
"confirmDelete": "确定删除该终端吗?",
|
||||
"confirmUnbind": "确定解除绑定吗?",
|
||||
"validateName": "请输入终端名称",
|
||||
"validateMchNo": "请选择商户",
|
||||
"validateChannelMchNo": "请选择通道商户",
|
||||
"validateType": "请选择报送类型",
|
||||
"validateEnable": "请选择启用状态",
|
||||
"pleaseSelectMch": "请选择商户",
|
||||
"pleaseSelectStore": "请选择门店(可选)",
|
||||
"pleaseSelectChannelMch": "请选择通道商户",
|
||||
"pleaseSelectTerminal": "请选择系统终端",
|
||||
"pleaseSelectChannelTerminal": "请选择通道终端",
|
||||
"boundChannel": "已绑定通道终端",
|
||||
"boundSystem": "已绑定系统终端",
|
||||
"noBound": "暂无绑定",
|
||||
"addBind": "添加绑定"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "商户用户",
|
||||
"cardUserDesc": "用户账号管理与权限分配",
|
||||
"cardStore": "门店管理",
|
||||
"cardStoreDesc": "商户物理门店档案与位置信息管理"
|
||||
"cardStoreDesc": "商户物理门店档案与位置信息管理",
|
||||
"cardTerminal": "系统终端",
|
||||
"cardTerminalDesc": "商户逻辑终端编号与通道终端绑定管理"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "系統終端",
|
||||
"channelTitle": "通道終端",
|
||||
"field": {
|
||||
"terminalNo": "终端编码",
|
||||
"name": "终端名称",
|
||||
"mchNo": "商户号",
|
||||
"merchant": "商户",
|
||||
"store": "门店",
|
||||
"storeNo": "门店号",
|
||||
"enable": "启用",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间",
|
||||
"channelMchNo": "通道商户号",
|
||||
"channel": "通道",
|
||||
"product": "支付产品",
|
||||
"type": "报送类型",
|
||||
"outTerminalNo": "通道终端号",
|
||||
"status": "登记状态",
|
||||
"errorMsg": "错误信息"
|
||||
},
|
||||
"enableYes": "启用",
|
||||
"enableNo": "停用",
|
||||
"add": "新增",
|
||||
"edit": "編輯",
|
||||
"bind": "綁定",
|
||||
"unbind": "解綁",
|
||||
"bindManage": "綁定管理",
|
||||
"confirmDelete": "確定刪除該終端嗎?",
|
||||
"confirmUnbind": "確定解除綁定嗎?",
|
||||
"validateName": "请输入终端名称",
|
||||
"validateMchNo": "请选择商户",
|
||||
"validateChannelMchNo": "请选择通道商户",
|
||||
"validateType": "请选择报送类型",
|
||||
"validateEnable": "请选择启用状态",
|
||||
"pleaseSelectMch": "请选择商户",
|
||||
"pleaseSelectStore": "请选择门店(可选)",
|
||||
"pleaseSelectChannelMch": "请选择通道商户",
|
||||
"pleaseSelectTerminal": "请选择系统终端",
|
||||
"pleaseSelectChannelTerminal": "请选择通道终端",
|
||||
"boundChannel": "已绑定通道终端",
|
||||
"boundSystem": "已绑定系统终端",
|
||||
"noBound": "暂无绑定",
|
||||
"addBind": "添加绑定"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "商户用户",
|
||||
"cardUserDesc": "用户賬號管理與權限分配",
|
||||
"cardStore": "門店管理",
|
||||
"cardStoreDesc": "商户物理門店檔案與位置信息管理"
|
||||
"cardStoreDesc": "商户物理門店檔案與位置信息管理",
|
||||
"cardTerminal": "系統終端",
|
||||
"cardTerminalDesc": "商户邏輯終端編號與通道終端綁定管理"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"systemTitle": "系統終端",
|
||||
"channelTitle": "通道終端",
|
||||
"field": {
|
||||
"terminalNo": "终端编码",
|
||||
"name": "终端名称",
|
||||
"mchNo": "商户号",
|
||||
"merchant": "商户",
|
||||
"store": "门店",
|
||||
"storeNo": "门店号",
|
||||
"enable": "启用",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间",
|
||||
"channelMchNo": "通道商户号",
|
||||
"channel": "通道",
|
||||
"product": "支付产品",
|
||||
"type": "报送类型",
|
||||
"outTerminalNo": "通道终端号",
|
||||
"status": "登记状态",
|
||||
"errorMsg": "错误信息"
|
||||
},
|
||||
"enableYes": "启用",
|
||||
"enableNo": "停用",
|
||||
"add": "新增",
|
||||
"edit": "編輯",
|
||||
"bind": "綁定",
|
||||
"unbind": "解綁",
|
||||
"bindManage": "綁定管理",
|
||||
"confirmDelete": "確定刪除該終端嗎?",
|
||||
"confirmUnbind": "確定解除綁定嗎?",
|
||||
"validateName": "请输入终端名称",
|
||||
"validateMchNo": "请选择商户",
|
||||
"validateChannelMchNo": "请选择通道商户",
|
||||
"validateType": "请选择报送类型",
|
||||
"validateEnable": "请选择启用状态",
|
||||
"pleaseSelectMch": "请选择商户",
|
||||
"pleaseSelectStore": "请选择门店(可选)",
|
||||
"pleaseSelectChannelMch": "请选择通道商户",
|
||||
"pleaseSelectTerminal": "请选择系统终端",
|
||||
"pleaseSelectChannelTerminal": "请选择通道终端",
|
||||
"boundChannel": "已绑定通道终端",
|
||||
"boundSystem": "已绑定系统终端",
|
||||
"noBound": "暂无绑定",
|
||||
"addBind": "添加绑定"
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
"cardUser": "商戶使用者",
|
||||
"cardUserDesc": "使用者賬號管理與許可權分配",
|
||||
"cardStore": "門店管理",
|
||||
"cardStoreDesc": "商戶物理門店檔案與位置資訊管理"
|
||||
"cardStoreDesc": "商戶物理門店檔案與位置資訊管理",
|
||||
"cardTerminal": "系統終端",
|
||||
"cardTerminalDesc": "商戶邏輯終端編號與通道終端綁定管理"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "Security Management",
|
||||
"menu.system.security.system": "System Security",
|
||||
"menu.system.security.pay": "Pay Security",
|
||||
"menu.device.terminal.system": "System Terminals",
|
||||
"menu.payment.merchant.terminal": "System Terminals",
|
||||
"menu.device.terminal.channel": "Channel Terminals"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "Manajemen Keamanan",
|
||||
"menu.system.security.system": "Keamanan Sistem",
|
||||
"menu.system.security.pay": "Keamanan Pembayaran",
|
||||
"menu.device.terminal.system": "Terminal Sistem",
|
||||
"menu.payment.merchant.terminal": "Terminal Sistem",
|
||||
"menu.device.terminal.channel": "Terminal Saluran"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "セキュリティ管理",
|
||||
"menu.system.security.system": "システムセキュリティ",
|
||||
"menu.system.security.pay": "決済セキュリティ",
|
||||
"menu.device.terminal.system": "システム端末",
|
||||
"menu.payment.merchant.terminal": "システム端末",
|
||||
"menu.device.terminal.channel": "チャネル端末"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "보안 관리",
|
||||
"menu.system.security.system": "시스템 보안",
|
||||
"menu.system.security.pay": "결제 보안",
|
||||
"menu.device.terminal.system": "시스템 단말",
|
||||
"menu.payment.merchant.terminal": "시스템 단말",
|
||||
"menu.device.terminal.channel": "채널 단말"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "Pengurusan Keselamatan",
|
||||
"menu.system.security.system": "Keselamatan Sistem",
|
||||
"menu.system.security.pay": "Keselamatan Pembayaran",
|
||||
"menu.device.terminal.system": "Terminal Sistem",
|
||||
"menu.payment.merchant.terminal": "Terminal Sistem",
|
||||
"menu.device.terminal.channel": "Terminal Saluran"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "การจัดการความปลอดภัย",
|
||||
"menu.system.security.system": "ความปลอดภัยระบบ",
|
||||
"menu.system.security.pay": "ความปลอดภัยการชำระเงิน",
|
||||
"menu.device.terminal.system": "เทอร์มินัลระบบ",
|
||||
"menu.payment.merchant.terminal": "เทอร์มินัลระบบ",
|
||||
"menu.device.terminal.channel": "เทอร์มินัลช่องทาง"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "Quản lý bảo mật",
|
||||
"menu.system.security.system": "Bảo mật hệ thống",
|
||||
"menu.system.security.pay": "Bảo mật thanh toán",
|
||||
"menu.device.terminal.system": "Thiết bị hệ thống",
|
||||
"menu.payment.merchant.terminal": "Thiết bị hệ thống",
|
||||
"menu.device.terminal.channel": "Thiết bị kênh"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "安全管理",
|
||||
"menu.system.security.system": "系统安全配置",
|
||||
"menu.system.security.pay": "支付安全配置",
|
||||
"menu.device.terminal.system": "系统终端",
|
||||
"menu.payment.merchant.terminal": "系统终端",
|
||||
"menu.device.terminal.channel": "通道终端"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "安全管理",
|
||||
"menu.system.security.system": "系統安全配置",
|
||||
"menu.system.security.pay": "支付安全配置",
|
||||
"menu.device.terminal.system": "系統終端",
|
||||
"menu.payment.merchant.terminal": "系統終端",
|
||||
"menu.device.terminal.channel": "通道終端"
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@
|
||||
"menu.system.security": "安全管理",
|
||||
"menu.system.security.system": "系統安全配置",
|
||||
"menu.system.security.pay": "支付安全配置",
|
||||
"menu.device.terminal.system": "系統終端",
|
||||
"menu.payment.merchant.terminal": "系統終端",
|
||||
"menu.device.terminal.channel": "通道終端"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ChannelTerminalApi,
|
||||
TerminalDeviceApi,
|
||||
type ChannelTerminalResult,
|
||||
type TerminalDeviceResult,
|
||||
} from '#/api/payment/device/terminal.api';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const current = ref<ChannelTerminalResult | null>(null);
|
||||
const boundList = ref<TerminalDeviceResult[]>([]);
|
||||
const candidateList = ref<TerminalDeviceResult[]>([]);
|
||||
const selectedTerminalNo = ref<string | undefined>();
|
||||
|
||||
const title = computed(() => {
|
||||
if (!current.value) {
|
||||
return $t('payment.device.terminal.bindManage');
|
||||
}
|
||||
return `${$t('payment.device.terminal.bindManage')} - ${current.value.name || current.value.id}`;
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开绑定抽屉
|
||||
*/
|
||||
async function show(row: ChannelTerminalResult) {
|
||||
current.value = row;
|
||||
selectedTerminalNo.value = undefined;
|
||||
visible.value = true;
|
||||
await refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新绑定列表
|
||||
*/
|
||||
async function refresh() {
|
||||
if (!current.value?.id || !current.value.mchNo) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const [boundRes, allRes] = await Promise.all([
|
||||
ChannelTerminalApi.boundSystemList(current.value.id),
|
||||
TerminalDeviceApi.listByMchNo(current.value.mchNo),
|
||||
]);
|
||||
boundList.value = boundRes.data || [];
|
||||
const boundNos = new Set(boundList.value.map((i) => i.terminalNo));
|
||||
candidateList.value = (allRes.data || []).filter((i) => !boundNos.has(i.terminalNo));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加绑定
|
||||
*/
|
||||
async function handleBind() {
|
||||
if (!selectedTerminalNo.value || !current.value?.id) {
|
||||
message.warning($t('payment.device.terminal.pleaseSelectTerminal'));
|
||||
return;
|
||||
}
|
||||
await ChannelTerminalApi.bind({
|
||||
systemTerminalNo: selectedTerminalNo.value,
|
||||
channelTerminalId: current.value.id,
|
||||
});
|
||||
message.success($t('common.operationSuccess'));
|
||||
selectedTerminalNo.value = undefined;
|
||||
await refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑
|
||||
*/
|
||||
function handleUnbind(row: TerminalDeviceResult) {
|
||||
if (!current.value?.id || !row.terminalNo) {
|
||||
return;
|
||||
}
|
||||
confirm({
|
||||
content: $t('payment.device.terminal.confirmUnbind'),
|
||||
onOk() {
|
||||
return ChannelTerminalApi.unbind({
|
||||
systemTerminalNo: row.terminalNo!,
|
||||
channelTerminalId: current.value!.id!,
|
||||
}).then(async () => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
await refresh();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ show });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-drawer v-model:open="visible" :title="title" :width="640" :destroy-on-close="true">
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="hasPermission(PermCodes.Channel.Merchant.MANAGE)" class="mb-4 flex gap-2">
|
||||
<a-select
|
||||
v-model:value="selectedTerminalNo"
|
||||
class="flex-1"
|
||||
:placeholder="$t('payment.device.terminal.pleaseSelectTerminal')"
|
||||
:options="
|
||||
candidateList.map((i) => ({
|
||||
label: `${i.name || '-'} (${i.terminalNo})`,
|
||||
value: i.terminalNo,
|
||||
}))
|
||||
"
|
||||
show-search
|
||||
option-filter-prop="label"
|
||||
allow-clear
|
||||
/>
|
||||
<a-button type="primary" @click="handleBind">{{ $t('payment.device.terminal.addBind') }}</a-button>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 font-medium">{{ $t('payment.device.terminal.boundSystem') }}</div>
|
||||
<a-empty v-if="boundList.length === 0" :description="$t('payment.device.terminal.noBound')" />
|
||||
<a-list v-else :data-source="boundList" bordered>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :title="item.name" :description="item.terminalNo" />
|
||||
<template #actions>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Channel.Merchant.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleUnbind(item)"
|
||||
>
|
||||
{{ $t('payment.device.terminal.unbind') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
</template>
|
||||
@@ -0,0 +1,178 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ChannelTerminalApi,
|
||||
type ChannelTerminalParam,
|
||||
type ChannelTerminalResult,
|
||||
} from '#/api/payment/device/terminal.api';
|
||||
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, addable } = useFormEdit();
|
||||
|
||||
// 通道商户上下文(新增时锁定)
|
||||
const context = ref<{ mchNo: string; channelMchNo: string }>({ mchNo: '', channelMchNo: '' });
|
||||
const editReadonly = ref<{ channelMchNo?: string; type?: string }>({});
|
||||
|
||||
const formState = ref<ChannelTerminalParam>({
|
||||
name: '',
|
||||
type: 'common',
|
||||
status: 'init',
|
||||
});
|
||||
|
||||
// 报送类型(前端 dict.json)
|
||||
const typeOptions = computed(() => [
|
||||
{ label: $t('dict.terminal_type.common'), value: 'common' },
|
||||
{ label: $t('dict.terminal_type.wechat'), value: 'wechat' },
|
||||
{ label: $t('dict.terminal_type.alipay'), value: 'alipay' },
|
||||
{ label: $t('dict.terminal_type.union'), value: 'union' },
|
||||
]);
|
||||
|
||||
// 登记状态(前端 dict.json)
|
||||
const statusOptions = computed(() => [
|
||||
{ label: $t('dict.channel_terminal_status.init'), value: 'init' },
|
||||
{ label: $t('dict.channel_terminal_status.wait'), value: 'wait' },
|
||||
{ label: $t('dict.channel_terminal_status.submit'), value: 'submit' },
|
||||
{ label: $t('dict.channel_terminal_status.logged'), value: 'logged' },
|
||||
{ label: $t('dict.channel_terminal_status.error'), value: 'error' },
|
||||
]);
|
||||
|
||||
const formRules = computed(() => ({
|
||||
type: [{ required: true, message: $t('payment.device.terminal.validateType') }],
|
||||
name: [{ required: true, message: $t('payment.device.terminal.validateName') }],
|
||||
}));
|
||||
|
||||
function resetForm() {
|
||||
formState.value = {
|
||||
name: '',
|
||||
type: 'common',
|
||||
status: 'init',
|
||||
mchNo: context.value.mchNo,
|
||||
channelMchNo: context.value.channelMchNo,
|
||||
};
|
||||
editReadonly.value = {};
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增(通道商户上下文已锁定)
|
||||
*/
|
||||
function showAdd(ctx: { mchNo: string; channelMchNo: string }) {
|
||||
context.value = ctx;
|
||||
initFormEditType(FormEditType.Add);
|
||||
resetForm();
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑
|
||||
*/
|
||||
async function showEdit(record: ChannelTerminalResult) {
|
||||
context.value = {
|
||||
mchNo: record.mchNo || '',
|
||||
channelMchNo: record.channelMchNo || '',
|
||||
};
|
||||
initFormEditType(FormEditType.Edit);
|
||||
resetForm();
|
||||
confirmLoading.value = true;
|
||||
visible.value = true;
|
||||
try {
|
||||
const { data } = await ChannelTerminalApi.get(record.id!);
|
||||
const row = data || record;
|
||||
formState.value = {
|
||||
id: row.id!,
|
||||
mchNo: row.mchNo,
|
||||
channelMchNo: row.channelMchNo,
|
||||
type: row.type,
|
||||
name: row.name,
|
||||
outTerminalNo: row.outTerminalNo,
|
||||
status: row.status,
|
||||
errorMsg: row.errorMsg,
|
||||
remark: row.remark,
|
||||
};
|
||||
editReadonly.value = { channelMchNo: row.channelMchNo, type: row.type };
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
async function handleOk() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
if (addable.value) {
|
||||
await ChannelTerminalApi.add({
|
||||
...formState.value,
|
||||
mchNo: context.value.mchNo,
|
||||
channelMchNo: context.value.channelMchNo,
|
||||
});
|
||||
} else {
|
||||
await ChannelTerminalApi.update(formState.value);
|
||||
}
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ showAdd, showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
:confirm-loading="confirmLoading"
|
||||
:destroy-on-close="true"
|
||||
:width="640"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="formState" :rules="formRules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<!-- 通道商户号只读展示 -->
|
||||
<a-form-item :label="$t('payment.device.terminal.field.channelMchNo')">
|
||||
<a-input :value="addable ? context.channelMchNo : editReadonly.channelMchNo" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.type')" name="type">
|
||||
<a-select
|
||||
v-model:value="formState.type"
|
||||
:options="typeOptions"
|
||||
:disabled="!addable"
|
||||
:placeholder="$t('common.pleaseSelect')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.name')" name="name">
|
||||
<a-input v-model:value="formState.name" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.outTerminalNo')" name="outTerminalNo">
|
||||
<a-input v-model:value="formState.outTerminalNo" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.status')" name="status">
|
||||
<a-select v-model:value="formState.status" :options="statusOptions" :placeholder="$t('common.pleaseSelect')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.errorMsg')" name="errorMsg">
|
||||
<a-input v-model:value="formState.errorMsg" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.remark')" name="remark">
|
||||
<a-textarea v-model:value="formState.remark" :rows="3" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -0,0 +1,234 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ChannelTerminalApi,
|
||||
type ChannelTerminalResult,
|
||||
} from '#/api/payment/device/terminal.api';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
import ChannelTerminalBindDrawer from './ChannelTerminalBindDrawer.vue';
|
||||
import ChannelTerminalEdit from './ChannelTerminalEdit.vue';
|
||||
|
||||
defineOptions({ name: 'ChannelTerminalPanel' });
|
||||
|
||||
const props = defineProps<{
|
||||
/** 系统商户号 */
|
||||
mchNo: string;
|
||||
/** 通道商户号 */
|
||||
channelMchNo: string;
|
||||
}>();
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
const editRef = ref<InstanceType<typeof ChannelTerminalEdit>>();
|
||||
const bindRef = ref<InstanceType<typeof ChannelTerminalBindDrawer>>();
|
||||
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const tableData = ref<ChannelTerminalResult[]>([]);
|
||||
|
||||
const drawerTitle = computed(() => {
|
||||
// 终端台账 - 通道商户号
|
||||
return `${$t('payment.merchant.channelMerchant.cardTerminal')} - ${props.channelMchNo || ''}`;
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开面板
|
||||
*/
|
||||
function open() {
|
||||
visible.value = true;
|
||||
pageConfig.value.currentPage = 1;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询(固定当前通道商户)
|
||||
*/
|
||||
function queryPage() {
|
||||
if (!props.channelMchNo) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
ChannelTerminalApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
mchNo: props.mchNo,
|
||||
channelMchNo: props.channelMchNo,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data.records || [];
|
||||
pageConfig.value.total = Number(res.data.total) || 0;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handlePageChange({ currentPage, pageSize }: any) {
|
||||
pageConfig.value.currentPage = currentPage;
|
||||
pageConfig.value.pageSize = pageSize;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.showAdd({ mchNo: props.mchNo, channelMchNo: props.channelMchNo });
|
||||
}
|
||||
|
||||
function handleEdit(row: ChannelTerminalResult) {
|
||||
editRef.value?.showEdit(row);
|
||||
}
|
||||
|
||||
function handleBind(row: ChannelTerminalResult) {
|
||||
bindRef.value?.show(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通道终端
|
||||
*/
|
||||
function handleDelete(row: ChannelTerminalResult) {
|
||||
confirm({
|
||||
// 确定删除该终端吗?
|
||||
content: $t('payment.device.terminal.confirmDelete'),
|
||||
onOk() {
|
||||
return ChannelTerminalApi.delete(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态文案
|
||||
*/
|
||||
function statusLabel(status?: string) {
|
||||
if (!status) {
|
||||
return '-';
|
||||
}
|
||||
return $t(`dict.channel_terminal_status.${status}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型文案
|
||||
*/
|
||||
function typeLabel(type?: string) {
|
||||
if (!type) {
|
||||
return '-';
|
||||
}
|
||||
return $t(`dict.terminal_type.${type}`);
|
||||
}
|
||||
|
||||
watch(visible, (open) => {
|
||||
if (open) {
|
||||
// 抽屉打开后连接 toolbar
|
||||
setTimeout(() => {
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-drawer
|
||||
v-model:open="visible"
|
||||
:title="drawerTitle"
|
||||
:width="960"
|
||||
:destroy-on-close="true"
|
||||
:body-style="{ paddingBottom: '24px' }"
|
||||
>
|
||||
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: queryPage }">
|
||||
<template #buttons>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Channel.Merchant.MANAGE)"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
{{ $t('payment.device.terminal.add') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" :loading="loading">
|
||||
<vxe-column field="name" :title="$t('payment.device.terminal.field.name')" :min-width="120" />
|
||||
<vxe-column field="type" :title="$t('payment.device.terminal.field.type')" :min-width="120">
|
||||
<template #default="{ row }">
|
||||
{{ typeLabel(row.type) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="outTerminalNo"
|
||||
:title="$t('payment.device.terminal.field.outTerminalNo')"
|
||||
:min-width="140"
|
||||
/>
|
||||
<vxe-column field="status" :title="$t('payment.device.terminal.field.status')" :min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<a-tag>{{ statusLabel(row.status) }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="errorMsg" :title="$t('payment.device.terminal.field.errorMsg')" :min-width="120" />
|
||||
<vxe-column fixed="right" width="220" :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.Channel.Merchant.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
>
|
||||
{{ $t('common.edit') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Channel.Merchant.VIEW)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleBind(row)"
|
||||
>
|
||||
{{ $t('payment.device.terminal.bindManage') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Channel.Merchant.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
{{ $t('common.delete') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<div class="mt-3 flex justify-end">
|
||||
<vxe-pager
|
||||
v-model:current-page="pageConfig.currentPage"
|
||||
v-model:page-size="pageConfig.pageSize"
|
||||
:total="pageConfig.total"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ChannelTerminalEdit ref="editRef" @ok="queryPage" />
|
||||
<ChannelTerminalBindDrawer ref="bindRef" />
|
||||
</a-drawer>
|
||||
</template>
|
||||
@@ -58,6 +58,15 @@
|
||||
description: $t('payment.merchant.workbench.workbench.cardStoreDesc'),
|
||||
route: '/payment/merchant/store',
|
||||
},
|
||||
{
|
||||
key: 'terminal',
|
||||
// 系统终端
|
||||
title: $t('payment.merchant.workbench.workbench.cardTerminal'),
|
||||
icon: 'ant-design:monitor-outlined',
|
||||
// 商户逻辑终端编号与通道终端绑定管理
|
||||
description: $t('payment.merchant.workbench.workbench.cardTerminalDesc'),
|
||||
route: '/payment/merchant/terminal',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ChannelTerminalApi,
|
||||
type ChannelTerminalResult,
|
||||
TerminalDeviceApi,
|
||||
type TerminalDeviceResult,
|
||||
} from '#/api/payment/device/terminal.api';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const current = ref<null | TerminalDeviceResult>(null);
|
||||
const boundList = ref<ChannelTerminalResult[]>([]);
|
||||
const candidateList = ref<ChannelTerminalResult[]>([]);
|
||||
const selectedChannelId = ref<string | undefined>();
|
||||
|
||||
const title = computed(() => {
|
||||
if (!current.value) {
|
||||
return $t('payment.device.terminal.bindManage');
|
||||
}
|
||||
return `${$t('payment.device.terminal.bindManage')} - ${current.value.terminalNo}`;
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开绑定抽屉
|
||||
*/
|
||||
async function show(row: TerminalDeviceResult) {
|
||||
current.value = row;
|
||||
selectedChannelId.value = undefined;
|
||||
visible.value = true;
|
||||
await refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新已绑定与候选列表
|
||||
*/
|
||||
async function refresh() {
|
||||
if (!current.value?.terminalNo || !current.value.mchNo) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const [boundRes, allRes] = await Promise.all([
|
||||
TerminalDeviceApi.boundChannelList(current.value.terminalNo),
|
||||
ChannelTerminalApi.listByMchNo(current.value.mchNo),
|
||||
]);
|
||||
boundList.value = boundRes.data || [];
|
||||
const boundIds = new Set(boundList.value.map((i) => i.id));
|
||||
candidateList.value = (allRes.data || []).filter((i) => !boundIds.has(i.id));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加绑定
|
||||
*/
|
||||
async function handleBind() {
|
||||
if (!selectedChannelId.value || !current.value?.terminalNo) {
|
||||
message.warning($t('payment.device.terminal.pleaseSelectChannelTerminal'));
|
||||
return;
|
||||
}
|
||||
await TerminalDeviceApi.bind({
|
||||
systemTerminalNo: current.value.terminalNo,
|
||||
channelTerminalId: selectedChannelId.value,
|
||||
});
|
||||
message.success($t('common.operationSuccess'));
|
||||
selectedChannelId.value = undefined;
|
||||
await refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑
|
||||
*/
|
||||
function handleUnbind(row: ChannelTerminalResult) {
|
||||
if (!current.value?.terminalNo || !row.id) {
|
||||
return;
|
||||
}
|
||||
confirm({
|
||||
// 确定解除绑定吗?
|
||||
content: $t('payment.device.terminal.confirmUnbind'),
|
||||
onOk() {
|
||||
return TerminalDeviceApi.unbind({
|
||||
systemTerminalNo: current.value!.terminalNo!,
|
||||
channelTerminalId: row.id!,
|
||||
}).then(async () => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
await refresh();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ show });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-drawer v-model:open="visible" :title="title" :width="640" :destroy-on-close="true">
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="hasPermission(PermCodes.Merchant.Terminal.MANAGE)" class="mb-4 flex gap-2">
|
||||
<a-select
|
||||
v-model:value="selectedChannelId"
|
||||
class="flex-1"
|
||||
:placeholder="$t('payment.device.terminal.pleaseSelectChannelTerminal')"
|
||||
:options="
|
||||
candidateList.map((i) => ({
|
||||
label: `${i.name || '-'} / ${i.channelMchNo || '-'} / ${i.type || '-'}`,
|
||||
value: i.id,
|
||||
}))
|
||||
"
|
||||
show-search
|
||||
option-filter-prop="label"
|
||||
allow-clear
|
||||
/>
|
||||
<a-button type="primary" @click="handleBind">{{ $t('payment.device.terminal.addBind') }}</a-button>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 font-medium">{{ $t('payment.device.terminal.boundChannel') }}</div>
|
||||
<a-empty v-if="boundList.length === 0" :description="$t('payment.device.terminal.noBound')" />
|
||||
<a-list v-else :data-source="boundList" bordered>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta
|
||||
:title="item.name"
|
||||
:description="`${item.channelMchNo || '-'} · ${item.type || '-'} · ${item.outTerminalNo || '-'}`"
|
||||
/>
|
||||
<template #actions>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Merchant.Terminal.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleUnbind(item)"
|
||||
>
|
||||
{{ $t('payment.device.terminal.unbind') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
</template>
|
||||
@@ -0,0 +1,163 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LabelValue } from '#/types/web';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
TerminalDeviceApi,
|
||||
type TerminalDeviceParam,
|
||||
type TerminalDeviceResult,
|
||||
} from '#/api/payment/device/terminal.api';
|
||||
import { MchStoreInfoApi } from '#/api/payment/merchant/store.api';
|
||||
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, addable } = useFormEdit();
|
||||
|
||||
const storeOptions = ref<LabelValue[]>([]);
|
||||
const editTerminalNo = ref('');
|
||||
// 锁定的商户号(由列表页传入, 新增/编辑均使用此商户)
|
||||
const lockedMchNo = ref<string>('');
|
||||
|
||||
const formState = ref<TerminalDeviceParam>({
|
||||
name: '',
|
||||
enable: true,
|
||||
});
|
||||
|
||||
const formRules = computed(() => ({
|
||||
name: [{ required: true, message: $t('payment.device.terminal.validateName') }],
|
||||
enable: [{ required: true, message: $t('payment.device.terminal.validateEnable') }],
|
||||
}));
|
||||
|
||||
/**
|
||||
* 按商户加载门店
|
||||
*/
|
||||
async function loadStoreOptions(mchNo?: string, keepStoreNo?: string) {
|
||||
storeOptions.value = [];
|
||||
if (!keepStoreNo) {
|
||||
formState.value.storeNo = undefined;
|
||||
}
|
||||
if (!mchNo) {
|
||||
return;
|
||||
}
|
||||
const { data } = await MchStoreInfoApi.page({ mchNo, current: 1, size: 200 });
|
||||
storeOptions.value = (data?.records || []).map((item) => ({
|
||||
label: item.storeName ? `${item.storeName} (${item.storeNo})` : (item.storeNo as string),
|
||||
value: item.storeNo as string,
|
||||
}));
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formState.value = { name: '', enable: true };
|
||||
editTerminalNo.value = '';
|
||||
storeOptions.value = [];
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增(传入锁定商户号)
|
||||
*/
|
||||
async function showAdd(mchNo: string) {
|
||||
initFormEditType(FormEditType.Add);
|
||||
resetForm();
|
||||
lockedMchNo.value = mchNo;
|
||||
formState.value.mchNo = mchNo;
|
||||
visible.value = true;
|
||||
await loadStoreOptions(mchNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开编辑
|
||||
*/
|
||||
async function showEdit(record: TerminalDeviceResult) {
|
||||
initFormEditType(FormEditType.Edit);
|
||||
resetForm();
|
||||
confirmLoading.value = true;
|
||||
visible.value = true;
|
||||
try {
|
||||
const { data } = await TerminalDeviceApi.get(record.id!);
|
||||
const row = data || record;
|
||||
lockedMchNo.value = row.mchNo || '';
|
||||
formState.value = {
|
||||
id: row.id!,
|
||||
mchNo: row.mchNo,
|
||||
name: row.name,
|
||||
storeNo: row.storeNo,
|
||||
enable: row.enable ?? true,
|
||||
remark: row.remark,
|
||||
};
|
||||
editTerminalNo.value = row.terminalNo || '';
|
||||
await loadStoreOptions(row.mchNo, row.storeNo);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
async function handleOk() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
await (addable.value ? TerminalDeviceApi.add(formState.value) : TerminalDeviceApi.update(formState.value));
|
||||
message.success($t('common.operationSuccess'));
|
||||
handleCancel();
|
||||
emit('ok');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ showAdd, showEdit });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
:confirm-loading="confirmLoading"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="formState" :rules="formRules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-item v-if="!addable" :label="$t('payment.device.terminal.field.terminalNo')">
|
||||
<a-input :value="editTerminalNo" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.store')" name="storeNo">
|
||||
<a-select
|
||||
v-model:value="formState.storeNo"
|
||||
:options="storeOptions"
|
||||
:placeholder="$t('payment.device.terminal.pleaseSelectStore')"
|
||||
allow-clear
|
||||
show-search
|
||||
option-filter-prop="label"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.name')" name="name">
|
||||
<a-input v-model:value="formState.name" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.enable')" name="enable">
|
||||
<a-radio-group v-model:value="formState.enable" button-style="solid">
|
||||
<a-radio-button :value="true">{{ $t('payment.device.terminal.enableYes') }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ $t('payment.device.terminal.enableNo') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.device.terminal.field.remark')" name="remark">
|
||||
<a-textarea v-model:value="formState.remark" :rows="3" :placeholder="$t('common.pleaseInput')" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -0,0 +1,304 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import { TerminalDeviceApi, type TerminalDeviceResult } from '#/api/payment/device/terminal.api';
|
||||
import { MerchantApi, type MerchantInfo } from '#/api/payment/merchant/merchant.api';
|
||||
import { BQuery, type QueryField } from '#/components/query';
|
||||
import RouteQueryMissingState from '#/components/route/RouteQueryMissingState.vue';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
import { useRequiredRouteQuery } from '#/hooks/useRequiredRouteQuery';
|
||||
|
||||
import SystemTerminalBindDrawer from './SystemTerminalBindDrawer.vue';
|
||||
import SystemTerminalEdit from './SystemTerminalEdit.vue';
|
||||
|
||||
defineOptions({ name: 'SystemTerminalList' });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 商户号从路由 query 获取
|
||||
const routeContext = useRequiredRouteQuery({
|
||||
keys: ['mchNo'],
|
||||
messageKey: 'payment.common.route.missingMchNo',
|
||||
fallbackPath: '/payment/merchant',
|
||||
});
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const mchNo = computed(() => routeContext.query.value.mchNo);
|
||||
// 商户信息(用于标题展示商户名称)
|
||||
const merchantInfo = ref<MerchantInfo>({});
|
||||
|
||||
const loading = ref(false);
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
const editRef = ref<InstanceType<typeof SystemTerminalEdit>>();
|
||||
const bindRef = ref<InstanceType<typeof SystemTerminalBindDrawer>>();
|
||||
|
||||
const queryForm = ref<Record<string, any>>({});
|
||||
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
type: 'string',
|
||||
field: 'terminalNo',
|
||||
// 终端编码
|
||||
name: $t('payment.device.terminal.field.terminalNo'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'name',
|
||||
// 终端名称
|
||||
name: $t('payment.device.terminal.field.name'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'storeNo',
|
||||
// 门店号
|
||||
name: $t('payment.device.terminal.field.storeNo'),
|
||||
placeholder: $t('common.pleaseInput'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'enable',
|
||||
// 启用
|
||||
name: $t('payment.device.terminal.field.enable'),
|
||||
placeholder: $t('common.pleaseSelect'),
|
||||
selectList: [
|
||||
{ label: $t('payment.device.terminal.enableYes'), value: true },
|
||||
{ label: $t('payment.device.terminal.enableNo'), value: false },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const tableData = ref<TerminalDeviceResult[]>([]);
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
function queryPage() {
|
||||
if (!mchNo.value) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
TerminalDeviceApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
mchNo: mchNo.value,
|
||||
...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();
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
editRef.value?.showAdd(mchNo.value!);
|
||||
}
|
||||
|
||||
function handleEdit(row: TerminalDeviceResult) {
|
||||
editRef.value?.showEdit(row);
|
||||
}
|
||||
|
||||
function handleBind(row: TerminalDeviceResult) {
|
||||
bindRef.value?.show(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除系统终端
|
||||
*/
|
||||
function handleDelete(row: TerminalDeviceResult) {
|
||||
confirm({
|
||||
// 确定删除该终端吗?
|
||||
content: $t('payment.device.terminal.confirmDelete'),
|
||||
onOk() {
|
||||
return TerminalDeviceApi.delete(row.id!).then(() => {
|
||||
message.success($t('common.operationSuccess'));
|
||||
queryPage();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!routeContext.isValid.value) {
|
||||
return;
|
||||
}
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
// 加载商户信息用于标题展示
|
||||
loadMerchantInfo();
|
||||
queryPage();
|
||||
});
|
||||
|
||||
/**
|
||||
* 返回商户工作台
|
||||
*/
|
||||
function handleBack() {
|
||||
router.push({
|
||||
path: '/payment/merchant/manage',
|
||||
query: { mchNo: mchNo.value },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载商户信息
|
||||
*/
|
||||
function loadMerchantInfo() {
|
||||
if (!mchNo.value) return;
|
||||
MerchantApi.findByMchNo(mchNo.value).then(({ data }) => {
|
||||
if (data) {
|
||||
merchantInfo.value = data;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouteQueryMissingState
|
||||
v-if="!routeContext.isValid"
|
||||
:description="$t('payment.common.route.missingMchNo')"
|
||||
:back-text="$t('payment.merchant.workbench.workbench.backToList')"
|
||||
@back="routeContext.goFallback"
|
||||
/>
|
||||
<div v-else class="m-3 p-3 bg-background rounded-lg list-page-compact">
|
||||
<a-card>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- 国际化:系统终端 -->
|
||||
<a-button
|
||||
type="text"
|
||||
class="flex items-center justify-center rounded-full hover:bg-accent"
|
||||
@click="handleBack"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ant-design:arrow-left-outlined" class="text-lg" />
|
||||
</template>
|
||||
</a-button>
|
||||
<span class="text-lg font-bold text-foreground">{{ $t('payment.device.terminal.systemTitle') }}</span>
|
||||
<span v-if="merchantInfo.mchName" class="text-sm text-muted-foreground">({{ merchantInfo.mchName }})</span>
|
||||
</div>
|
||||
</template>
|
||||
<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-button v-if="hasPermission(PermCodes.Merchant.Terminal.MANAGE)" type="primary" @click="handleAdd">
|
||||
{{ $t('payment.device.terminal.add') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" :loading="loading">
|
||||
<vxe-column field="terminalNo" :title="$t('payment.device.terminal.field.terminalNo')" :min-width="200" />
|
||||
<vxe-column field="name" :title="$t('payment.device.terminal.field.name')" :min-width="140" />
|
||||
<vxe-column field="storeName" :title="$t('payment.device.terminal.field.store')" :min-width="160">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.storeNo" class="flex flex-col">
|
||||
<span>{{ row.storeName || '-' }}</span>
|
||||
<span class="text-xs text-muted-foreground">{{ row.storeNo }}</span>
|
||||
</div>
|
||||
<span v-else style="color: var(--text-color-placeholder)">-</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="enable"
|
||||
:title="$t('payment.device.terminal.field.enable')"
|
||||
:min-width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<a-tag v-if="row.enable" color="green">{{ $t('payment.device.terminal.enableYes') }}</a-tag>
|
||||
<a-tag v-else color="default">{{ $t('payment.device.terminal.enableNo') }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
:title="$t('payment.device.terminal.field.createTime')"
|
||||
:min-width="180"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column fixed="right" width="220" :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.Merchant.Terminal.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
>
|
||||
{{ $t('common.edit') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Merchant.Terminal.VIEW)"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleBind(row)"
|
||||
>
|
||||
{{ $t('payment.device.terminal.bindManage') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Merchant.Terminal.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleDelete(row)"
|
||||
>
|
||||
{{ $t('common.delete') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<div class="mt-3 flex justify-end">
|
||||
<vxe-pager
|
||||
v-model:current-page="pageConfig.currentPage"
|
||||
v-model:page-size="pageConfig.pageSize"
|
||||
:total="pageConfig.total"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<SystemTerminalEdit ref="editRef" @ok="queryPage" />
|
||||
<SystemTerminalBindDrawer ref="bindRef" />
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user