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