This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
:disabled="v.disabled || false"
|
:disabled="v.disabled || false"
|
||||||
>
|
>
|
||||||
</el-option>
|
</el-option>
|
||||||
|
<el-option v-if="options.length === 0" label="无数据" value="__NO_DATA__" disabled />
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
|
|
||||||
// 合并默认配置和自定义配置
|
// 合并默认配置和自定义配置
|
||||||
const config = reactive({
|
const config = reactive({
|
||||||
placeholder: `${t('table.searchBar.searchSelectPlaceholder')}${prop.item.label}`, // 修改默认placeholder文案
|
placeholder: `${t('table.searchBar.searchSelectPlaceholder')}${prop.item.label}`,
|
||||||
...(prop.item.config || {})
|
...(prop.item.config || {})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -56,14 +57,12 @@
|
|||||||
// 计算属性:处理选项数据
|
// 计算属性:处理选项数据
|
||||||
const options = computed(() => {
|
const options = computed(() => {
|
||||||
if (prop.item.options) {
|
if (prop.item.options) {
|
||||||
// 判断options是数组还是函数
|
|
||||||
if (Array.isArray(prop.item.options)) {
|
if (Array.isArray(prop.item.options)) {
|
||||||
return prop.item.options
|
return prop.item.options
|
||||||
} else {
|
} else {
|
||||||
return prop.item.options()
|
return prop.item.options()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
return []
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ export interface AssetPackageUsageRecord {
|
|||||||
virtual_remain_mb?: number // 剩余虚流量 MB
|
virtual_remain_mb?: number // 剩余虚流量 MB
|
||||||
virtual_ratio?: number // 虚流量比例(real/virtual)
|
virtual_ratio?: number // 虚流量比例(real/virtual)
|
||||||
enable_virtual_data?: boolean // 是否启用虚流量
|
enable_virtual_data?: boolean // 是否启用虚流量
|
||||||
|
paid_amount?: number // 实际支付金额(分)
|
||||||
package_price?: number // 套餐价格(分)
|
package_price?: number // 套餐价格(分)
|
||||||
activated_at?: string // 激活时间
|
activated_at?: string // 激活时间
|
||||||
expires_at?: string // 到期时间
|
expires_at?: string // 到期时间
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export interface Order {
|
|||||||
actual_paid_amount?: number // 实付金额(分)
|
actual_paid_amount?: number // 实付金额(分)
|
||||||
paid_at: string | null
|
paid_at: string | null
|
||||||
commission_status: OrderCommissionStatus
|
commission_status: OrderCommissionStatus
|
||||||
|
commission_status_name?: string // 佣金状态名称(中文)
|
||||||
commission_config_version: number
|
commission_config_version: number
|
||||||
device_id: number | null
|
device_id: number | null
|
||||||
iot_card_id: number | null
|
iot_card_id: number | null
|
||||||
@@ -71,13 +72,15 @@ export interface OrderQueryParams {
|
|||||||
page?: number
|
page?: number
|
||||||
page_size?: number
|
page_size?: number
|
||||||
payment_status?: PaymentStatus
|
payment_status?: PaymentStatus
|
||||||
|
payment_method?: OrderPaymentMethod
|
||||||
order_type?: OrderType
|
order_type?: OrderType
|
||||||
order_no?: string
|
order_no?: string
|
||||||
identifier?: string // 按资产标识符过滤
|
identifier?: string // 按资产标识符过滤(ICCID 或 VirtualNo)
|
||||||
purchase_role?: string // 订单渠道
|
purchase_role?: string // 订单渠道
|
||||||
buyer_phone?: string // 按买家手机号精确过滤
|
buyer_phone?: string // 按买家手机号精确过滤
|
||||||
start_time?: string
|
start_time?: string
|
||||||
end_time?: string
|
end_time?: string
|
||||||
|
is_expired?: boolean // 是否已过期
|
||||||
dateRange?: string[] | any // For date range picker in UI
|
dateRange?: string[] | any // For date range picker in UI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ export interface Refund {
|
|||||||
id: number
|
id: number
|
||||||
refund_no: string
|
refund_no: string
|
||||||
order_id: number
|
order_id: number
|
||||||
|
order_no: string // 订单号
|
||||||
shop_id: number
|
shop_id: number
|
||||||
|
shop_name: string // 店铺名称
|
||||||
package_usage_id: number | null
|
package_usage_id: number | null
|
||||||
|
asset_identifier: string // 资产标识符
|
||||||
|
asset_type: string // 资产类型
|
||||||
requested_refund_amount: number // 申请退款金额(分)
|
requested_refund_amount: number // 申请退款金额(分)
|
||||||
approved_refund_amount: number | null // 实际退款金额(分)
|
approved_refund_amount: number | null // 实际退款金额(分)
|
||||||
actual_received_amount: number // 实收金额(分)
|
actual_received_amount: number // 实收金额(分)
|
||||||
@@ -41,6 +45,7 @@ export interface RefundQueryParams {
|
|||||||
status?: RefundStatus
|
status?: RefundStatus
|
||||||
order_id?: number
|
order_id?: number
|
||||||
shop_id?: number
|
shop_id?: number
|
||||||
|
shop_name?: string // 店铺名称(模糊查询)
|
||||||
dateRange?: string[]
|
dateRange?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@
|
|||||||
// 响应式表单数据
|
// 响应式表单数据
|
||||||
const formFilters = reactive({ ...initialSearchState })
|
const formFilters = reactive({ ...initialSearchState })
|
||||||
|
|
||||||
// 店铺和企业列表
|
// 店铺和企业列表
|
||||||
const shopList = ref<any[]>([])
|
const shopList = ref<any[]>([])
|
||||||
const enterpriseList = ref<any[]>([])
|
const enterpriseList = ref<any[]>([])
|
||||||
|
|
||||||
@@ -359,7 +359,18 @@
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
remote: true,
|
remote: true,
|
||||||
remoteMethod: handleShopSearch,
|
remoteMethod: handleShopSearch,
|
||||||
placeholder: '请输入店铺名称搜索'
|
placeholder: '请输入店铺名称搜索',
|
||||||
|
reserveKeyword: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
prop: 'status',
|
||||||
|
type: 'select',
|
||||||
|
options: STATUS_SELECT_OPTIONS,
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
placeholder: '请选择状态'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -375,17 +386,8 @@
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
remote: true,
|
remote: true,
|
||||||
remoteMethod: handleEnterpriseSearch,
|
remoteMethod: handleEnterpriseSearch,
|
||||||
placeholder: '请输入企业名称搜索'
|
placeholder: '请输入企业名称搜索',
|
||||||
}
|
reserveKeyword: true
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '状态',
|
|
||||||
prop: 'status',
|
|
||||||
type: 'select',
|
|
||||||
options: STATUS_SELECT_OPTIONS,
|
|
||||||
config: {
|
|
||||||
clearable: true,
|
|
||||||
placeholder: '请选择状态'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -194,6 +194,11 @@
|
|||||||
</ElTag>
|
</ElTag>
|
||||||
</template>
|
</template>
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
|
<ElTableColumn label="实名认证策略" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getRealnamePolicyName(scope.row.realname_policy) }}
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
<ElTableColumn label="实名时间" width="180">
|
<ElTableColumn label="实名时间" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }}
|
{{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }}
|
||||||
@@ -229,7 +234,7 @@
|
|||||||
|
|
||||||
<!-- 设备实时信息 -->
|
<!-- 设备实时信息 -->
|
||||||
<ElDivider content-position="left">设备实时信息</ElDivider>
|
<ElDivider content-position="left">设备实时信息</ElDivider>
|
||||||
<div v-if="deviceRealtime" style="margin-top: 16px">
|
<div v-if="deviceRealtime" v-loading="realtimeLoading" style="margin-top: 16px">
|
||||||
<ElDescriptions :column="4" border>
|
<ElDescriptions :column="4" border>
|
||||||
<!-- 基本状态 -->
|
<!-- 基本状态 -->
|
||||||
<ElDescriptionsItem label="在线状态">
|
<ElDescriptionsItem label="在线状态">
|
||||||
@@ -522,9 +527,12 @@
|
|||||||
cardInfo: AssetInfo
|
cardInfo: AssetInfo
|
||||||
deviceRealtime: DeviceRealtimeInfo | null
|
deviceRealtime: DeviceRealtimeInfo | null
|
||||||
pollingEnabled: boolean
|
pollingEnabled: boolean
|
||||||
|
realtimeLoading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
realtimeLoading: false
|
||||||
|
})
|
||||||
|
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
|
|
||||||
|
|||||||
@@ -2,66 +2,9 @@
|
|||||||
<ElCard shadow="never" class="info-card current-package-card">
|
<ElCard shadow="never" class="info-card current-package-card">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="card-header-left">
|
<span class="header-title">当前生效套餐</span>
|
||||||
<span class="header-title">当前生效套餐</span>
|
<div class="header-actions">
|
||||||
<ElTag
|
<slot name="header-actions" />
|
||||||
v-if="currentPackage"
|
|
||||||
:type="getPackageStatusTagType(currentPackage.status)"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
{{ currentPackage?.status_name || '-' }}
|
|
||||||
</ElTag>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 中间:流量进度 -->
|
|
||||||
<div v-if="currentPackage && !props.boundDeviceId" class="header-progress">
|
|
||||||
<div class="header-progress-info">
|
|
||||||
<span class="progress-title">流量</span>
|
|
||||||
<span class="progress-text">
|
|
||||||
<template v-if="isAdminOrPlatform">
|
|
||||||
总量 {{ formatDataSize(currentPackage.data_limit_mb || currentPackage.package_total_data || 0) }} / 实际可用
|
|
||||||
{{ formatDataSize(currentPackage.enable_virtual_data ? (currentPackage.virtual_limit_mb || 0) : (currentPackage.data_limit_mb || currentPackage.package_total_data || 0)) }} / 剩余可用
|
|
||||||
{{ formatDataSize(currentPackage.enable_virtual_data ? (currentPackage.virtual_remain_mb || 0) : ((currentPackage.data_limit_mb || currentPackage.package_total_data || 0) - (currentPackage.data_usage_mb || 0))) }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
总量 {{ formatDataSize(currentPackage.data_limit_mb || currentPackage.package_total_data || 0) }} / 剩余可用
|
|
||||||
{{ formatDataSize(currentPackage.enable_virtual_data ? (currentPackage.virtual_remain_mb || 0) : ((currentPackage.data_limit_mb || currentPackage.package_total_data || 0) - (currentPackage.data_usage_mb || 0))) }}
|
|
||||||
</template>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ElProgress
|
|
||||||
:percentage="
|
|
||||||
currentPackage.enable_virtual_data
|
|
||||||
? getUsageProgress(
|
|
||||||
currentPackage.virtual_data_used || 0,
|
|
||||||
currentPackage.virtual_data_total || 0
|
|
||||||
)
|
|
||||||
: getUsageProgress(
|
|
||||||
currentPackage.real_data_used || 0,
|
|
||||||
currentPackage.real_data_total || 0
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:color="
|
|
||||||
currentPackage.enable_virtual_data
|
|
||||||
? getProgressColorByPercentage(
|
|
||||||
getUsageProgress(
|
|
||||||
currentPackage.virtual_data_used || 0,
|
|
||||||
currentPackage.virtual_data_total || 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: getProgressColorByPercentage(
|
|
||||||
getUsageProgress(
|
|
||||||
currentPackage.real_data_used || 0,
|
|
||||||
currentPackage.real_data_total || 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:stroke-width="10"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="currentPackage" class="card-header-right">
|
|
||||||
<ElButton type="primary" size="small" @click="handleShowRecharge"> 套餐充值 </ElButton>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -86,33 +29,80 @@
|
|||||||
<!-- 显示错误信息 -->
|
<!-- 显示错误信息 -->
|
||||||
<ElAlert v-else-if="errorMsg" :title="errorMsg" type="warning" :closable="false" />
|
<ElAlert v-else-if="errorMsg" :title="errorMsg" type="warning" :closable="false" />
|
||||||
|
|
||||||
<!-- 显示套餐详情 -->
|
<template v-else-if="currentPackage">
|
||||||
<ElDescriptions v-else-if="currentPackage" :column="3" border>
|
<!-- 流量使用情况 - 横向进度条卡片 -->
|
||||||
<ElDescriptionsItem label="套餐名称">
|
<div class="flow-progress-card">
|
||||||
{{ currentPackage.package_name || '-' }}
|
<div class="flow-stats-row">
|
||||||
</ElDescriptionsItem>
|
<div class="flow-stat-item">
|
||||||
<ElDescriptionsItem label="套餐价格">
|
<span class="flow-stat-label">总量</span>
|
||||||
{{ formatAmount(currentPackage.package_price) }}
|
<span class="flow-stat-value">
|
||||||
</ElDescriptionsItem>
|
{{ formatDataSize(currentPackage.data_limit_mb || currentPackage.package_total_data || 0) }}
|
||||||
<ElDescriptionsItem label="套餐类型">
|
</span>
|
||||||
{{ getPackageTypeName(currentPackage.package_type) }}
|
</div>
|
||||||
</ElDescriptionsItem>
|
<div v-if="isAdminOrPlatform && currentPackage.enable_virtual_data" class="flow-stat-item">
|
||||||
<ElDescriptionsItem
|
<span class="flow-stat-label">实际可用</span>
|
||||||
v-if="currentPackage.enable_virtual_data && isAdminOrPlatform"
|
<span class="flow-stat-value">
|
||||||
label="虚流量比例"
|
{{ formatDataSize(currentPackage.virtual_limit_mb || 0) }}
|
||||||
>
|
</span>
|
||||||
{{ currentPackage.virtual_ratio || '-' }}
|
</div>
|
||||||
</ElDescriptionsItem>
|
<div class="flow-stat-item">
|
||||||
<ElDescriptionsItem v-if="currentPackage.duration_days" label="套餐时长" :span="3">
|
<span class="flow-stat-label">剩余可用</span>
|
||||||
{{ currentPackage.duration_days }}天
|
<span class="flow-stat-value">
|
||||||
</ElDescriptionsItem>
|
{{
|
||||||
<ElDescriptionsItem label="开始时间">
|
formatDataSize(
|
||||||
{{ formatDateTime(currentPackage.start_time) || '-' }}
|
currentPackage.enable_virtual_data
|
||||||
</ElDescriptionsItem>
|
? currentPackage.virtual_remain_mb || 0
|
||||||
<ElDescriptionsItem label="到期时间">
|
: (currentPackage.data_limit_mb || currentPackage.package_total_data || 0) - (currentPackage.data_usage_mb || 0)
|
||||||
{{ formatDateTime(currentPackage.expire_time) || '-' }}
|
)
|
||||||
</ElDescriptionsItem>
|
}}
|
||||||
</ElDescriptions>
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-progress-bar">
|
||||||
|
<ElProgress
|
||||||
|
:percentage="
|
||||||
|
currentPackage.enable_virtual_data
|
||||||
|
? getUsageProgress(currentPackage.virtual_data_used || 0, currentPackage.virtual_data_total || 0)
|
||||||
|
: getUsageProgress(currentPackage.real_data_used || 0, currentPackage.real_data_total || 0)
|
||||||
|
"
|
||||||
|
:color="
|
||||||
|
currentPackage.enable_virtual_data
|
||||||
|
? getProgressColorByPercentage(getUsageProgress(currentPackage.virtual_data_used || 0, currentPackage.virtual_data_total || 0))
|
||||||
|
: getProgressColorByPercentage(getUsageProgress(currentPackage.real_data_used || 0, currentPackage.real_data_total || 0))
|
||||||
|
"
|
||||||
|
:stroke-width="14"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 套餐详情表格 -->
|
||||||
|
<ElDescriptions :column="3" border class="package-info-table">
|
||||||
|
<ElDescriptionsItem label="套餐名称">
|
||||||
|
{{ currentPackage.package_name || '-' }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem label="套餐价格">
|
||||||
|
{{ formatAmount(currentPackage.paid_amount || currentPackage.package_price || 0) }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem label="套餐类型">
|
||||||
|
{{ getPackageTypeName(currentPackage.package_type) }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem v-if="isAdminOrPlatform && currentPackage.enable_virtual_data" label="虚流量比例">
|
||||||
|
{{ currentPackage.virtual_ratio || '-' }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem label="开始时间">
|
||||||
|
{{ formatDateTime(currentPackage.start_time) || '-' }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem label="到期时间">
|
||||||
|
{{ formatDateTime(currentPackage.expire_time) || '-' }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem label="购买时间">
|
||||||
|
{{ formatDateTime(currentPackage.created_at) || '-' }}
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
<ElDescriptionsItem v-if="currentPackage.duration_days" label="套餐时长">
|
||||||
|
{{ currentPackage.duration_days }}天
|
||||||
|
</ElDescriptionsItem>
|
||||||
|
</ElDescriptions>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<ElEmpty v-else-if="!loading" :description="errorMsg || '暂无当前生效套餐'" :image-size="100" />
|
<ElEmpty v-else-if="!loading" :description="errorMsg || '暂无当前生效套餐'" :image-size="100" />
|
||||||
@@ -122,7 +112,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
ElCard,
|
ElCard,
|
||||||
ElTag,
|
|
||||||
ElButton,
|
ElButton,
|
||||||
ElDescriptions,
|
ElDescriptions,
|
||||||
ElDescriptionsItem,
|
ElDescriptionsItem,
|
||||||
@@ -136,21 +125,19 @@
|
|||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import type { PackageInfo } from '../types'
|
import type { PackageInfo } from '../types'
|
||||||
|
|
||||||
// Use formatters
|
|
||||||
const {
|
const {
|
||||||
formatAmount,
|
formatAmount,
|
||||||
formatDataSize,
|
formatDataSize,
|
||||||
getPackageStatusTagType,
|
|
||||||
getUsageProgress,
|
getUsageProgress,
|
||||||
getProgressColorByPercentage
|
getProgressColorByPercentage
|
||||||
} = useAssetFormatters()
|
} = useAssetFormatters()
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { info: userInfo } = storeToRefs(userStore)
|
const { info: userInfo } = storeToRefs(userStore)
|
||||||
const isSuperAdmin = computed(() => userInfo.value.user_type === 1)
|
const isAdminOrPlatform = computed(
|
||||||
const isAdminOrPlatform = computed(() => userInfo.value.user_type === 1 || userInfo.value.user_type === 2)
|
() => userInfo.value.user_type === 1 || userInfo.value.user_type === 2
|
||||||
|
)
|
||||||
|
|
||||||
// Props
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currentPackage: PackageInfo | null
|
currentPackage: PackageInfo | null
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
@@ -168,18 +155,11 @@
|
|||||||
boundDeviceName: ''
|
boundDeviceName: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// Emits
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
showRecharge: []
|
showRecharge: []
|
||||||
navigateToDevice: [deviceNo: string]
|
navigateToDevice: [deviceNo: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// Methods
|
|
||||||
const handleShowRecharge = () => {
|
|
||||||
emit('showRecharge')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 套餐类型名称
|
|
||||||
const getPackageTypeName = (type?: string) => {
|
const getPackageTypeName = (type?: string) => {
|
||||||
const typeMap: Record<string, string> = {
|
const typeMap: Record<string, string> = {
|
||||||
formal: '正式套餐',
|
formal: '正式套餐',
|
||||||
@@ -195,52 +175,49 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.card-header-left {
|
.header-title {
|
||||||
display: flex;
|
font-size: 16px;
|
||||||
gap: 12px;
|
font-weight: 500;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.header-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header-right {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-progress {
|
.flow-progress-card {
|
||||||
flex: 1;
|
padding: 20px;
|
||||||
min-width: 0;
|
background: var(--el-fill-color-light);
|
||||||
padding: 0 24px;
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.header-progress-info {
|
.flow-stats-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
gap: 32px;
|
||||||
gap: 12px;
|
margin-bottom: 16px;
|
||||||
margin-bottom: 6px;
|
|
||||||
|
|
||||||
.progress-title {
|
.flow-stat-item {
|
||||||
flex-shrink: 0;
|
display: flex;
|
||||||
font-size: 13px;
|
align-items: baseline;
|
||||||
font-weight: 600;
|
gap: 8px;
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-text {
|
.flow-stat-label {
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
white-space: nowrap;
|
}
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
.flow-stat-value {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flow-progress-bar {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-info-table {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>套餐列表</span>
|
<span>套餐列表</span>
|
||||||
|
<div class="card-header-right">
|
||||||
|
<ElButton type="primary" @click="handleShowRecharge">套餐充值</ElButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="package-table-wrapper">
|
<div v-loading="loading" class="package-table-wrapper">
|
||||||
<!-- 绑定设备提示 -->
|
<!-- 绑定设备提示 -->
|
||||||
<ElEmpty v-if="props.boundDeviceId" :image-size="100">
|
<ElEmpty v-if="props.boundDeviceId" :image-size="100">
|
||||||
<template #description>
|
<template #description>
|
||||||
@@ -36,7 +39,7 @@
|
|||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
<ElTableColumn label="套餐价格" width="120">
|
<ElTableColumn label="套餐价格" width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ formatAmount(scope.row.package_price || 0) }}
|
{{ formatAmount(scope.row.paid_amount || scope.row.package_price || 0) }}
|
||||||
</template>
|
</template>
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
<ElTableColumn prop="status_name" label="套餐状态" width="100">
|
<ElTableColumn prop="status_name" label="套餐状态" width="100">
|
||||||
@@ -104,6 +107,11 @@
|
|||||||
{{ formatDateTime(scope.row.expires_at) }}
|
{{ formatDateTime(scope.row.expires_at) }}
|
||||||
</template>
|
</template>
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
|
<ElTableColumn prop="created_at" label="创建时间" width="170" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatDateTime(scope.row.created_at) }}
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
</ElTable>
|
</ElTable>
|
||||||
<ElPagination
|
<ElPagination
|
||||||
v-model:current-page="pagination.page"
|
v-model:current-page="pagination.page"
|
||||||
@@ -152,6 +160,7 @@
|
|||||||
pageSize?: number
|
pageSize?: number
|
||||||
boundDeviceId?: number
|
boundDeviceId?: number
|
||||||
boundDeviceNo?: string
|
boundDeviceNo?: string
|
||||||
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -159,12 +168,14 @@
|
|||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
boundDeviceId: undefined,
|
boundDeviceId: undefined,
|
||||||
boundDeviceNo: ''
|
boundDeviceNo: '',
|
||||||
|
loading: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'showDailyRecords', payload: { packageRow: PackageInfo }): void
|
(e: 'showDailyRecords', payload: { packageRow: PackageInfo }): void
|
||||||
|
(e: 'showRecharge'): void
|
||||||
(e: 'page-change', page: number): void
|
(e: 'page-change', page: number): void
|
||||||
(e: 'size-change', size: number): void
|
(e: 'size-change', size: number): void
|
||||||
(e: 'navigateToDevice', deviceNo: string): void
|
(e: 'navigateToDevice', deviceNo: string): void
|
||||||
@@ -212,6 +223,10 @@
|
|||||||
emit('showDailyRecords', { packageRow })
|
emit('showDailyRecords', { packageRow })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleShowRecharge = () => {
|
||||||
|
emit('showRecharge')
|
||||||
|
}
|
||||||
|
|
||||||
// 页码变化
|
// 页码变化
|
||||||
const handlePageChange = (page: number) => {
|
const handlePageChange = (page: number) => {
|
||||||
emit('page-change', page)
|
emit('page-change', page)
|
||||||
@@ -229,6 +244,13 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden; // 防止内容溢出
|
overflow: hidden; // 防止内容溢出
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.package-table-wrapper {
|
.package-table-wrapper {
|
||||||
.table-scroll-container {
|
.table-scroll-container {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElCard shadow="never" class="info-card wallet-info">
|
<ElCard shadow="never" class="info-card wallet-info" v-loading="walletLoading">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header wallet-header">
|
<div class="card-header wallet-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export function useAssetInfo() {
|
|||||||
const packageList = ref<AssetPackageUsageRecord[]>([])
|
const packageList = ref<AssetPackageUsageRecord[]>([])
|
||||||
const walletInfo = ref<AssetWalletResponse | null>(null)
|
const walletInfo = ref<AssetWalletResponse | null>(null)
|
||||||
const walletLoading = ref(false)
|
const walletLoading = ref(false)
|
||||||
|
const packageLoading = ref(false)
|
||||||
|
const realtimeLoading = ref(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取资产详情
|
* 获取资产详情
|
||||||
@@ -142,6 +144,7 @@ export function useAssetInfo() {
|
|||||||
*/
|
*/
|
||||||
const loadPackageList = async (identifier: string, page: number = 1, pageSize: number = 10) => {
|
const loadPackageList = async (identifier: string, page: number = 1, pageSize: number = 10) => {
|
||||||
try {
|
try {
|
||||||
|
packageLoading.value = true
|
||||||
const response = await AssetService.getAssetPackages(identifier, {
|
const response = await AssetService.getAssetPackages(identifier, {
|
||||||
page,
|
page,
|
||||||
page_size: pageSize
|
page_size: pageSize
|
||||||
@@ -161,6 +164,8 @@ export function useAssetInfo() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取套餐列表失败:', error)
|
console.error('获取套餐列表失败:', error)
|
||||||
return { items: [], total: 0, page: 1, pageSize }
|
return { items: [], total: 0, page: 1, pageSize }
|
||||||
|
} finally {
|
||||||
|
packageLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,23 +178,17 @@ export function useAssetInfo() {
|
|||||||
currentPackageLoading.value = true
|
currentPackageLoading.value = true
|
||||||
currentPackageErrorMsg.value = ''
|
currentPackageErrorMsg.value = ''
|
||||||
|
|
||||||
const [currentPkgResponse, packageListResponse] = await Promise.all([
|
const response = await AssetService.getCurrentPackage(identifier)
|
||||||
AssetService.getCurrentPackage(identifier),
|
|
||||||
AssetService.getAssetPackages(identifier, { page: 1, page_size: 50 })
|
|
||||||
])
|
|
||||||
|
|
||||||
const activePackage = packageListResponse.data?.items?.find((p: any) => p.status === 1)
|
if (response.code === 0) {
|
||||||
const activePaidAmount = activePackage?.paid_amount || 0
|
if (response.data) {
|
||||||
|
const pkg = response.data
|
||||||
if (currentPkgResponse.code === 0) {
|
|
||||||
if (currentPkgResponse.data) {
|
|
||||||
const pkg = currentPkgResponse.data
|
|
||||||
|
|
||||||
currentPackage.value = {
|
currentPackage.value = {
|
||||||
id: pkg.package_usage_id || 0,
|
id: pkg.package_usage_id || 0,
|
||||||
package_id: pkg.package_id || 0,
|
package_id: pkg.package_id || 0,
|
||||||
package_name: pkg.package_name || '',
|
package_name: pkg.package_name || '',
|
||||||
package_price: activePaidAmount,
|
paid_amount: pkg.paid_amount || 0,
|
||||||
package_total_data: pkg.data_limit_mb || 0,
|
package_total_data: pkg.data_limit_mb || 0,
|
||||||
real_data_used: pkg.data_usage_mb || 0,
|
real_data_used: pkg.data_usage_mb || 0,
|
||||||
real_data_remaining: (pkg.data_limit_mb || 0) - (pkg.data_usage_mb || 0),
|
real_data_remaining: (pkg.data_limit_mb || 0) - (pkg.data_usage_mb || 0),
|
||||||
@@ -208,7 +207,8 @@ export function useAssetInfo() {
|
|||||||
data_limit_mb: pkg.data_limit_mb,
|
data_limit_mb: pkg.data_limit_mb,
|
||||||
virtual_limit_mb: pkg.virtual_limit_mb,
|
virtual_limit_mb: pkg.virtual_limit_mb,
|
||||||
virtual_remain_mb: pkg.virtual_remain_mb,
|
virtual_remain_mb: pkg.virtual_remain_mb,
|
||||||
data_usage_mb: pkg.data_usage_mb
|
data_usage_mb: pkg.data_usage_mb,
|
||||||
|
created_at: pkg.created_at || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同时更新 cardInfo 中的流量显示信息(根据是否启用虚流量选择数据源)
|
// 同时更新 cardInfo 中的流量显示信息(根据是否启用虚流量选择数据源)
|
||||||
@@ -260,6 +260,7 @@ export function useAssetInfo() {
|
|||||||
*/
|
*/
|
||||||
const loadRealtimeStatus = async (identifier: string, assetType: string) => {
|
const loadRealtimeStatus = async (identifier: string, assetType: string) => {
|
||||||
try {
|
try {
|
||||||
|
realtimeLoading.value = true
|
||||||
const response = await AssetService.getRealtimeStatus(identifier)
|
const response = await AssetService.getRealtimeStatus(identifier)
|
||||||
if (response.code === 0 && response.data) {
|
if (response.code === 0 && response.data) {
|
||||||
const data = response.data
|
const data = response.data
|
||||||
@@ -323,6 +324,8 @@ export function useAssetInfo() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取实时状态失败:', error)
|
console.error('获取实时状态失败:', error)
|
||||||
|
} finally {
|
||||||
|
realtimeLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,8 +380,10 @@ export function useAssetInfo() {
|
|||||||
currentPackageLoading,
|
currentPackageLoading,
|
||||||
currentPackageErrorMsg,
|
currentPackageErrorMsg,
|
||||||
packageList,
|
packageList,
|
||||||
|
packageLoading,
|
||||||
walletInfo,
|
walletInfo,
|
||||||
walletLoading,
|
walletLoading,
|
||||||
|
realtimeLoading,
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
fetchAssetDetail,
|
fetchAssetDetail,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
:card-info="cardInfo"
|
:card-info="cardInfo"
|
||||||
:device-realtime="deviceRealtime"
|
:device-realtime="deviceRealtime"
|
||||||
v-model:polling-enabled="pollingEnabled"
|
v-model:polling-enabled="pollingEnabled"
|
||||||
|
:realtime-loading="realtimeLoading"
|
||||||
@enable-card="handleEnableCard"
|
@enable-card="handleEnableCard"
|
||||||
@disable-card="handleDisableCard"
|
@disable-card="handleDisableCard"
|
||||||
@manual-deactivate="handleManualDeactivate"
|
@manual-deactivate="handleManualDeactivate"
|
||||||
@@ -56,7 +57,9 @@
|
|||||||
:page-size="packagePagination.pageSize"
|
:page-size="packagePagination.pageSize"
|
||||||
:bound-device-id="cardInfo.bound_device_id"
|
:bound-device-id="cardInfo.bound_device_id"
|
||||||
:bound-device-no="cardInfo.bound_device_no"
|
:bound-device-no="cardInfo.bound_device_no"
|
||||||
|
:loading="packageLoading"
|
||||||
@show-daily-records="handleShowDailyRecords"
|
@show-daily-records="handleShowDailyRecords"
|
||||||
|
@show-recharge="showRechargeDialog"
|
||||||
@page-change="handlePackagePageChange"
|
@page-change="handlePackagePageChange"
|
||||||
@size-change="handlePackageSizeChange"
|
@size-change="handlePackageSizeChange"
|
||||||
@navigate-to-device="handleNavigateToDevice"
|
@navigate-to-device="handleNavigateToDevice"
|
||||||
@@ -172,8 +175,10 @@
|
|||||||
currentPackageLoading,
|
currentPackageLoading,
|
||||||
currentPackageErrorMsg,
|
currentPackageErrorMsg,
|
||||||
packageList,
|
packageList,
|
||||||
|
packageLoading,
|
||||||
walletInfo,
|
walletInfo,
|
||||||
walletLoading,
|
walletLoading,
|
||||||
|
realtimeLoading,
|
||||||
fetchAssetDetail,
|
fetchAssetDetail,
|
||||||
loadPackageList,
|
loadPackageList,
|
||||||
loadCurrentPackage,
|
loadCurrentPackage,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<ArtSearchBar
|
<ArtSearchBar
|
||||||
v-model:filter="searchForm"
|
v-model:filter="searchForm"
|
||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
|
label-width="100"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></ArtSearchBar>
|
></ArtSearchBar>
|
||||||
@@ -340,7 +341,11 @@
|
|||||||
<ElRow :gutter="20">
|
<ElRow :gutter="20">
|
||||||
<ElCol :span="6">
|
<ElCol :span="6">
|
||||||
<ElFormItem label="搜索类型" prop="search_type">
|
<ElFormItem label="搜索类型" prop="search_type">
|
||||||
<ElSelect v-model="bindCardForm.search_type" placeholder="请选择搜索类型" style="width: 100%">
|
<ElSelect
|
||||||
|
v-model="bindCardForm.search_type"
|
||||||
|
placeholder="请选择搜索类型"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<ElOption label="ICCID" value="iccid" />
|
<ElOption label="ICCID" value="iccid" />
|
||||||
<ElOption label="运营商名称" value="carrier_name" />
|
<ElOption label="运营商名称" value="carrier_name" />
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
@@ -655,6 +660,10 @@
|
|||||||
})
|
})
|
||||||
const seriesBindingResult = ref<BatchSetDeviceSeriesBindingResponse | null>(null)
|
const seriesBindingResult = ref<BatchSetDeviceSeriesBindingResponse | null>(null)
|
||||||
|
|
||||||
|
// 搜索表单店铺/系列选项
|
||||||
|
const shopOptions = ref<any[]>([])
|
||||||
|
const searchSeriesOptions = ref<any[]>([])
|
||||||
|
|
||||||
// 设备详情弹窗相关
|
// 设备详情弹窗相关
|
||||||
const deviceDetailDialogVisible = ref(false)
|
const deviceDetailDialogVisible = ref(false)
|
||||||
const deviceDetailLoading = ref(false)
|
const deviceDetailLoading = ref(false)
|
||||||
@@ -734,7 +743,12 @@
|
|||||||
status: undefined as DeviceStatus | undefined,
|
status: undefined as DeviceStatus | undefined,
|
||||||
batch_no: '',
|
batch_no: '',
|
||||||
device_type: '',
|
device_type: '',
|
||||||
manufacturer: ''
|
manufacturer: '',
|
||||||
|
shop_id: undefined as number | undefined,
|
||||||
|
series_id: undefined as number | undefined,
|
||||||
|
dateRange: [] as string[],
|
||||||
|
created_at_start: '',
|
||||||
|
created_at_end: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
@@ -801,6 +815,54 @@
|
|||||||
clearable: true,
|
clearable: true,
|
||||||
placeholder: '请输入制造商'
|
placeholder: '请输入制造商'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '店铺名称',
|
||||||
|
prop: 'shop_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchShops(query),
|
||||||
|
loading: false,
|
||||||
|
placeholder: '请选择或搜索店铺名称'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
shopOptions.value.map((shop) => ({
|
||||||
|
label: shop.shop_name,
|
||||||
|
value: shop.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '套餐系列',
|
||||||
|
prop: 'series_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchSeries(query),
|
||||||
|
loading: seriesLoading.value,
|
||||||
|
placeholder: '请选择或搜索套餐系列'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
searchSeriesOptions.value.map((s) => ({
|
||||||
|
label: s.series_name,
|
||||||
|
value: s.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '起始至结束',
|
||||||
|
prop: 'dateRange',
|
||||||
|
type: 'date',
|
||||||
|
config: {
|
||||||
|
type: 'datetimerange',
|
||||||
|
rangeSeparator: '至',
|
||||||
|
startPlaceholder: '开始时间',
|
||||||
|
endPlaceholder: '结束时间',
|
||||||
|
valueFormat: 'YYYY-MM-DDTHH:mm:ssZZ'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -814,21 +876,26 @@
|
|||||||
// 列配置
|
// 列配置
|
||||||
const columnOptions = [
|
const columnOptions = [
|
||||||
{ label: '设备号', prop: 'virtual_no' },
|
{ label: '设备号', prop: 'virtual_no' },
|
||||||
|
{ label: 'IMEI', prop: 'imei' },
|
||||||
{ label: '设备名称', prop: 'device_name' },
|
{ label: '设备名称', prop: 'device_name' },
|
||||||
{ label: '设备型号', prop: 'device_model' },
|
|
||||||
{ label: '设备类型', prop: 'device_type' },
|
|
||||||
{ label: '店铺名称', prop: 'shop_name' },
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
{ label: '套餐系列', prop: 'series_name' },
|
{ label: '套餐系列', prop: 'series_name' },
|
||||||
|
{ label: '设备型号', prop: 'device_model' },
|
||||||
|
{ label: '设备类型', prop: 'device_type' },
|
||||||
|
{ label: '状态', prop: 'status' },
|
||||||
|
{ label: '状态名称', prop: 'status_name' },
|
||||||
|
{ label: '在线状态', prop: 'online_status' },
|
||||||
|
{ label: '实名策略', prop: 'realname_policy' },
|
||||||
|
{ label: '切卡模式', prop: 'switch_mode' },
|
||||||
|
{ label: '激活时间', prop: 'activated_at' },
|
||||||
{ label: '制造商', prop: 'manufacturer' },
|
{ label: '制造商', prop: 'manufacturer' },
|
||||||
|
{ label: '软件版本', prop: 'software_version' },
|
||||||
{ label: '最大插槽数', prop: 'max_sim_slots' },
|
{ label: '最大插槽数', prop: 'max_sim_slots' },
|
||||||
{ label: '已绑定卡数', prop: 'bound_card_count' },
|
{ label: '已绑定卡数', prop: 'bound_card_count' },
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '序列号', prop: 'sn' },
|
||||||
{ label: '在线状态', prop: 'online_status' },
|
{ label: '批次号', prop: 'batch_no' },
|
||||||
|
|
||||||
{ label: '切卡模式', prop: 'switch_mode' },
|
|
||||||
{ label: '最后在线时间', prop: 'last_online_time' },
|
{ label: '最后在线时间', prop: 'last_online_time' },
|
||||||
{ label: '最后同步时间', prop: 'last_gateway_sync_at' },
|
{ label: '最后同步时间', prop: 'last_gateway_sync_at' },
|
||||||
{ label: '批次号', prop: 'batch_no' },
|
|
||||||
{ label: '创建时间', prop: 'created_at' },
|
{ label: '创建时间', prop: 'created_at' },
|
||||||
{ label: '操作', prop: 'operation' }
|
{ label: '操作', prop: 'operation' }
|
||||||
]
|
]
|
||||||
@@ -875,10 +942,7 @@
|
|||||||
bindCardForm.iot_card_id = undefined
|
bindCardForm.iot_card_id = undefined
|
||||||
bindCardForm.slot_position = 1
|
bindCardForm.slot_position = 1
|
||||||
// 加载未绑定设备的卡列表
|
// 加载未绑定设备的卡列表
|
||||||
await Promise.all([
|
await Promise.all([loadDeviceCards(device.virtual_no), loadDefaultIotCards()])
|
||||||
loadDeviceCards(device.virtual_no),
|
|
||||||
loadDefaultIotCards()
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载设备绑定的卡列表
|
// 加载设备绑定的卡列表
|
||||||
@@ -1055,7 +1119,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'virtual_no',
|
prop: 'virtual_no',
|
||||||
label: '设备号',
|
label: '设备号',
|
||||||
minWidth: 150,
|
minWidth: 170,
|
||||||
showOverflowTooltip: true,
|
showOverflowTooltip: true,
|
||||||
formatter: (row: Device) => {
|
formatter: (row: Device) => {
|
||||||
return h(
|
return h(
|
||||||
@@ -1071,21 +1135,18 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'imei',
|
||||||
|
label: 'IMEI',
|
||||||
|
minWidth: 150,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: Device) => row.imei || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'device_name',
|
prop: 'device_name',
|
||||||
label: '设备名称',
|
label: '设备名称',
|
||||||
minWidth: 120
|
minWidth: 120
|
||||||
},
|
},
|
||||||
{
|
|
||||||
prop: 'device_model',
|
|
||||||
label: '设备型号',
|
|
||||||
minWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'device_type',
|
|
||||||
label: '设备类型',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: 'shop_name',
|
prop: 'shop_name',
|
||||||
label: '店铺名称',
|
label: '店铺名称',
|
||||||
@@ -1097,23 +1158,14 @@
|
|||||||
minWidth: 120
|
minWidth: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'manufacturer',
|
prop: 'device_model',
|
||||||
label: '制造商',
|
label: '设备型号',
|
||||||
minWidth: 120
|
minWidth: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'max_sim_slots',
|
prop: 'device_type',
|
||||||
label: '最大插槽数',
|
label: '设备类型',
|
||||||
width: 100
|
width: 120
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'bound_card_count',
|
|
||||||
label: '已绑定卡数',
|
|
||||||
width: 110,
|
|
||||||
formatter: (row: Device) => {
|
|
||||||
const color = row.bound_card_count > 0 ? '#67c23a' : '#909399'
|
|
||||||
return h('span', { style: { color, fontWeight: 'bold' } }, row.bound_card_count)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
@@ -1130,6 +1182,12 @@
|
|||||||
return h(ElTag, { type: status.type }, () => status.text)
|
return h(ElTag, { type: status.type }, () => status.text)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'status_name',
|
||||||
|
label: '状态名称',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: Device) => row.status_name || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'online_status',
|
prop: 'online_status',
|
||||||
label: '在线状态',
|
label: '在线状态',
|
||||||
@@ -1144,6 +1202,20 @@
|
|||||||
return h(ElTag, { type: status.type }, () => status.text)
|
return h(ElTag, { type: status.type }, () => status.text)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'realname_policy',
|
||||||
|
label: '实名策略',
|
||||||
|
width: 150,
|
||||||
|
formatter: (row: Device) => {
|
||||||
|
const policy = row.realname_policy || ''
|
||||||
|
const policyMap: Record<string, string> = {
|
||||||
|
none: '无需实名',
|
||||||
|
before_order: '先实名后充值/购买',
|
||||||
|
after_order: '先充值/购买后实名'
|
||||||
|
}
|
||||||
|
return policyMap[policy] || (policy ? policy : '未知')
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'switch_mode',
|
prop: 'switch_mode',
|
||||||
label: '切卡模式',
|
label: '切卡模式',
|
||||||
@@ -1157,19 +1229,51 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'realname_policy',
|
prop: 'activated_at',
|
||||||
label: '实名认证策略',
|
label: '激活时间',
|
||||||
width: 150,
|
width: 180,
|
||||||
|
formatter: (row: Device) => (row.activated_at ? formatDateTime(row.activated_at) : '-')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'manufacturer',
|
||||||
|
label: '制造商',
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'software_version',
|
||||||
|
label: '软件版本',
|
||||||
|
minWidth: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: Device) => row.software_version || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'max_sim_slots',
|
||||||
|
label: '最大插槽数',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'bound_card_count',
|
||||||
|
label: '已绑定卡数',
|
||||||
|
width: 110,
|
||||||
formatter: (row: Device) => {
|
formatter: (row: Device) => {
|
||||||
const policy = row.realname_policy || ''
|
const color = row.bound_card_count > 0 ? '#67c23a' : '#909399'
|
||||||
const policyMap: Record<string, string> = {
|
return h('span', { style: { color, fontWeight: 'bold' } }, row.bound_card_count)
|
||||||
none: '无需实名',
|
|
||||||
before_order: '先实名后充值/购买',
|
|
||||||
after_order: '先充值/购买后实名'
|
|
||||||
}
|
|
||||||
return policyMap[policy] || (policy ? policy : '未知')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'sn',
|
||||||
|
label: '序列号',
|
||||||
|
minWidth: 120,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: Device) => row.sn || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'batch_no',
|
||||||
|
label: '批次号',
|
||||||
|
minWidth: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: Device) => row.batch_no || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'last_online_time',
|
prop: 'last_online_time',
|
||||||
label: '最后在线时间',
|
label: '最后在线时间',
|
||||||
@@ -1184,12 +1288,6 @@
|
|||||||
formatter: (row: Device) =>
|
formatter: (row: Device) =>
|
||||||
row.last_gateway_sync_at ? formatDateTime(row.last_gateway_sync_at) : '-'
|
row.last_gateway_sync_at ? formatDateTime(row.last_gateway_sync_at) : '-'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
prop: 'batch_no',
|
|
||||||
label: '批次号',
|
|
||||||
minWidth: 180,
|
|
||||||
formatter: (row: Device) => row.batch_no || '-'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: 'created_at',
|
prop: 'created_at',
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
@@ -1198,9 +1296,50 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 搜索店铺(用于搜索表单)
|
||||||
|
const searchShops = async (query: string) => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.shop_name = query
|
||||||
|
}
|
||||||
|
const res = await ShopService.getShops(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
shopOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索店铺失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索套餐系列(用于搜索表单)
|
||||||
|
const searchSeries = async (query: string) => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.series_name = query
|
||||||
|
}
|
||||||
|
const res = await PackageSeriesService.getPackageSeries(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
searchSeriesOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索套餐系列失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let isFirstActivation = true
|
let isFirstActivation = true
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
|
searchShops('')
|
||||||
|
searchSeries('')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 当页面被 keep-alive 激活时自动刷新数据
|
// 当页面被 keep-alive 激活时自动刷新数据
|
||||||
@@ -1223,7 +1362,11 @@
|
|||||||
status: searchForm.status,
|
status: searchForm.status,
|
||||||
batch_no: searchForm.batch_no || undefined,
|
batch_no: searchForm.batch_no || undefined,
|
||||||
device_type: searchForm.device_type || undefined,
|
device_type: searchForm.device_type || undefined,
|
||||||
manufacturer: searchForm.manufacturer || undefined
|
manufacturer: searchForm.manufacturer || undefined,
|
||||||
|
shop_id: searchForm.shop_id || undefined,
|
||||||
|
series_id: searchForm.series_id || undefined,
|
||||||
|
created_at_start: searchForm.created_at_start || undefined,
|
||||||
|
created_at_end: searchForm.created_at_end || undefined
|
||||||
}
|
}
|
||||||
const res = await DeviceService.getDevices(params)
|
const res = await DeviceService.getDevices(params)
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
@@ -1246,6 +1389,14 @@
|
|||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
|
// 处理日期范围
|
||||||
|
if (searchForm.dateRange && Array.isArray(searchForm.dateRange)) {
|
||||||
|
searchForm.created_at_start = searchForm.dateRange[0] || ''
|
||||||
|
searchForm.created_at_end = searchForm.dateRange[1] || ''
|
||||||
|
} else {
|
||||||
|
searchForm.created_at_start = ''
|
||||||
|
searchForm.created_at_end = ''
|
||||||
|
}
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
getTableData()
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,6 +229,7 @@
|
|||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
status: undefined,
|
status: undefined,
|
||||||
identifier: '',
|
identifier: '',
|
||||||
|
created_at_range: [],
|
||||||
created_at_start: '',
|
created_at_start: '',
|
||||||
created_at_end: ''
|
created_at_end: ''
|
||||||
})
|
})
|
||||||
@@ -306,11 +307,11 @@
|
|||||||
prop: 'created_at_range',
|
prop: 'created_at_range',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
config: {
|
config: {
|
||||||
type: 'datetimerange',
|
type: 'daterange',
|
||||||
rangeSeparator: '至',
|
rangeSeparator: '至',
|
||||||
startPlaceholder: '开始时间',
|
startPlaceholder: '开始日期',
|
||||||
endPlaceholder: '结束时间',
|
endPlaceholder: '结束日期',
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
valueFormat: 'YYYY-MM-DD'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -417,12 +418,23 @@
|
|||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
|
// 处理日期范围
|
||||||
|
if (searchForm.created_at_range && Array.isArray(searchForm.created_at_range)) {
|
||||||
|
searchForm.created_at_start = searchForm.created_at_range[0] || ''
|
||||||
|
searchForm.created_at_end = searchForm.created_at_range[1] || ''
|
||||||
|
} else {
|
||||||
|
searchForm.created_at_start = ''
|
||||||
|
searchForm.created_at_end = ''
|
||||||
|
}
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
loadExchangeList()
|
loadExchangeList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
|
searchForm.created_at_range = []
|
||||||
|
searchForm.created_at_start = ''
|
||||||
|
searchForm.created_at_end = ''
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
loadExchangeList()
|
loadExchangeList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -562,7 +562,13 @@
|
|||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { RoutesAlias } from '@/router/routesAlias'
|
import { RoutesAlias } from '@/router/routesAlias'
|
||||||
import { CardService, ShopService, PackageSeriesService, AssetService, CarrierService } from '@/api/modules'
|
import {
|
||||||
|
CardService,
|
||||||
|
ShopService,
|
||||||
|
PackageSeriesService,
|
||||||
|
AssetService,
|
||||||
|
CarrierService
|
||||||
|
} from '@/api/modules'
|
||||||
import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus'
|
||||||
import { Loading } from '@element-plus/icons-vue'
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue'
|
import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue'
|
||||||
@@ -659,6 +665,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 套餐系列搜索相关
|
||||||
|
const searchSeriesOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
// 搜索套餐系列(用于搜索栏)
|
||||||
|
const handleSearchSeries = async (query: string) => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.series_name = query
|
||||||
|
}
|
||||||
|
const res = await PackageSeriesService.getPackageSeries(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
searchSeriesOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索套餐系列失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 卡详情弹窗相关
|
// 卡详情弹窗相关
|
||||||
const cardDetailDialogVisible = ref(false)
|
const cardDetailDialogVisible = ref(false)
|
||||||
const cardDetailLoading = ref(false)
|
const cardDetailLoading = ref(false)
|
||||||
@@ -767,6 +796,7 @@
|
|||||||
msisdn: string
|
msisdn: string
|
||||||
virtual_no: string
|
virtual_no: string
|
||||||
device_virtual_no: string
|
device_virtual_no: string
|
||||||
|
series_id: undefined | number
|
||||||
is_distributed: undefined | number
|
is_distributed: undefined | number
|
||||||
is_standalone: undefined | boolean
|
is_standalone: undefined | boolean
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
@@ -777,6 +807,7 @@
|
|||||||
msisdn: '',
|
msisdn: '',
|
||||||
virtual_no: '',
|
virtual_no: '',
|
||||||
device_virtual_no: '',
|
device_virtual_no: '',
|
||||||
|
series_id: undefined,
|
||||||
is_distributed: undefined,
|
is_distributed: undefined,
|
||||||
is_standalone: undefined,
|
is_standalone: undefined,
|
||||||
is_replaced: undefined,
|
is_replaced: undefined,
|
||||||
@@ -952,6 +983,23 @@
|
|||||||
placeholder: '请输入绑定设备虚拟号'
|
placeholder: '请输入绑定设备虚拟号'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '套餐系列',
|
||||||
|
prop: 'series_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: handleSearchSeries,
|
||||||
|
placeholder: '请选择或搜索套餐系列'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
searchSeriesOptions.value.map((s) => ({
|
||||||
|
label: s.series_name,
|
||||||
|
value: s.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '是否已分销',
|
label: '是否已分销',
|
||||||
prop: 'is_distributed',
|
prop: 'is_distributed',
|
||||||
@@ -974,8 +1022,8 @@
|
|||||||
placeholder: '全部'
|
placeholder: '全部'
|
||||||
},
|
},
|
||||||
options: () => [
|
options: () => [
|
||||||
{ label: '是(未绑定设备)', value: true },
|
{ label: '未绑定设备', value: true },
|
||||||
{ label: '否(已绑定设备)', value: false }
|
{ label: '已绑定设备', value: false }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1016,18 +1064,28 @@
|
|||||||
{ label: 'ICCID', prop: 'iccid' },
|
{ label: 'ICCID', prop: 'iccid' },
|
||||||
{ label: 'MSISDN', prop: 'msisdn' },
|
{ label: 'MSISDN', prop: 'msisdn' },
|
||||||
{ label: '卡虚拟号', prop: 'virtual_no' },
|
{ label: '卡虚拟号', prop: 'virtual_no' },
|
||||||
{ label: '绑定设备虚拟号', prop: 'device_virtual_no' },
|
|
||||||
{ label: '卡业务类型', prop: 'card_category' },
|
|
||||||
{ label: '运营商', prop: 'carrier_name' },
|
{ label: '运营商', prop: 'carrier_name' },
|
||||||
{ label: '店铺名称', prop: 'shop_name' },
|
{ label: '卡业务类型', prop: 'card_category' },
|
||||||
{ label: '套餐系列', prop: 'series_name' },
|
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '激活状态', prop: 'activation_status' },
|
{ label: '激活状态', prop: 'activation_status' },
|
||||||
|
{ label: '激活时间', prop: 'activated_at' },
|
||||||
{ label: '网络状态', prop: 'network_status' },
|
{ label: '网络状态', prop: 'network_status' },
|
||||||
{ label: '实名状态', prop: 'real_name_status' },
|
{ label: '实名状态', prop: 'real_name_status' },
|
||||||
{ label: '实名认证策略', prop: 'realname_policy' },
|
{ label: '实名认证策略', prop: 'realname_policy' },
|
||||||
|
{ label: '绑定设备虚拟号', prop: 'device_virtual_no' },
|
||||||
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
|
{ label: '套餐系列', prop: 'series_name' },
|
||||||
|
{ label: '自然月累计流量(MB)', prop: 'current_month_usage_mb' },
|
||||||
|
{ label: '本月开始日期', prop: 'current_month_start_date' },
|
||||||
|
{ label: '上月流量总量(MB)', prop: 'last_month_total_mb' },
|
||||||
{ label: '累计流量(MB)', prop: 'data_usage_mb' },
|
{ label: '累计流量(MB)', prop: 'data_usage_mb' },
|
||||||
{ label: '创建时间', prop: 'created_at' }
|
{ label: '最后流量检查时间', prop: 'last_data_check_at' },
|
||||||
|
{ label: '最后实名检查时间', prop: 'last_real_name_check_at' },
|
||||||
|
{ label: '启用轮询', prop: 'enable_polling' },
|
||||||
|
{ label: '批次号', prop: 'batch_no' },
|
||||||
|
{ label: '供应商', prop: 'supplier' },
|
||||||
|
{ label: '创建时间', prop: 'created_at' },
|
||||||
|
{ label: '更新时间', prop: 'updated_at' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const cardList = ref<StandaloneIotCard[]>([])
|
const cardList = ref<StandaloneIotCard[]>([])
|
||||||
@@ -1233,12 +1291,70 @@
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'activated_at',
|
||||||
|
label: '激活时间',
|
||||||
|
width: 180,
|
||||||
|
formatter: (row: StandaloneIotCard) =>
|
||||||
|
row.activated_at ? formatDateTime(row.activated_at) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'current_month_usage_mb',
|
||||||
|
label: '自然月累计流量(MB)',
|
||||||
|
width: 180,
|
||||||
|
formatter: (row: StandaloneIotCard) => row.current_month_usage_mb?.toLocaleString() || '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'current_month_start_date',
|
||||||
|
label: '本月开始日期',
|
||||||
|
width: 200,
|
||||||
|
formatter: (row: StandaloneIotCard) =>
|
||||||
|
row.current_month_start_date ? formatDateTime(row.current_month_start_date) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'last_month_total_mb',
|
||||||
|
label: '上月流量总量(MB)',
|
||||||
|
width: 150,
|
||||||
|
formatter: (row: StandaloneIotCard) => row.last_month_total_mb?.toLocaleString() || '0'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'data_usage_mb',
|
prop: 'data_usage_mb',
|
||||||
label: '累计流量(MB)',
|
label: '累计流量(MB)',
|
||||||
width: 120,
|
width: 120,
|
||||||
formatter: (row: StandaloneIotCard) => row.data_usage_mb?.toLocaleString() || '0'
|
formatter: (row: StandaloneIotCard) => row.data_usage_mb?.toLocaleString() || '0'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'last_data_check_at',
|
||||||
|
label: '最后流量检查时间',
|
||||||
|
width: 180,
|
||||||
|
formatter: (row: StandaloneIotCard) =>
|
||||||
|
row.last_data_check_at ? formatDateTime(row.last_data_check_at) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'last_real_name_check_at',
|
||||||
|
label: '最后实名检查时间',
|
||||||
|
width: 180,
|
||||||
|
formatter: (row: StandaloneIotCard) =>
|
||||||
|
row.last_real_name_check_at ? formatDateTime(row.last_real_name_check_at) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'enable_polling',
|
||||||
|
label: '启用轮询',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: StandaloneIotCard) => (row.enable_polling ? '是' : '否')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'batch_no',
|
||||||
|
label: '批次号',
|
||||||
|
width: 200,
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'supplier',
|
||||||
|
label: '供应商',
|
||||||
|
width: 150,
|
||||||
|
formatter: (row: StandaloneIotCard) => row.supplier || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'created_at',
|
prop: 'created_at',
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
@@ -1289,7 +1405,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadSearchCarrierOptions()
|
|
||||||
getTableData()
|
getTableData()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,27 @@
|
|||||||
},
|
},
|
||||||
{ label: '资产标识符', prop: 'asset_identifier' },
|
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||||
{ label: '来源所有者', prop: 'from_owner_name' },
|
{ label: '来源所有者', prop: 'from_owner_name' },
|
||||||
|
{
|
||||||
|
label: '来源类型',
|
||||||
|
formatter: (_, data) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
platform: '平台',
|
||||||
|
shop: '店铺'
|
||||||
|
}
|
||||||
|
return typeMap[data.from_owner_type] || data.from_owner_type || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
{ label: '目标所有者', prop: 'to_owner_name' },
|
{ label: '目标所有者', prop: 'to_owner_name' },
|
||||||
|
{
|
||||||
|
label: '目标类型',
|
||||||
|
formatter: (_, data) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
platform: '平台',
|
||||||
|
shop: '店铺'
|
||||||
|
}
|
||||||
|
return typeMap[data.to_owner_type] || data.to_owner_type || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
{ label: '操作人', prop: 'operator_name' },
|
{ label: '操作人', prop: 'operator_name' },
|
||||||
{ label: '关联卡数量', prop: 'related_card_count' },
|
{ label: '关联卡数量', prop: 'related_card_count' },
|
||||||
{ label: '创建时间', prop: 'created_at', formatter: (value) => formatDateTime(value) },
|
{ label: '创建时间', prop: 'created_at', formatter: (value) => formatDateTime(value) },
|
||||||
|
|||||||
@@ -28,28 +28,13 @@
|
|||||||
:pageSize="pagination.pageSize"
|
:pageSize="pagination.pageSize"
|
||||||
:total="pagination.total"
|
:total="pagination.total"
|
||||||
:marginTop="10"
|
:marginTop="10"
|
||||||
:row-class-name="getRowClassName"
|
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@row-contextmenu="handleRowContextMenu"
|
|
||||||
@cell-mouse-enter="handleCellMouseEnter"
|
|
||||||
@cell-mouse-leave="handleCellMouseLeave"
|
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||||
</template>
|
</template>
|
||||||
</ArtTable>
|
</ArtTable>
|
||||||
|
|
||||||
<!-- 鼠标悬浮提示 -->
|
|
||||||
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
|
|
||||||
|
|
||||||
<!-- 右键菜单 -->
|
|
||||||
<ArtMenuRight
|
|
||||||
ref="contextMenuRef"
|
|
||||||
:menu-items="contextMenuItems"
|
|
||||||
:menu-width="120"
|
|
||||||
@select="handleContextMenuSelect"
|
|
||||||
/>
|
|
||||||
</ElCard>
|
</ElCard>
|
||||||
</div>
|
</div>
|
||||||
</ArtTableFullScreen>
|
</ArtTableFullScreen>
|
||||||
@@ -58,7 +43,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { CardService } from '@/api/modules'
|
import { CardService, ShopService } from '@/api/modules'
|
||||||
import { ElMessage, ElTag } from 'element-plus'
|
import { ElMessage, ElTag } from 'element-plus'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
@@ -67,28 +52,14 @@
|
|||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import type { AssetAllocationRecord, AllocationTypeEnum, AssetTypeEnum } from '@/types/api/card'
|
import type { AssetAllocationRecord, AllocationTypeEnum, AssetTypeEnum } from '@/types/api/card'
|
||||||
import { RoutesAlias } from '@/router/routesAlias'
|
import { RoutesAlias } from '@/router/routesAlias'
|
||||||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
|
||||||
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
|
|
||||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'AssetAllocationRecords' })
|
defineOptions({ name: 'AssetAllocationRecords' })
|
||||||
|
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// 使用表格右键菜单功能
|
|
||||||
const {
|
|
||||||
showContextMenuHint,
|
|
||||||
hintPosition,
|
|
||||||
getRowClassName,
|
|
||||||
handleCellMouseEnter,
|
|
||||||
handleCellMouseLeave
|
|
||||||
} = useTableContextMenu()
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
|
||||||
const currentRow = ref<AssetAllocationRecord | null>(null)
|
|
||||||
|
|
||||||
// 搜索表单初始值
|
// 搜索表单初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
@@ -99,6 +70,7 @@
|
|||||||
from_shop_id: undefined as number | undefined,
|
from_shop_id: undefined as number | undefined,
|
||||||
to_shop_id: undefined as number | undefined,
|
to_shop_id: undefined as number | undefined,
|
||||||
operator_id: undefined as number | undefined,
|
operator_id: undefined as number | undefined,
|
||||||
|
dateRange: [] as string[],
|
||||||
created_at_start: '',
|
created_at_start: '',
|
||||||
created_at_end: ''
|
created_at_end: ''
|
||||||
}
|
}
|
||||||
@@ -159,16 +131,51 @@
|
|||||||
placeholder: 'ICCID或设备号'
|
placeholder: 'ICCID或设备号'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '来源店铺',
|
||||||
|
prop: 'from_shop_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchShops(query, 'from'),
|
||||||
|
placeholder: '请选择或搜索来源店铺'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
fromShopOptions.value.map((shop) => ({
|
||||||
|
label: shop.shop_name,
|
||||||
|
value: shop.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '目标店铺',
|
||||||
|
prop: 'to_shop_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchShops(query, 'to'),
|
||||||
|
placeholder: '请选择或搜索目标店铺'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
toShopOptions.value.map((shop) => ({
|
||||||
|
label: shop.shop_name,
|
||||||
|
value: shop.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '创建时间',
|
label: '创建时间',
|
||||||
prop: 'created_at_range',
|
prop: 'dateRange',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
config: {
|
config: {
|
||||||
type: 'datetimerange',
|
type: 'daterange',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
startPlaceholder: '开始时间',
|
rangeSeparator: '至',
|
||||||
endPlaceholder: '结束时间',
|
startPlaceholder: '开始日期',
|
||||||
valueFormat: 'YYYY-MM-DDTHH:mm:ssZ'
|
endPlaceholder: '结束日期',
|
||||||
|
valueFormat: 'YYYY-MM-DD'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -186,6 +193,33 @@
|
|||||||
{ label: '创建时间', prop: 'created_at' }
|
{ label: '创建时间', prop: 'created_at' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 店铺搜索选项
|
||||||
|
const fromShopOptions = ref<any[]>([])
|
||||||
|
const toShopOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
// 搜索店铺
|
||||||
|
const searchShops = async (query: string, type: 'from' | 'to') => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.shop_name = query
|
||||||
|
}
|
||||||
|
const res = await ShopService.getShops(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
if (type === 'from') {
|
||||||
|
fromShopOptions.value = res.data.items || []
|
||||||
|
} else {
|
||||||
|
toShopOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索店铺失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const recordList = ref<AssetAllocationRecord[]>([])
|
const recordList = ref<AssetAllocationRecord[]>([])
|
||||||
|
|
||||||
// 获取分配类型标签类型
|
// 获取分配类型标签类型
|
||||||
@@ -246,12 +280,38 @@
|
|||||||
{
|
{
|
||||||
prop: 'from_owner_name',
|
prop: 'from_owner_name',
|
||||||
label: '来源所有者',
|
label: '来源所有者',
|
||||||
width: 150
|
width: 150,
|
||||||
|
formatter: (row: AssetAllocationRecord) => row.from_owner_name || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'from_owner_type',
|
||||||
|
label: '来源类型',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: AssetAllocationRecord) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
platform: '平台',
|
||||||
|
shop: '店铺'
|
||||||
|
}
|
||||||
|
return typeMap[row.from_owner_type] || row.from_owner_type || '-'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'to_owner_name',
|
prop: 'to_owner_name',
|
||||||
label: '目标所有者',
|
label: '目标所有者',
|
||||||
width: 150
|
width: 150,
|
||||||
|
formatter: (row: AssetAllocationRecord) => row.to_owner_name || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'to_owner_type',
|
||||||
|
label: '目标类型',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: AssetAllocationRecord) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
platform: '平台',
|
||||||
|
shop: '店铺'
|
||||||
|
}
|
||||||
|
return typeMap[row.to_owner_type] || row.to_owner_type || '-'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'operator_name',
|
prop: 'operator_name',
|
||||||
@@ -279,6 +339,8 @@
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
|
searchShops('', 'from')
|
||||||
|
searchShops('', 'to')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取分配记录列表
|
// 获取分配记录列表
|
||||||
@@ -291,13 +353,6 @@
|
|||||||
...formFilters
|
...formFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理日期范围
|
|
||||||
if ((params as any).created_at_range && (params as any).created_at_range.length === 2) {
|
|
||||||
params.created_at_start = (params as any).created_at_range[0]
|
|
||||||
params.created_at_end = (params as any).created_at_range[1]
|
|
||||||
delete (params as any).created_at_range
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理空值
|
// 清理空值
|
||||||
Object.keys(params).forEach((key) => {
|
Object.keys(params).forEach((key) => {
|
||||||
if (params[key] === '' || params[key] === undefined) {
|
if (params[key] === '' || params[key] === undefined) {
|
||||||
@@ -327,6 +382,14 @@
|
|||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
|
// 处理日期范围
|
||||||
|
if (formFilters.dateRange && Array.isArray(formFilters.dateRange)) {
|
||||||
|
formFilters.created_at_start = formFilters.dateRange[0] || ''
|
||||||
|
formFilters.created_at_end = formFilters.dateRange[1] || ''
|
||||||
|
} else {
|
||||||
|
formFilters.created_at_start = ''
|
||||||
|
formFilters.created_at_end = ''
|
||||||
|
}
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
getTableData()
|
getTableData()
|
||||||
}
|
}
|
||||||
@@ -362,40 +425,10 @@
|
|||||||
ElMessage.warning('您没有查看详情的权限')
|
ElMessage.warning('您没有查看详情的权限')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右键菜单项配置
|
|
||||||
const contextMenuItems = computed((): MenuItemType[] => {
|
|
||||||
const items: MenuItemType[] = []
|
|
||||||
|
|
||||||
return items
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理表格行右键菜单
|
|
||||||
const handleRowContextMenu = (row: AssetAllocationRecord, column: any, event: MouseEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
currentRow.value = row
|
|
||||||
contextMenuRef.value?.show(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理右键菜单选择
|
|
||||||
const handleContextMenuSelect = (item: MenuItemType) => {
|
|
||||||
if (!currentRow.value) return
|
|
||||||
|
|
||||||
switch (
|
|
||||||
item.key
|
|
||||||
// No cases available
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.asset-allocation-records-page {
|
.asset-allocation-records-page {
|
||||||
// Allocation records page styles
|
// Allocation records page styles
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table__row.table-row-with-context-menu) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { AuthorizationService } from '@/api/modules'
|
import { AuthorizationService, EnterpriseService } from '@/api/modules'
|
||||||
import { ElMessage, ElTag } from 'element-plus'
|
import { ElMessage, ElTag } from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
@@ -125,6 +125,28 @@
|
|||||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||||
const currentRow = ref<AuthorizationItem | null>(null)
|
const currentRow = ref<AuthorizationItem | null>(null)
|
||||||
|
|
||||||
|
// 企业搜索选项
|
||||||
|
const enterpriseOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
// 搜索企业
|
||||||
|
const searchEnterprises = async (query: string) => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.enterprise_name = query
|
||||||
|
}
|
||||||
|
const res = await EnterpriseService.getEnterprises(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
enterpriseOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索企业失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 搜索表单初始值
|
// 搜索表单初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
enterprise_id: undefined as number | undefined,
|
enterprise_id: undefined as number | undefined,
|
||||||
@@ -165,6 +187,23 @@
|
|||||||
placeholder: '请输入ICCID'
|
placeholder: '请输入ICCID'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '企业名称',
|
||||||
|
prop: 'enterprise_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: searchEnterprises,
|
||||||
|
placeholder: '请选择或搜索企业'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
enterpriseOptions.value.map((e) => ({
|
||||||
|
label: e.enterprise_name,
|
||||||
|
value: e.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '授权人类型',
|
label: '授权人类型',
|
||||||
prop: 'authorizer_type',
|
prop: 'authorizer_type',
|
||||||
@@ -309,6 +348,7 @@
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
|
searchEnterprises('')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取授权记录列表
|
// 获取授权记录列表
|
||||||
|
|||||||
@@ -44,8 +44,7 @@
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const detailData = ref<AgentRecharge | null>(null)
|
const detailData = ref<AgentRecharge | null>(null)
|
||||||
|
|
||||||
const rechargeId = computed(() => Number(route.params.id))
|
const pageTitle = computed(() => `充值订单详情`)
|
||||||
const pageTitle = computed(() => `充值订单详情 #${rechargeId.value}`)
|
|
||||||
|
|
||||||
// 格式化货币 - 将分转换为元
|
// 格式化货币 - 将分转换为元
|
||||||
const formatCurrency = (amount: number): string => {
|
const formatCurrency = (amount: number): string => {
|
||||||
@@ -159,7 +158,7 @@
|
|||||||
const loadDetailData = async () => {
|
const loadDetailData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await AgentRechargeService.getAgentRechargeById(rechargeId.value)
|
const res = await AgentRechargeService.getAgentRechargeById(Number(route.params.id))
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
detailData.value = res.data
|
detailData.value = res.data
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
v-model:filter="searchForm"
|
v-model:filter="searchForm"
|
||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
:show-expand="false"
|
:show-expand="false"
|
||||||
|
label-width="100"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></ArtSearchBar>
|
></ArtSearchBar>
|
||||||
@@ -214,6 +215,7 @@
|
|||||||
const initialSearchState: AgentRechargeQueryParams = {
|
const initialSearchState: AgentRechargeQueryParams = {
|
||||||
shop_id: undefined,
|
shop_id: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
|
dateRange: [],
|
||||||
start_date: '',
|
start_date: '',
|
||||||
end_date: ''
|
end_date: ''
|
||||||
}
|
}
|
||||||
@@ -288,11 +290,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '创建时间',
|
label: '开始至结束',
|
||||||
prop: 'dateRange',
|
prop: 'dateRange',
|
||||||
type: 'daterange',
|
type: 'date',
|
||||||
config: {
|
config: {
|
||||||
clearable: true,
|
type: 'daterange',
|
||||||
|
rangeSeparator: '至',
|
||||||
startPlaceholder: '开始日期',
|
startPlaceholder: '开始日期',
|
||||||
endPlaceholder: '结束日期',
|
endPlaceholder: '结束日期',
|
||||||
valueFormat: 'YYYY-MM-DD'
|
valueFormat: 'YYYY-MM-DD'
|
||||||
@@ -414,7 +417,7 @@
|
|||||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||||
onClick: (e: MouseEvent) => {
|
onClick: (e: MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
handleViewDetail(row)
|
handleNameClick(row)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
row.recharge_no
|
row.recharge_no
|
||||||
@@ -733,6 +736,15 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理名称点击
|
||||||
|
const handleNameClick = (row: AgentRecharge) => {
|
||||||
|
if (hasAuth('agent_recharge:detail_page')) {
|
||||||
|
handleViewDetail(row)
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('您没有查看详情的权限')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情
|
||||||
const handleViewDetail = (row: AgentRecharge) => {
|
const handleViewDetail = (row: AgentRecharge) => {
|
||||||
router.push({
|
router.push({
|
||||||
|
|||||||
@@ -1,77 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="refund-detail-page">
|
<div class="refund-detail-page">
|
||||||
<ElCard shadow="never" v-loading="loading">
|
<ElCard shadow="never">
|
||||||
<template #header>
|
<!-- 页面头部 -->
|
||||||
<div class="card-header">
|
<div class="detail-header">
|
||||||
<span>退款详情</span>
|
<ElButton @click="handleBack">
|
||||||
<ElButton @click="handleGoBack">返回</ElButton>
|
<template #icon>
|
||||||
</div>
|
<ElIcon><ArrowLeft /></ElIcon>
|
||||||
</template>
|
</template>
|
||||||
|
返回
|
||||||
|
</ElButton>
|
||||||
|
<h2 class="detail-title">{{ pageTitle }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ElDescriptions :column="2" border v-if="refund">
|
<!-- 详情内容 -->
|
||||||
<ElDescriptionsItem label="退款单号">{{ refund.refund_no }}</ElDescriptionsItem>
|
<DetailPage v-if="refund" :sections="detailSections" :data="refund" />
|
||||||
<ElDescriptionsItem label="状态">
|
|
||||||
<ElTag :type="getStatusType(refund.status)">{{ getStatusText(refund.status) }}</ElTag>
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="申请退款金额">
|
<!-- 加载中 -->
|
||||||
{{ formatCurrency(refund.requested_refund_amount) }}
|
<div v-if="loading" class="loading-container">
|
||||||
</ElDescriptionsItem>
|
<ElIcon class="is-loading"><Loading /></ElIcon>
|
||||||
|
<span>加载中...</span>
|
||||||
<ElDescriptionsItem label="实际退款金额">
|
|
||||||
{{ refund.approved_refund_amount ? formatCurrency(refund.approved_refund_amount) : '-' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="实收金额">
|
|
||||||
{{ formatCurrency(refund.actual_received_amount) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="资产重置">
|
|
||||||
<ElTag :type="refund.asset_reset ? 'success' : 'info'">
|
|
||||||
{{ refund.asset_reset ? '是' : '否' }}
|
|
||||||
</ElTag>
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="佣金扣除">
|
|
||||||
<ElTag :type="refund.commission_deducted ? 'success' : 'info'">
|
|
||||||
{{ refund.commission_deducted ? '是' : '否' }}
|
|
||||||
</ElTag>
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="退款原因">
|
|
||||||
{{ refund.refund_reason || '-' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="拒绝原因">
|
|
||||||
{{ refund.reject_reason || '-' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="审批时间">
|
|
||||||
{{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="审批备注">
|
|
||||||
{{ refund.remark || '-' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="创建时间">
|
|
||||||
{{ formatDateTime(refund.created_at) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
|
|
||||||
<ElDescriptionsItem label="更新时间">
|
|
||||||
{{ formatDateTime(refund.updated_at) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
</ElDescriptions>
|
|
||||||
|
|
||||||
<div class="action-buttons" v-if="refund">
|
|
||||||
<!-- 待审批状态显示审批操作 -->
|
|
||||||
<template v-if="refund.status === 1">
|
|
||||||
<ElButton type="primary" @click="handleShowApprove">审批通过</ElButton>
|
|
||||||
<ElButton type="danger" @click="handleShowReject">审批拒绝</ElButton>
|
|
||||||
<ElButton type="warning" @click="handleShowReturn">退回</ElButton>
|
|
||||||
</template>
|
|
||||||
<!-- 已拒绝或已退回状态显示重新提交 -->
|
|
||||||
<template v-if="refund.status === 3 || refund.status === 4">
|
|
||||||
<ElButton type="primary" @click="handleShowResubmit">重新提交</ElButton>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
|
|
||||||
@@ -247,11 +194,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, computed, h } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElCard, ElButton, ElIcon, ElMessage, ElTag } from 'element-plus'
|
||||||
|
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||||
|
import DetailPage from '@/components/common/DetailPage.vue'
|
||||||
|
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||||
import { RefundService } from '@/api/modules'
|
import { RefundService } from '@/api/modules'
|
||||||
import { ElMessage, ElTag } from 'element-plus'
|
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
|
||||||
import type {
|
import type {
|
||||||
Refund,
|
Refund,
|
||||||
RefundStatus,
|
RefundStatus,
|
||||||
@@ -261,38 +210,39 @@
|
|||||||
ResubmitRefundRequest
|
ResubmitRefundRequest
|
||||||
} from '@/types/api'
|
} from '@/types/api'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
|
import { RoutesAlias } from '@/router/routesAlias'
|
||||||
|
|
||||||
defineOptions({ name: 'RefundDetail' })
|
defineOptions({ name: 'RefundDetail' })
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const approveLoading = ref(false)
|
|
||||||
const rejectLoading = ref(false)
|
|
||||||
const returnLoading = ref(false)
|
|
||||||
const resubmitLoading = ref(false)
|
|
||||||
const refund = ref<Refund | null>(null)
|
const refund = ref<Refund | null>(null)
|
||||||
|
|
||||||
|
const pageTitle = computed(() => `退款详情`)
|
||||||
|
|
||||||
const approveDialogVisible = ref(false)
|
const approveDialogVisible = ref(false)
|
||||||
const rejectDialogVisible = ref(false)
|
const rejectDialogVisible = ref(false)
|
||||||
const returnDialogVisible = ref(false)
|
const returnDialogVisible = ref(false)
|
||||||
const resubmitDialogVisible = ref(false)
|
const resubmitDialogVisible = ref(false)
|
||||||
|
|
||||||
const approveFormRef = ref<FormInstance>()
|
const approveLoading = ref(false)
|
||||||
const rejectFormRef = ref<FormInstance>()
|
const rejectLoading = ref(false)
|
||||||
const returnFormRef = ref<FormInstance>()
|
const returnLoading = ref(false)
|
||||||
const resubmitFormRef = ref<FormInstance>()
|
const resubmitLoading = ref(false)
|
||||||
|
|
||||||
const approveRules = reactive<FormRules>({})
|
const approveFormRef = ref()
|
||||||
|
const rejectFormRef = ref()
|
||||||
|
const returnFormRef = ref()
|
||||||
|
const resubmitFormRef = ref()
|
||||||
|
|
||||||
const rejectRules = reactive<FormRules>({
|
const approveRules = ref()
|
||||||
|
const rejectRules = ref({
|
||||||
reject_reason: [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
|
reject_reason: [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
|
||||||
})
|
})
|
||||||
|
const returnRules = ref()
|
||||||
const returnRules = reactive<FormRules>({})
|
const resubmitRules = ref()
|
||||||
|
|
||||||
const resubmitRules = reactive<FormRules>({})
|
|
||||||
|
|
||||||
const approveForm = reactive<ApproveRefundRequest>({
|
const approveForm = reactive<ApproveRefundRequest>({
|
||||||
approved_refund_amount: undefined,
|
approved_refund_amount: undefined,
|
||||||
@@ -317,23 +267,20 @@
|
|||||||
refund_reason: ''
|
refund_reason: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 格式化货币 - 将分转换为元
|
|
||||||
const formatCurrency = (amount: number): string => {
|
const formatCurrency = (amount: number): string => {
|
||||||
return `¥${(amount / 100).toFixed(2)}`
|
return `¥${(amount / 100).toFixed(2)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态标签类型
|
|
||||||
const getStatusType = (status: RefundStatus): 'warning' | 'success' | 'danger' | 'info' => {
|
const getStatusType = (status: RefundStatus): 'warning' | 'success' | 'danger' | 'info' => {
|
||||||
const statusMap: Record<RefundStatus, 'warning' | 'success' | 'danger' | 'info'> = {
|
const statusMap: Record<RefundStatus, 'warning' | 'success' | 'danger' | 'info'> = {
|
||||||
1: 'warning', // 待审批
|
1: 'warning',
|
||||||
2: 'success', // 已通过
|
2: 'success',
|
||||||
3: 'danger', // 已拒绝
|
3: 'danger',
|
||||||
4: 'info' // 已退回
|
4: 'info'
|
||||||
}
|
}
|
||||||
return statusMap[status] || 'info'
|
return statusMap[status] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态文本
|
|
||||||
const getStatusText = (status: RefundStatus): string => {
|
const getStatusText = (status: RefundStatus): string => {
|
||||||
const statusMap: Record<RefundStatus, string> = {
|
const statusMap: Record<RefundStatus, string> = {
|
||||||
1: '待审批',
|
1: '待审批',
|
||||||
@@ -344,14 +291,94 @@
|
|||||||
return statusMap[status] || '-'
|
return statusMap[status] || '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const detailSections = computed((): DetailSection[] => [
|
||||||
const id = route.params.id as string
|
{
|
||||||
if (id) {
|
title: '退款信息',
|
||||||
fetchRefundDetail(Number(id))
|
fields: [
|
||||||
|
{ label: '退款单号', prop: 'refund_no' },
|
||||||
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
|
{ label: '订单号', prop: 'order_no' },
|
||||||
|
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||||
|
{ label: '资产类型', prop: 'asset_type' },
|
||||||
|
{
|
||||||
|
label: '申请退款金额',
|
||||||
|
formatter: (_, data) => formatCurrency(data.requested_refund_amount)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '实际退款金额',
|
||||||
|
formatter: (_, data) =>
|
||||||
|
data.approved_refund_amount ? formatCurrency(data.approved_refund_amount) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '实收金额',
|
||||||
|
formatter: (_, data) => formatCurrency(data.actual_received_amount)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
render: (data) =>
|
||||||
|
h(ElTag, { type: getStatusType(data.status) }, () => getStatusText(data.status))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '资产重置',
|
||||||
|
render: (data) =>
|
||||||
|
h(ElTag, { type: data.asset_reset ? 'success' : 'info' }, () =>
|
||||||
|
data.asset_reset ? '是' : '否'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '佣金扣除',
|
||||||
|
render: (data) =>
|
||||||
|
h(ElTag, { type: data.commission_deducted ? 'success' : 'info' }, () =>
|
||||||
|
data.commission_deducted ? '是' : '否'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '退款原因',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: '退款原因',
|
||||||
|
prop: 'refund_reason',
|
||||||
|
formatter: (value) => value || '-',
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '拒绝原因',
|
||||||
|
prop: 'reject_reason',
|
||||||
|
formatter: (value) => value || '-',
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '审批备注',
|
||||||
|
prop: 'remark',
|
||||||
|
formatter: (value) => value || '-',
|
||||||
|
fullWidth: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时间信息',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: '创建时间',
|
||||||
|
prop: 'created_at',
|
||||||
|
formatter: (value) => formatDateTime(value)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '审批时间',
|
||||||
|
prop: 'processed_at',
|
||||||
|
formatter: (value) => (value ? formatDateTime(value) : '-')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '更新时间',
|
||||||
|
prop: 'updated_at',
|
||||||
|
formatter: (value) => formatDateTime(value)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
})
|
])
|
||||||
|
|
||||||
// 获取退款详情
|
|
||||||
const fetchRefundDetail = async (id: number) => {
|
const fetchRefundDetail = async (id: number) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -366,24 +393,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回
|
const handleBack = () => {
|
||||||
const handleGoBack = () => {
|
router.push(RoutesAlias.RefundManagement)
|
||||||
router.back()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示审批通过对话框
|
onMounted(() => {
|
||||||
const handleShowApprove = () => {
|
const id = route.params.id as string
|
||||||
approveDialogVisible.value = true
|
if (id) {
|
||||||
}
|
fetchRefundDetail(Number(id))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 审批通过对话框关闭后的清理
|
|
||||||
const handleApproveDialogClosed = () => {
|
const handleApproveDialogClosed = () => {
|
||||||
approveFormRef.value?.resetFields()
|
approveFormRef.value?.resetFields()
|
||||||
approveForm.approved_refund_amount = undefined
|
approveForm.approved_refund_amount = undefined
|
||||||
approveForm.remark = ''
|
approveForm.remark = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审批通过
|
const handleRejectDialogClosed = () => {
|
||||||
|
rejectFormRef.value?.resetFields()
|
||||||
|
rejectForm.reject_reason = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReturnDialogClosed = () => {
|
||||||
|
returnFormRef.value?.resetFields()
|
||||||
|
returnForm.remark = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResubmitDialogClosed = () => {
|
||||||
|
resubmitFormRef.value?.resetFields()
|
||||||
|
resubmitForm.requested_refund_amount = undefined
|
||||||
|
resubmitForm.actual_received_amount = undefined
|
||||||
|
resubmitForm.refund_reason = ''
|
||||||
|
}
|
||||||
|
|
||||||
const handleApproveRefund = async () => {
|
const handleApproveRefund = async () => {
|
||||||
if (!approveFormRef.value || !refund.value) return
|
if (!approveFormRef.value || !refund.value) return
|
||||||
|
|
||||||
@@ -411,18 +454,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示审批拒绝对话框
|
|
||||||
const handleShowReject = () => {
|
|
||||||
rejectDialogVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 审批拒绝对话框关闭后的清理
|
|
||||||
const handleRejectDialogClosed = () => {
|
|
||||||
rejectFormRef.value?.resetFields()
|
|
||||||
rejectForm.reject_reason = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 审批拒绝
|
|
||||||
const handleRejectRefund = async () => {
|
const handleRejectRefund = async () => {
|
||||||
if (!rejectFormRef.value || !refund.value) return
|
if (!rejectFormRef.value || !refund.value) return
|
||||||
|
|
||||||
@@ -443,18 +474,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示退回对话框
|
|
||||||
const handleShowReturn = () => {
|
|
||||||
returnDialogVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 退回对话框关闭后的清理
|
|
||||||
const handleReturnDialogClosed = () => {
|
|
||||||
returnFormRef.value?.resetFields()
|
|
||||||
returnForm.remark = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 退回
|
|
||||||
const handleReturnRefund = async () => {
|
const handleReturnRefund = async () => {
|
||||||
if (!returnFormRef.value || !refund.value) return
|
if (!returnFormRef.value || !refund.value) return
|
||||||
|
|
||||||
@@ -475,25 +494,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示重新提交对话框
|
|
||||||
const handleShowResubmit = () => {
|
|
||||||
if (!refund.value) return
|
|
||||||
// 预填充原有数据
|
|
||||||
resubmitForm.requested_refund_amount = refund.value.requested_refund_amount / 100
|
|
||||||
resubmitForm.actual_received_amount = refund.value.actual_received_amount / 100
|
|
||||||
resubmitForm.refund_reason = refund.value.refund_reason
|
|
||||||
resubmitDialogVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新提交对话框关闭后的清理
|
|
||||||
const handleResubmitDialogClosed = () => {
|
|
||||||
resubmitFormRef.value?.resetFields()
|
|
||||||
resubmitForm.requested_refund_amount = undefined
|
|
||||||
resubmitForm.actual_received_amount = undefined
|
|
||||||
resubmitForm.refund_reason = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新提交
|
|
||||||
const handleResubmitRefund = async () => {
|
const handleResubmitRefund = async () => {
|
||||||
if (!resubmitFormRef.value || !refund.value) return
|
if (!resubmitFormRef.value || !refund.value) return
|
||||||
|
|
||||||
@@ -529,17 +529,32 @@
|
|||||||
.refund-detail-page {
|
.refund-detail-page {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
.card-header {
|
.detail-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.detail-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.loading-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
flex-direction: column;
|
||||||
justify-content: flex-end;
|
gap: 12px;
|
||||||
margin-top: 20px;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
:loading="orderSearchLoading"
|
:loading="orderSearchLoading"
|
||||||
placeholder="请输入订单号搜索"
|
placeholder="请输入订单号搜索"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@focus="handleOrderSelectFocus"
|
|
||||||
@change="handleOrderSelectChange"
|
@change="handleOrderSelectChange"
|
||||||
>
|
>
|
||||||
<ElOption
|
<ElOption
|
||||||
@@ -84,11 +83,7 @@
|
|||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="实收金额">
|
<ElFormItem label="实收金额">
|
||||||
<ElInput
|
<ElInput :model-value="selectedOrderActualPaid" disabled style="width: 100%" />
|
||||||
:model-value="selectedOrderActualPaid"
|
|
||||||
disabled
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="退款原因">
|
<ElFormItem label="退款原因">
|
||||||
<ElInput
|
<ElInput
|
||||||
@@ -315,7 +310,6 @@
|
|||||||
defineOptions({ name: 'RefundList' })
|
defineOptions({ name: 'RefundList' })
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore()
|
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -364,7 +358,26 @@
|
|||||||
})),
|
})),
|
||||||
config: {
|
config: {
|
||||||
clearable: true,
|
clearable: true,
|
||||||
filterable: true
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchShops(query)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '订单号',
|
||||||
|
prop: 'order_id',
|
||||||
|
type: 'select',
|
||||||
|
placeholder: '请选择订单号',
|
||||||
|
options: () =>
|
||||||
|
orderOptions.value.map((order) => ({
|
||||||
|
label: `${order.order_no}`,
|
||||||
|
value: order.id
|
||||||
|
})),
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchOrdersForFilter(query)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -381,21 +394,6 @@
|
|||||||
config: {
|
config: {
|
||||||
clearable: true
|
clearable: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '订单',
|
|
||||||
prop: 'order_id',
|
|
||||||
type: 'select',
|
|
||||||
placeholder: '请选择订单',
|
|
||||||
options: () =>
|
|
||||||
orderOptions.value.map((order) => ({
|
|
||||||
label: `订单号: ${order.order_no} (ID: ${order.id})`,
|
|
||||||
value: order.id
|
|
||||||
})),
|
|
||||||
config: {
|
|
||||||
clearable: true,
|
|
||||||
filterable: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -409,8 +407,10 @@
|
|||||||
// 列配置
|
// 列配置
|
||||||
const columnOptions = [
|
const columnOptions = [
|
||||||
{ label: '退款单号', prop: 'refund_no' },
|
{ label: '退款单号', prop: 'refund_no' },
|
||||||
{ label: '订单ID', prop: 'order_id' },
|
{ label: '店铺名称', prop: 'shop_name' },
|
||||||
{ label: '店铺ID', prop: 'shop_id' },
|
{ label: '订单号', prop: 'order_no' },
|
||||||
|
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||||
|
{ label: '资产类型', prop: 'asset_type' },
|
||||||
{ label: '申请退款金额', prop: 'requested_refund_amount' },
|
{ label: '申请退款金额', prop: 'requested_refund_amount' },
|
||||||
{ label: '实际退款金额', prop: 'approved_refund_amount' },
|
{ label: '实际退款金额', prop: 'approved_refund_amount' },
|
||||||
{ label: '实收金额', prop: 'actual_received_amount' },
|
{ label: '实收金额', prop: 'actual_received_amount' },
|
||||||
@@ -523,7 +523,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'refund_no',
|
prop: 'refund_no',
|
||||||
label: '退款单号',
|
label: '退款单号',
|
||||||
minWidth: 240,
|
minWidth: 210,
|
||||||
formatter: (row: Refund) => {
|
formatter: (row: Refund) => {
|
||||||
return h(
|
return h(
|
||||||
'span',
|
'span',
|
||||||
@@ -531,7 +531,7 @@
|
|||||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||||
onClick: (e: MouseEvent) => {
|
onClick: (e: MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
handleViewDetail(row)
|
handleNameClick(row)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
row.refund_no
|
row.refund_no
|
||||||
@@ -539,14 +539,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'order_id',
|
prop: 'shop_name',
|
||||||
label: '订单ID',
|
label: '店铺名称',
|
||||||
width: 120
|
width: 160
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'shop_id',
|
prop: 'order_no',
|
||||||
label: '店铺ID',
|
label: '订单号',
|
||||||
width: 120
|
width: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'asset_identifier',
|
||||||
|
label: '资产标识符',
|
||||||
|
width: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'asset_type',
|
||||||
|
label: '资产类型',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: Refund) =>
|
||||||
|
row.asset_type === 'device'
|
||||||
|
? '设备'
|
||||||
|
: row.asset_type === 'iot_card'
|
||||||
|
? 'IoT卡'
|
||||||
|
: row.asset_type || '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'requested_refund_amount',
|
prop: 'requested_refund_amount',
|
||||||
@@ -629,39 +645,45 @@
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTableData()
|
getTableData()
|
||||||
loadShops()
|
searchShops('')
|
||||||
loadOrders()
|
searchOrdersForFilter('')
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载店铺列表
|
// 搜索店铺(用于搜索表单)
|
||||||
const loadShops = async () => {
|
const searchShops = async (query: string) => {
|
||||||
try {
|
try {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 9999
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.shop_name = query
|
||||||
}
|
}
|
||||||
const res = await ShopService.getShops(params)
|
const res = await ShopService.getShops(params)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
shopOptions.value = res.data.items || []
|
shopOptions.value = res.data.items || []
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Load shops failed:', error)
|
console.error('Search shops failed:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载订单列表
|
// 搜索订单(用于搜索表单)
|
||||||
const loadOrders = async () => {
|
const searchOrdersForFilter = async (query: string) => {
|
||||||
try {
|
try {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 9999
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.order_no = query
|
||||||
}
|
}
|
||||||
const res = await OrderService.getOrders(params)
|
const res = await OrderService.getOrders(params)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
orderOptions.value = res.data.items || []
|
orderOptions.value = res.data.items || []
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Load orders failed:', error)
|
console.error('Search orders failed:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,14 +781,6 @@
|
|||||||
createDialogVisible.value = true
|
createDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订单选择框获取焦点时加载默认数据
|
|
||||||
const handleOrderSelectFocus = () => {
|
|
||||||
// 如果还没有数据,则加载默认的20条
|
|
||||||
if (orderSearchOptions.value.length === 0) {
|
|
||||||
searchOrders('')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对话框关闭后的清理
|
// 对话框关闭后的清理
|
||||||
const handleCreateDialogClosed = () => {
|
const handleCreateDialogClosed = () => {
|
||||||
createFormRef.value?.resetFields()
|
createFormRef.value?.resetFields()
|
||||||
@@ -965,6 +979,15 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理名称点击
|
||||||
|
const handleNameClick = (row: Refund) => {
|
||||||
|
if (hasAuth('refund:detail')) {
|
||||||
|
handleViewDetail(row)
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('您没有查看详情的权限')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情
|
||||||
const handleViewDetail = (row: Refund) => {
|
const handleViewDetail = (row: Refund) => {
|
||||||
router.push({
|
router.push({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<ArtSearchBar
|
<ArtSearchBar
|
||||||
v-model:filter="searchForm"
|
v-model:filter="searchForm"
|
||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
:show-expand="false"
|
show-expand
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
label-width="100"
|
label-width="100"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
@@ -356,8 +356,11 @@
|
|||||||
order_no: '',
|
order_no: '',
|
||||||
buyer_phone: '',
|
buyer_phone: '',
|
||||||
payment_status: undefined,
|
payment_status: undefined,
|
||||||
|
payment_method: undefined,
|
||||||
order_type: undefined,
|
order_type: undefined,
|
||||||
purchase_role: undefined,
|
purchase_role: undefined,
|
||||||
|
identifier: '',
|
||||||
|
is_expired: undefined,
|
||||||
start_time: '',
|
start_time: '',
|
||||||
end_time: ''
|
end_time: ''
|
||||||
}
|
}
|
||||||
@@ -378,6 +381,28 @@
|
|||||||
|
|
||||||
// 搜索表单配置
|
// 搜索表单配置
|
||||||
const searchFormItems: SearchFormItem[] = [
|
const searchFormItems: SearchFormItem[] = [
|
||||||
|
{
|
||||||
|
label: '订单号',
|
||||||
|
prop: 'order_no',
|
||||||
|
type: 'input',
|
||||||
|
placeholder: '请输入订单号',
|
||||||
|
config: {
|
||||||
|
maxlength: 50,
|
||||||
|
clearable: true
|
||||||
|
},
|
||||||
|
labelWidth: '100'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '资产标识符',
|
||||||
|
prop: 'identifier',
|
||||||
|
type: 'input',
|
||||||
|
placeholder: 'ICCID 或 VirtualNo',
|
||||||
|
config: {
|
||||||
|
maxlength: 30,
|
||||||
|
clearable: true
|
||||||
|
},
|
||||||
|
labelWidth: '100'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '买家手机号',
|
label: '买家手机号',
|
||||||
prop: 'buyer_phone',
|
prop: 'buyer_phone',
|
||||||
@@ -389,6 +414,21 @@
|
|||||||
},
|
},
|
||||||
labelWidth: '100'
|
labelWidth: '100'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '支付方式',
|
||||||
|
prop: 'payment_method',
|
||||||
|
type: 'select',
|
||||||
|
placeholder: '请选择支付方式',
|
||||||
|
options: [
|
||||||
|
{ label: '钱包支付', value: 'wallet' },
|
||||||
|
{ label: '微信支付', value: 'wechat' },
|
||||||
|
{ label: '支付宝支付', value: 'alipay' },
|
||||||
|
{ label: '线下支付', value: 'offline' }
|
||||||
|
],
|
||||||
|
config: {
|
||||||
|
clearable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '支付状态',
|
label: '支付状态',
|
||||||
prop: 'payment_status',
|
prop: 'payment_status',
|
||||||
@@ -404,6 +444,19 @@
|
|||||||
clearable: true
|
clearable: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '是否过期',
|
||||||
|
prop: 'is_expired',
|
||||||
|
type: 'select',
|
||||||
|
placeholder: '请选择',
|
||||||
|
options: [
|
||||||
|
{ label: '已过期', value: true },
|
||||||
|
{ label: '未过期', value: false }
|
||||||
|
],
|
||||||
|
config: {
|
||||||
|
clearable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '订单类型',
|
label: '订单类型',
|
||||||
prop: 'order_type',
|
prop: 'order_type',
|
||||||
@@ -433,14 +486,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '创建时间',
|
label: '开始至结束',
|
||||||
prop: 'dateRange',
|
prop: 'dateRange',
|
||||||
type: 'daterange',
|
type: 'date',
|
||||||
config: {
|
config: {
|
||||||
clearable: true,
|
type: 'daterange',
|
||||||
startPlaceholder: '开始时间',
|
rangeSeparator: '至',
|
||||||
endPlaceholder: '结束时间',
|
startPlaceholder: '开始日期',
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
endPlaceholder: '结束日期',
|
||||||
|
valueFormat: 'YYYY-MM-DD'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -459,15 +513,16 @@
|
|||||||
{ label: '买家昵称', prop: 'buyer_nickname' },
|
{ label: '买家昵称', prop: 'buyer_nickname' },
|
||||||
{ label: '订单类型', prop: 'order_type' },
|
{ label: '订单类型', prop: 'order_type' },
|
||||||
{ label: '买家类型', prop: 'buyer_type' },
|
{ label: '买家类型', prop: 'buyer_type' },
|
||||||
|
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||||
{ label: '订单渠道', prop: 'purchase_role' },
|
{ label: '订单渠道', prop: 'purchase_role' },
|
||||||
{ label: '购买备注', prop: 'purchase_remark' },
|
{ label: '购买备注', prop: 'purchase_remark' },
|
||||||
{ label: '操作者', prop: 'operator_name' },
|
{ label: '操作者', prop: 'operator_name' },
|
||||||
{ label: '支付状态', prop: 'payment_status' },
|
{ label: '支付状态', prop: 'payment_status' },
|
||||||
|
{ label: '佣金状态', prop: 'commission_status_name' },
|
||||||
{ label: '订单金额', prop: 'total_amount' },
|
{ label: '订单金额', prop: 'total_amount' },
|
||||||
{ label: '实付金额', prop: 'actual_paid_amount' },
|
{ label: '实付金额', prop: 'actual_paid_amount' },
|
||||||
{ label: '支付方式', prop: 'payment_method' },
|
{ label: '支付方式', prop: 'payment_method' },
|
||||||
{ label: '支付时间', prop: 'paid_at' },
|
{ label: '支付时间', prop: 'paid_at' }
|
||||||
{ label: '创建时间', prop: 'created_at' }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const createFormRef = ref<FormInstance>()
|
const createFormRef = ref<FormInstance>()
|
||||||
@@ -794,6 +849,12 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'asset_identifier',
|
||||||
|
label: '资产标识符',
|
||||||
|
width: 180,
|
||||||
|
formatter: (row: Order) => row.asset_identifier || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'purchase_role',
|
prop: 'purchase_role',
|
||||||
label: '订单渠道',
|
label: '订单渠道',
|
||||||
@@ -838,6 +899,12 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'commission_status_name',
|
||||||
|
label: '佣金状态',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: Order) => row.commission_status_name || '-'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'total_amount',
|
prop: 'total_amount',
|
||||||
label: '订单金额',
|
label: '订单金额',
|
||||||
@@ -864,12 +931,6 @@
|
|||||||
label: '支付时间',
|
label: '支付时间',
|
||||||
width: 180,
|
width: 180,
|
||||||
formatter: (row: Order) => (row.paid_at ? formatDateTime(row.paid_at) : '-')
|
formatter: (row: Order) => (row.paid_at ? formatDateTime(row.paid_at) : '-')
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'created_at',
|
|
||||||
label: '创建时间',
|
|
||||||
width: 180,
|
|
||||||
formatter: (row: Order) => formatDateTime(row.created_at)
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -896,8 +957,11 @@
|
|||||||
order_no: searchForm.order_no || undefined,
|
order_no: searchForm.order_no || undefined,
|
||||||
buyer_phone: searchForm.buyer_phone || undefined,
|
buyer_phone: searchForm.buyer_phone || undefined,
|
||||||
payment_status: searchForm.payment_status,
|
payment_status: searchForm.payment_status,
|
||||||
|
payment_method: searchForm.payment_method,
|
||||||
order_type: searchForm.order_type,
|
order_type: searchForm.order_type,
|
||||||
purchase_role: searchForm.purchase_role,
|
purchase_role: searchForm.purchase_role,
|
||||||
|
identifier: searchForm.identifier || undefined,
|
||||||
|
is_expired: searchForm.is_expired,
|
||||||
start_time: searchForm.start_time || undefined,
|
start_time: searchForm.start_time || undefined,
|
||||||
end_time: searchForm.end_time || undefined
|
end_time: searchForm.end_time || undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -511,15 +511,46 @@
|
|||||||
{ label: '套餐名称', prop: 'package_name' },
|
{ label: '套餐名称', prop: 'package_name' },
|
||||||
{ label: '所属系列', prop: 'series_name' },
|
{ label: '所属系列', prop: 'series_name' },
|
||||||
{ label: '套餐类型', prop: 'package_type' },
|
{ label: '套餐类型', prop: 'package_type' },
|
||||||
|
{ label: '套餐周期类型', prop: 'calendar_type' },
|
||||||
|
{ label: '有效期', prop: 'duration_months' },
|
||||||
{ label: '真流量', prop: 'real_data_mb' },
|
{ label: '真流量', prop: 'real_data_mb' },
|
||||||
{ label: '虚流量', prop: 'virtual_data_mb' },
|
{ label: '虚流量', prop: 'virtual_data_mb' },
|
||||||
{ label: '虚流量比例', prop: 'virtual_ratio' },
|
{ label: '虚流量比例', prop: 'virtual_ratio' },
|
||||||
{ label: '有效期', prop: 'duration_months' },
|
{
|
||||||
|
label: '启用虚流量',
|
||||||
|
prop: 'enable_virtual_data',
|
||||||
|
formatter: (row: any) => (row.enable_virtual_data ? '是' : '否')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '流量重置周期',
|
||||||
|
prop: 'data_reset_cycle',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
daily: '每日',
|
||||||
|
weekly: '每周',
|
||||||
|
monthly: '每月',
|
||||||
|
none: '不重置'
|
||||||
|
}
|
||||||
|
return map[row.data_reset_cycle] || row.data_reset_cycle || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '到期时间基准',
|
||||||
|
prop: 'expiry_base',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
from_activation: '实名激活',
|
||||||
|
from_purchase: '购买时'
|
||||||
|
}
|
||||||
|
return map[row.expiry_base] || row.expiry_base || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
{ label: '成本价', prop: 'cost_price' },
|
{ label: '成本价', prop: 'cost_price' },
|
||||||
{ label: '建议售价', prop: 'suggested_retail_price' },
|
{ label: '建议售价', prop: 'suggested_retail_price' },
|
||||||
{ label: '上架状态', prop: 'shelf_status' },
|
{ label: '上架状态', prop: 'shelf_status' },
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '创建时间', prop: 'created_at' }
|
{ label: '创建时间', prop: 'created_at' },
|
||||||
|
{ label: '更新时间', prop: 'updated_at' }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
@@ -729,6 +760,44 @@
|
|||||||
width: 100,
|
width: 100,
|
||||||
formatter: (row: PackageResponse) => `${row.duration_months}月`
|
formatter: (row: PackageResponse) => `${row.duration_months}月`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'calendar_type',
|
||||||
|
label: '套餐周期类型',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: PackageResponse) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
natural_month: '自然月',
|
||||||
|
by_day: '按天'
|
||||||
|
}
|
||||||
|
return map[row.calendar_type] || row.calendar_type || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'data_reset_cycle',
|
||||||
|
label: '流量重置周期',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: PackageResponse) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
daily: '每日',
|
||||||
|
weekly: '每周',
|
||||||
|
monthly: '每月',
|
||||||
|
none: '不重置'
|
||||||
|
}
|
||||||
|
return map[row.data_reset_cycle] || row.data_reset_cycle || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'expiry_base',
|
||||||
|
label: '到期时间基准',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: PackageResponse) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
from_activation: '实名激活',
|
||||||
|
from_purchase: '购买时'
|
||||||
|
}
|
||||||
|
return map[row.expiry_base] || row.expiry_base || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'cost_price',
|
prop: 'cost_price',
|
||||||
label: '成本价',
|
label: '成本价',
|
||||||
@@ -850,8 +919,6 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadSeriesOptions()
|
|
||||||
loadSearchSeriesOptions()
|
|
||||||
getTableData()
|
getTableData()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -460,14 +460,46 @@
|
|||||||
{ label: '系列编码', prop: 'series_code' },
|
{ label: '系列编码', prop: 'series_code' },
|
||||||
{ label: '系列名称', prop: 'series_name' },
|
{ label: '系列名称', prop: 'series_name' },
|
||||||
{ label: '描述', prop: 'description' },
|
{ label: '描述', prop: 'description' },
|
||||||
{ label: '一次性佣金', prop: 'enable_one_time_commission' },
|
{
|
||||||
{ label: '佣金类型', prop: 'commission_type' },
|
label: '一次性佣金',
|
||||||
{ label: '触发阈值', prop: 'commission_threshold' },
|
prop: 'enable_one_time_commission',
|
||||||
{ label: '触发类型', prop: 'trigger_type' },
|
formatter: (row: any) => (row.enable_one_time_commission ? '启用' : '未启用')
|
||||||
{ label: '强充状态', prop: 'enable_force_recharge' },
|
},
|
||||||
{ label: '强充金额', prop: 'force_amount' },
|
{
|
||||||
{ label: '强充计算类型', prop: 'force_calc_type' },
|
label: '佣金类型',
|
||||||
{ label: '时效类型', prop: 'validity_type' },
|
prop: 'commission_type',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.commission_type || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '触发阈值',
|
||||||
|
prop: 'threshold',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.threshold ?? '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '触发类型',
|
||||||
|
prop: 'trigger_type',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.trigger_type || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '强充状态',
|
||||||
|
prop: 'enable_force_recharge',
|
||||||
|
formatter: (row: any) => (row.one_time_commission_config?.enable_force_recharge ? '启用' : '未启用')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '强充金额',
|
||||||
|
prop: 'force_amount',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.force_amount ?? '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '强充计算类型',
|
||||||
|
prop: 'force_calc_type',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.force_calc_type || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '时效类型',
|
||||||
|
prop: 'validity_type',
|
||||||
|
formatter: (row: any) => row.one_time_commission_config?.validity_type || '-'
|
||||||
|
},
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '创建时间', prop: 'created_at' }
|
{ label: '创建时间', prop: 'created_at' }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -459,10 +459,16 @@
|
|||||||
<ElRow :gutter="20">
|
<ElRow :gutter="20">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
<ElFormItem label="启用强制充值">
|
<ElFormItem label="启用强制充值">
|
||||||
<ElSwitch v-model="form.enable_force_recharge" />
|
<ElSwitch
|
||||||
|
v-model="form.enable_force_recharge"
|
||||||
|
:disabled="form.force_recharge_locked && isAgentAccount"
|
||||||
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
<ElCol :span="12" v-if="form.enable_force_recharge">
|
<ElCol
|
||||||
|
:span="12"
|
||||||
|
v-if="form.enable_force_recharge && !(form.force_recharge_locked && isAgentAccount)"
|
||||||
|
>
|
||||||
<ElFormItem label="强充金额(元)" prop="force_recharge_amount">
|
<ElFormItem label="强充金额(元)" prop="force_recharge_amount">
|
||||||
<ElInputNumber
|
<ElInputNumber
|
||||||
v-model="form.force_recharge_amount"
|
v-model="form.force_recharge_amount"
|
||||||
@@ -625,10 +631,13 @@
|
|||||||
<ElRow :gutter="20">
|
<ElRow :gutter="20">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
<ElFormItem label="启用强制充值">
|
<ElFormItem label="启用强制充值">
|
||||||
<ElSwitch v-model="form.enable_force_recharge" />
|
<ElSwitch
|
||||||
|
v-model="form.enable_force_recharge"
|
||||||
|
:disabled="form.force_recharge_locked && isAgentAccount"
|
||||||
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
<ElCol :span="12" v-if="form.enable_force_recharge">
|
<ElCol :span="12" v-if="form.enable_force_recharge && !(form.force_recharge_locked && isAgentAccount)">
|
||||||
<ElFormItem label="强充金额(元)" prop="force_recharge_amount">
|
<ElFormItem label="强充金额(元)" prop="force_recharge_amount">
|
||||||
<ElInputNumber
|
<ElInputNumber
|
||||||
v-model="form.force_recharge_amount"
|
v-model="form.force_recharge_amount"
|
||||||
@@ -783,6 +792,8 @@
|
|||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
import { useAuth } from '@/composables/useAuth'
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import {
|
import {
|
||||||
CommonStatus,
|
CommonStatus,
|
||||||
@@ -797,6 +808,9 @@
|
|||||||
|
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { info: userInfo } = storeToRefs(userStore)
|
||||||
|
const isAgentAccount = computed(() => userInfo.value.user_type === 3)
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -1036,6 +1050,7 @@
|
|||||||
}>, // 梯度配置(包含完整字段,从系列继承)
|
}>, // 梯度配置(包含完整字段,从系列继承)
|
||||||
enable_force_recharge: false,
|
enable_force_recharge: false,
|
||||||
force_recharge_amount: undefined,
|
force_recharge_amount: undefined,
|
||||||
|
force_recharge_locked: false, // 强充是否被套餐系列锁定
|
||||||
series_force_recharge_amount: 0, // 系列强充金额(元)
|
series_force_recharge_amount: 0, // 系列强充金额(元)
|
||||||
packages: [] as Array<{ package_id: number; cost_price: number }> // 套餐配置
|
packages: [] as Array<{ package_id: number; cost_price: number }> // 套餐配置
|
||||||
})
|
})
|
||||||
@@ -1207,6 +1222,27 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: 'force_recharge_locked',
|
||||||
|
label: '强充锁定',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: ShopSeriesGrantResponse) => {
|
||||||
|
return h(
|
||||||
|
ElTag,
|
||||||
|
{ type: row.force_recharge_locked ? 'danger' : 'success', size: 'small' },
|
||||||
|
() => (row.force_recharge_locked ? '是' : '否')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'force_recharge_amount',
|
||||||
|
label: '强充金额',
|
||||||
|
width: 100,
|
||||||
|
formatter: (row: ShopSeriesGrantResponse) => {
|
||||||
|
if (!row.force_recharge_enabled) return '-'
|
||||||
|
return `¥${((row.force_recharge_amount || 0) / 100).toFixed(2)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'package_count',
|
prop: 'package_count',
|
||||||
label: '套餐数量',
|
label: '套餐数量',
|
||||||
@@ -1794,6 +1830,7 @@
|
|||||||
form.force_recharge_amount = detail.force_recharge_amount
|
form.force_recharge_amount = detail.force_recharge_amount
|
||||||
? detail.force_recharge_amount / 100
|
? detail.force_recharge_amount / 100
|
||||||
: undefined
|
: undefined
|
||||||
|
form.force_recharge_locked = detail.force_recharge_locked || false
|
||||||
|
|
||||||
// 设置套餐数据
|
// 设置套餐数据
|
||||||
if (detail.packages && detail.packages.length > 0) {
|
if (detail.packages && detail.packages.length > 0) {
|
||||||
@@ -1842,6 +1879,7 @@
|
|||||||
form.commission_tiers = []
|
form.commission_tiers = []
|
||||||
form.enable_force_recharge = false
|
form.enable_force_recharge = false
|
||||||
form.force_recharge_amount = undefined
|
form.force_recharge_amount = undefined
|
||||||
|
form.force_recharge_locked = false
|
||||||
form.packages = []
|
form.packages = []
|
||||||
selectedPackageIds.value = []
|
selectedPackageIds.value = []
|
||||||
packageOptions.value = [] // 清空套餐选项
|
packageOptions.value = [] // 清空套餐选项
|
||||||
@@ -1874,6 +1912,7 @@
|
|||||||
form.commission_tiers = []
|
form.commission_tiers = []
|
||||||
form.enable_force_recharge = false
|
form.enable_force_recharge = false
|
||||||
form.force_recharge_amount = undefined
|
form.force_recharge_amount = undefined
|
||||||
|
form.force_recharge_locked = false
|
||||||
form.packages = []
|
form.packages = []
|
||||||
selectedPackageIds.value = []
|
selectedPackageIds.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-table
|
<el-table v-loading="loading" :data="tableData" border stripe>
|
||||||
v-loading="loading"
|
|
||||||
:data="tableData"
|
|
||||||
border
|
|
||||||
stripe
|
|
||||||
:row-class-name="getRowClassName"
|
|
||||||
@row-contextmenu="handleRowContextMenu"
|
|
||||||
@cell-mouse-enter="handleCellMouseEnter"
|
|
||||||
@cell-mouse-leave="handleCellMouseLeave"
|
|
||||||
>
|
|
||||||
<el-table-column prop="task_type" label="任务类型" min-width="200" />
|
<el-table-column prop="task_type" label="任务类型" min-width="200" />
|
||||||
<el-table-column prop="task_type_name" label="任务名称" min-width="150" />
|
<el-table-column prop="task_type_name" label="任务名称" min-width="150" />
|
||||||
<el-table-column prop="max_concurrency" label="最大并发数" min-width="120" align="center" />
|
<el-table-column prop="max_concurrency" label="最大并发数" min-width="120" align="center" />
|
||||||
@@ -50,18 +41,13 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="handleEdit(row)">修改配置</el-button>
|
||||||
|
<el-button type="danger" link @click="handleReset(row)">重置</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 鼠标悬浮提示 -->
|
|
||||||
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
|
|
||||||
|
|
||||||
<!-- 右键菜单 -->
|
|
||||||
<ArtMenuRight
|
|
||||||
ref="contextMenuRef"
|
|
||||||
:menu-items="contextMenuItems"
|
|
||||||
:menu-width="120"
|
|
||||||
@select="handleContextMenuSelect"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 编辑对话框 -->
|
<!-- 编辑对话框 -->
|
||||||
@@ -94,22 +80,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { PollingConcurrencyService } from '@/api/modules'
|
import { PollingConcurrencyService } from '@/api/modules'
|
||||||
import type { PollingConcurrency, UpdateConcurrencyParams } from '@/types/api'
|
import type { PollingConcurrency, UpdateConcurrencyParams } from '@/types/api'
|
||||||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
|
||||||
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
|
|
||||||
import { useTableContextMenu } from '@/composables/useTableContextMenu'
|
|
||||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<PollingConcurrency[]>([])
|
const tableData = ref<PollingConcurrency[]>([])
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
|
||||||
const currentClickRow = ref<PollingConcurrency | null>(null)
|
|
||||||
|
|
||||||
const currentRow = ref<PollingConcurrency>({
|
const currentRow = ref<PollingConcurrency>({
|
||||||
task_type: '',
|
task_type: '',
|
||||||
@@ -131,27 +111,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
showContextMenuHint,
|
|
||||||
hintPosition,
|
|
||||||
getRowClassName,
|
|
||||||
handleCellMouseEnter,
|
|
||||||
handleCellMouseLeave
|
|
||||||
} = useTableContextMenu()
|
|
||||||
|
|
||||||
const contextMenuItems = computed<MenuItemType[]>(() => [
|
|
||||||
{
|
|
||||||
label: '修改配置',
|
|
||||||
key: 'edit',
|
|
||||||
permission: 'polling_concurrency_update'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '重置',
|
|
||||||
key: 'reset',
|
|
||||||
permission: 'polling_concurrency_reset'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const getUtilizationColor = (percentage: number) => {
|
const getUtilizationColor = (percentage: number) => {
|
||||||
if (percentage >= 90) return '#f56c6c'
|
if (percentage >= 90) return '#f56c6c'
|
||||||
if (percentage >= 70) return '#e6a23c'
|
if (percentage >= 70) return '#e6a23c'
|
||||||
@@ -240,28 +199,6 @@
|
|||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理表格行右键菜单
|
|
||||||
const handleRowContextMenu = (row: PollingConcurrency, column: any, event: MouseEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
currentClickRow.value = row
|
|
||||||
contextMenuRef.value?.show(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理右键菜单选择
|
|
||||||
const handleContextMenuSelect = (item: MenuItemType) => {
|
|
||||||
if (!currentClickRow.value) return
|
|
||||||
|
|
||||||
switch (item.key) {
|
|
||||||
case 'edit':
|
|
||||||
handleEdit(currentClickRow.value)
|
|
||||||
break
|
|
||||||
case 'reset':
|
|
||||||
handleReset(currentClickRow.value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -35,28 +35,14 @@
|
|||||||
:pageSize="pagination.page_size"
|
:pageSize="pagination.page_size"
|
||||||
:total="pagination.total"
|
:total="pagination.total"
|
||||||
:marginTop="10"
|
:marginTop="10"
|
||||||
:row-class-name="getRowClassName"
|
:actions="getActions"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@row-contextmenu="handleRowContextMenu"
|
|
||||||
@cell-mouse-enter="handleCellMouseEnter"
|
|
||||||
@cell-mouse-leave="handleCellMouseLeave"
|
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-table-column v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
<el-table-column v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||||
</template>
|
</template>
|
||||||
</ArtTable>
|
</ArtTable>
|
||||||
|
|
||||||
<!-- 鼠标悬浮提示 -->
|
|
||||||
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
|
|
||||||
|
|
||||||
<!-- 右键菜单 -->
|
|
||||||
<ArtMenuRight
|
|
||||||
ref="contextMenuRef"
|
|
||||||
:menu-items="contextMenuItems"
|
|
||||||
:menu-width="120"
|
|
||||||
@select="handleContextMenuSelect"
|
|
||||||
/>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 新增/编辑对话框 -->
|
<!-- 新增/编辑对话框 -->
|
||||||
@@ -256,9 +242,7 @@
|
|||||||
} from '@/types/api'
|
} from '@/types/api'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import { useTableContextMenu } from '@/composables/useTableContextMenu'
|
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<PollingConfig[]>([])
|
const tableData = ref<PollingConfig[]>([])
|
||||||
@@ -266,8 +250,6 @@
|
|||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogType = ref<'create' | 'edit'>('create')
|
const dialogType = ref<'create' | 'edit'>('create')
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const contextMenuRef = ref()
|
|
||||||
const currentClickRow = ref<PollingConfig | null>(null)
|
|
||||||
const carrierList = ref<any[]>([])
|
const carrierList = ref<any[]>([])
|
||||||
|
|
||||||
// 分页数据
|
// 分页数据
|
||||||
@@ -327,14 +309,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
showContextMenuHint,
|
|
||||||
hintPosition,
|
|
||||||
getRowClassName,
|
|
||||||
handleCellMouseEnter,
|
|
||||||
handleCellMouseLeave
|
|
||||||
} = useTableContextMenu()
|
|
||||||
|
|
||||||
// 搜索表单配置
|
// 搜索表单配置
|
||||||
const searchFormItems = computed<SearchFormItem[]>(() => [
|
const searchFormItems = computed<SearchFormItem[]>(() => [
|
||||||
{
|
{
|
||||||
@@ -501,23 +475,28 @@
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
const contextMenuItems = computed<MenuItemType[]>(() => [
|
const getActions = (row: PollingConfig) => {
|
||||||
{
|
const actions: any[] = [
|
||||||
label: '编辑',
|
{
|
||||||
key: 'edit',
|
label: '编辑',
|
||||||
permission: 'polling_config_update'
|
handler: () => handleEdit(row)
|
||||||
},
|
}
|
||||||
{
|
]
|
||||||
label: currentClickRow.value?.status === 1 ? '禁用' : '启用',
|
|
||||||
key: 'toggle',
|
actions.push({
|
||||||
permission: 'polling_config_update_status'
|
label: row.status === 1 ? '禁用' : '启用',
|
||||||
},
|
type: row.status === 1 ? 'danger' : 'primary',
|
||||||
{
|
handler: () => handleToggleStatus(row)
|
||||||
|
})
|
||||||
|
|
||||||
|
actions.push({
|
||||||
label: '删除',
|
label: '删除',
|
||||||
key: 'delete',
|
type: 'danger',
|
||||||
permission: 'polling_config_delete'
|
handler: () => handleDelete(row)
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
const loadCarriers = async (carrierName?: string) => {
|
const loadCarriers = async (carrierName?: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -708,29 +687,6 @@
|
|||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRowContextMenu = (row: PollingConfig, column: any, event: MouseEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
currentClickRow.value = row
|
|
||||||
contextMenuRef.value?.show(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleContextMenuSelect = (item: MenuItemType) => {
|
|
||||||
if (!currentClickRow.value) return
|
|
||||||
|
|
||||||
switch (item.key) {
|
|
||||||
case 'edit':
|
|
||||||
handleEdit(currentClickRow.value)
|
|
||||||
break
|
|
||||||
case 'toggle':
|
|
||||||
handleToggleStatus(currentClickRow.value)
|
|
||||||
break
|
|
||||||
case 'delete':
|
|
||||||
handleDelete(currentClickRow.value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadCarriers()
|
loadCarriers()
|
||||||
loadData()
|
loadData()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<ArtSearchBar
|
<ArtSearchBar
|
||||||
v-model:filter="searchForm"
|
v-model:filter="searchForm"
|
||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
:show-expand="false"
|
show-expand
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></ArtSearchBar>
|
></ArtSearchBar>
|
||||||
@@ -347,6 +347,28 @@
|
|||||||
const availableRoles = ref<ShopRoleResponse[]>([])
|
const availableRoles = ref<ShopRoleResponse[]>([])
|
||||||
const selectedRoleId = ref<number | undefined>(undefined)
|
const selectedRoleId = ref<number | undefined>(undefined)
|
||||||
|
|
||||||
|
// 上级店铺搜索选项
|
||||||
|
const parentShopOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
// 搜索上级店铺
|
||||||
|
const searchParentShops = async (query: string) => {
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
page: 1,
|
||||||
|
page_size: 20
|
||||||
|
}
|
||||||
|
if (query) {
|
||||||
|
params.shop_name = query
|
||||||
|
}
|
||||||
|
const res = await ShopService.getShops(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
parentShopOptions.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索店铺失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 定义表单搜索初始值
|
// 定义表单搜索初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
shop_name: '',
|
shop_name: '',
|
||||||
@@ -408,6 +430,23 @@
|
|||||||
placeholder: '请输入店铺编号'
|
placeholder: '请输入店铺编号'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '上级店铺',
|
||||||
|
prop: 'parent_id',
|
||||||
|
type: 'select',
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: (query: string) => searchParentShops(query),
|
||||||
|
placeholder: '请选择或搜索上级店铺'
|
||||||
|
},
|
||||||
|
options: () =>
|
||||||
|
parentShopOptions.value.map((shop) => ({
|
||||||
|
label: shop.shop_name,
|
||||||
|
value: shop.id
|
||||||
|
}))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '状态',
|
label: '状态',
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
@@ -722,6 +761,7 @@
|
|||||||
getShopList()
|
getShopList()
|
||||||
loadTopLevelShops() // 加载顶级店铺用于级联选择
|
loadTopLevelShops() // 加载顶级店铺用于级联选择
|
||||||
searchDefaultRoles('') // 加载初始默认角色列表
|
searchDefaultRoles('') // 加载初始默认角色列表
|
||||||
|
searchParentShops('') // 加载上级店铺选项
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取店铺列表
|
// 获取店铺列表
|
||||||
|
|||||||
Reference in New Issue
Block a user