This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="detail-container">
|
<div class="detail-container">
|
||||||
<ElCard v-for="(section, index) in sections" :key="index" class="detail-section">
|
<ElCard v-for="(section, index) in sections" :key="index" class="detail-section" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="section-title">{{ section.title }}</div>
|
<div class="section-title">{{ section.title }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -400,7 +400,7 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "设置管理",
|
"title": "设置管理",
|
||||||
"wechatPayConfiguration": "微信支付配置",
|
"wechatPayConfiguration": "微信配置",
|
||||||
"detailsOfPaymentConfiguration": "支付配置详情",
|
"detailsOfPaymentConfiguration": "支付配置详情",
|
||||||
"withdrawalSettings": "提现配置"
|
"withdrawalSettings": "提现配置"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ export const asyncRoutes: AppRouteRecord[] = [
|
|||||||
icon: ''
|
icon: ''
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
{
|
{
|
||||||
path: 'wechat-config',
|
path: 'wechat-config',
|
||||||
name: 'WechatConfig',
|
name: 'WechatConfig',
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export enum RoutesAlias {
|
|||||||
|
|
||||||
// 设置管理
|
// 设置管理
|
||||||
WithdrawalSettings = '/settings/withdrawal-settings', // 提现配置
|
WithdrawalSettings = '/settings/withdrawal-settings', // 提现配置
|
||||||
WechatConfig = '/settings/wechat-config', // 微信支付配置
|
WechatConfig = '/settings/wechat-config', // 微信配置
|
||||||
WechatConfigDetail = '/settings/wechat-config/detail', // 微信支付配置详情
|
WechatConfigDetail = '/settings/wechat-config/detail', // 微信支付配置详情
|
||||||
|
|
||||||
// 轮询管理
|
// 轮询管理
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
// 充值状态
|
// 充值状态
|
||||||
export enum AgentRechargeStatus {
|
export enum AgentRechargeStatus {
|
||||||
PENDING = 1, // 待支付
|
PENDING = 1, // 待支付
|
||||||
COMPLETED = 2, // 已完成
|
PAID = 2, // 已支付
|
||||||
CANCELLED = 3 // 已取消
|
COMPLETED = 3, // 已完成
|
||||||
|
CLOSED = 4, // 已关闭
|
||||||
|
REFUNDED = 5 // 已退款
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支付方式
|
// 支付方式
|
||||||
@@ -24,6 +26,7 @@ export interface AgentRecharge {
|
|||||||
shop_name: string
|
shop_name: string
|
||||||
amount: number // 充值金额(单位:分)
|
amount: number // 充值金额(单位:分)
|
||||||
status: AgentRechargeStatus
|
status: AgentRechargeStatus
|
||||||
|
status_name: string // 状态名称
|
||||||
payment_method: AgentRechargePaymentMethod
|
payment_method: AgentRechargePaymentMethod
|
||||||
payment_channel: AgentRechargePaymentChannel
|
payment_channel: AgentRechargePaymentChannel
|
||||||
payment_config_id: number | null
|
payment_config_id: number | null
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export interface PaymentMerchantSetting {
|
|||||||
publicKey: string
|
publicKey: string
|
||||||
notifyUrl?: string
|
notifyUrl?: string
|
||||||
}
|
}
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
wechat?: {
|
wechat?: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
appId: string
|
appId: string
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// 支付渠道类型
|
// 支付渠道类型
|
||||||
export type PaymentProviderType = 'wechat' | 'wechat_v2' | 'fuiou'
|
export type PaymentProviderType = 'wechat' | 'wechat_v2' | 'fuiou'
|
||||||
|
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
export interface WechatConfig {
|
export interface WechatConfig {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
@@ -24,7 +24,7 @@ export interface WechatConfig {
|
|||||||
oa_aes_key: string
|
oa_aes_key: string
|
||||||
oa_oauth_redirect_url: string
|
oa_oauth_redirect_url: string
|
||||||
|
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
wx_mch_id: string
|
wx_mch_id: string
|
||||||
wx_api_v2_key: string
|
wx_api_v2_key: string
|
||||||
wx_api_v3_key: string
|
wx_api_v3_key: string
|
||||||
@@ -79,7 +79,7 @@ export interface CreateWechatConfigRequest {
|
|||||||
oa_aes_key?: string
|
oa_aes_key?: string
|
||||||
oa_oauth_redirect_url?: string
|
oa_oauth_redirect_url?: string
|
||||||
|
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
wx_mch_id?: string
|
wx_mch_id?: string
|
||||||
wx_api_v2_key?: string
|
wx_api_v2_key?: string
|
||||||
wx_api_v3_key?: string
|
wx_api_v3_key?: string
|
||||||
@@ -115,7 +115,7 @@ export interface UpdateWechatConfigRequest {
|
|||||||
oa_aes_key?: string
|
oa_aes_key?: string
|
||||||
oa_oauth_redirect_url?: string
|
oa_oauth_redirect_url?: string
|
||||||
|
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
wx_mch_id?: string
|
wx_mch_id?: string
|
||||||
wx_api_v2_key?: string
|
wx_api_v2_key?: string
|
||||||
wx_api_v3_key?: string
|
wx_api_v3_key?: string
|
||||||
|
|||||||
@@ -131,18 +131,34 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<ElEmpty v-if="filteredAvailableRoles.length === 0" description="暂无角色" />
|
<ElEmpty v-if="filteredAvailableRoles.length === 0" description="暂无角色" />
|
||||||
<ElRadioGroup v-else v-model="roleToAdd" class="role-list">
|
<template v-else-if="isPlatformUser">
|
||||||
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
<ElCheckboxGroup v-model="rolesToAdd" class="role-list">
|
||||||
<ElRadio :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
||||||
<span class="role-info">
|
<ElCheckbox :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
<span>{{ role.role_name }}</span>
|
<span class="role-info">
|
||||||
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
<span>{{ role.role_name }}</span>
|
||||||
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
||||||
</ElTag>
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
</span>
|
</ElTag>
|
||||||
</ElRadio>
|
</span>
|
||||||
</div>
|
</ElCheckbox>
|
||||||
</ElRadioGroup>
|
</div>
|
||||||
|
</ElCheckboxGroup>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ElRadioGroup v-model="roleToAdd" class="role-list">
|
||||||
|
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
||||||
|
<ElRadio :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
|
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
||||||
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
|
</ElTag>
|
||||||
|
</span>
|
||||||
|
</ElRadio>
|
||||||
|
</div>
|
||||||
|
</ElRadioGroup>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -152,7 +168,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
:icon="'ArrowRight'"
|
:icon="'ArrowRight'"
|
||||||
@click="addRoles"
|
@click="addRoles"
|
||||||
:disabled="!roleToAdd"
|
:disabled="isPlatformUser ? rolesToAdd.length === 0 : !roleToAdd"
|
||||||
>
|
>
|
||||||
分配
|
分配
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -205,7 +221,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { FormInstance, ElSwitch, ElCheckbox, ElCheckboxGroup, ElTag, ElEmpty } from 'element-plus'
|
import {
|
||||||
|
FormInstance,
|
||||||
|
ElSwitch,
|
||||||
|
ElCheckbox,
|
||||||
|
ElCheckboxGroup,
|
||||||
|
ElTag,
|
||||||
|
ElEmpty,
|
||||||
|
ElRadioGroup,
|
||||||
|
ElRadio
|
||||||
|
} from 'element-plus'
|
||||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||||
import type { FormRules } from 'element-plus'
|
import type { FormRules } from 'element-plus'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
@@ -232,10 +257,14 @@
|
|||||||
const currentAccountType = ref<number>(0)
|
const currentAccountType = ref<number>(0)
|
||||||
const selectedRoles = ref<number[]>([])
|
const selectedRoles = ref<number[]>([])
|
||||||
const allRoles = ref<PlatformRole[]>([])
|
const allRoles = ref<PlatformRole[]>([])
|
||||||
const roleToAdd = ref<number | undefined>(undefined)
|
const rolesToAdd = ref<number[]>([]) // 多选时使用
|
||||||
|
const roleToAdd = ref<number | undefined>(undefined) // 单选时使用
|
||||||
const leftRoleFilter = ref('')
|
const leftRoleFilter = ref('')
|
||||||
const rightRoleFilter = ref('')
|
const rightRoleFilter = ref('')
|
||||||
|
|
||||||
|
// 是否为平台用户(平台用户可多选,其他单选)
|
||||||
|
const isPlatformUser = computed(() => currentAccountType.value === 2)
|
||||||
|
|
||||||
// 定义表单搜索初始值
|
// 定义表单搜索初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
name: '',
|
name: '',
|
||||||
@@ -648,6 +677,7 @@
|
|||||||
currentAccountName.value = row.username
|
currentAccountName.value = row.username
|
||||||
currentAccountType.value = row.user_type
|
currentAccountType.value = row.user_type
|
||||||
selectedRoles.value = []
|
selectedRoles.value = []
|
||||||
|
rolesToAdd.value = []
|
||||||
roleToAdd.value = undefined
|
roleToAdd.value = undefined
|
||||||
leftRoleFilter.value = ''
|
leftRoleFilter.value = ''
|
||||||
rightRoleFilter.value = ''
|
rightRoleFilter.value = ''
|
||||||
@@ -671,17 +701,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加角色(单选)
|
// 添加角色
|
||||||
const addRoles = async () => {
|
const addRoles = async () => {
|
||||||
if (!roleToAdd.value) return
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 新角色会替换之前的角色
|
let newRoles: number[]
|
||||||
const newRoles = [roleToAdd.value]
|
|
||||||
|
// 平台用户可以分配多个角色,其他用户只能分配一个角色
|
||||||
|
if (isPlatformUser.value) {
|
||||||
|
if (rolesToAdd.value.length === 0) return
|
||||||
|
// 平台用户可以分配多个角色
|
||||||
|
newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
||||||
|
} else {
|
||||||
|
// 其他用户只能分配一个角色(单选)
|
||||||
|
if (!roleToAdd.value) return
|
||||||
|
newRoles = [...selectedRoles.value, roleToAdd.value]
|
||||||
|
}
|
||||||
|
|
||||||
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
selectedRoles.value = newRoles
|
selectedRoles.value = newRoles
|
||||||
|
rolesToAdd.value = []
|
||||||
roleToAdd.value = undefined
|
roleToAdd.value = undefined
|
||||||
|
|
||||||
ElMessage.success('角色分配成功')
|
ElMessage.success('角色分配成功')
|
||||||
|
|||||||
@@ -15,9 +15,17 @@
|
|||||||
reserve-keyword
|
reserve-keyword
|
||||||
:remote-method="handlePackageSearch"
|
:remote-method="handlePackageSearch"
|
||||||
:loading="packageSearchLoading"
|
:loading="packageSearchLoading"
|
||||||
|
:disabled="!props.seriesId"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
|
<template v-if="!props.seriesId">
|
||||||
|
<ElOption :value="null" disabled>
|
||||||
|
<span style="color: var(--el-text-color-warning)"
|
||||||
|
>该设备未关联套餐系列,无法购买套餐</span
|
||||||
|
>
|
||||||
|
</ElOption>
|
||||||
|
</template>
|
||||||
<ElOption
|
<ElOption
|
||||||
v-for="pkg in packageOptions"
|
v-for="pkg in packageOptions"
|
||||||
:key="pkg.id"
|
:key="pkg.id"
|
||||||
@@ -143,32 +151,54 @@
|
|||||||
// 监听对话框打开,加载套餐列表
|
// 监听对话框打开,加载套餐列表
|
||||||
watch(visible, async (newVal) => {
|
watch(visible, async (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
if (!props.seriesId) {
|
||||||
|
packageOptions.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
await loadPackages()
|
await loadPackages()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听 seriesId 变化,重新加载套餐列表
|
||||||
|
watch(
|
||||||
|
() => props.seriesId,
|
||||||
|
async (newSeriesId) => {
|
||||||
|
if (visible.value && newSeriesId) {
|
||||||
|
await loadPackages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let loadPackagesPromise: Promise<void> | null = null
|
||||||
|
|
||||||
const loadPackages = async () => {
|
const loadPackages = async () => {
|
||||||
if (!props.seriesId) return
|
if (!props.seriesId) return
|
||||||
|
|
||||||
|
// 防止重复调用
|
||||||
|
if (loadPackagesPromise) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
packageSearchLoading.value = true
|
packageSearchLoading.value = true
|
||||||
|
loadPackagesPromise = PackageManageService.getPackages({
|
||||||
const response = await PackageManageService.getPackages({
|
|
||||||
series_id: props.seriesId,
|
series_id: props.seriesId,
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
status: 1, // 只获取启用的套餐
|
status: 1, // 只获取启用的套餐
|
||||||
shelf_status: 1 // 只获取已上架的套餐
|
shelf_status: 1 // 只获取已上架的套餐
|
||||||
})
|
})
|
||||||
|
.then((response) => {
|
||||||
if (response.code === 0 && response.data) {
|
if (response.code === 0 && response.data) {
|
||||||
packageOptions.value = response.data.items || []
|
packageOptions.value = response.data.items || []
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
packageSearchLoading.value = false
|
||||||
|
loadPackagesPromise = null
|
||||||
|
})
|
||||||
|
await loadPackagesPromise
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('加载套餐列表失败:', error)
|
console.error('加载套餐列表失败:', error)
|
||||||
console.log(error?.message || '加载套餐列表失败')
|
console.log(error?.message || '加载套餐列表失败')
|
||||||
} finally {
|
|
||||||
packageSearchLoading.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1045,14 +1045,12 @@
|
|||||||
{
|
{
|
||||||
prop: 'max_sim_slots',
|
prop: 'max_sim_slots',
|
||||||
label: '最大插槽数',
|
label: '最大插槽数',
|
||||||
width: 100,
|
width: 100
|
||||||
align: 'center'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'bound_card_count',
|
prop: 'bound_card_count',
|
||||||
label: '已绑定卡数',
|
label: '已绑定卡数',
|
||||||
width: 110,
|
width: 110,
|
||||||
align: 'center',
|
|
||||||
formatter: (row: Device) => {
|
formatter: (row: Device) => {
|
||||||
const color = row.bound_card_count > 0 ? '#67c23a' : '#909399'
|
const color = row.bound_card_count > 0 ? '#67c23a' : '#909399'
|
||||||
return h('span', { style: { color, fontWeight: 'bold' } }, row.bound_card_count)
|
return h('span', { style: { color, fontWeight: 'bold' } }, row.bound_card_count)
|
||||||
@@ -1091,6 +1089,7 @@
|
|||||||
prop: 'software_version',
|
prop: 'software_version',
|
||||||
label: '固件版本',
|
label: '固件版本',
|
||||||
width: 120,
|
width: 120,
|
||||||
|
showOverflowTooltip: true,
|
||||||
formatter: (row: Device) => row.software_version || '-'
|
formatter: (row: Device) => row.software_version || '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1015,13 +1015,15 @@
|
|||||||
{
|
{
|
||||||
prop: 'virtual_no',
|
prop: 'virtual_no',
|
||||||
label: '卡虚拟号',
|
label: '卡虚拟号',
|
||||||
width: 160,
|
width: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
formatter: (row: StandaloneIotCard) => row.virtual_no || '-'
|
formatter: (row: StandaloneIotCard) => row.virtual_no || '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'device_virtual_no',
|
prop: 'device_virtual_no',
|
||||||
label: '绑定设备虚拟号',
|
label: '绑定设备虚拟号',
|
||||||
width: 160,
|
width: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
formatter: (row: StandaloneIotCard) => row.device_virtual_no || '-'
|
formatter: (row: StandaloneIotCard) => row.device_virtual_no || '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1033,7 +1035,8 @@
|
|||||||
{
|
{
|
||||||
prop: 'carrier_name',
|
prop: 'carrier_name',
|
||||||
label: '运营商',
|
label: '运营商',
|
||||||
width: 150
|
width: 150,
|
||||||
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'shop_name',
|
prop: 'shop_name',
|
||||||
|
|||||||
@@ -384,7 +384,8 @@
|
|||||||
{
|
{
|
||||||
prop: 'carrier_name',
|
prop: 'carrier_name',
|
||||||
label: '运营商',
|
label: '运营商',
|
||||||
width: 120
|
width: 120,
|
||||||
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'card_category',
|
prop: 'card_category',
|
||||||
|
|||||||
@@ -515,7 +515,8 @@
|
|||||||
{
|
{
|
||||||
prop: 'shop_code',
|
prop: 'shop_code',
|
||||||
label: '店铺编码',
|
label: '店铺编码',
|
||||||
minWidth: 150
|
minWidth: 150,
|
||||||
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'shop_name',
|
prop: 'shop_name',
|
||||||
|
|||||||
@@ -118,18 +118,34 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<ElEmpty v-if="filteredAvailableRoles.length === 0" description="暂无角色" />
|
<ElEmpty v-if="filteredAvailableRoles.length === 0" description="暂无角色" />
|
||||||
<ElCheckboxGroup v-else v-model="rolesToAdd" class="role-list">
|
<template v-else-if="isPlatformUser">
|
||||||
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
<ElCheckboxGroup v-model="rolesToAdd" class="role-list">
|
||||||
<ElCheckbox :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
||||||
<span class="role-info">
|
<ElCheckbox :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
<span>{{ role.role_name }}</span>
|
<span class="role-info">
|
||||||
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
<span>{{ role.role_name }}</span>
|
||||||
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
||||||
</ElTag>
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
</span>
|
</ElTag>
|
||||||
</ElCheckbox>
|
</span>
|
||||||
</div>
|
</ElCheckbox>
|
||||||
</ElCheckboxGroup>
|
</div>
|
||||||
|
</ElCheckboxGroup>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ElRadioGroup v-model="roleToAdd" class="role-list">
|
||||||
|
<div v-for="role in filteredAvailableRoles" :key="role.ID" class="role-item">
|
||||||
|
<ElRadio :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
|
<ElTag :type="role.role_type === 1 ? 'primary' : 'success'" size="small">
|
||||||
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
|
</ElTag>
|
||||||
|
</span>
|
||||||
|
</ElRadio>
|
||||||
|
</div>
|
||||||
|
</ElRadioGroup>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -139,7 +155,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
:icon="'ArrowRight'"
|
:icon="'ArrowRight'"
|
||||||
@click="addRoles"
|
@click="addRoles"
|
||||||
:disabled="rolesToAdd.length === 0"
|
:disabled="isPlatformUser ? rolesToAdd.length === 0 : !roleToAdd"
|
||||||
>
|
>
|
||||||
添加
|
添加
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -204,6 +220,8 @@
|
|||||||
ElInput,
|
ElInput,
|
||||||
ElCheckbox,
|
ElCheckbox,
|
||||||
ElCheckboxGroup,
|
ElCheckboxGroup,
|
||||||
|
ElRadioGroup,
|
||||||
|
ElRadio,
|
||||||
ElEmpty
|
ElEmpty
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
@@ -258,9 +276,13 @@
|
|||||||
const selectedRoles = ref<number[]>([])
|
const selectedRoles = ref<number[]>([])
|
||||||
const allRoles = ref<PlatformRole[]>([])
|
const allRoles = ref<PlatformRole[]>([])
|
||||||
const rolesToAdd = ref<number[]>([])
|
const rolesToAdd = ref<number[]>([])
|
||||||
|
const roleToAdd = ref<number | undefined>(undefined) // 单选时使用
|
||||||
const leftRoleFilter = ref('')
|
const leftRoleFilter = ref('')
|
||||||
const rightRoleFilter = ref('')
|
const rightRoleFilter = ref('')
|
||||||
|
|
||||||
|
// 是否为平台用户(平台用户可多选,其他单选)
|
||||||
|
const isPlatformUser = computed(() => currentAccountType.value === 2)
|
||||||
|
|
||||||
// 搜索表单初始值
|
// 搜索表单初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
username: '',
|
username: '',
|
||||||
@@ -619,6 +641,7 @@
|
|||||||
currentAccountType.value = row.user_type
|
currentAccountType.value = row.user_type
|
||||||
selectedRoles.value = []
|
selectedRoles.value = []
|
||||||
rolesToAdd.value = []
|
rolesToAdd.value = []
|
||||||
|
roleToAdd.value = undefined
|
||||||
leftRoleFilter.value = ''
|
leftRoleFilter.value = ''
|
||||||
rightRoleFilter.value = ''
|
rightRoleFilter.value = ''
|
||||||
|
|
||||||
@@ -643,28 +666,25 @@
|
|||||||
|
|
||||||
// 批量添加角色
|
// 批量添加角色
|
||||||
const addRoles = async () => {
|
const addRoles = async () => {
|
||||||
if (rolesToAdd.value.length === 0) return
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let newRoles: number[]
|
let newRoles: number[]
|
||||||
|
|
||||||
// 代理账号只能分配一个角色,会覆盖之前的角色
|
// 平台用户可以分配多个角色,其他用户只能分配一个角色
|
||||||
if (currentAccountType.value === 3) {
|
if (isPlatformUser.value) {
|
||||||
if (rolesToAdd.value.length > 1) {
|
if (rolesToAdd.value.length === 0) return
|
||||||
ElMessage.warning('代理账号只能分配一个角色')
|
// 平台用户可以分配多个角色
|
||||||
return
|
|
||||||
}
|
|
||||||
// 只保留新选择的一个角色
|
|
||||||
newRoles = rolesToAdd.value
|
|
||||||
} else {
|
|
||||||
// 其他账号类型可以分配多个角色
|
|
||||||
newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
||||||
|
} else {
|
||||||
|
// 其他用户只能分配一个角色(单选)
|
||||||
|
if (!roleToAdd.value) return
|
||||||
|
newRoles = [...selectedRoles.value, roleToAdd.value]
|
||||||
}
|
}
|
||||||
|
|
||||||
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
selectedRoles.value = newRoles
|
selectedRoles.value = newRoles
|
||||||
rolesToAdd.value = []
|
rolesToAdd.value = []
|
||||||
|
roleToAdd.value = undefined
|
||||||
|
|
||||||
ElMessage.success('角色分配成功')
|
ElMessage.success('角色分配成功')
|
||||||
// 刷新列表以更新角色显示
|
// 刷新列表以更新角色显示
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElCard shadow="never" class="commission-stats-widget" v-if="stats">
|
<ElCard shadow="never" class="commission-stats-widget">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<span class="header-title">佣金资金统计</span>
|
<span class="header-title">佣金资金统计</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right" v-if="stats">
|
||||||
<span class="date-range">{{ startTime }} ~ {{ endTime }}</span>
|
<span class="date-range">{{ startTime }} ~ {{ endTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="stats-info">
|
<div v-if="stats" class="stats-info">
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stats-icon"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-breakdown">
|
<div class="stats-breakdown" v-if="stats">
|
||||||
<div class="breakdown-item">
|
<div class="breakdown-item">
|
||||||
<span class="breakdown-label">成本价差占比</span>
|
<span class="breakdown-label">成本价差占比</span>
|
||||||
<span class="breakdown-value">{{ formatPercent(stats.cost_diff_percent) }}</span>
|
<span class="breakdown-value">{{ formatPercent(stats.cost_diff_percent) }}</span>
|
||||||
@@ -70,9 +70,7 @@
|
|||||||
<span class="breakdown-value">{{ formatPercent(stats.one_time_percent) }}</span>
|
<span class="breakdown-value">{{ formatPercent(stats.one_time_percent) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ElCard>
|
<div v-if="!stats" class="empty-content">
|
||||||
<ElCard shadow="never" v-else class="commission-stats-widget empty-state">
|
|
||||||
<div class="empty-content">
|
|
||||||
<i class="el-icon-info" style="font-size: 48px; color: var(--el-text-color-placeholder)"></i>
|
<i class="el-icon-info" style="font-size: 48px; color: var(--el-text-color-placeholder)"></i>
|
||||||
<p>暂无佣金统计数据</p>
|
<p>暂无佣金统计数据</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElCard shadow="never" class="commission-summary-widget" v-if="!errorMsg">
|
<ElCard shadow="never" class="commission-summary-widget">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
@@ -7,77 +7,77 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="stats-info">
|
<div v-if="!errorMsg" class="stats-info">
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%)"
|
style="background: linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">预充值余额</div>
|
<div class="stat-label">预充值余额</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.main_balance) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.main_balance) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
|
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">总佣金</div>
|
<div class="stat-label">总佣金</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.total_commission) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.total_commission) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
|
style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">可提现佣金</div>
|
<div class="stat-label">可提现佣金</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.available_commission) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.available_commission) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)"
|
style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">冻结佣金</div>
|
<div class="stat-label">冻结佣金</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.frozen_commission) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.frozen_commission) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)"
|
style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">提现中佣金</div>
|
<div class="stat-label">提现中佣金</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.withdrawing_commission) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.withdrawing_commission) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stat-icon"
|
||||||
style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)"
|
style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)"
|
||||||
>
|
>
|
||||||
<i class="iconfont-sys"></i>
|
<i class="iconfont-sys"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-content">
|
<div class="stat-content">
|
||||||
<div class="stats-label">已提现佣金</div>
|
<div class="stat-label">已提现佣金</div>
|
||||||
<div class="stats-value">{{ formatMoney(summary.withdrawn_commission) }}</div>
|
<div class="stat-value">{{ formatMoney(summary.withdrawn_commission) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,9 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ElCard>
|
<div v-if="errorMsg" class="error-content">
|
||||||
<ElCard shadow="never" v-else class="commission-summary-widget error-state">
|
|
||||||
<div class="error-content">
|
|
||||||
<i class="el-icon-warning" style="font-size: 48px; color: var(--el-color-warning)"></i>
|
<i class="el-icon-warning" style="font-size: 48px; color: var(--el-color-warning)"></i>
|
||||||
<p>{{ errorMsg }}</p>
|
<p>{{ errorMsg }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -250,7 +248,7 @@
|
|||||||
background: var(--el-fill-color-blank);
|
background: var(--el-fill-color-blank);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
.stats-icon {
|
.stat-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -262,17 +260,17 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-content {
|
.stat-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
.stats-label {
|
.stat-label {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-value {
|
.stat-value {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -320,20 +318,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.error-state {
|
.error-content {
|
||||||
.error-content {
|
display: flex;
|
||||||
display: flex;
|
flex-direction: column;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
justify-content: center;
|
padding: 40px 20px;
|
||||||
padding: 40px 20px;
|
color: var(--el-text-color-secondary);
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElCard shadow="never" class="daily-commission-stats-widget" v-if="statsList.length > 0">
|
<ElCard shadow="never" class="daily-commission-stats-widget">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<span class="header-title">每日佣金统计</span>
|
<span class="header-title">每日佣金统计</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right" v-if="statsList.length > 0">
|
||||||
<span class="date-range"
|
<span class="date-range"
|
||||||
>{{ formatDateRange(startDate) }} ~ {{ formatDateRange(endDate) }}</span
|
>{{ formatDateRange(startDate) }} ~ {{ formatDateRange(endDate) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="stats-info">
|
<div v-if="statsList.length > 0" class="stats-info">
|
||||||
<div class="stats-card">
|
<div class="stats-card">
|
||||||
<div
|
<div
|
||||||
class="stats-icon"
|
class="stats-icon"
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-chart">
|
<div class="stats-chart" v-if="statsList.length > 0">
|
||||||
<div class="chart-bar" v-for="item in statsList" :key="item.date">
|
<div class="chart-bar" v-for="item in statsList" :key="item.date">
|
||||||
<div class="bar-label">{{ formatDateShort(item.date) }}</div>
|
<div class="bar-label">{{ formatDateShort(item.date) }}</div>
|
||||||
<div class="bar-value" :style="{ height: getBarHeight(item.total_amount) + 'px' }">
|
<div class="bar-value" :style="{ height: getBarHeight(item.total_amount) + 'px' }">
|
||||||
@@ -48,9 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ElCard>
|
<div v-if="statsList.length === 0" class="empty-content">
|
||||||
<ElCard shadow="never" v-else class="daily-commission-stats-widget empty-state">
|
|
||||||
<div class="empty-content">
|
|
||||||
<i class="el-icon-info" style="font-size: 48px; color: var(--el-text-color-placeholder)"></i>
|
<i class="el-icon-info" style="font-size: 48px; color: var(--el-text-color-placeholder)"></i>
|
||||||
<p>暂无佣金统计数据</p>
|
<p>暂无佣金统计数据</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,21 +53,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态标签类型
|
// 获取状态标签类型
|
||||||
const getStatusType = (status: AgentRechargeStatus): 'warning' | 'success' | 'info' => {
|
const getStatusType = (
|
||||||
const statusMap: Record<AgentRechargeStatus, 'warning' | 'success' | 'info'> = {
|
status: AgentRechargeStatus
|
||||||
|
): 'warning' | 'success' | 'info' | 'danger' => {
|
||||||
|
const statusMap: Record<AgentRechargeStatus, 'warning' | 'success' | 'info' | 'danger'> = {
|
||||||
1: 'warning', // 待支付
|
1: 'warning', // 待支付
|
||||||
2: 'success', // 已完成
|
2: 'success', // 已支付
|
||||||
3: 'info' // 已取消
|
3: 'success', // 已完成
|
||||||
|
4: 'info', // 已关闭
|
||||||
|
5: 'danger' // 已退款
|
||||||
}
|
}
|
||||||
return statusMap[status] || 'info'
|
return statusMap[status] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态文本
|
// 获取状态文本(优先使用 status_name,否则使用本地映射)
|
||||||
const getStatusText = (status: AgentRechargeStatus): string => {
|
const getStatusText = (status: AgentRechargeStatus, statusName?: string): string => {
|
||||||
|
if (statusName) return statusName
|
||||||
const statusMap: Record<AgentRechargeStatus, string> = {
|
const statusMap: Record<AgentRechargeStatus, string> = {
|
||||||
1: '待支付',
|
1: '待支付',
|
||||||
2: '已完成',
|
2: '已支付',
|
||||||
3: '已取消'
|
3: '已完成',
|
||||||
|
4: '已关闭',
|
||||||
|
5: '已退款'
|
||||||
}
|
}
|
||||||
return statusMap[status] || '-'
|
return statusMap[status] || '-'
|
||||||
}
|
}
|
||||||
@@ -97,7 +104,9 @@
|
|||||||
{
|
{
|
||||||
label: '状态',
|
label: '状态',
|
||||||
render: (data) =>
|
render: (data) =>
|
||||||
h(ElTag, { type: getStatusType(data.status) }, () => getStatusText(data.status))
|
h(ElTag, { type: getStatusType(data.status) }, () =>
|
||||||
|
getStatusText(data.status, data.status_name)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -327,21 +327,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态标签类型
|
// 获取状态标签类型
|
||||||
const getStatusType = (status: AgentRechargeStatus): 'warning' | 'success' | 'info' => {
|
const getStatusType = (
|
||||||
const statusMap: Record<AgentRechargeStatus, 'warning' | 'success' | 'info'> = {
|
status: AgentRechargeStatus
|
||||||
|
): 'warning' | 'success' | 'info' | 'danger' => {
|
||||||
|
const statusMap: Record<AgentRechargeStatus, 'warning' | 'success' | 'info' | 'danger'> = {
|
||||||
1: 'warning', // 待支付
|
1: 'warning', // 待支付
|
||||||
2: 'success', // 已完成
|
2: 'success', // 已支付
|
||||||
3: 'info' // 已取消
|
3: 'success', // 已完成
|
||||||
|
4: 'info', // 已关闭
|
||||||
|
5: 'danger' // 已退款
|
||||||
}
|
}
|
||||||
return statusMap[status] || 'info'
|
return statusMap[status] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态文本
|
// 获取状态文本(优先使用 status_name,否则使用本地映射)
|
||||||
const getStatusText = (status: AgentRechargeStatus): string => {
|
const getStatusText = (status: AgentRechargeStatus, statusName?: string): string => {
|
||||||
|
if (statusName) return statusName
|
||||||
const statusMap: Record<AgentRechargeStatus, string> = {
|
const statusMap: Record<AgentRechargeStatus, string> = {
|
||||||
1: '待支付',
|
1: '待支付',
|
||||||
2: '已完成',
|
2: '已支付',
|
||||||
3: '已取消'
|
3: '已完成',
|
||||||
|
4: '已关闭',
|
||||||
|
5: '已退款'
|
||||||
}
|
}
|
||||||
return statusMap[status] || '-'
|
return statusMap[status] || '-'
|
||||||
}
|
}
|
||||||
@@ -391,7 +398,9 @@
|
|||||||
label: '状态',
|
label: '状态',
|
||||||
width: 100,
|
width: 100,
|
||||||
formatter: (row: AgentRecharge) => {
|
formatter: (row: AgentRecharge) => {
|
||||||
return h(ElTag, { type: getStatusType(row.status) }, () => getStatusText(row.status))
|
return h(ElTag, { type: getStatusType(row.status) }, () =>
|
||||||
|
getStatusText(row.status, row.status_name)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,38 +15,40 @@
|
|||||||
<!-- 详情内容 -->
|
<!-- 详情内容 -->
|
||||||
<DetailPage v-if="detailData" :sections="detailSections" :data="detailData" />
|
<DetailPage v-if="detailData" :sections="detailSections" :data="detailData" />
|
||||||
|
|
||||||
<!-- 订单项列表 -->
|
<!-- 订单列表 -->
|
||||||
<div
|
<ElCard style="margin-top: 20px" shadow="never">
|
||||||
v-if="detailData && detailData.items && detailData.items.length > 0"
|
<div
|
||||||
class="order-items-section"
|
v-if="detailData && detailData.items && detailData.items.length > 0"
|
||||||
>
|
class="order-items-section"
|
||||||
<h3 class="section-title">订单项列表</h3>
|
>
|
||||||
<ElTable :data="detailData.items" border style="margin-top: 10px">
|
<h3 class="section-title">订单列表</h3>
|
||||||
<ElTableColumn prop="package_name" label="套餐名称" min-width="150" />
|
<ElTable :data="detailData.items" border>
|
||||||
<ElTableColumn prop="package_type" label="套餐类型" width="120">
|
<ElTableColumn prop="package_name" label="套餐名称" />
|
||||||
<template #default="{ row }">
|
<ElTableColumn prop="package_type" label="套餐类型">
|
||||||
{{
|
<template #default="{ row }">
|
||||||
row.package_type === 'formal'
|
{{
|
||||||
? '正式套餐'
|
row.package_type === 'formal'
|
||||||
: row.package_type === 'addon'
|
? '正式套餐'
|
||||||
? '加油包'
|
: row.package_type === 'addon'
|
||||||
: '-'
|
? '加油包'
|
||||||
}}
|
: '-'
|
||||||
</template>
|
}}
|
||||||
</ElTableColumn>
|
</template>
|
||||||
<ElTableColumn prop="quantity" label="数量" width="100" />
|
</ElTableColumn>
|
||||||
<ElTableColumn prop="unit_price" label="单价" width="120">
|
<ElTableColumn prop="quantity" label="数量" />
|
||||||
<template #default="{ row }">
|
<ElTableColumn prop="unit_price" label="单价">
|
||||||
{{ formatCurrency(row.unit_price) }}
|
<template #default="{ row }">
|
||||||
</template>
|
{{ formatCurrency(row.unit_price) }}
|
||||||
</ElTableColumn>
|
</template>
|
||||||
<ElTableColumn prop="amount" label="金额" width="120">
|
</ElTableColumn>
|
||||||
<template #default="{ row }">
|
<ElTableColumn prop="amount" label="金额">
|
||||||
{{ formatCurrency(row.amount) }}
|
<template #default="{ row }">
|
||||||
</template>
|
{{ formatCurrency(row.amount) }}
|
||||||
</ElTableColumn>
|
</template>
|
||||||
</ElTable>
|
</ElTableColumn>
|
||||||
</div>
|
</ElTable>
|
||||||
|
</div>
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<div v-if="loading" class="loading-container">
|
<div v-if="loading" class="loading-container">
|
||||||
@@ -286,10 +288,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-items-section {
|
.order-items-section {
|
||||||
padding-top: 20px;
|
|
||||||
margin-top: 30px;
|
|
||||||
border-top: 1px solid var(--el-border-color-lighter);
|
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
margin: 0 0 16px;
|
margin: 0 0 16px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
:show-expand="false"
|
:show-expand="false"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
|
label-width="100"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></ArtSearchBar>
|
></ArtSearchBar>
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@
|
|||||||
:loading="cardSearchLoading"
|
:loading="cardSearchLoading"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
clearable
|
clearable
|
||||||
|
@change="handleIotCardChange"
|
||||||
>
|
>
|
||||||
<ElOption
|
<ElOption
|
||||||
v-for="card in iotCardOptions"
|
v-for="card in iotCardOptions"
|
||||||
@@ -104,6 +106,7 @@
|
|||||||
:loading="deviceSearchLoading"
|
:loading="deviceSearchLoading"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
clearable
|
clearable
|
||||||
|
@change="handleDeviceChange"
|
||||||
>
|
>
|
||||||
<ElOption
|
<ElOption
|
||||||
v-for="device in deviceOptions"
|
v-for="device in deviceOptions"
|
||||||
@@ -125,15 +128,9 @@
|
|||||||
v-model="createForm.package_id"
|
v-model="createForm.package_id"
|
||||||
:placeholder="'请选择套餐'"
|
:placeholder="'请选择套餐'"
|
||||||
filterable
|
filterable
|
||||||
remote
|
|
||||||
reserve-keyword
|
reserve-keyword
|
||||||
:remote-method="handlePackageSearch"
|
|
||||||
:loading="packageSearchLoading"
|
:loading="packageSearchLoading"
|
||||||
:disabled="
|
:disabled="!createForm.iot_card_id && !createForm.device_id"
|
||||||
(!createForm.iot_card_id && !createForm.device_id) ||
|
|
||||||
(createForm.order_type === 'single_card' && !createForm.iot_card_id) ||
|
|
||||||
(createForm.order_type === 'device' && !createForm.device_id)
|
|
||||||
"
|
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
@@ -151,6 +148,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</ElOption>
|
</ElOption>
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
|
<template v-if="noSeriesId">
|
||||||
|
<div style="color: var(--el-text-color-warning); font-size: 12px; margin-top: 4px">
|
||||||
|
该设备未关联套餐系列,无法购买套餐
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="packagesEmptyWithSeriesId">
|
||||||
|
<div style="color: var(--el-text-color-info); font-size: 12px; margin-top: 4px">
|
||||||
|
该系列暂无可用套餐
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="支付方式" prop="payment_method">
|
<ElFormItem label="支付方式" prop="payment_method">
|
||||||
<ElSelect
|
<ElSelect
|
||||||
@@ -379,7 +386,8 @@
|
|||||||
config: {
|
config: {
|
||||||
maxlength: 20,
|
maxlength: 20,
|
||||||
clearable: true
|
clearable: true
|
||||||
}
|
},
|
||||||
|
labelWidth: '100'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '支付状态',
|
label: '支付状态',
|
||||||
@@ -567,7 +575,9 @@
|
|||||||
order_type: 'single_card' as OrderType,
|
order_type: 'single_card' as OrderType,
|
||||||
package_id: undefined as number | undefined, // 前端使用单选
|
package_id: undefined as number | undefined, // 前端使用单选
|
||||||
iot_card_id: null as number | null,
|
iot_card_id: null as number | null,
|
||||||
|
iot_card_series_id: null as number | null, // IoT卡的套餐系列ID
|
||||||
device_id: null as number | null,
|
device_id: null as number | null,
|
||||||
|
device_series_id: null as number | null, // 设备的套餐系列ID
|
||||||
payment_method: 'wallet' as OrderPaymentMethod, // 默认使用钱包支付
|
payment_method: 'wallet' as OrderPaymentMethod, // 默认使用钱包支付
|
||||||
payment_voucher_key: undefined as string | undefined // 线下支付凭证
|
payment_voucher_key: undefined as string | undefined // 线下支付凭证
|
||||||
})
|
})
|
||||||
@@ -577,6 +587,7 @@
|
|||||||
// 套餐搜索相关
|
// 套餐搜索相关
|
||||||
const packageOptions = ref<PackageResponse[]>([])
|
const packageOptions = ref<PackageResponse[]>([])
|
||||||
const packageSearchLoading = ref(false)
|
const packageSearchLoading = ref(false)
|
||||||
|
const isLoadingPackagesFromSelection = ref(false)
|
||||||
|
|
||||||
// IoT卡搜索相关
|
// IoT卡搜索相关
|
||||||
const iotCardOptions = ref<StandaloneIotCard[]>([])
|
const iotCardOptions = ref<StandaloneIotCard[]>([])
|
||||||
@@ -586,46 +597,60 @@
|
|||||||
const deviceOptions = ref<Device[]>([])
|
const deviceOptions = ref<Device[]>([])
|
||||||
const deviceSearchLoading = ref(false)
|
const deviceSearchLoading = ref(false)
|
||||||
|
|
||||||
// 搜索套餐(根据套餐名称,可选按series_id筛选)
|
// 套餐列表为空但有seriesId的提示
|
||||||
const searchPackages = async (query: string, seriesId?: number) => {
|
const packagesEmptyWithSeriesId = ref(false)
|
||||||
packageSearchLoading.value = true
|
|
||||||
try {
|
// 检查是否选择了卡/设备但没有关联套餐系列
|
||||||
const res = await PackageManageService.getPackages({
|
const noSeriesId = computed(() => {
|
||||||
package_name: query || undefined,
|
if (createForm.order_type === 'single_card' && createForm.iot_card_id) {
|
||||||
series_id: seriesId,
|
return !createForm.iot_card_series_id
|
||||||
page: 1,
|
}
|
||||||
page_size: 20,
|
if (createForm.order_type === 'device' && createForm.device_id) {
|
||||||
status: 1, // 只获取启用的套餐
|
return !createForm.device_series_id
|
||||||
shelf_status: 1 // 只获取已上架的套餐
|
}
|
||||||
})
|
return false
|
||||||
if (res.code === 0) {
|
})
|
||||||
packageOptions.value = res.data.items || []
|
|
||||||
}
|
// IoT卡选择变化时,根据series_id加载套餐列表
|
||||||
} catch (error) {
|
const handleIotCardChange = (cardId: number | null) => {
|
||||||
console.error('Search packages failed:', error)
|
if (!cardId) {
|
||||||
|
createForm.iot_card_series_id = null
|
||||||
|
createForm.package_id = undefined
|
||||||
|
packageOptions.value = []
|
||||||
|
packagesEmptyWithSeriesId.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selectedCard = iotCardOptions.value.find((card) => card.id === cardId)
|
||||||
|
createForm.iot_card_series_id = selectedCard?.series_id || null
|
||||||
|
createForm.package_id = undefined
|
||||||
|
packageOptions.value = []
|
||||||
|
packagesEmptyWithSeriesId.value = false
|
||||||
|
if (selectedCard?.series_id) {
|
||||||
|
loadDefaultPackages(selectedCard.series_id)
|
||||||
|
} else {
|
||||||
packageOptions.value = []
|
packageOptions.value = []
|
||||||
} finally {
|
|
||||||
packageSearchLoading.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 套餐远程搜索方法(自动使用当前选中的IOT卡/设备的series_id)
|
// 设备选择变化时,根据series_id加载套餐列表
|
||||||
const handlePackageSearch = (query: string) => {
|
const handleDeviceChange = (deviceId: number | null) => {
|
||||||
let seriesId: number | undefined
|
if (!deviceId) {
|
||||||
// 如果是单卡订单并且已选择IOT卡,使用该卡的series_id筛选
|
createForm.device_series_id = null
|
||||||
if (createForm.order_type === 'single_card' && createForm.iot_card_id) {
|
createForm.package_id = undefined
|
||||||
const selectedCard = iotCardOptions.value.find((card) => card.id === createForm.iot_card_id)
|
packageOptions.value = []
|
||||||
if (selectedCard && selectedCard.series_id) {
|
packagesEmptyWithSeriesId.value = false
|
||||||
seriesId = selectedCard.series_id
|
return
|
||||||
}
|
}
|
||||||
} else if (createForm.order_type === 'device' && createForm.device_id) {
|
const selectedDevice = deviceOptions.value.find((dev) => dev.id === deviceId)
|
||||||
// 如果是设备订单并且已选择设备,使用设备的series_id筛选
|
createForm.device_series_id = selectedDevice?.series_id || null
|
||||||
const selectedDevice = deviceOptions.value.find((dev) => dev.id === createForm.device_id)
|
createForm.package_id = undefined
|
||||||
if (selectedDevice && selectedDevice.series_id) {
|
packageOptions.value = []
|
||||||
seriesId = selectedDevice.series_id
|
packagesEmptyWithSeriesId.value = false
|
||||||
}
|
if (selectedDevice?.series_id) {
|
||||||
|
loadDefaultPackages(selectedDevice.series_id)
|
||||||
|
} else {
|
||||||
|
packageOptions.value = []
|
||||||
}
|
}
|
||||||
searchPackages(query, seriesId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索IoT卡(根据ICCID)
|
// 搜索IoT卡(根据ICCID)
|
||||||
@@ -848,39 +873,12 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// 当选择IOT卡时,根据series_id筛选套餐
|
// 当订单类型切换时,清空已选择的套餐
|
||||||
watch(
|
watch(
|
||||||
() => createForm.iot_card_id,
|
() => createForm.order_type,
|
||||||
(newCardId) => {
|
() => {
|
||||||
if (newCardId && createForm.order_type === 'single_card') {
|
createForm.package_id = undefined
|
||||||
// 找到选中的IOT卡
|
packageOptions.value = []
|
||||||
const selectedCard = iotCardOptions.value.find((card) => card.id === newCardId)
|
|
||||||
if (selectedCard && selectedCard.series_id) {
|
|
||||||
// 根据series_id重新加载套餐列表
|
|
||||||
loadDefaultPackages(selectedCard.series_id)
|
|
||||||
} else {
|
|
||||||
// 如果没有series_id,加载所有套餐
|
|
||||||
loadDefaultPackages()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 当选择设备时,根据series_id筛选套餐
|
|
||||||
watch(
|
|
||||||
() => createForm.device_id,
|
|
||||||
(newDeviceId) => {
|
|
||||||
if (newDeviceId && createForm.order_type === 'device') {
|
|
||||||
// 找到选中的设备
|
|
||||||
const selectedDevice = deviceOptions.value.find((dev) => dev.id === newDeviceId)
|
|
||||||
if (selectedDevice && selectedDevice.series_id) {
|
|
||||||
// 根据series_id重新加载套餐列表
|
|
||||||
loadDefaultPackages(selectedDevice.series_id)
|
|
||||||
} else {
|
|
||||||
// 如果没有series_id,加载所有套餐
|
|
||||||
loadDefaultPackages()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -955,13 +953,14 @@
|
|||||||
// 显示创建订单对话框
|
// 显示创建订单对话框
|
||||||
const showCreateDialog = async () => {
|
const showCreateDialog = async () => {
|
||||||
createDialogVisible.value = true
|
createDialogVisible.value = true
|
||||||
// 加载IoT卡和设备列表,套餐列表在选择IoT卡/设备后才加载
|
// IoT卡和设备列表在用户搜索时才加载,不在此处预加载
|
||||||
await Promise.all([loadDefaultIotCards(), loadDefaultDevices()])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载默认套餐列表(可选按series_id筛选)
|
// 加载默认套餐列表(可选按series_id筛选)
|
||||||
const loadDefaultPackages = async (seriesId?: number) => {
|
const loadDefaultPackages = async (seriesId?: number) => {
|
||||||
packageSearchLoading.value = true
|
packageSearchLoading.value = true
|
||||||
|
isLoadingPackagesFromSelection.value = true
|
||||||
|
packagesEmptyWithSeriesId.value = false
|
||||||
try {
|
try {
|
||||||
const res = await PackageManageService.getPackages({
|
const res = await PackageManageService.getPackages({
|
||||||
series_id: seriesId,
|
series_id: seriesId,
|
||||||
@@ -972,50 +971,17 @@
|
|||||||
})
|
})
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
packageOptions.value = res.data.items || []
|
packageOptions.value = res.data.items || []
|
||||||
|
// seriesId存在但返回空列表时,显示提示
|
||||||
|
if (seriesId && (!res.data.items || res.data.items.length === 0)) {
|
||||||
|
packagesEmptyWithSeriesId.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Load default packages failed:', error)
|
console.error('Load default packages failed:', error)
|
||||||
packageOptions.value = []
|
packageOptions.value = []
|
||||||
} finally {
|
} finally {
|
||||||
packageSearchLoading.value = false
|
packageSearchLoading.value = false
|
||||||
}
|
isLoadingPackagesFromSelection.value = false
|
||||||
}
|
|
||||||
|
|
||||||
// 加载默认IoT卡列表
|
|
||||||
const loadDefaultIotCards = async () => {
|
|
||||||
cardSearchLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await CardService.getStandaloneIotCards({
|
|
||||||
page: 1,
|
|
||||||
page_size: 20
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
iotCardOptions.value = res.data.items || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Load default IoT cards failed:', error)
|
|
||||||
iotCardOptions.value = []
|
|
||||||
} finally {
|
|
||||||
cardSearchLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载默认设备列表
|
|
||||||
const loadDefaultDevices = async () => {
|
|
||||||
deviceSearchLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await DeviceService.getDevices({
|
|
||||||
page: 1,
|
|
||||||
page_size: 20
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
deviceOptions.value = res.data.items || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Load default devices failed:', error)
|
|
||||||
deviceOptions.value = []
|
|
||||||
} finally {
|
|
||||||
deviceSearchLoading.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,11 +167,8 @@
|
|||||||
</ElCol>
|
</ElCol>
|
||||||
</ElRow>
|
</ElRow>
|
||||||
|
|
||||||
<!-- 套餐天数(流量重置周期为每日,或calendar_type为by_day时显示) -->
|
<!-- 套餐天数(calendar_type为by_day时显示) -->
|
||||||
<ElRow
|
<ElRow :gutter="20" v-if="form.calendar_type === 'by_day'">
|
||||||
:gutter="20"
|
|
||||||
v-if="form.data_reset_cycle === 'daily' || form.calendar_type === 'by_day'"
|
|
||||||
>
|
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
<ElFormItem label="套餐天数" prop="duration_days">
|
<ElFormItem label="套餐天数" prop="duration_days">
|
||||||
<ElInputNumber
|
<ElInputNumber
|
||||||
@@ -837,18 +834,15 @@
|
|||||||
watch(
|
watch(
|
||||||
() => form.data_reset_cycle,
|
() => form.data_reset_cycle,
|
||||||
(cycle) => {
|
(cycle) => {
|
||||||
// 不是每日且calendar_type不是按天时,清空套餐天数
|
// 流量重置周期变化时不需要清空套餐天数,套餐天数只受calendar_type控制
|
||||||
if (cycle !== 'daily' && form.calendar_type !== 'by_day') {
|
|
||||||
form.duration_days = undefined
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 监听套餐周期类型变化,不是按天且流量重置周期不是每日时清空套餐天数
|
// 监听套餐周期类型变化,不是按天时清空套餐天数
|
||||||
watch(
|
watch(
|
||||||
() => form.calendar_type,
|
() => form.calendar_type,
|
||||||
(type) => {
|
(type) => {
|
||||||
if (type !== 'by_day' && form.data_reset_cycle !== 'daily') {
|
if (type !== 'by_day') {
|
||||||
form.duration_days = undefined
|
form.duration_days = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1135,7 +1129,8 @@
|
|||||||
if (form.data_reset_cycle) {
|
if (form.data_reset_cycle) {
|
||||||
data.data_reset_cycle = form.data_reset_cycle
|
data.data_reset_cycle = form.data_reset_cycle
|
||||||
}
|
}
|
||||||
if (form.duration_days !== undefined) {
|
// 只有 calendar_type 为 by_day 时才传 duration_days
|
||||||
|
if (form.calendar_type === 'by_day' && form.duration_days !== undefined) {
|
||||||
data.duration_days = form.duration_days
|
data.duration_days = form.duration_days
|
||||||
}
|
}
|
||||||
if (form.enable_virtual_data !== undefined) {
|
if (form.enable_virtual_data !== undefined) {
|
||||||
|
|||||||
@@ -146,9 +146,9 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 微信支付配置
|
// 微信配置
|
||||||
const wechatPaySection: DetailSection = {
|
const wechatPaySection: DetailSection = {
|
||||||
title: '微信支付配置',
|
title: '微信配置',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
label: '微信商户号',
|
label: '微信商户号',
|
||||||
|
|||||||
@@ -163,7 +163,7 @@
|
|||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElDivider content-position="left">
|
<ElDivider content-position="left">
|
||||||
<span class="divider-title">微信支付配置</span>
|
<span class="divider-title">微信配置</span>
|
||||||
</ElDivider>
|
</ElDivider>
|
||||||
<ElRow :gutter="20">
|
<ElRow :gutter="20">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElDivider content-position="left">
|
<ElDivider content-position="left">
|
||||||
<span class="divider-title">微信支付配置</span>
|
<span class="divider-title">微信配置</span>
|
||||||
</ElDivider>
|
</ElDivider>
|
||||||
<ElRow :gutter="20">
|
<ElRow :gutter="20">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
|
|||||||
@@ -150,6 +150,7 @@
|
|||||||
:icon="'ArrowRight'"
|
:icon="'ArrowRight'"
|
||||||
@click="addPermissions"
|
@click="addPermissions"
|
||||||
:disabled="getLeftCheckedKeys().length === 0"
|
:disabled="getLeftCheckedKeys().length === 0"
|
||||||
|
:loading="addingPermissionLoading"
|
||||||
>
|
>
|
||||||
添加
|
添加
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -245,6 +246,7 @@
|
|||||||
const permissionDialogVisible = ref(false)
|
const permissionDialogVisible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const submitLoading = ref(false)
|
const submitLoading = ref(false)
|
||||||
|
const addingPermissionLoading = ref(false)
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
const leftTreeRef = ref()
|
const leftTreeRef = ref()
|
||||||
const rightTreeRef = ref()
|
const rightTreeRef = ref()
|
||||||
@@ -799,6 +801,7 @@
|
|||||||
const checkedKeys = getLeftCheckedKeys()
|
const checkedKeys = getLeftCheckedKeys()
|
||||||
if (checkedKeys.length === 0) return
|
if (checkedKeys.length === 0) return
|
||||||
|
|
||||||
|
addingPermissionLoading.value = true
|
||||||
try {
|
try {
|
||||||
// 提交到服务器时包含勾选和半选节点
|
// 提交到服务器时包含勾选和半选节点
|
||||||
const keysToSubmit = getLeftCheckedKeysWithHalf()
|
const keysToSubmit = getLeftCheckedKeysWithHalf()
|
||||||
@@ -826,6 +829,8 @@
|
|||||||
ElMessage.success('权限添加成功')
|
ElMessage.success('权限添加成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('添加权限失败:', error)
|
console.error('添加权限失败:', error)
|
||||||
|
} finally {
|
||||||
|
addingPermissionLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user