Compare commits
2 Commits
690568861b
...
e39b88d1db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e39b88d1db | ||
|
|
630dfddc59 |
50
db/add-menu-icons.sql
Normal file
50
db/add-menu-icons.sql
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 为所有菜单补齐图标(只更新 icon 为空的,已有的保留)
|
||||||
|
-- 图标使用 Element Plus 图标名(PascalCase),侧边栏通过 iconMap[icon] 渲染
|
||||||
|
-- 执行前建议先跑:SELECT id,name,icon FROM sys_menu WHERE type IN(0,1) ORDER BY parent_id,order_num;
|
||||||
|
-- 若菜单 name 与下方不一致,请按实际 name 调整 WHERE 条件
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- ===== 一级目录 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='Briefcase' WHERE `name`='业务管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Connection' WHERE `name`='第三方接口管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='VideoCamera' WHERE `name`='媒资管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Setting' WHERE `name`='系统管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='OfficeBuilding' WHERE `name`='组织管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
|
||||||
|
-- ===== 业务管理 下 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='Share' WHERE `name`='渠道管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Shop' WHERE `name`='门店管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Monitor' WHERE `name`='设备管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='PriceTag' WHERE `name`='定价策略' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Wallet' WHERE `name`='支付配置' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Tickets' WHERE `name`='天卡池管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Document' WHERE `name`='天卡订单' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Key' WHERE `name`='天卡激活码' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Coin' WHERE `name`='分润规则' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Wallet' WHERE `name`='分账记录' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='RefreshLeft' WHERE `name`='退款记录' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
|
||||||
|
-- ===== 第三方接口管理 下 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='UserFilled' WHERE `name`='合作伙伴管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Files' WHERE `name`='内容包配置' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='ShoppingCart' WHERE `name`='天卡库存' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Document' WHERE `name`='审计日志' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Monitor' WHERE `name`='设备列表' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
|
||||||
|
-- ===== 媒资管理 下 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='Film' WHERE `name`='橘视厅媒资' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
|
||||||
|
-- ===== 系统管理 下 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='User' WHERE `name`='用户管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='UserFilled'WHERE `name`='角色管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Menu' WHERE `name`='菜单管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Tools' WHERE `name`='参数配置' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Memo' WHERE `name`='日志管理' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Memo' WHERE `name`='操作日志' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Memo' WHERE `name`='审计日志记录' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Timer' WHERE `name`='定时任务' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
UPDATE `sys_menu` SET `icon`='Document' WHERE `name`='定时日志' AND (`icon` IS NULL OR `icon`='');
|
||||||
|
|
||||||
|
-- ===== 通用兜底:仍为空的菜单统一给一个默认图标 =====
|
||||||
|
UPDATE `sys_menu` SET `icon`='Menu' WHERE (`icon` IS NULL OR `icon`='') AND `type` IN (0,1);
|
||||||
54
src/api/manage/auditLog.ts
Normal file
54
src/api/manage/auditLog.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import http from '@/utils/httpRequest'
|
||||||
|
import type { ManageListResponse, ManagePageParams } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审计日志(管理后台操作审计)
|
||||||
|
*/
|
||||||
|
export interface AuditLogItem {
|
||||||
|
id: number
|
||||||
|
/** 操作人ID */
|
||||||
|
actorUserId: number
|
||||||
|
/** 操作人名 */
|
||||||
|
name?: string
|
||||||
|
/** 操作描述(中文,如"创建天卡订单(设备端)") */
|
||||||
|
head?: string
|
||||||
|
/** 请求方法枚举(如 DAYCARD_ORDER_CREATE) */
|
||||||
|
method?: string
|
||||||
|
/** 接口路径 */
|
||||||
|
api?: string
|
||||||
|
/** 请求参数 */
|
||||||
|
param?: string
|
||||||
|
/** 中文描述(旧字段,多为空) */
|
||||||
|
paramDesc?: string
|
||||||
|
/** 操作结果(完整返回字符串,含 code=/msg=) */
|
||||||
|
result?: string
|
||||||
|
/** 变更前 */
|
||||||
|
before?: string
|
||||||
|
/** 变更后 */
|
||||||
|
after?: string
|
||||||
|
/** 操作原因 */
|
||||||
|
reason?: string
|
||||||
|
ip?: string
|
||||||
|
/** 设备信息 */
|
||||||
|
deviceInfo?: string
|
||||||
|
/** 工单号 */
|
||||||
|
ticketNo?: string
|
||||||
|
createdAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审计日志查询参数
|
||||||
|
*/
|
||||||
|
export interface AuditLogListParams extends ManagePageParams {
|
||||||
|
actorUserId?: number
|
||||||
|
api?: string
|
||||||
|
method?: string
|
||||||
|
head?: string
|
||||||
|
ip?: string
|
||||||
|
ticketNo?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 审计日志列表 */
|
||||||
|
export function getAuditLogList(params?: AuditLogListParams): Promise<ManageListResponse<AuditLogItem>> {
|
||||||
|
return http.get(http.adornUrl('/manage/audit-log/list'), { params: http.adornParams(params || {}) }).then(res => res.data)
|
||||||
|
}
|
||||||
@@ -114,6 +114,13 @@ export interface DayCardOrderItem {
|
|||||||
/** 非表字段 */
|
/** 非表字段 */
|
||||||
partnerName?: string
|
partnerName?: string
|
||||||
deviceMac?: string
|
deviceMac?: string
|
||||||
|
/** 设备MAC(deviceMac 为空时用此字段) */
|
||||||
|
mac?: string
|
||||||
|
deviceSn?: string
|
||||||
|
agentOrgId?: number
|
||||||
|
/** 组织路径快照 */
|
||||||
|
orgPathSnapshot?: string
|
||||||
|
channelOrderNo?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 订单列表参数 */
|
/** 订单列表参数 */
|
||||||
|
|||||||
@@ -32,21 +32,22 @@ export interface DeviceInfo {
|
|||||||
channelOrgId?: number
|
channelOrgId?: number
|
||||||
channelName?: string
|
channelName?: string
|
||||||
agentOrgName?: string
|
agentOrgName?: string
|
||||||
/** 设备来源:PLATFORM-平台 / PARTNER-第三方接口注册 */
|
/** 设备来源:DIRECT-自营 / PARTNER-第三方接口注册 */
|
||||||
source?: string
|
source?: string
|
||||||
/** 合作伙伴UUID(source=PARTNER 时) */
|
/** 合作伙伴UUID(source=PARTNER 时) */
|
||||||
partnerId?: string
|
partnerId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端列表查询参数
|
* 终端保存/更新数据
|
||||||
*/
|
*/
|
||||||
export interface DeviceListParams extends ManagePageParams {
|
export interface DeviceListParams extends ManagePageParams {
|
||||||
storeOrgId?: number
|
storeOrgId?: number
|
||||||
status?: string
|
status?: string
|
||||||
deviceName?: string
|
deviceName?: string
|
||||||
mac?: string
|
mac?: string
|
||||||
/** 设备来源:PLATFORM / PARTNER */
|
deviceSn?: string
|
||||||
|
/** 设备来源:DIRECT / PARTNER */
|
||||||
source?: string
|
source?: string
|
||||||
/** 合作伙伴UUID(source=PARTNER 时筛选) */
|
/** 合作伙伴UUID(source=PARTNER 时筛选) */
|
||||||
partnerId?: string
|
partnerId?: string
|
||||||
@@ -68,9 +69,25 @@ export interface DeviceSaveData {
|
|||||||
remark?: string
|
remark?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 下划线转驼峰(兼容后端返回 snake_case 字段,统一为前端驼峰访问) */
|
||||||
|
function toCamelCase (obj: any): any {
|
||||||
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj
|
||||||
|
const result: any = {}
|
||||||
|
for (const key in obj) {
|
||||||
|
result[key.replace(/_([a-z])/g, (_, c) => c.toUpperCase())] = obj[key]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
/** 终端分页列表 */
|
/** 终端分页列表 */
|
||||||
export function getDeviceList(params?: DeviceListParams): Promise<ManageListResponse<DeviceInfo>> {
|
export function getDeviceList(params?: DeviceListParams): Promise<ManageListResponse<DeviceInfo>> {
|
||||||
return http.get(http.adornUrl('/manage/device/list'), { params: http.adornParams(params || {}) }).then(res => res.data)
|
return http.get(http.adornUrl('/manage/device/list'), { params: http.adornParams(params || {}) }).then(res => {
|
||||||
|
const body = res.data as any
|
||||||
|
if (body && body.data && body.data.page && Array.isArray(body.data.page.list)) {
|
||||||
|
body.data.page.list = body.data.page.list.map(toCamelCase)
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 终端详情 */
|
/** 终端详情 */
|
||||||
|
|||||||
@@ -26,3 +26,5 @@ export * from './partner'
|
|||||||
export * from './content-package'
|
export * from './content-package'
|
||||||
export * from './partner-card'
|
export * from './partner-card'
|
||||||
export * from './openapiCallLog'
|
export * from './openapiCallLog'
|
||||||
|
export * from './auditLog'
|
||||||
|
export * from './jstCallLog'
|
||||||
|
|||||||
64
src/api/manage/jstCallLog.ts
Normal file
64
src/api/manage/jstCallLog.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import http from '@/utils/httpRequest'
|
||||||
|
import type { ManageListResponse, ManagePageParams } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 橘视厅调用日志(设备调用橘视厅 API 的日志)
|
||||||
|
*/
|
||||||
|
export interface JstCallLogItem {
|
||||||
|
id: number
|
||||||
|
/** 请求URL(可能带反引号和query参数) */
|
||||||
|
apiPath: string
|
||||||
|
/** 请求方法 */
|
||||||
|
requestMethod?: string
|
||||||
|
/** 请求体 */
|
||||||
|
requestBody?: string
|
||||||
|
/** 响应体 */
|
||||||
|
responseBody?: string
|
||||||
|
/** HTTP状态码 */
|
||||||
|
responseCode?: number
|
||||||
|
/** 是否成功(1=成功,0=失败) */
|
||||||
|
success: number
|
||||||
|
/** 耗时(ms) */
|
||||||
|
duration?: number
|
||||||
|
/** 关联数据ID */
|
||||||
|
dataId?: number
|
||||||
|
/** 请求头(JSON字符串) */
|
||||||
|
requestHeaders?: string
|
||||||
|
/** 客户端IP */
|
||||||
|
clientIp?: string
|
||||||
|
/** 错误信息 */
|
||||||
|
errorMessage?: string
|
||||||
|
/** 关联设备MAC */
|
||||||
|
mac?: string
|
||||||
|
/** 关联设备SN */
|
||||||
|
deviceSn?: string
|
||||||
|
/** 关联设备UID */
|
||||||
|
deviceUid?: string
|
||||||
|
/** 门店组织ID */
|
||||||
|
storeOrgId?: number
|
||||||
|
/** 设备所属门店名称 */
|
||||||
|
storeOrgName?: string
|
||||||
|
/** 门店编码 */
|
||||||
|
storeOrgCode?: string
|
||||||
|
/** 门店类型 */
|
||||||
|
storeOrgType?: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 橘视厅调用日志查询参数
|
||||||
|
*/
|
||||||
|
export interface JstCallLogListParams extends ManagePageParams {
|
||||||
|
mac?: string
|
||||||
|
beginTime?: string
|
||||||
|
endTime?: string
|
||||||
|
apiPath?: string
|
||||||
|
requestBody?: string
|
||||||
|
/** 结果筛选:1=成功,0=失败 */
|
||||||
|
success?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 橘视厅调用日志列表 */
|
||||||
|
export function getJstCallLogList(params?: JstCallLogListParams): Promise<ManageListResponse<JstCallLogItem>> {
|
||||||
|
return http.get(http.adornUrl('/manage/jst/call-log/list'), { params: http.adornParams(params || {}) }).then(res => res.data)
|
||||||
|
}
|
||||||
@@ -2,40 +2,56 @@ import http from '@/utils/httpRequest'
|
|||||||
import type { ManageListResponse, ManageResponse, ManagePageParams } from './types'
|
import type { ManageListResponse, ManageResponse, ManagePageParams } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信支付配置信息
|
* 微信支付配置(按层级:平台全局/渠道/代理商/门店,三选一留空=全局)
|
||||||
*/
|
*/
|
||||||
export interface PayConfigInfo {
|
export interface PayConfigInfo {
|
||||||
id: number
|
id: number
|
||||||
partnerId: number | null
|
/** 渠道组织ID(渠道级配置) */
|
||||||
|
channelOrgId?: number
|
||||||
|
/** 代理商组织ID(代理商级配置) */
|
||||||
|
agentOrgId?: number
|
||||||
|
/** 门店组织ID(门店级配置) */
|
||||||
|
storeOrgId?: number
|
||||||
appId: string
|
appId: string
|
||||||
mchId: string
|
mchId: string
|
||||||
apiV3Key: string
|
apiV3Key: string
|
||||||
certSerialNo: string
|
certSerialNo: string
|
||||||
privateKeyPath: string
|
/** 商户 API 私钥(apiclient_key.pem 的 PEM 内容) */
|
||||||
|
privateKey: string
|
||||||
notifyUrl: string
|
notifyUrl: string
|
||||||
status: string
|
status: string
|
||||||
remark: string
|
remark: string
|
||||||
|
/** join 名称(后端返回) */
|
||||||
|
channelName?: string
|
||||||
|
agentOrgName?: string
|
||||||
|
storeName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付配置列表查询参数
|
* 支付配置列表查询参数
|
||||||
*/
|
*/
|
||||||
export interface PayConfigListParams extends ManagePageParams {
|
export interface PayConfigListParams extends ManagePageParams {
|
||||||
partnerId?: number
|
channelOrgId?: number
|
||||||
|
agentOrgId?: number
|
||||||
|
storeOrgId?: number
|
||||||
status?: string
|
status?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付配置保存/更新数据
|
* 支付配置保存/更新数据
|
||||||
|
* 配置层级三选一:channelOrgId/agentOrgId/storeOrgId,都留空=平台全局
|
||||||
*/
|
*/
|
||||||
export interface PayConfigSaveData {
|
export interface PayConfigSaveData {
|
||||||
id?: number
|
id?: number
|
||||||
partnerId?: number
|
channelOrgId?: number
|
||||||
|
agentOrgId?: number
|
||||||
|
storeOrgId?: number
|
||||||
appId: string
|
appId: string
|
||||||
mchId: string
|
mchId: string
|
||||||
apiV3Key: string
|
apiV3Key: string
|
||||||
certSerialNo?: string
|
certSerialNo?: string
|
||||||
privateKeyPath?: string
|
/** 商户 API 私钥(apiclient_key.pem 的 PEM 内容) */
|
||||||
|
privateKey?: string
|
||||||
notifyUrl: string
|
notifyUrl: string
|
||||||
status?: string
|
status?: string
|
||||||
remark?: string
|
remark?: string
|
||||||
@@ -47,7 +63,7 @@ export function getPayConfigList(params?: PayConfigListParams): Promise<ManageLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 支付配置详情 */
|
/** 支付配置详情 */
|
||||||
export function getPayConfigInfo(id: number): Promise<ManageResponse<{ config?: PayConfigInfo }>> {
|
export function getPayConfigInfo(id: number): Promise<ManageResponse<PayConfigInfo>> {
|
||||||
return http.get(http.adornUrl(`/manage/payconfig/info/${id}`), { params: http.adornParams() }).then(res => res.data)
|
return http.get(http.adornUrl(`/manage/payconfig/info/${id}`), { params: http.adornParams() }).then(res => res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
225
src/views/modules/manage/audit-log.vue
Normal file
225
src/views/modules/manage/audit-log.vue
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-audit-log">
|
||||||
|
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.api" placeholder="接口路径" clearable style="width:220px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.method" placeholder="方法枚举" clearable style="width:180px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.head" placeholder="操作描述" clearable style="width:180px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.ip" placeholder="IP" clearable style="width:150px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="dataForm.resultStatus" placeholder="结果" clearable style="width:120px">
|
||||||
|
<el-option label="成功" value="SUCCESS"></el-option>
|
||||||
|
<el-option label="失败" value="FAIL"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="getDataList()">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="table-page-wrap">
|
||||||
|
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
|
||||||
|
<el-table-column type="index" header-align="center" align="center" width="55" label="序号" />
|
||||||
|
<el-table-column header-align="center" align="center" min-width="160" label="操作描述">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.head || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="method" header-align="center" align="center" width="170" label="方法" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="api" header-align="center" align="center" min-width="200" label="接口" show-overflow-tooltip />
|
||||||
|
<el-table-column header-align="center" align="center" width="100" label="操作人">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.name">{{ scope.row.name }}</span>
|
||||||
|
<span v-else-if="scope.row.actorUserId">用户#{{ scope.row.actorUserId }}</span>
|
||||||
|
<el-tag v-else size="small" type="info">系统</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" width="90" label="结果">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="resultTag(scope.row).type" size="small">{{ resultTag(scope.row).label }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="ip" header-align="center" align="center" width="140" label="IP" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="createdAt" header-align="center" align="center" width="170" label="操作时间" />
|
||||||
|
<el-table-column fixed="right" header-align="center" align="center" width="90" label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" size="small" @click="viewDetail(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
:current-page="pageIndex"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="totalPage"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<el-dialog v-model="detailVisible" title="审计日志详情" width="820px" top="5vh">
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="操作描述">{{ detailData.head || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="方法">{{ detailData.method || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="接口">{{ detailData.api || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="操作人">
|
||||||
|
<span v-if="detailData.name">{{ detailData.name }}</span>
|
||||||
|
<span v-else-if="detailData.actorUserId">用户#{{ detailData.actorUserId }}</span>
|
||||||
|
<span v-else>系统</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="结果">
|
||||||
|
<el-tag :type="resultTag(detailData).type" size="small">{{ resultTag(detailData).label }}</el-tag>
|
||||||
|
<span style="margin-left:8px; color:#909399; font-size:12px;">{{ resultSummary(detailData.result) }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="IP">{{ detailData.ip || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="设备信息" :content-style="{ wordBreak: 'break-all' }">{{ detailData.deviceInfo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="工单号">{{ detailData.ticketNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="原因">{{ detailData.reason || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="操作时间">{{ detailData.createdAt || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="请求参数">
|
||||||
|
<pre class="log-pre">{{ detailData.param || '-' }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="返回结果">
|
||||||
|
<pre class="log-pre">{{ detailData.result || '-' }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变更前">
|
||||||
|
<pre class="log-pre">{{ detailData.before || '-' }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变更后">
|
||||||
|
<pre class="log-pre">{{ detailData.after || '-' }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="detailVisible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
|
import { getAuditLogList, type AuditLogItem } from '@/api/manage/auditLog'
|
||||||
|
|
||||||
|
interface DataForm {
|
||||||
|
api: string
|
||||||
|
method: string
|
||||||
|
head: string
|
||||||
|
ip: string
|
||||||
|
resultStatus: string
|
||||||
|
}
|
||||||
|
const dataForm = reactive<DataForm>({ api: '', method: '', head: '', ip: '', resultStatus: '' })
|
||||||
|
const dataList = ref<AuditLogItem[]>([])
|
||||||
|
const pageIndex = ref<number>(1)
|
||||||
|
const pageSize = ref<number>(10)
|
||||||
|
const totalPage = ref<number>(0)
|
||||||
|
const dataListLoading = ref<boolean>(false)
|
||||||
|
|
||||||
|
/** 解析 result 字符串,返回标签类型与文案 */
|
||||||
|
function resultTag (row: Partial<AuditLogItem> | undefined): { type: 'success' | 'danger' | 'warning' | 'info', label: string } {
|
||||||
|
if (!row) return { type: 'info', label: '-' }
|
||||||
|
const r = row.result || ''
|
||||||
|
if (!r) return { type: 'info', label: '-' }
|
||||||
|
// 异常
|
||||||
|
if (r.startsWith('exception') || /Exception/i.test(r)) return { type: 'danger', label: '异常' }
|
||||||
|
// 解析 code=N
|
||||||
|
const m = r.match(/code=(\d+)/)
|
||||||
|
if (m) {
|
||||||
|
const code = Number(m[1])
|
||||||
|
if (code === 0) return { type: 'success', label: '成功' }
|
||||||
|
return { type: 'danger', label: `失败(${code})` }
|
||||||
|
}
|
||||||
|
// 兜底:msg=success
|
||||||
|
if (/msg=success/i.test(r)) return { type: 'success', label: '成功' }
|
||||||
|
if (/msg=/i.test(r) && !/msg=success/i.test(r)) return { type: 'danger', label: '失败' }
|
||||||
|
return { type: 'warning', label: '未知' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回结果摘要(取 msg 部分) */
|
||||||
|
function resultSummary (result?: string): string {
|
||||||
|
if (!result) return ''
|
||||||
|
const m = result.match(/msg=([^,}]+)/)
|
||||||
|
return m ? `msg: ${m[1]}` : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDataList (): Promise<void> {
|
||||||
|
dataListLoading.value = true
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: pageIndex.value,
|
||||||
|
limit: pageSize.value,
|
||||||
|
api: dataForm.api || undefined,
|
||||||
|
method: dataForm.method || undefined,
|
||||||
|
head: dataForm.head || undefined,
|
||||||
|
ip: dataForm.ip || undefined
|
||||||
|
}
|
||||||
|
// 结果状态由前端筛选(后端无此参数)
|
||||||
|
const res = await getAuditLogList(params)
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
let list = res.data?.page?.list || []
|
||||||
|
if (dataForm.resultStatus) {
|
||||||
|
const wantSuccess = dataForm.resultStatus === 'SUCCESS'
|
||||||
|
list = list.filter((item: AuditLogItem) => {
|
||||||
|
const tag = resultTag(item)
|
||||||
|
const isSuccess = tag.type === 'success'
|
||||||
|
return wantSuccess ? isSuccess : !isSuccess
|
||||||
|
})
|
||||||
|
}
|
||||||
|
dataList.value = list
|
||||||
|
totalPage.value = res.data?.page?.totalCount || 0
|
||||||
|
} else {
|
||||||
|
dataList.value = []
|
||||||
|
totalPage.value = 0
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
dataListLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sizeChangeHandle (val: number): void {
|
||||||
|
pageSize.value = val
|
||||||
|
pageIndex.value = 1
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentChangeHandle (val: number): void {
|
||||||
|
pageIndex.value = val
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailVisible = ref<boolean>(false)
|
||||||
|
const detailData = reactive<Partial<AuditLogItem>>({})
|
||||||
|
|
||||||
|
function viewDetail (row: AuditLogItem): void {
|
||||||
|
Object.assign(detailData, row)
|
||||||
|
detailVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mod-audit-log {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.log-pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow: auto;
|
||||||
|
margin: 0;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,9 +13,6 @@
|
|||||||
<el-option label="已取消" value="CANCELLED"></el-option>
|
<el-option label="已取消" value="CANCELLED"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
|
||||||
<org-tree-select v-model="dataForm.partnerId" placeholder="合作商" width="160px" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="dateRange"
|
v-model="dateRange"
|
||||||
@@ -35,11 +32,18 @@
|
|||||||
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%;">
|
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%;">
|
||||||
<el-table-column type="index" header-align="center" align="center" width="60" label="序号" />
|
<el-table-column type="index" header-align="center" align="center" width="60" label="序号" />
|
||||||
<el-table-column prop="orderNo" header-align="center" align="center" min-width="180" label="订单号" show-overflow-tooltip />
|
<el-table-column prop="orderNo" header-align="center" align="center" min-width="180" label="订单号" show-overflow-tooltip />
|
||||||
<el-table-column prop="partnerId" header-align="center" align="center" width="100" label="合作商" />
|
<el-table-column header-align="center" align="center" width="140" label="设备MAC" show-overflow-tooltip>
|
||||||
<el-table-column prop="deviceId" header-align="center" align="center" width="90" label="设备" />
|
<template #default="{ row }">{{ row.mac || row.deviceMac || '-' }}</template>
|
||||||
<el-table-column prop="deviceMac" header-align="center" align="center" width="140" label="设备MAC" show-overflow-tooltip />
|
</el-table-column>
|
||||||
<el-table-column prop="storeOrgId" header-align="center" align="center" width="100" label="门店" />
|
<el-table-column header-align="center" align="center" width="110" label="渠道">
|
||||||
<el-table-column prop="channelOrgId" header-align="center" align="center" width="100" label="渠道" />
|
<template #default="{ row }">{{ orgNameOf(row.channelOrgId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" width="110" label="代理商">
|
||||||
|
<template #default="{ row }">{{ orgNameOf(row.agentOrgId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" width="110" label="门店">
|
||||||
|
<template #default="{ row }">{{ orgNameOf(row.storeOrgId) }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="cardNo" header-align="center" align="center" min-width="160" label="卡密(激活码)" show-overflow-tooltip />
|
<el-table-column prop="cardNo" header-align="center" align="center" min-width="160" label="卡密(激活码)" show-overflow-tooltip />
|
||||||
<el-table-column prop="status" header-align="center" align="center" width="100" label="订单状态">
|
<el-table-column prop="status" header-align="center" align="center" width="100" label="订单状态">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -77,11 +81,11 @@
|
|||||||
<el-dialog v-model="detailVisible" title="订单详情" width="720px">
|
<el-dialog v-model="detailVisible" title="订单详情" width="720px">
|
||||||
<el-descriptions :column="2" border v-loading="detailLoading">
|
<el-descriptions :column="2" border v-loading="detailLoading">
|
||||||
<el-descriptions-item label="订单号">{{ detailData?.orderNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="订单号">{{ detailData?.orderNo || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="合作商">{{ detailData?.partnerId ?? '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="设备">{{ detailData?.deviceId ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="设备">{{ detailData?.deviceId ?? '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="设备MAC">{{ detailData?.deviceMac || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="设备MAC">{{ detailData?.mac || detailData?.deviceMac || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="门店">{{ detailData?.storeOrgId ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="渠道">{{ orgNameOf(detailData?.channelOrgId) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="渠道">{{ detailData?.channelOrgId ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="代理商">{{ orgNameOf(detailData?.agentOrgId) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="门店">{{ orgNameOf(detailData?.storeOrgId) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="卡密(激活码)">{{ detailData?.cardNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="卡密(激活码)">{{ detailData?.cardNo || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="订单状态">
|
<el-descriptions-item label="订单状态">
|
||||||
<el-tag v-if="detailData?.status" :type="getStatusTagType(detailData.status)" size="small">
|
<el-tag v-if="detailData?.status" :type="getStatusTagType(detailData.status)" size="small">
|
||||||
@@ -109,17 +113,16 @@
|
|||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getDayCardOrderList, getDayCardOrderInfo, type DayCardOrderItem } from '@/api/manage/daycard'
|
import { getDayCardOrderList, getDayCardOrderInfo, type DayCardOrderItem } from '@/api/manage/daycard'
|
||||||
|
import { getOrgAllTree } from '@/api/org'
|
||||||
|
|
||||||
interface DataForm {
|
interface DataForm {
|
||||||
orderNo: string
|
orderNo: string
|
||||||
status: string
|
status: string
|
||||||
partnerId: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataForm = reactive<DataForm>({
|
const dataForm = reactive<DataForm>({
|
||||||
orderNo: '',
|
orderNo: '',
|
||||||
status: '',
|
status: ''
|
||||||
partnerId: ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const dateRange = ref<[string, string] | []>([])
|
const dateRange = ref<[string, string] | []>([])
|
||||||
@@ -164,6 +167,37 @@ function formatAmount (amount: number | null | undefined): string {
|
|||||||
return '¥' + Number(amount).toFixed(2)
|
return '¥' + Number(amount).toFixed(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 组织 ID -> 名称 映射(回显渠道/代理商/门店) */
|
||||||
|
const orgMap = ref<Map<number, string>>(new Map())
|
||||||
|
|
||||||
|
function flattenOrgTree (nodes: any[]): Map<number, string> {
|
||||||
|
const map = new Map<number, string>()
|
||||||
|
const walk = (list: any[]) => {
|
||||||
|
list.forEach(n => {
|
||||||
|
map.set(n.id, n.name)
|
||||||
|
if (n.children?.length) walk(n.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
walk(nodes)
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadOrgMap (): Promise<void> {
|
||||||
|
try {
|
||||||
|
const res = await getOrgAllTree()
|
||||||
|
const body = (res as any).data
|
||||||
|
const list = body?.data?.list || body?.data || []
|
||||||
|
orgMap.value = flattenOrgTree(Array.isArray(list) ? list : [])
|
||||||
|
} catch {
|
||||||
|
// 静默
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function orgNameOf (orgId?: number | null): string {
|
||||||
|
if (orgId == null) return '-'
|
||||||
|
return orgMap.value.get(orgId) || `#${orgId}`
|
||||||
|
}
|
||||||
|
|
||||||
async function getDataList (): Promise<void> {
|
async function getDataList (): Promise<void> {
|
||||||
dataListLoading.value = true
|
dataListLoading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -173,7 +207,6 @@ async function getDataList (): Promise<void> {
|
|||||||
}
|
}
|
||||||
if (dataForm.orderNo) params.orderNo = dataForm.orderNo
|
if (dataForm.orderNo) params.orderNo = dataForm.orderNo
|
||||||
if (dataForm.status) params.status = dataForm.status
|
if (dataForm.status) params.status = dataForm.status
|
||||||
if (dataForm.partnerId) params.partnerId = dataForm.partnerId
|
|
||||||
if (dateRange.value && dateRange.value.length === 2) {
|
if (dateRange.value && dateRange.value.length === 2) {
|
||||||
params.startDate = dateRange.value[0]
|
params.startDate = dateRange.value[0]
|
||||||
params.endDate = dateRange.value[1]
|
params.endDate = dateRange.value[1]
|
||||||
@@ -231,6 +264,7 @@ async function detailHandle (row: DayCardOrderItem): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
loadOrgMap()
|
||||||
getDataList()
|
getDataList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -33,12 +33,6 @@
|
|||||||
width="60"
|
width="60"
|
||||||
label="序号">
|
label="序号">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
prop="deviceName"
|
|
||||||
header-align="center"
|
|
||||||
align="center"
|
|
||||||
label="设备名称">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="mac"
|
prop="mac"
|
||||||
header-align="center"
|
header-align="center"
|
||||||
@@ -91,12 +85,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="storeName"
|
|
||||||
header-align="center"
|
header-align="center"
|
||||||
align="center"
|
align="center"
|
||||||
|
min-width="140"
|
||||||
label="所属门店">
|
label="所属门店">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ scope.row.storeName || '-' }}
|
{{ scope.row.storeName || orgNameOf(scope.row.storeOrgId) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@@ -228,16 +222,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import { getDeviceList, saveOrUpdateDevice, deleteDevice, unbindDeviceOrg, unbindDayCard, type DeviceInfo, type DeviceSaveData } from '@/api/manage/device'
|
import { getDeviceList, saveOrUpdateDevice, deleteDevice, unbindDeviceOrg, unbindDayCard, type DeviceInfo, type DeviceSaveData } from '@/api/manage/device'
|
||||||
import { getDeviceBenefits, type DeviceBenefits } from '@/api/manage/daycard'
|
import { getDeviceBenefits, type DeviceBenefits } from '@/api/manage/daycard'
|
||||||
|
import { getOrgAllTree } from '@/api/org'
|
||||||
import OrgTreeSelect from '@/components/org-tree-select/index.vue'
|
import OrgTreeSelect from '@/components/org-tree-select/index.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
/** 组织 ID -> 名称(回显门店) */
|
||||||
|
const orgMap = ref<Record<number, string>>({})
|
||||||
|
async function loadOrgMap (): Promise<void> {
|
||||||
|
try {
|
||||||
|
const res = await getOrgAllTree()
|
||||||
|
const body = (res as any).data
|
||||||
|
const list = body?.data?.list || body?.data || []
|
||||||
|
const walk = (nodes: any[]) => {
|
||||||
|
nodes.forEach(n => {
|
||||||
|
orgMap.value[n.id] = n.name
|
||||||
|
if (n.children?.length) walk(n.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (Array.isArray(list)) walk(list)
|
||||||
|
} catch { /* 静默 */ }
|
||||||
|
}
|
||||||
|
function orgNameOf (orgId?: number | null): string {
|
||||||
|
if (orgId == null) return '-'
|
||||||
|
return orgMap.value[orgId] || `#${orgId}`
|
||||||
|
}
|
||||||
|
|
||||||
// ===================== 列表 =====================
|
// ===================== 列表 =====================
|
||||||
|
|
||||||
const dataForm = reactive({
|
const dataForm = reactive({
|
||||||
deviceName: '',
|
deviceName: '',
|
||||||
mac: '',
|
mac: '',
|
||||||
status: ''
|
status: '',
|
||||||
|
storeOrgId: undefined as number | undefined
|
||||||
})
|
})
|
||||||
const dataList = ref<DeviceInfo[]>([])
|
const dataList = ref<DeviceInfo[]>([])
|
||||||
const pageIndex = ref<number>(1)
|
const pageIndex = ref<number>(1)
|
||||||
@@ -253,7 +273,9 @@ async function getDataList (): Promise<void> {
|
|||||||
limit: pageSize.value,
|
limit: pageSize.value,
|
||||||
deviceName: dataForm.deviceName,
|
deviceName: dataForm.deviceName,
|
||||||
mac: dataForm.mac,
|
mac: dataForm.mac,
|
||||||
status: dataForm.status
|
status: dataForm.status,
|
||||||
|
storeOrgId: dataForm.storeOrgId,
|
||||||
|
source: 'DIRECT'
|
||||||
})
|
})
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
dataList.value = res.data?.page?.list || []
|
dataList.value = res.data?.page?.list || []
|
||||||
@@ -477,10 +499,9 @@ async function showBenefits (row: DeviceInfo): Promise<void> {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await getDeviceBenefits(row.id)
|
const res = await getDeviceBenefits(row.id)
|
||||||
if (res && res.code === 0 && res.data) {
|
const benefits = res?.data
|
||||||
benefitsData.value = { ...res.data, deviceName: row.deviceName, deviceId: row.id, mac: row.mac }
|
if (benefits) {
|
||||||
} else if (res && res.code === 200 && res.data) {
|
benefitsData.value = { ...benefits, deviceName: row.deviceName, deviceId: row.id, mac: row.mac }
|
||||||
benefitsData.value = { ...res.data, deviceName: row.deviceName, deviceId: row.id, mac: row.mac }
|
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(res?.msg || '查询权益失败')
|
ElMessage.error(res?.msg || '查询权益失败')
|
||||||
}
|
}
|
||||||
@@ -492,6 +513,12 @@ async function showBenefits (row: DeviceInfo): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 从门店终端管理跳转来时,带 storeOrgId 筛选该门店设备
|
||||||
|
const q = route.query.storeOrgId
|
||||||
|
if (q != null && q !== '') {
|
||||||
|
dataForm.storeOrgId = Number(q)
|
||||||
|
}
|
||||||
|
loadOrgMap()
|
||||||
getDataList()
|
getDataList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
240
src/views/modules/manage/jst-call-log.vue
Normal file
240
src/views/modules/manage/jst-call-log.vue
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mod-jst-call-log">
|
||||||
|
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.mac" placeholder="MAC地址" clearable style="width:160px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-input v-model="dataForm.apiPath" placeholder="接口路径" clearable style="width:200px"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="dataForm.success" placeholder="结果" clearable style="width:120px">
|
||||||
|
<el-option label="成功" :value="1"></el-option>
|
||||||
|
<el-option label="失败" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开始">
|
||||||
|
<el-date-picker v-model="dataForm.beginTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="开始时间" style="width:200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结束">
|
||||||
|
<el-date-picker v-model="dataForm.endTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="结束时间" style="width:200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="getDataList()">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="table-page-wrap">
|
||||||
|
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
|
||||||
|
<el-table-column type="index" header-align="center" align="center" width="55" label="序号" />
|
||||||
|
<el-table-column header-align="center" align="center" min-width="260" label="接口路径" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ apiShort(scope.row.apiPath) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="requestMethod" header-align="center" align="center" width="70" label="方法" />
|
||||||
|
<el-table-column header-align="center" align="center" width="90" label="结果">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.success === 1" size="small" type="success">成功</el-tag>
|
||||||
|
<el-tag v-else size="small" type="danger">失败</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" width="90" label="状态码">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.responseCode && scope.row.responseCode >= 200 && scope.row.responseCode < 300" size="small" type="success">{{ scope.row.responseCode }}</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.responseCode" size="small" type="danger">{{ scope.row.responseCode }}</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" width="100" label="耗时">
|
||||||
|
<template #default="scope">
|
||||||
|
<span :class="durationClass(scope.row.duration)">{{ scope.row.duration ?? '-' }} ms</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mac" header-align="center" align="center" width="140" label="MAC" show-overflow-tooltip>
|
||||||
|
<template #default="scope">{{ scope.row.mac || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="storeOrgName" header-align="center" align="center" min-width="140" label="门店" show-overflow-tooltip>
|
||||||
|
<template #default="scope">{{ scope.row.storeOrgName || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" header-align="center" align="center" width="170" label="调用时间" />
|
||||||
|
<el-table-column fixed="right" header-align="center" align="center" width="90" label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" size="small" @click="viewDetail(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
:current-page="pageIndex"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="totalPage"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<el-dialog v-model="detailVisible" title="橘视厅调用详情" width="860px" top="5vh">
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="接口路径">
|
||||||
|
<div style="word-break: break-all">{{ cleanUrl(detailData.apiPath) }}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="请求方法">{{ detailData.requestMethod || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="结果">
|
||||||
|
<el-tag v-if="detailData.success === 1" size="small" type="success">成功</el-tag>
|
||||||
|
<el-tag v-else size="small" type="danger">失败</el-tag>
|
||||||
|
<span style="margin-left:8px; color:#909399; font-size:12px;">HTTP {{ detailData.responseCode || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="耗时">{{ detailData.duration ?? '-' }} ms</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="MAC">{{ detailData.mac || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="设备SN">{{ detailData.deviceSn || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="设备UID">{{ detailData.deviceUid || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="门店">{{ detailData.storeOrgName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="detailData.clientIp" label="客户端IP">{{ detailData.clientIp }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="detailData.errorMessage" label="错误信息">
|
||||||
|
<pre class="log-pre">{{ detailData.errorMessage }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="请求头">
|
||||||
|
<pre class="log-pre">{{ formatJson(detailData.requestHeaders) }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="请求体">
|
||||||
|
<pre class="log-pre">{{ formatJson(detailData.requestBody) }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="响应体">
|
||||||
|
<pre class="log-pre">{{ formatJson(detailData.responseBody) }}</pre>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="调用时间">{{ detailData.createTime || '-' }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="detailVisible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
|
import { getJstCallLogList, type JstCallLogItem } from '@/api/manage/jstCallLog'
|
||||||
|
|
||||||
|
interface DataForm {
|
||||||
|
mac: string
|
||||||
|
apiPath: string
|
||||||
|
success: number | ''
|
||||||
|
beginTime: string
|
||||||
|
endTime: string
|
||||||
|
}
|
||||||
|
const dataForm = reactive<DataForm>({ mac: '', apiPath: '', success: '', beginTime: '', endTime: '' })
|
||||||
|
const dataList = ref<JstCallLogItem[]>([])
|
||||||
|
const pageIndex = ref<number>(1)
|
||||||
|
const pageSize = ref<number>(10)
|
||||||
|
const totalPage = ref<number>(0)
|
||||||
|
const dataListLoading = ref<boolean>(false)
|
||||||
|
|
||||||
|
/** 清理 URL 首尾的反引号和空格 */
|
||||||
|
function cleanUrl (url?: string): string {
|
||||||
|
if (!url) return '-'
|
||||||
|
return url.trim().replace(/^`|`$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表展示用:仅取 host + path,去掉 query 串 */
|
||||||
|
function apiShort (url?: string): string {
|
||||||
|
const clean = cleanUrl(url)
|
||||||
|
if (clean === '-') return clean
|
||||||
|
// 去掉 query
|
||||||
|
const qIdx = clean.indexOf('?')
|
||||||
|
const noQuery = qIdx > -1 ? clean.substring(0, qIdx) : clean
|
||||||
|
// 提取 host + path
|
||||||
|
const m = noQuery.match(/^https?:\/\/[^/]+(.*)$/i)
|
||||||
|
if (m && m[1]) return m[1]
|
||||||
|
return noQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 耗时颜色:<300ms 绿,<1000ms 普通,>=1000ms 红 */
|
||||||
|
function durationClass (duration?: number): string {
|
||||||
|
if (duration == null) return ''
|
||||||
|
if (duration < 300) return 'dur-fast'
|
||||||
|
if (duration >= 1000) return 'dur-slow'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 尝试格式化 JSON 字符串,失败则原样返回 */
|
||||||
|
function formatJson (str?: string): string {
|
||||||
|
if (!str) return '-'
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(str), null, 2)
|
||||||
|
} catch {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDataList (): Promise<void> {
|
||||||
|
dataListLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getJstCallLogList({
|
||||||
|
page: pageIndex.value,
|
||||||
|
limit: pageSize.value,
|
||||||
|
mac: dataForm.mac || undefined,
|
||||||
|
apiPath: dataForm.apiPath || undefined,
|
||||||
|
success: dataForm.success === '' ? undefined : (dataForm.success as number),
|
||||||
|
beginTime: dataForm.beginTime || undefined,
|
||||||
|
endTime: dataForm.endTime || undefined
|
||||||
|
})
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
dataList.value = res.data?.page?.list || []
|
||||||
|
totalPage.value = res.data?.page?.totalCount || 0
|
||||||
|
} else {
|
||||||
|
dataList.value = []
|
||||||
|
totalPage.value = 0
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
dataListLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sizeChangeHandle (val: number): void {
|
||||||
|
pageSize.value = val
|
||||||
|
pageIndex.value = 1
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentChangeHandle (val: number): void {
|
||||||
|
pageIndex.value = val
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailVisible = ref<boolean>(false)
|
||||||
|
const detailData = reactive<Partial<JstCallLogItem>>({})
|
||||||
|
|
||||||
|
function viewDetail (row: JstCallLogItem): void {
|
||||||
|
Object.assign(detailData, row)
|
||||||
|
detailVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mod-jst-call-log {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.log-pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 240px;
|
||||||
|
overflow: auto;
|
||||||
|
margin: 0;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.dur-fast {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
.dur-slow {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,15 +2,15 @@
|
|||||||
<div class="mod-partner-device">
|
<div class="mod-partner-device">
|
||||||
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-select v-model="dataForm.partnerId" placeholder="合作商" clearable filterable style="width:180px">
|
<el-select v-model="dataForm.partnerId" placeholder="合作伙伴" clearable filterable style="width:180px">
|
||||||
<el-option v-for="p in partnerOptions" :key="p.id" :label="p.name" :value="p.partnerId"></el-option>
|
<el-option v-for="p in partnerOptions" :key="p.id" :label="p.name" :value="p.partnerId"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input v-model="dataForm.deviceName" placeholder="设备名称" clearable style="width:160px"></el-input>
|
<el-input v-model="dataForm.mac" placeholder="MAC地址" clearable style="width:160px"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input v-model="dataForm.mac" placeholder="MAC地址" clearable style="width:160px"></el-input>
|
<el-input v-model="dataForm.deviceSn" placeholder="设备SN" clearable style="width:160px"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="getDataList()">查询</el-button>
|
<el-button @click="getDataList()">查询</el-button>
|
||||||
@@ -18,25 +18,37 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
<div class="table-page-wrap">
|
<div class="table-page-wrap">
|
||||||
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
|
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
|
||||||
<el-table-column type="index" header-align="center" align="center" width="60" label="序号" />
|
<el-table-column type="index" header-align="center" align="center" width="55" label="序号" />
|
||||||
<el-table-column header-align="center" align="center" min-width="140" label="合作商">
|
<el-table-column header-align="center" align="center" width="90" label="来源">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.source === 'PARTNER'" size="small" type="warning">第三方</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.source === 'DIRECT'" size="small" type="success">自营</el-tag>
|
||||||
|
<span v-else>{{ scope.row.source || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mac" header-align="center" align="center" width="150" label="MAC" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="wifiMac" header-align="center" align="center" width="150" label="WiFi MAC" show-overflow-tooltip>
|
||||||
|
<template #default="scope">{{ scope.row.wifiMac || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="deviceSn" header-align="center" align="center" min-width="160" label="SN" show-overflow-tooltip>
|
||||||
|
<template #default="scope">{{ scope.row.deviceSn || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="deviceUid" header-align="center" align="center" min-width="140" label="设备UID" show-overflow-tooltip>
|
||||||
|
<template #default="scope">{{ scope.row.deviceUid || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" min-width="140" label="合作伙伴">
|
||||||
<template #default="scope">{{ partnerNameOf(scope.row.partnerId) }}</template>
|
<template #default="scope">{{ partnerNameOf(scope.row.partnerId) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="deviceName" header-align="center" align="center" min-width="140" label="设备名称" />
|
<el-table-column prop="status" header-align="center" align="center" width="90" label="状态">
|
||||||
<el-table-column prop="mac" header-align="center" align="center" width="140" label="MAC" />
|
|
||||||
<el-table-column prop="deviceSn" header-align="center" align="center" min-width="160" label="设备SN" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="deviceModel" header-align="center" align="center" width="120" label="型号" />
|
|
||||||
<el-table-column prop="status" header-align="center" align="center" width="100" label="状态">
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag v-if="scope.row.status === 'ACTIVE'" size="small" type="success">启用</el-tag>
|
<el-tag v-if="scope.row.status === 'ACTIVE'" size="small" type="success">已激活</el-tag>
|
||||||
<el-tag v-else-if="scope.row.status === 'INACTIVE'" size="small" type="danger">禁用</el-tag>
|
<el-tag v-else-if="scope.row.status === 'INACTIVE'" size="small" type="info">未激活</el-tag>
|
||||||
<el-tag v-else size="small">{{ scope.row.status }}</el-tag>
|
<el-tag v-else size="small">{{ scope.row.status }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="storeName" header-align="center" align="center" min-width="140" label="绑定门店">
|
<el-table-column prop="lastHeartbeat" header-align="center" align="center" width="170" label="心跳时间">
|
||||||
<template #default="scope">{{ scope.row.storeName || '-' }}</template>
|
<template #default="scope">{{ scope.row.lastHeartbeat || '-' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="createdAt" header-align="center" align="center" width="170" label="注册时间" />
|
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
@size-change="sizeChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
@@ -58,10 +70,10 @@ import { getPartnerList, type PartnerInfo } from '@/api/manage/partner'
|
|||||||
|
|
||||||
interface DataForm {
|
interface DataForm {
|
||||||
partnerId: string | null
|
partnerId: string | null
|
||||||
deviceName: string
|
|
||||||
mac: string
|
mac: string
|
||||||
|
deviceSn: string
|
||||||
}
|
}
|
||||||
const dataForm = reactive<DataForm>({ partnerId: null, deviceName: '', mac: '' })
|
const dataForm = reactive<DataForm>({ partnerId: null, mac: '', deviceSn: '' })
|
||||||
const dataList = ref<DeviceInfo[]>([])
|
const dataList = ref<DeviceInfo[]>([])
|
||||||
const pageIndex = ref<number>(1)
|
const pageIndex = ref<number>(1)
|
||||||
const pageSize = ref<number>(10)
|
const pageSize = ref<number>(10)
|
||||||
@@ -94,8 +106,8 @@ async function getDataList (): Promise<void> {
|
|||||||
limit: pageSize.value,
|
limit: pageSize.value,
|
||||||
source: 'PARTNER',
|
source: 'PARTNER',
|
||||||
partnerId: dataForm.partnerId ?? undefined,
|
partnerId: dataForm.partnerId ?? undefined,
|
||||||
deviceName: dataForm.deviceName || undefined,
|
mac: dataForm.mac || undefined,
|
||||||
mac: dataForm.mac || undefined
|
deviceSn: dataForm.deviceSn || undefined
|
||||||
})
|
})
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
dataList.value = res.data?.page?.list || []
|
dataList.value = res.data?.page?.list || []
|
||||||
|
|||||||
@@ -2,11 +2,9 @@
|
|||||||
<div class="mod-pay-config">
|
<div class="mod-pay-config">
|
||||||
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
<el-form :inline="true" :model="dataForm" @keyup.enter="getDataList()">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<org-tree-select v-model="dataForm.partnerId" placeholder="合作商" width="180px" />
|
<el-select v-model="dataForm.status" placeholder="状态" clearable style="width: 120px">
|
||||||
</el-form-item>
|
<el-option label="启用" value="ACTIVE"></el-option>
|
||||||
<el-form-item>
|
<el-option label="禁用" value="INACTIVE"></el-option>
|
||||||
<el-select v-model="dataForm.status" placeholder="状态" clearable style="width: 120px;">
|
|
||||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
@@ -16,10 +14,23 @@
|
|||||||
<el-button type="primary" @click="addHandle()">新增</el-button>
|
<el-button type="primary" @click="addHandle()">新增</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<div style="margin-bottom: 8px; color: #909399; font-size: 13px">
|
||||||
|
配置优先级:门店 > 代理商 > 渠道 > 平台全局(下单时逐级查找)
|
||||||
|
</div>
|
||||||
<div class="table-page-wrap">
|
<div class="table-page-wrap">
|
||||||
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%;">
|
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
|
||||||
<el-table-column type="index" header-align="center" align="center" width="50" label="序号"></el-table-column>
|
<el-table-column type="index" header-align="center" align="center" width="50" label="序号"></el-table-column>
|
||||||
<el-table-column prop="partnerId" header-align="center" align="center" label="合作商"></el-table-column>
|
<el-table-column header-align="center" align="center" width="100" label="层级">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.storeOrgId" size="small" type="danger">门店</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.agentOrgId" size="small" type="warning">代理商</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.channelOrgId" size="small" type="success">渠道</el-tag>
|
||||||
|
<el-tag v-else size="small" type="info">全局</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column header-align="center" align="center" min-width="160" label="所属组织">
|
||||||
|
<template #default="scope">{{ orgNameOf(scope.row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="appId" header-align="center" align="center" label="应用ID"></el-table-column>
|
<el-table-column prop="appId" header-align="center" align="center" label="应用ID"></el-table-column>
|
||||||
<el-table-column prop="mchId" header-align="center" align="center" label="商户号"></el-table-column>
|
<el-table-column prop="mchId" header-align="center" align="center" label="商户号"></el-table-column>
|
||||||
<el-table-column prop="certSerialNo" header-align="center" align="center" label="证书序列号" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="certSerialNo" header-align="center" align="center" label="证书序列号" show-overflow-tooltip></el-table-column>
|
||||||
@@ -49,10 +60,19 @@
|
|||||||
layout="total, sizes, prev, pager, next, jumper">
|
layout="total, sizes, prev, pager, next, jumper">
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog v-model="addOrUpdateVisible" :title="addOrUpdateTitle" width="500px">
|
|
||||||
|
<el-dialog v-model="addOrUpdateVisible" :title="addOrUpdateTitle" width="560px">
|
||||||
<el-form :model="addOrUpdateForm" :rules="addOrUpdateRules" ref="addOrUpdateFormRef" label-width="120px">
|
<el-form :model="addOrUpdateForm" :rules="addOrUpdateRules" ref="addOrUpdateFormRef" label-width="120px">
|
||||||
<el-form-item label="合作商" prop="partnerId">
|
<el-form-item label="配置层级" prop="configLevel">
|
||||||
<org-tree-select v-model="addOrUpdateForm.partnerId" placeholder="请选择合作商" />
|
<el-radio-group v-model="addOrUpdateForm.configLevel" @change="onLevelChange">
|
||||||
|
<el-radio label="GLOBAL">平台全局</el-radio>
|
||||||
|
<el-radio label="CHANNEL">渠道</el-radio>
|
||||||
|
<el-radio label="AGENT">代理商</el-radio>
|
||||||
|
<el-radio label="STORE">门店</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="addOrUpdateForm.configLevel !== 'GLOBAL'" label="所属组织" prop="orgId">
|
||||||
|
<org-tree-select v-model="addOrUpdateForm.orgId" :hide-node-types="hideTypesByLevel" placeholder="请选择组织" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="应用ID" prop="appId">
|
<el-form-item label="应用ID" prop="appId">
|
||||||
<el-input v-model="addOrUpdateForm.appId" placeholder="请输入应用ID"></el-input>
|
<el-input v-model="addOrUpdateForm.appId" placeholder="请输入应用ID"></el-input>
|
||||||
@@ -66,20 +86,32 @@
|
|||||||
<el-form-item label="证书序列号" prop="certSerialNo">
|
<el-form-item label="证书序列号" prop="certSerialNo">
|
||||||
<el-input v-model="addOrUpdateForm.certSerialNo" placeholder="请输入证书序列号"></el-input>
|
<el-input v-model="addOrUpdateForm.certSerialNo" placeholder="请输入证书序列号"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="私钥路径" prop="privateKeyPath">
|
<el-form-item label="商户API私钥" prop="privateKey">
|
||||||
<el-input v-model="addOrUpdateForm.privateKeyPath" placeholder="请输入私钥路径"></el-input>
|
<div style="display:flex; flex-direction:column; gap:8px; width:100%;">
|
||||||
|
<el-input
|
||||||
|
v-model="addOrUpdateForm.privateKey"
|
||||||
|
type="textarea"
|
||||||
|
:rows="6"
|
||||||
|
placeholder="请粘贴 apiclient_key.pem 文件内容(-----BEGIN PRIVATE KEY----- 开头)或上传文件">
|
||||||
|
</el-input>
|
||||||
|
<el-upload
|
||||||
|
:auto-upload="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
:limit="1"
|
||||||
|
accept=".pem"
|
||||||
|
:on-change="onPemFileChange">
|
||||||
|
<el-button size="small" type="primary" plain>上传 .pem 文件</el-button>
|
||||||
|
<template #tip>
|
||||||
|
<span style="margin-left:8px; color:#909399; font-size:12px;">仅读取本地文件内容,不会上传到服务器</span>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="回调地址" prop="notifyUrl">
|
<el-form-item label="回调地址" prop="notifyUrl">
|
||||||
<el-input v-model="addOrUpdateForm.notifyUrl" placeholder="请输入回调地址"></el-input>
|
<el-input v-model="addOrUpdateForm.notifyUrl" placeholder="请输入回调地址"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-switch
|
<el-switch v-model="addOrUpdateForm.status" active-value="ACTIVE" inactive-value="INACTIVE" active-text="启用" inactive-text="禁用"></el-switch>
|
||||||
v-model="addOrUpdateForm.status"
|
|
||||||
active-value="ACTIVE"
|
|
||||||
inactive-value="INACTIVE"
|
|
||||||
active-text="启用"
|
|
||||||
inactive-text="禁用">
|
|
||||||
</el-switch>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="addOrUpdateForm.remark" placeholder="请输入备注"></el-input>
|
<el-input v-model="addOrUpdateForm.remark" placeholder="请输入备注"></el-input>
|
||||||
@@ -94,64 +126,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted } from 'vue'
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
import { ElMessage, ElMessageBox, type FormInstance, type FormRules, type UploadFile } from 'element-plus'
|
||||||
import { getPayConfigList, saveOrUpdatePayConfig, deletePayConfig, type PayConfigInfo } from '@/api/manage/payConfig'
|
import { getPayConfigList, saveOrUpdatePayConfig, deletePayConfig, type PayConfigInfo } from '@/api/manage/payConfig'
|
||||||
|
import OrgTreeSelect from '@/components/org-tree-select/index.vue'
|
||||||
|
|
||||||
const statusOptions = [
|
const dataForm = reactive<{ status: string }>({ status: '' })
|
||||||
{ label: '启用', value: 'ACTIVE' },
|
|
||||||
{ label: '禁用', value: 'INACTIVE' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const dataForm = reactive<{
|
|
||||||
partnerId: string | number
|
|
||||||
status: string
|
|
||||||
}>({
|
|
||||||
partnerId: '',
|
|
||||||
status: ''
|
|
||||||
})
|
|
||||||
const dataList = ref<PayConfigInfo[]>([])
|
const dataList = ref<PayConfigInfo[]>([])
|
||||||
const pageIndex = ref<number>(1)
|
const pageIndex = ref<number>(1)
|
||||||
const pageSize = ref<number>(10)
|
const pageSize = ref<number>(10)
|
||||||
const totalPage = ref<number>(0)
|
const totalPage = ref<number>(0)
|
||||||
const dataListLoading = ref<boolean>(false)
|
const dataListLoading = ref<boolean>(false)
|
||||||
|
|
||||||
const addOrUpdateVisible = ref<boolean>(false)
|
/** 列表:层级名称回显 */
|
||||||
const addOrUpdateTitle = ref<string>('')
|
function orgNameOf (row: PayConfigInfo): string {
|
||||||
const addOrUpdateFormRef = ref<FormInstance>()
|
if (row.storeOrgId) return row.storeName || `门店#${row.storeOrgId}`
|
||||||
|
if (row.agentOrgId) return row.agentOrgName || `代理商#${row.agentOrgId}`
|
||||||
interface AddOrUpdateForm {
|
if (row.channelOrgId) return row.channelName || `渠道#${row.channelOrgId}`
|
||||||
id: number | null
|
return '平台全局'
|
||||||
partnerId: number | null
|
|
||||||
appId: string
|
|
||||||
mchId: string
|
|
||||||
apiV3Key: string
|
|
||||||
certSerialNo: string
|
|
||||||
privateKeyPath: string
|
|
||||||
notifyUrl: string
|
|
||||||
status: string
|
|
||||||
remark: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const addOrUpdateForm = reactive<AddOrUpdateForm>({
|
|
||||||
id: null,
|
|
||||||
partnerId: null,
|
|
||||||
appId: '',
|
|
||||||
mchId: '',
|
|
||||||
apiV3Key: '',
|
|
||||||
certSerialNo: '',
|
|
||||||
privateKeyPath: '',
|
|
||||||
notifyUrl: '',
|
|
||||||
status: 'ACTIVE',
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const addOrUpdateRules: FormRules = {
|
|
||||||
partnerId: [{ required: true, message: '请选择合作商', trigger: 'change' }],
|
|
||||||
appId: [{ required: true, message: '应用ID不能为空', trigger: 'blur' }],
|
|
||||||
mchId: [{ required: true, message: '商户号不能为空', trigger: 'blur' }],
|
|
||||||
apiV3Key: [{ required: true, message: 'API V3密钥不能为空', trigger: 'blur' }],
|
|
||||||
notifyUrl: [{ required: true, message: '回调地址不能为空', trigger: 'blur' }]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDataList (): void {
|
function getDataList (): void {
|
||||||
@@ -159,8 +151,7 @@ function getDataList (): void {
|
|||||||
getPayConfigList({
|
getPayConfigList({
|
||||||
page: pageIndex.value,
|
page: pageIndex.value,
|
||||||
limit: pageSize.value,
|
limit: pageSize.value,
|
||||||
partnerId: dataForm.partnerId ? Number(dataForm.partnerId) : undefined,
|
status: dataForm.status || undefined
|
||||||
status: dataForm.status
|
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
dataList.value = res.data?.page?.list || []
|
dataList.value = res.data?.page?.list || []
|
||||||
@@ -186,14 +177,96 @@ function currentChangeHandle (val: number): void {
|
|||||||
getDataList()
|
getDataList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================== 新增/修改 =====================
|
||||||
|
|
||||||
|
const addOrUpdateVisible = ref<boolean>(false)
|
||||||
|
const addOrUpdateTitle = ref<string>('')
|
||||||
|
const addOrUpdateFormRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
type ConfigLevel = 'GLOBAL' | 'CHANNEL' | 'AGENT' | 'STORE'
|
||||||
|
|
||||||
|
interface AddOrUpdateForm {
|
||||||
|
id: number | null
|
||||||
|
configLevel: ConfigLevel
|
||||||
|
orgId: number | string | null
|
||||||
|
appId: string
|
||||||
|
mchId: string
|
||||||
|
apiV3Key: string
|
||||||
|
certSerialNo: string
|
||||||
|
privateKey: string
|
||||||
|
notifyUrl: string
|
||||||
|
status: string
|
||||||
|
remark: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const addOrUpdateForm = reactive<AddOrUpdateForm>({
|
||||||
|
id: null,
|
||||||
|
configLevel: 'GLOBAL',
|
||||||
|
orgId: null,
|
||||||
|
appId: '',
|
||||||
|
mchId: '',
|
||||||
|
apiV3Key: '',
|
||||||
|
certSerialNo: '',
|
||||||
|
privateKey: '',
|
||||||
|
notifyUrl: '',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 按层级过滤组织树:只显示对应类型的节点 */
|
||||||
|
const hideTypesByLevel = computed<string[]>(() => {
|
||||||
|
switch (addOrUpdateForm.configLevel) {
|
||||||
|
case 'CHANNEL': return ['STORE', 'SERVICE_PROVIDER']
|
||||||
|
case 'AGENT': return ['STORE', 'CHANNEL']
|
||||||
|
case 'STORE': return ['CHANNEL', 'SERVICE_PROVIDER']
|
||||||
|
default: return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const addOrUpdateRules: FormRules = {
|
||||||
|
appId: [{ required: true, message: '应用ID不能为空', trigger: 'blur' }],
|
||||||
|
mchId: [{ required: true, message: '商户号不能为空', trigger: 'blur' }],
|
||||||
|
apiV3Key: [{ required: true, message: 'API V3密钥不能为空', trigger: 'blur' }],
|
||||||
|
notifyUrl: [{ required: true, message: '回调地址不能为空', trigger: 'blur' }],
|
||||||
|
orgId: [{ required: true, message: '请选择所属组织', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLevelChange (): void {
|
||||||
|
addOrUpdateForm.orgId = null
|
||||||
|
addOrUpdateFormRef.value?.clearValidate('orgId')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 读取本地 .pem 文件内容到 privateKey(不上传服务器) */
|
||||||
|
function onPemFileChange (file: UploadFile): void {
|
||||||
|
if (!file.raw) return
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => {
|
||||||
|
const text = String(reader.result || '').trim()
|
||||||
|
if (!text) {
|
||||||
|
ElMessage.warning('文件内容为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!text.includes('BEGIN') && !text.includes('PRIVATE KEY')) {
|
||||||
|
ElMessage.warning('文件内容不像 PEM 私钥,请确认文件正确')
|
||||||
|
}
|
||||||
|
addOrUpdateForm.privateKey = text
|
||||||
|
ElMessage.success('已读取私钥文件内容')
|
||||||
|
}
|
||||||
|
reader.onerror = () => {
|
||||||
|
ElMessage.error('读取文件失败')
|
||||||
|
}
|
||||||
|
reader.readAsText(file.raw)
|
||||||
|
}
|
||||||
|
|
||||||
function resetForm (): void {
|
function resetForm (): void {
|
||||||
addOrUpdateForm.id = null
|
addOrUpdateForm.id = null
|
||||||
addOrUpdateForm.partnerId = null
|
addOrUpdateForm.configLevel = 'GLOBAL'
|
||||||
|
addOrUpdateForm.orgId = null
|
||||||
addOrUpdateForm.appId = ''
|
addOrUpdateForm.appId = ''
|
||||||
addOrUpdateForm.mchId = ''
|
addOrUpdateForm.mchId = ''
|
||||||
addOrUpdateForm.apiV3Key = ''
|
addOrUpdateForm.apiV3Key = ''
|
||||||
addOrUpdateForm.certSerialNo = ''
|
addOrUpdateForm.certSerialNo = ''
|
||||||
addOrUpdateForm.privateKeyPath = ''
|
addOrUpdateForm.privateKey = ''
|
||||||
addOrUpdateForm.notifyUrl = ''
|
addOrUpdateForm.notifyUrl = ''
|
||||||
addOrUpdateForm.status = 'ACTIVE'
|
addOrUpdateForm.status = 'ACTIVE'
|
||||||
addOrUpdateForm.remark = ''
|
addOrUpdateForm.remark = ''
|
||||||
@@ -201,74 +274,79 @@ function resetForm (): void {
|
|||||||
|
|
||||||
function addHandle (): void {
|
function addHandle (): void {
|
||||||
resetForm()
|
resetForm()
|
||||||
addOrUpdateTitle.value = '新增'
|
addOrUpdateTitle.value = '新增支付配置'
|
||||||
addOrUpdateVisible.value = true
|
addOrUpdateVisible.value = true
|
||||||
addOrUpdateFormRef.value?.clearValidate()
|
addOrUpdateFormRef.value?.clearValidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
function editHandle (row: PayConfigInfo): void {
|
function editHandle (row: PayConfigInfo): void {
|
||||||
resetForm()
|
resetForm()
|
||||||
addOrUpdateTitle.value = '修改'
|
addOrUpdateTitle.value = '修改支付配置'
|
||||||
addOrUpdateVisible.value = true
|
|
||||||
addOrUpdateForm.id = row.id
|
addOrUpdateForm.id = row.id
|
||||||
addOrUpdateForm.partnerId = row.partnerId
|
|
||||||
addOrUpdateForm.appId = row.appId
|
addOrUpdateForm.appId = row.appId
|
||||||
addOrUpdateForm.mchId = row.mchId
|
addOrUpdateForm.mchId = row.mchId
|
||||||
addOrUpdateForm.apiV3Key = row.apiV3Key
|
addOrUpdateForm.apiV3Key = row.apiV3Key
|
||||||
addOrUpdateForm.certSerialNo = row.certSerialNo
|
addOrUpdateForm.certSerialNo = row.certSerialNo
|
||||||
addOrUpdateForm.privateKeyPath = row.privateKeyPath
|
addOrUpdateForm.privateKey = row.privateKey
|
||||||
addOrUpdateForm.notifyUrl = row.notifyUrl
|
addOrUpdateForm.notifyUrl = row.notifyUrl
|
||||||
addOrUpdateForm.status = row.status
|
addOrUpdateForm.status = row.status
|
||||||
addOrUpdateForm.remark = row.remark
|
addOrUpdateForm.remark = row.remark
|
||||||
|
// 根据现有 orgId 判断层级
|
||||||
|
if (row.storeOrgId) {
|
||||||
|
addOrUpdateForm.configLevel = 'STORE'
|
||||||
|
addOrUpdateForm.orgId = row.storeOrgId
|
||||||
|
} else if (row.agentOrgId) {
|
||||||
|
addOrUpdateForm.configLevel = 'AGENT'
|
||||||
|
addOrUpdateForm.orgId = row.agentOrgId
|
||||||
|
} else if (row.channelOrgId) {
|
||||||
|
addOrUpdateForm.configLevel = 'CHANNEL'
|
||||||
|
addOrUpdateForm.orgId = row.channelOrgId
|
||||||
|
} else {
|
||||||
|
addOrUpdateForm.configLevel = 'GLOBAL'
|
||||||
|
addOrUpdateForm.orgId = null
|
||||||
|
}
|
||||||
|
addOrUpdateVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitForm (): void {
|
function submitForm (): void {
|
||||||
addOrUpdateFormRef.value?.validate((valid) => {
|
addOrUpdateFormRef.value?.validate((valid) => {
|
||||||
if (valid) {
|
if (!valid) return
|
||||||
saveOrUpdatePayConfig({
|
// 根据层级填对应 orgId,其他留空
|
||||||
id: addOrUpdateForm.id || undefined,
|
const payload: any = {
|
||||||
partnerId: addOrUpdateForm.partnerId ?? undefined,
|
id: addOrUpdateForm.id || undefined,
|
||||||
appId: addOrUpdateForm.appId,
|
appId: addOrUpdateForm.appId,
|
||||||
mchId: addOrUpdateForm.mchId,
|
mchId: addOrUpdateForm.mchId,
|
||||||
apiV3Key: addOrUpdateForm.apiV3Key,
|
apiV3Key: addOrUpdateForm.apiV3Key,
|
||||||
certSerialNo: addOrUpdateForm.certSerialNo,
|
certSerialNo: addOrUpdateForm.certSerialNo,
|
||||||
privateKeyPath: addOrUpdateForm.privateKeyPath,
|
privateKey: addOrUpdateForm.privateKey,
|
||||||
notifyUrl: addOrUpdateForm.notifyUrl,
|
notifyUrl: addOrUpdateForm.notifyUrl,
|
||||||
status: addOrUpdateForm.status,
|
status: addOrUpdateForm.status,
|
||||||
remark: addOrUpdateForm.remark
|
remark: addOrUpdateForm.remark
|
||||||
}).then((res) => {
|
|
||||||
if (res && res.code === 0) {
|
|
||||||
ElMessage.success({
|
|
||||||
message: '操作成功',
|
|
||||||
duration: 1500,
|
|
||||||
onClose: () => {
|
|
||||||
addOrUpdateVisible.value = false
|
|
||||||
getDataList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ElMessage.error(res.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
if (addOrUpdateForm.configLevel === 'CHANNEL') {
|
||||||
|
payload.channelOrgId = addOrUpdateForm.orgId
|
||||||
|
} else if (addOrUpdateForm.configLevel === 'AGENT') {
|
||||||
|
payload.agentOrgId = addOrUpdateForm.orgId
|
||||||
|
} else if (addOrUpdateForm.configLevel === 'STORE') {
|
||||||
|
payload.storeOrgId = addOrUpdateForm.orgId
|
||||||
|
}
|
||||||
|
saveOrUpdatePayConfig(payload).then((res) => {
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
ElMessage.success({ message: '操作成功', duration: 1500, onClose: () => { addOrUpdateVisible.value = false; getDataList() } })
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteHandle (id: number): void {
|
function deleteHandle (id: number): void {
|
||||||
ElMessageBox.confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', {
|
ElMessageBox.confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
deletePayConfig([id]).then((res) => {
|
deletePayConfig([id]).then((res) => {
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
ElMessage.success({
|
ElMessage.success({ message: '操作成功', duration: 1500, onClose: () => getDataList() })
|
||||||
message: '操作成功',
|
|
||||||
duration: 1500,
|
|
||||||
onClose: () => {
|
|
||||||
getDataList()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(res.msg)
|
ElMessage.error(res.msg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ function goTerminalManage (row: ShopRow) {
|
|||||||
ElMessage.warning('无法跳转:缺少门店标识')
|
ElMessage.warning('无法跳转:缺少门店标识')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
router.push({ name: 'device-device', query: { storeOrgId: String(id) } }).catch(() => {})
|
router.push({ name: 'manage-device', query: { storeOrgId: String(id) } }).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMoreMenuCommand (cmd: string, row: ShopRow) {
|
function onMoreMenuCommand (cmd: string, row: ShopRow) {
|
||||||
|
|||||||
@@ -455,12 +455,14 @@ interface StoreSearchResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickStoreSearchPage(data: StoreSearchResponse | undefined): { list: ShopRow[]; total: number } {
|
function pickStoreSearchPage(data: any): { list: ShopRow[]; total: number } {
|
||||||
if (!data) return { list: [], total: 0 }
|
if (!data) return { list: [], total: 0 }
|
||||||
const block = data.data ?? data.page ?? {}
|
// 兼容:data.data.page.list(manage统一)/ data.data.list(PageResult)/ data.page.list / data.list
|
||||||
const list = Array.isArray(block.list) ? block.list : []
|
const inner = data.data ?? data.page ?? data
|
||||||
const total = block.totalCount ?? 0
|
const block = (inner && typeof inner === 'object' && !Array.isArray(inner)) ? (inner.page ?? inner) : inner
|
||||||
return { list, total }
|
const list = Array.isArray(block?.list) ? block.list : (Array.isArray(inner) ? inner : [])
|
||||||
|
const total = block?.totalCount ?? inner?.totalCount ?? 0
|
||||||
|
return { list, total: Number(total) || 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDataList() {
|
function getDataList() {
|
||||||
|
|||||||
@@ -198,9 +198,8 @@ function init (id?: number): void {
|
|||||||
// 修改
|
// 修改
|
||||||
getMenuInfo(dataForm.id).then((data) => {
|
getMenuInfo(dataForm.id).then((data) => {
|
||||||
if (!data || data.code !== 0) return
|
if (!data || data.code !== 0) return
|
||||||
const body = data as unknown as { data?: MenuInfo; menu?: MenuInfo }
|
const menu = data.data?.menu
|
||||||
const menu = body.data ?? body.menu
|
if (menu && typeof menu === 'object' && menu.id) {
|
||||||
if (menu && typeof menu === 'object') {
|
|
||||||
dataForm.id = menu.id
|
dataForm.id = menu.id
|
||||||
dataForm.type = menu.type ?? 1
|
dataForm.type = menu.type ?? 1
|
||||||
dataForm.name = menu.name
|
dataForm.name = menu.name
|
||||||
|
|||||||
@@ -104,13 +104,8 @@ function init (id?: number): void {
|
|||||||
if (dataForm.id) {
|
if (dataForm.id) {
|
||||||
getRoleInfo(dataForm.id).then((body) => {
|
getRoleInfo(dataForm.id).then((body) => {
|
||||||
if (!body || body.code !== 0) return
|
if (!body || body.code !== 0) return
|
||||||
const env = body as unknown as {
|
const role = body.data?.role as (RoleInfo & { menuIdList?: number[] }) | undefined
|
||||||
data?: RoleInfo & { menuIdList?: number[] }
|
if (!role || typeof role !== 'object') return
|
||||||
role?: RoleInfo & { menuIdList?: number[] }
|
|
||||||
}
|
|
||||||
const rolePayload = env.data ?? env.role
|
|
||||||
if (!rolePayload || typeof rolePayload !== 'object') return
|
|
||||||
const role = rolePayload as RoleInfo & { menuIdList: number[] }
|
|
||||||
dataForm.roleName = role.roleName || ''
|
dataForm.roleName = role.roleName || ''
|
||||||
dataForm.remark = role.remark || ''
|
dataForm.remark = role.remark || ''
|
||||||
const menuIdList = Array.isArray(role.menuIdList) ? role.menuIdList : []
|
const menuIdList = Array.isArray(role.menuIdList) ? role.menuIdList : []
|
||||||
|
|||||||
@@ -265,8 +265,7 @@ function init (id?: number | string | null): void {
|
|||||||
if (id !== null && id !== undefined && id !== '') {
|
if (id !== null && id !== undefined && id !== '') {
|
||||||
getUserInfo(Number(id)).then((body) => {
|
getUserInfo(Number(id)).then((body) => {
|
||||||
if (!body || body.code !== 0) return
|
if (!body || body.code !== 0) return
|
||||||
const env = body as unknown as { data?: UserInfo; user?: UserInfo }
|
const user = body.data?.user
|
||||||
const user = env.data ?? env.user
|
|
||||||
if (!user || typeof user !== 'object') return
|
if (!user || typeof user !== 'object') return
|
||||||
dataForm.id = user.id
|
dataForm.id = user.id
|
||||||
dataForm.userName = user.username || ''
|
dataForm.userName = user.username || ''
|
||||||
|
|||||||
Reference in New Issue
Block a user