This commit is contained in:
@@ -105,7 +105,7 @@
|
||||
})
|
||||
|
||||
const validateRefundAmount = (rule: any, value: number, callback: any) => {
|
||||
if (value === undefined || value === null || value === 0) {
|
||||
if (value === undefined || value === null) {
|
||||
callback(new Error('请输入申请退款金额'))
|
||||
} else if (value > formData.actual_received_amount) {
|
||||
callback(new Error('申请退款金额不能大于实收金额'))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ElDialog v-model="visible" title="设置实名认证策略" width="500px">
|
||||
<ElDialog v-model="visible" title="设置实名认证策略" width="35%">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<ElFormItem label="资产标识">
|
||||
<span style="font-weight: bold; color: #409eff">{{ assetIdentifier }}</span>
|
||||
@@ -21,7 +21,9 @@
|
||||
<p>说明:</p>
|
||||
<p>• <strong>无需实名</strong>:用户使用设备无需进行实名认证</p>
|
||||
<p>• <strong>先实名后充值/购买</strong>:用户必须先完成实名认证才能进行充值或购买套餐</p>
|
||||
<p>• <strong>先充值/购买后实名</strong>:用户可以先充值或购买套餐,之后需要完成实名认证</p>
|
||||
<p
|
||||
>• <strong>先充值/购买后实名</strong>:用户可以先充值或购买套餐,之后需要完成实名认证</p
|
||||
>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<ElDialog v-model="visible" title="设置WiFi" width="500px" @close="handleClose">
|
||||
<ElForm ref="formRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<ElFormItem label="设备信息">
|
||||
<span style="font-weight: bold; color: #409eff">{{ deviceInfo }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi状态" prop="enabled">
|
||||
<ElRadioGroup v-model="formData.enabled">
|
||||
<ElRadio :value="1">启用</ElRadio>
|
||||
<ElRadio :value="0">禁用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi名称" prop="ssid">
|
||||
<ElInput
|
||||
v-model="formData.ssid"
|
||||
placeholder="请输入WiFi名称(1-32个字符)"
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
clearable
|
||||
autocomplete="off"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi密码" prop="password">
|
||||
<ElInput
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
placeholder="请输入WiFi密码(8-63个字符)"
|
||||
maxlength="63"
|
||||
show-word-limit
|
||||
show-password
|
||||
clearable
|
||||
autocomplete="off"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="visible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleConfirm" :loading="confirmLoading">
|
||||
确认设置
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import {
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElButton,
|
||||
ElRadioGroup,
|
||||
ElRadio
|
||||
} from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
deviceInfo: string
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm', data: { enabled: number; ssid: string; password: string }): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const visible = ref(props.modelValue)
|
||||
const confirmLoading = ref(props.loading)
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const formData = reactive({
|
||||
enabled: 1,
|
||||
ssid: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
ssid: [
|
||||
{ required: true, message: '请输入WiFi名称', trigger: 'blur' },
|
||||
{ min: 1, max: 32, message: 'WiFi名称长度为1-32个字符', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入WiFi密码', trigger: 'blur' },
|
||||
{ min: 8, max: 63, message: 'WiFi密码长度为8-63个字符', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
visible.value = val
|
||||
if (val) {
|
||||
formData.enabled = 1
|
||||
formData.ssid = ''
|
||||
formData.password = ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(visible, (val) => {
|
||||
emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.loading,
|
||||
(val) => {
|
||||
confirmLoading.value = val
|
||||
}
|
||||
)
|
||||
|
||||
const handleClose = () => {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
emit('confirm', {
|
||||
enabled: formData.enabled,
|
||||
ssid: formData.ssid,
|
||||
password: formData.password
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -216,7 +216,8 @@ export interface AssetPackageUsageRecord {
|
||||
master_usage_id?: number | null // 主套餐 ID(加油包时有值)
|
||||
priority?: number // 优先级
|
||||
created_at?: string // 创建时间
|
||||
order_no?: string // 订单号
|
||||
order_id?: number // 订单ID
|
||||
refund_id?: number // 退款ID(有退款时才有值)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -587,8 +587,6 @@
|
||||
}
|
||||
|
||||
getAccountList()
|
||||
loadShopList()
|
||||
loadEnterpriseList()
|
||||
})
|
||||
|
||||
// 加载所有角色列表
|
||||
@@ -597,7 +595,7 @@
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: keyword ? 100 : 20, // 搜索时加载更多,默认20条
|
||||
role_type: 1
|
||||
role_type: currentAccountType.value === 2 ? 1 : 2
|
||||
}
|
||||
if (keyword) {
|
||||
params.role_name = keyword
|
||||
@@ -646,6 +644,10 @@
|
||||
|
||||
// 显示分配角色对话框
|
||||
const showRoleDialog = async (row: any) => {
|
||||
if (row.user_type === 1) {
|
||||
ElMessage.warning('超级管理员无法分配角色')
|
||||
return
|
||||
}
|
||||
currentAccountId.value = row.id
|
||||
currentAccountName.value = row.username
|
||||
currentAccountType.value = row.user_type
|
||||
@@ -1019,7 +1021,7 @@
|
||||
.role-info {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
{{ getRealnamePolicyName(scope.row.realname_policy) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="实名时间" width="180">
|
||||
<ElTableColumn label="实名时间" width="170">
|
||||
<template #default="scope">
|
||||
{{ scope.row.real_name_at ? formatDateTime(scope.row.real_name_at) : '-' }}
|
||||
</template>
|
||||
|
||||
@@ -36,10 +36,17 @@
|
||||
<div class="flow-stat-item">
|
||||
<span class="flow-stat-label">总量</span>
|
||||
<span class="flow-stat-value">
|
||||
{{ formatDataSize(currentPackage.data_limit_mb || currentPackage.package_total_data || 0) }}
|
||||
{{
|
||||
formatDataSize(
|
||||
currentPackage.data_limit_mb || currentPackage.package_total_data || 0
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="isAdminOrPlatform && currentPackage.enable_virtual_data" class="flow-stat-item">
|
||||
<div
|
||||
v-if="isAdminOrPlatform && currentPackage.enable_virtual_data"
|
||||
class="flow-stat-item"
|
||||
>
|
||||
<span class="flow-stat-label">实际可用</span>
|
||||
<span class="flow-stat-value">
|
||||
{{ formatDataSize(currentPackage.virtual_limit_mb || 0) }}
|
||||
@@ -52,7 +59,8 @@
|
||||
formatDataSize(
|
||||
currentPackage.enable_virtual_data
|
||||
? currentPackage.virtual_remain_mb || 0
|
||||
: (currentPackage.data_limit_mb || currentPackage.package_total_data || 0) - (currentPackage.data_usage_mb || 0)
|
||||
: (currentPackage.data_limit_mb || currentPackage.package_total_data || 0) -
|
||||
(currentPackage.data_usage_mb || 0)
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
@@ -62,13 +70,29 @@
|
||||
<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)
|
||||
? 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))
|
||||
? 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"
|
||||
/>
|
||||
@@ -89,7 +113,10 @@
|
||||
<ElDescriptionsItem label="套餐类型">
|
||||
{{ getPackageTypeName(currentPackage.package_type) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem v-if="isAdminOrPlatform && currentPackage.enable_virtual_data" label="虚流量比例">
|
||||
<ElDescriptionsItem
|
||||
v-if="isAdminOrPlatform && currentPackage.enable_virtual_data"
|
||||
label="虚流量比例"
|
||||
>
|
||||
{{ currentPackage.virtual_ratio || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="开始时间">
|
||||
@@ -98,7 +125,7 @@
|
||||
<ElDescriptionsItem label="到期时间">
|
||||
{{ formatDateTime(currentPackage.expire_time) || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="购买时间">
|
||||
<ElDescriptionsItem label="下单时间">
|
||||
{{ formatDateTime(currentPackage.created_at) || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem v-if="currentPackage.duration_days" label="套餐时长">
|
||||
@@ -128,12 +155,8 @@
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import type { PackageInfo } from '../types'
|
||||
|
||||
const {
|
||||
formatAmount,
|
||||
formatDataSize,
|
||||
getUsageProgress,
|
||||
getProgressColorByPercentage
|
||||
} = useAssetFormatters()
|
||||
const { formatAmount, formatDataSize, getUsageProgress, getProgressColorByPercentage } =
|
||||
useAssetFormatters()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { info: userInfo } = storeToRefs(userStore)
|
||||
|
||||
@@ -25,7 +25,19 @@
|
||||
</ElEmpty>
|
||||
<div v-else-if="packageList && packageList.length > 0" class="table-scroll-container">
|
||||
<ElTable :data="packageList" class="package-table" border>
|
||||
<ElTableColumn prop="order_no" label="订单号" width="180" show-overflow-tooltip />
|
||||
<ElTableColumn label="订单号" width="240">
|
||||
<template #default="scope">
|
||||
<ElButton
|
||||
type="primary"
|
||||
link
|
||||
@click="handleOrderNoClick(scope.row)"
|
||||
v-if="hasAuth('order:detail')"
|
||||
>
|
||||
{{ scope.row.order_no }}
|
||||
</ElButton>
|
||||
<span v-else>{{ scope.row.order_no }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="套餐名称" width="150">
|
||||
<template #default="scope">
|
||||
<ElButton
|
||||
@@ -50,11 +62,6 @@
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="data_limit_mb" label="总流量" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatDataSize(scope.row.data_limit_mb) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="流量" min-width="320">
|
||||
<template #default="scope">
|
||||
<div class="traffic-progress">
|
||||
@@ -108,12 +115,12 @@
|
||||
{{ formatDateTime(scope.row.expires_at) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="created_at" label="购买时间" width="170" show-overflow-tooltip>
|
||||
<ElTableColumn prop="created_at" label="下单时间" width="170" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ formatDateTime(scope.row.created_at) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="100" fixed="right">
|
||||
<ElTableColumn label="操作" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<ElButton
|
||||
v-if="scope.row.status !== 4 && hasAuth('package:create_refund')"
|
||||
@@ -123,6 +130,18 @@
|
||||
>
|
||||
退款申请
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-if="
|
||||
scope.row.status === 4 &&
|
||||
scope.row.refund_id &&
|
||||
hasAuth('refund:package_list_detail')
|
||||
"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleViewRefundDetail(scope.row)"
|
||||
>
|
||||
退款详情
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
@@ -217,6 +236,8 @@
|
||||
(e: 'page-change', page: number): void
|
||||
(e: 'size-change', size: number): void
|
||||
(e: 'navigateToDevice', deviceNo: string): void
|
||||
(e: 'navigateToOrder', orderId: number): void
|
||||
(e: 'navigateToRefund', refundId: number): void
|
||||
(e: 'refresh'): void
|
||||
}
|
||||
|
||||
@@ -262,6 +283,24 @@
|
||||
emit('showDailyRecords', { packageRow })
|
||||
}
|
||||
|
||||
// 点击订单号
|
||||
const handleOrderNoClick = (packageRow: PackageInfo) => {
|
||||
if (!hasAuth('order:package_list_detail')) {
|
||||
ElMessage.warning('您没有查看订单详情的权限')
|
||||
return
|
||||
}
|
||||
emit('navigateToOrder', packageRow.order_id!)
|
||||
}
|
||||
|
||||
// 查看退款详情
|
||||
const handleViewRefundDetail = (packageRow: PackageInfo) => {
|
||||
if (!hasAuth('refund:detail')) {
|
||||
ElMessage.warning('您没有查看退款详情的权限')
|
||||
return
|
||||
}
|
||||
emit('navigateToRefund', packageRow.refund_id!)
|
||||
}
|
||||
|
||||
const handleShowRecharge = () => {
|
||||
emit('showRecharge')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ElDialog v-model="visible" title="设置切卡模式" width="500px">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<ElFormItem label="设备标识">
|
||||
<span style="font-weight: bold; color: #409eff">{{ deviceIdentifier }}</span>
|
||||
</ElFormItem>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ElDialog v-model="visible" title="设置WiFi" width="500px" :append-to-body="true">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<ElDialog v-model="visible" title="设置WiFi" width="30%" :append-to-body="true">
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="80px" autocomplete="off">
|
||||
<ElFormItem label="WiFi状态" prop="enabled">
|
||||
<ElRadioGroup v-model="form.enabled">
|
||||
<ElRadio :value="1">启用</ElRadio>
|
||||
@@ -15,6 +15,7 @@
|
||||
show-word-limit
|
||||
clearable
|
||||
autocomplete="off"
|
||||
name="wifi_ssid"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi密码" prop="password">
|
||||
@@ -27,6 +28,7 @@
|
||||
show-password
|
||||
clearable
|
||||
autocomplete="off"
|
||||
name="wifi_password"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
@@ -53,14 +55,33 @@
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
cardNo?: string // 流量卡号(ICCID)
|
||||
deviceIdentifier?: string // 设备标识(IMEI)
|
||||
wifiEnabled?: number // WiFi启用状态
|
||||
wifiSsid?: string // WiFi名称
|
||||
wifiPassword?: string // WiFi密码
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm', data: { card_no: string; enabled: number; ssid: string; password: string }): void
|
||||
(
|
||||
e: 'confirm',
|
||||
data: {
|
||||
deviceIdentifier?: string
|
||||
card_no: string
|
||||
enabled: number
|
||||
ssid: string
|
||||
password: string
|
||||
}
|
||||
): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
cardNo: '',
|
||||
deviceIdentifier: '',
|
||||
wifiEnabled: 1,
|
||||
wifiSsid: '',
|
||||
wifiPassword: ''
|
||||
})
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
@@ -91,9 +112,9 @@
|
||||
// 监听对话框打开,重置表单
|
||||
watch(visible, (newVal) => {
|
||||
if (newVal) {
|
||||
form.enabled = 1
|
||||
form.ssid = ''
|
||||
form.password = ''
|
||||
form.enabled = props.wifiEnabled ?? 1
|
||||
form.ssid = props.wifiSsid ?? ''
|
||||
form.password = props.wifiPassword ?? ''
|
||||
}
|
||||
})
|
||||
|
||||
@@ -107,6 +128,7 @@
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
emit('confirm', {
|
||||
deviceIdentifier: props.deviceIdentifier,
|
||||
card_no: props.cardNo || '',
|
||||
enabled: form.enabled,
|
||||
ssid: form.ssid,
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
@page-change="handlePackagePageChange"
|
||||
@size-change="handlePackageSizeChange"
|
||||
@navigate-to-device="handleNavigateToDevice"
|
||||
@navigate-to-order="handleNavigateToOrder"
|
||||
@navigate-to-refund="handleNavigateToRefund"
|
||||
@refresh="handlePackageRefresh"
|
||||
/>
|
||||
</div>
|
||||
@@ -106,6 +108,9 @@
|
||||
<WiFiConfigDialog
|
||||
v-model="setWiFiDialogVisible"
|
||||
:card-no="deviceRealtime?.current_iccid"
|
||||
:wifi-enabled="deviceRealtime?.wifi_enabled ? 1 : 0"
|
||||
:wifi-ssid="deviceRealtime?.ssid || ''"
|
||||
:wifi-password="deviceRealtime?.wifi_password || ''"
|
||||
@confirm="handleConfirmSetWiFi"
|
||||
/>
|
||||
<PackageRechargeDialog
|
||||
@@ -147,8 +152,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElCard, ElEmpty } from 'element-plus'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
|
||||
// 引入子组件
|
||||
import AssetSearchCard from './components/AssetSearchCard.vue'
|
||||
@@ -179,6 +185,7 @@
|
||||
defineOptions({ name: 'SingleCard' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const assetSearchCardRef = ref<InstanceType<typeof AssetSearchCard>>()
|
||||
@@ -560,6 +567,20 @@
|
||||
await handleSearch({ identifier: deviceNo })
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到订单详情
|
||||
*/
|
||||
const handleNavigateToOrder = (orderId: number) => {
|
||||
router.push(`${RoutesAlias.OrderDetail}/${orderId}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到退款详情
|
||||
*/
|
||||
const handleNavigateToRefund = (refundId: number) => {
|
||||
router.push(`${RoutesAlias.RefundDetail}/${refundId}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* URL参数自动加载
|
||||
*/
|
||||
|
||||
@@ -351,7 +351,7 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<ElCol :span="9">
|
||||
<ElFormItem label="ICCID" prop="iot_card_id">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.iot_card_id"
|
||||
@@ -401,6 +401,7 @@
|
||||
</ElCol>
|
||||
<ElCol :span="3">
|
||||
<ElButton
|
||||
v-permission="'device:bind_card'"
|
||||
type="primary"
|
||||
@click="handleConfirmBindCard"
|
||||
:loading="bindCardLoading"
|
||||
@@ -419,11 +420,9 @@
|
||||
</div>
|
||||
<div v-else>
|
||||
<ElTable :data="deviceCards" border style="width: 100%">
|
||||
<ElTableColumn prop="slot_position" label="插槽位置" width="120" align="center">
|
||||
<ElTableColumn prop="slot_position" label="插槽位置" width="120">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
style="display: flex; gap: 10px; align-items: center; justify-content: center"
|
||||
>
|
||||
<div style="display: flex; gap: 10px; align-items: center">
|
||||
<span>SIM-{{ row.slot_position }}</span>
|
||||
<ElTag v-if="row.is_current" type="success" size="small">当前</ElTag>
|
||||
</div>
|
||||
@@ -431,7 +430,7 @@
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="iccid" label="ICCID" min-width="120" />
|
||||
<ElTableColumn prop="msisdn" label="接入号" width="160" />
|
||||
<ElTableColumn prop="network_status" label="网络状态" width="100" align="center">
|
||||
<ElTableColumn prop="network_status" label="网络状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<ElTag :type="getNetworkStatusTagType(row.network_status)">
|
||||
{{ getNetworkStatusText(row.network_status) }}
|
||||
@@ -445,7 +444,13 @@
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="100" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElButton type="danger" size="small" link @click="handleUnbindCard(row)">
|
||||
<ElButton
|
||||
v-permission="'device:unbind_card'"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
@click="handleUnbindCard(row)"
|
||||
>
|
||||
解绑
|
||||
</ElButton>
|
||||
</template>
|
||||
@@ -470,93 +475,13 @@
|
||||
@confirm="handleConfirmSwitchCard"
|
||||
/>
|
||||
|
||||
<!-- 设置WiFi对话框 -->
|
||||
<ElDialog v-model="setWiFiDialogVisible" title="设置WiFi" width="500px">
|
||||
<ElForm
|
||||
ref="setWiFiFormRef"
|
||||
:model="setWiFiForm"
|
||||
:rules="setWiFiRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<ElFormItem label="设备号">
|
||||
<span style="font-weight: bold; color: #409eff">{{ currentOperatingImei }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi状态" prop="enabled">
|
||||
<ElRadioGroup v-model="setWiFiForm.enabled">
|
||||
<ElRadio :value="1">启用</ElRadio>
|
||||
<ElRadio :value="0">禁用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi名称" prop="ssid">
|
||||
<ElInput
|
||||
v-model="setWiFiForm.ssid"
|
||||
placeholder="请输入WiFi名称(1-32个字符)"
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
clearable
|
||||
autocomplete="off"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="WiFi密码" prop="password">
|
||||
<ElInput
|
||||
v-model="setWiFiForm.password"
|
||||
type="password"
|
||||
placeholder="请输入WiFi密码(8-63个字符)"
|
||||
maxlength="63"
|
||||
show-word-limit
|
||||
show-password
|
||||
clearable
|
||||
autocomplete="off"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="setWiFiDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleConfirmSetWiFi" :loading="setWiFiLoading">
|
||||
确认设置
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 设置切卡模式对话框 -->
|
||||
<ElDialog v-model="switchModeDialogVisible" title="设置切卡模式" width="500px">
|
||||
<ElForm
|
||||
ref="switchModeFormRef"
|
||||
:model="switchModeForm"
|
||||
:rules="switchModeRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<ElFormItem label="设备标识">
|
||||
<span style="font-weight: bold; color: #409eff">{{
|
||||
currentOperatingDeviceIdentifier
|
||||
}}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="当前模式">
|
||||
<ElTag :type="currentDeviceSwitchMode === 0 ? 'success' : 'warning'">
|
||||
{{ currentDeviceSwitchMode === 0 ? '自动切卡' : '手动切卡' }}
|
||||
</ElTag>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="切卡模式" prop="switch_mode">
|
||||
<ElRadioGroup v-model="switchModeForm.switch_mode">
|
||||
<ElRadio :value="0">自动切卡</ElRadio>
|
||||
<ElRadio :value="1">手动切卡</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<div style="font-size: 12px; color: #909399; line-height: 1.6">
|
||||
<p>说明:</p>
|
||||
<p>• <strong>自动切卡</strong>:设备根据信号强度自动切换到最优的SIM卡</p>
|
||||
<p>• <strong>手动切卡</strong>:需要手动触发切换SIM卡操作</p>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="switchModeDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleConfirmSwitchMode" :loading="switchModeLoading">
|
||||
确认设置
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
<SwitchModeDialog
|
||||
v-model="switchModeDialogVisible"
|
||||
:device-identifier="currentOperatingDeviceIdentifier"
|
||||
:current-mode="currentDeviceSwitchMode"
|
||||
@confirm="handleConfirmSwitchMode"
|
||||
/>
|
||||
|
||||
<!-- 实名认证策略对话框 -->
|
||||
<RealnamePolicyDialog
|
||||
@@ -586,6 +511,7 @@
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import SwitchCardDialog from '@/components/device/SwitchCardDialog.vue'
|
||||
import SwitchModeDialog from '@/views/asset-management/asset-information/components/dialogs/SwitchModeDialog.vue'
|
||||
import type {
|
||||
Device,
|
||||
DeviceStatus,
|
||||
@@ -663,6 +589,7 @@
|
||||
// 搜索表单店铺/系列选项
|
||||
const shopOptions = ref<any[]>([])
|
||||
const searchSeriesOptions = ref<any[]>([])
|
||||
const searchBatchNoOptions = ref<any[]>([])
|
||||
|
||||
// 设备详情弹窗相关
|
||||
const deviceDetailDialogVisible = ref(false)
|
||||
@@ -699,37 +626,11 @@
|
||||
const deviceBindingCards = ref<any[]>([]) // 设备绑定的卡列表
|
||||
const loadingDeviceCards = ref(false) // 加载设备绑定卡列表的状态
|
||||
|
||||
const setWiFiDialogVisible = ref(false)
|
||||
const setWiFiLoading = ref(false)
|
||||
const setWiFiFormRef = ref<FormInstance>()
|
||||
const setWiFiForm = reactive({
|
||||
enabled: 1,
|
||||
ssid: '',
|
||||
password: ''
|
||||
})
|
||||
const setWiFiRules = reactive<FormRules>({
|
||||
ssid: [
|
||||
{ required: true, message: '请输入WiFi名称', trigger: 'blur' },
|
||||
{ min: 1, max: 32, message: 'WiFi名称长度为1-32个字符', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入WiFi密码', trigger: 'blur' },
|
||||
{ min: 8, max: 63, message: 'WiFi密码长度为8-63个字符', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
// 切换模式相关
|
||||
const switchModeDialogVisible = ref(false)
|
||||
const switchModeLoading = ref(false)
|
||||
const switchModeFormRef = ref<FormInstance>()
|
||||
const currentOperatingDeviceIdentifier = ref<string>('')
|
||||
const currentDeviceSwitchMode = ref<number>(0)
|
||||
const switchModeForm = reactive({
|
||||
switch_mode: 0
|
||||
})
|
||||
const switchModeRules = reactive<FormRules>({
|
||||
switch_mode: [{ required: true, message: '请选择切卡模式', trigger: 'change' }]
|
||||
})
|
||||
|
||||
// 实名认证策略相关
|
||||
const realnamePolicyDialogVisible = ref(false)
|
||||
@@ -754,6 +655,34 @@
|
||||
// 搜索表单
|
||||
const searchForm = reactive({ ...initialSearchState })
|
||||
|
||||
// 搜索批次号(用于搜索表单)
|
||||
const loadSearchBatchNoOptions = async (batchNo?: string) => {
|
||||
try {
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 20
|
||||
}
|
||||
if (batchNo) {
|
||||
params.batch_no = batchNo
|
||||
}
|
||||
const res = await CardService.getIotCardImportTasks(params)
|
||||
if (res.code === 0) {
|
||||
searchBatchNoOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载批次号选项失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索批次号
|
||||
const handleSearchBatchNo = (query: string) => {
|
||||
if (query) {
|
||||
loadSearchBatchNoOptions(query)
|
||||
} else {
|
||||
loadSearchBatchNoOptions()
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索表单配置
|
||||
const searchFormItems: SearchFormItem[] = [
|
||||
{
|
||||
@@ -792,11 +721,20 @@
|
||||
{
|
||||
label: '批次号',
|
||||
prop: 'batch_no',
|
||||
type: 'input',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请输入批次号'
|
||||
}
|
||||
filterable: true,
|
||||
remote: true,
|
||||
remoteMethod: handleSearchBatchNo,
|
||||
loading: false,
|
||||
placeholder: '请选择或搜索批次号'
|
||||
},
|
||||
options: () =>
|
||||
searchBatchNoOptions.value.map((b) => ({
|
||||
label: b.batch_no,
|
||||
value: b.batch_no
|
||||
}))
|
||||
},
|
||||
{
|
||||
label: '设备类型',
|
||||
@@ -941,6 +879,7 @@
|
||||
bindCardForm.search_type = 'iccid'
|
||||
bindCardForm.iot_card_id = undefined
|
||||
bindCardForm.slot_position = 1
|
||||
bindCardFormRef.value?.resetFields()
|
||||
// 加载未绑定设备的卡列表
|
||||
await Promise.all([loadDeviceCards(device.virtual_no), loadDefaultIotCards()])
|
||||
}
|
||||
@@ -1338,8 +1277,6 @@
|
||||
let isFirstActivation = true
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
searchShops('')
|
||||
searchSeries('')
|
||||
})
|
||||
|
||||
// 当页面被 keep-alive 激活时自动刷新数据
|
||||
@@ -1492,6 +1429,7 @@
|
||||
allocateForm.target_shop_id = undefined
|
||||
allocateForm.remark = ''
|
||||
allocateResult.value = null
|
||||
shopCascadeOptions.value = []
|
||||
allocateDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -1505,7 +1443,7 @@
|
||||
try {
|
||||
const data = {
|
||||
device_ids: selectedDevices.value.map((d) => d.id),
|
||||
target_shop_id: allocateForm.target_shop_id![0],
|
||||
target_shop_id: allocateForm.target_shop_id![allocateForm.target_shop_id.length - 1],
|
||||
remark: allocateForm.remark
|
||||
}
|
||||
const res = await DeviceService.allocateDevices(data)
|
||||
@@ -1720,15 +1658,6 @@
|
||||
await showSwitchCardDialog(imei)
|
||||
break
|
||||
}
|
||||
case 'set-wifi': {
|
||||
const imei = device?.imei || (await getDeviceImei(deviceNo))
|
||||
if (!imei) {
|
||||
ElMessage.error('无法获取设备IMEI,无法执行WiFi设置操作')
|
||||
return
|
||||
}
|
||||
showSetWiFiDialog(imei)
|
||||
break
|
||||
}
|
||||
case 'manual-deactivate':
|
||||
handleManualDeactivateDevice()
|
||||
break
|
||||
@@ -1926,77 +1855,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 显示设置WiFi对话框
|
||||
const showSetWiFiDialog = (imei: string) => {
|
||||
currentOperatingImei.value = imei
|
||||
setWiFiForm.enabled = 1
|
||||
setWiFiForm.ssid = ''
|
||||
setWiFiForm.password = ''
|
||||
setWiFiDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认设置WiFi
|
||||
const handleConfirmSetWiFi = async () => {
|
||||
if (!setWiFiFormRef.value) return
|
||||
|
||||
await setWiFiFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
setWiFiLoading.value = true
|
||||
try {
|
||||
const res = await DeviceService.setWiFi(currentOperatingImei.value, {
|
||||
enabled: setWiFiForm.enabled,
|
||||
ssid: setWiFiForm.ssid,
|
||||
password: setWiFiForm.password
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('WiFi设置成功')
|
||||
setWiFiDialogVisible.value = false
|
||||
} else {
|
||||
ElMessage.error(res.msg || '设置失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('设置WiFi失败:', error)
|
||||
} finally {
|
||||
setWiFiLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 显示切换模式对话框
|
||||
const showSwitchModeDialog = (deviceIdentifier: string, currentMode?: number) => {
|
||||
currentOperatingDeviceIdentifier.value = deviceIdentifier
|
||||
currentDeviceSwitchMode.value = currentMode ?? 0
|
||||
switchModeForm.switch_mode = currentMode ?? 0
|
||||
switchModeDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认切换模式
|
||||
const handleConfirmSwitchMode = async () => {
|
||||
if (!switchModeFormRef.value) return
|
||||
|
||||
await switchModeFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
switchModeLoading.value = true
|
||||
try {
|
||||
const res = await DeviceService.setSwitchMode(
|
||||
currentOperatingDeviceIdentifier.value,
|
||||
switchModeForm.switch_mode
|
||||
)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('设置切卡模式成功')
|
||||
switchModeDialogVisible.value = false
|
||||
await getTableData()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '设置切卡模式失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('设置切卡模式失败:', error)
|
||||
} finally {
|
||||
switchModeLoading.value = false
|
||||
}
|
||||
const handleConfirmSwitchMode = async (data: { switch_mode: number }) => {
|
||||
switchModeLoading.value = true
|
||||
try {
|
||||
const res = await DeviceService.setSwitchMode(
|
||||
currentOperatingDeviceIdentifier.value,
|
||||
data.switch_mode
|
||||
)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('设置切卡模式成功')
|
||||
switchModeDialogVisible.value = false
|
||||
await getTableData()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '设置切卡模式失败')
|
||||
}
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.error('设置切卡模式失败:', error)
|
||||
} finally {
|
||||
switchModeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 设备操作菜单项配置
|
||||
@@ -2048,14 +1933,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('device:set_wifi')) {
|
||||
moreActions.push({
|
||||
label: '设置WiFi',
|
||||
handler: () => handleDeviceOperation('set-wifi', row.virtual_no, row),
|
||||
type: 'primary'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('device:clear_series')) {
|
||||
moreActions.push({
|
||||
label: '清除关联',
|
||||
|
||||
@@ -140,13 +140,20 @@
|
||||
>
|
||||
<ElSelect
|
||||
v-model="allocateForm.carrier_id"
|
||||
placeholder="请选择运营商"
|
||||
placeholder="请选择或搜索运营商"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearchAllocateCarrier"
|
||||
:loading="allocateCarrierLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption label="中国移动" :value="1" />
|
||||
<ElOption label="中国联通" :value="2" />
|
||||
<ElOption label="中国电信" :value="3" />
|
||||
<ElOption
|
||||
v-for="item in allocateCarrierOptions"
|
||||
:key="item.id"
|
||||
:label="item.carrier_name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
@@ -166,10 +173,25 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="allocateForm.selection_type === CardSelectionType.FILTER"
|
||||
v-if="allocateForm.selection_type === CardSelectionType.FILTER && userInfo.user_type !== 3"
|
||||
label="批次号"
|
||||
>
|
||||
<ElInput v-model="allocateForm.batch_no" placeholder="请输入批次号" />
|
||||
<ElSelect
|
||||
v-model="allocateForm.batch_no"
|
||||
placeholder="请选择或搜索批次号"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearchAllocateBatchNo"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in allocateBatchNoOptions"
|
||||
:key="item.id"
|
||||
:label="item.batch_no"
|
||||
:value="item.batch_no"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="备注">
|
||||
@@ -235,20 +257,42 @@
|
||||
>
|
||||
<ElSelect
|
||||
v-model="recallForm.carrier_id"
|
||||
placeholder="请选择运营商"
|
||||
placeholder="请选择或搜索运营商"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearchAllocateCarrier"
|
||||
:loading="allocateCarrierLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption label="中国移动" :value="1" />
|
||||
<ElOption label="中国联通" :value="2" />
|
||||
<ElOption label="中国电信" :value="3" />
|
||||
<ElOption
|
||||
v-for="item in allocateCarrierOptions"
|
||||
:key="item.id"
|
||||
:label="item.carrier_name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="recallForm.selection_type === CardSelectionType.FILTER"
|
||||
v-if="recallForm.selection_type === CardSelectionType.FILTER && userInfo.user_type !== 3"
|
||||
label="批次号"
|
||||
>
|
||||
<ElInput v-model="recallForm.batch_no" placeholder="请输入批次号" />
|
||||
<ElSelect
|
||||
v-model="recallForm.batch_no"
|
||||
placeholder="请选择或搜索批次号"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearchAllocateBatchNo"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in allocateBatchNoOptions"
|
||||
:key="item.id"
|
||||
:label="item.batch_no"
|
||||
:value="item.batch_no"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="备注">
|
||||
@@ -585,6 +629,8 @@
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
||||
@@ -602,6 +648,8 @@
|
||||
defineOptions({ name: 'StandaloneCardList' })
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
const userStore = useUserStore()
|
||||
const { info: userInfo } = storeToRefs(userStore)
|
||||
const router = useRouter()
|
||||
|
||||
const loading = ref(false)
|
||||
@@ -646,6 +694,10 @@
|
||||
const carrierLoading = ref(false)
|
||||
const searchCarrierOptions = ref<any[]>([])
|
||||
|
||||
// 批量分配对话框运营商选项
|
||||
const allocateCarrierOptions = ref<any[]>([])
|
||||
const allocateCarrierLoading = ref(false)
|
||||
|
||||
// 加载搜索栏运营商选项(默认加载10条)
|
||||
const loadSearchCarrierOptions = async (carrierName?: string) => {
|
||||
try {
|
||||
@@ -665,6 +717,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 加载批量分配对话框运营商选项
|
||||
const loadAllocateCarrierOptions = async (carrierName?: string) => {
|
||||
try {
|
||||
allocateCarrierLoading.value = true
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 20
|
||||
}
|
||||
if (carrierName) {
|
||||
params.carrier_name = carrierName
|
||||
}
|
||||
const res = await CarrierService.getCarriers(params)
|
||||
if (res.code === 0) {
|
||||
allocateCarrierOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载运营商选项失败:', error)
|
||||
} finally {
|
||||
allocateCarrierLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索批量分配对话框运营商
|
||||
const handleSearchAllocateCarrier = (query: string) => {
|
||||
if (query) {
|
||||
loadAllocateCarrierOptions(query)
|
||||
} else {
|
||||
loadAllocateCarrierOptions()
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索运营商(用于搜索栏)
|
||||
const handleSearchCarrier = (query: string) => {
|
||||
if (query) {
|
||||
@@ -674,6 +757,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 批量分配对话框批次号选项
|
||||
const allocateBatchNoOptions = ref<any[]>([])
|
||||
|
||||
// 加载批量分配对话框批次号选项
|
||||
const loadAllocateBatchNoOptions = async (batchNo?: string) => {
|
||||
try {
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 20
|
||||
}
|
||||
if (batchNo) {
|
||||
params.batch_no = batchNo
|
||||
}
|
||||
const res = await CardService.getIotCardImportTasks(params)
|
||||
if (res.code === 0) {
|
||||
allocateBatchNoOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载批次号选项失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索批量分配对话框批次号
|
||||
const handleSearchAllocateBatchNo = (query: string) => {
|
||||
if (query) {
|
||||
loadAllocateBatchNoOptions(query)
|
||||
} else {
|
||||
loadAllocateBatchNoOptions()
|
||||
}
|
||||
}
|
||||
|
||||
// 套餐系列搜索相关
|
||||
const searchSeriesOptions = ref<any[]>([])
|
||||
|
||||
@@ -939,22 +1053,7 @@
|
||||
})
|
||||
|
||||
// 搜索表单配置
|
||||
const formItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '全部'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '在库', value: 1 },
|
||||
{ label: '已分销', value: 2 },
|
||||
{ label: '已激活', value: 3 },
|
||||
{ label: '已停用', value: 4 }
|
||||
]
|
||||
},
|
||||
const baseFormItems: SearchFormItem[] = [
|
||||
{
|
||||
label: 'ICCID',
|
||||
prop: 'iccid',
|
||||
@@ -983,13 +1082,19 @@
|
||||
}))
|
||||
},
|
||||
{
|
||||
label: 'MSISDN',
|
||||
prop: 'msisdn',
|
||||
type: 'input',
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请输入MSISDN'
|
||||
}
|
||||
placeholder: '全部'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '在库', value: 1 },
|
||||
{ label: '已分销', value: 2 },
|
||||
{ label: '已激活', value: 3 },
|
||||
{ label: '已停用', value: 4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '卡虚拟号',
|
||||
@@ -1000,6 +1105,15 @@
|
||||
placeholder: '请输入卡虚拟号'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'MSISDN',
|
||||
prop: 'msisdn',
|
||||
type: 'input',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '请输入MSISDN'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '绑定设备虚拟号',
|
||||
prop: 'device_virtual_no',
|
||||
@@ -1085,6 +1199,9 @@
|
||||
}
|
||||
]
|
||||
|
||||
// 搜索表单配置
|
||||
const formItems: SearchFormItem[] = baseFormItems
|
||||
|
||||
// 列配置
|
||||
const columnOptions = [
|
||||
{ label: 'ICCID', prop: 'iccid' },
|
||||
@@ -1521,6 +1638,8 @@
|
||||
batch_no: '',
|
||||
remark: ''
|
||||
})
|
||||
loadAllocateCarrierOptions()
|
||||
loadAllocateBatchNoOptions()
|
||||
if (allocateFormRef.value) {
|
||||
allocateFormRef.value.resetFields()
|
||||
}
|
||||
@@ -1545,6 +1664,8 @@
|
||||
if (recallFormRef.value) {
|
||||
recallFormRef.value.resetFields()
|
||||
}
|
||||
loadAllocateCarrierOptions()
|
||||
loadAllocateBatchNoOptions()
|
||||
}
|
||||
|
||||
// 关闭批量分配对话框
|
||||
|
||||
@@ -233,7 +233,6 @@
|
||||
const columnOptions = [
|
||||
{ label: '任务编号', prop: 'task_no' },
|
||||
{ label: '任务状态', prop: 'status' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '总数', prop: 'total_count' },
|
||||
{ label: '成功数', prop: 'success_count' },
|
||||
{ label: '失败数', prop: 'fail_count' },
|
||||
@@ -241,6 +240,8 @@
|
||||
{ label: '开始时间', prop: 'started_at' },
|
||||
{ label: '完成时间', prop: 'completed_at' },
|
||||
{ label: '错误信息', prop: 'error_message' },
|
||||
{ label: '批次号', prop: 'batch_no' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
@@ -311,12 +312,6 @@
|
||||
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_text)
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'total_count',
|
||||
label: '总数',
|
||||
@@ -343,6 +338,12 @@
|
||||
label: '跳过数',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'batch_no',
|
||||
label: '批次号',
|
||||
width: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'started_at',
|
||||
label: '开始时间',
|
||||
@@ -363,6 +364,12 @@
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: DeviceImportTask) => row.error_message || '-'
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
|
||||
@@ -344,7 +344,6 @@
|
||||
{ label: '任务状态', prop: 'status' },
|
||||
{ label: '运营商', prop: 'carrier_name' },
|
||||
{ label: '卡业务类型', prop: 'card_category' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '总数', prop: 'total_count' },
|
||||
{ label: '成功数', prop: 'success_count' },
|
||||
{ label: '失败数', prop: 'fail_count' },
|
||||
@@ -352,6 +351,8 @@
|
||||
{ label: '开始时间', prop: 'started_at' },
|
||||
{ label: '完成时间', prop: 'completed_at' },
|
||||
{ label: '错误信息', prop: 'error_message' },
|
||||
{ label: '批次号', prop: 'batch_no' },
|
||||
{ label: '文件名', prop: 'file_name' },
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
@@ -440,12 +441,6 @@
|
||||
: row.card_category
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 250,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'total_count',
|
||||
label: '总数',
|
||||
@@ -472,6 +467,12 @@
|
||||
label: '跳过数',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
prop: 'batch_no',
|
||||
label: '批次号',
|
||||
width: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'started_at',
|
||||
label: '开始时间',
|
||||
@@ -492,6 +493,12 @@
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: IotCardImportTask) => row.error_message || '-'
|
||||
},
|
||||
{
|
||||
prop: 'file_name',
|
||||
label: '文件名',
|
||||
minWidth: 250,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '创建时间',
|
||||
|
||||
@@ -803,7 +803,7 @@
|
||||
.role-info {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,9 +172,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 返回列表
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.push(RoutesAlias.AgentRecharge)
|
||||
router.back()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -492,6 +492,14 @@
|
||||
loadShops()
|
||||
})
|
||||
|
||||
let isFirstActivation = true
|
||||
onActivated(() => {
|
||||
if (!isFirstActivation) {
|
||||
getTableData()
|
||||
}
|
||||
isFirstActivation = false
|
||||
})
|
||||
|
||||
// 构建树形数据
|
||||
const buildTreeData = (items: ShopResponse[]) => {
|
||||
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
|
||||
|
||||
@@ -393,8 +393,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.push(RoutesAlias.RefundManagement)
|
||||
router.back()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -560,6 +560,14 @@
|
||||
searchOrdersForFilter('')
|
||||
})
|
||||
|
||||
let isFirstActivation = true
|
||||
onActivated(() => {
|
||||
if (!isFirstActivation) {
|
||||
getTableData()
|
||||
}
|
||||
isFirstActivation = false
|
||||
})
|
||||
|
||||
// 搜索店铺(用于搜索表单)
|
||||
const searchShops = async (query: string) => {
|
||||
try {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:items="searchFormItems"
|
||||
show-expand
|
||||
@reset="handleReset"
|
||||
label-width="100"
|
||||
label-width="90"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
|
||||
@@ -421,8 +421,7 @@
|
||||
config: {
|
||||
maxlength: 50,
|
||||
clearable: true
|
||||
},
|
||||
labelWidth: '100'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '资产标识符',
|
||||
@@ -431,9 +430,8 @@
|
||||
config: {
|
||||
maxlength: 30,
|
||||
clearable: true,
|
||||
placeholder: 'ICCID/VirtualNo/IMEI/SN/MSISDN'
|
||||
},
|
||||
labelWidth: '100'
|
||||
placeholder: 'ICCID/VirtualNo/IMEI/MSISDN/SN'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '买家手机号',
|
||||
@@ -443,8 +441,24 @@
|
||||
config: {
|
||||
maxlength: 20,
|
||||
clearable: true
|
||||
},
|
||||
labelWidth: '100'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '所属代理商',
|
||||
prop: 'seller_shop_id',
|
||||
type: 'select',
|
||||
placeholder: '请选择所属代理商',
|
||||
options: () =>
|
||||
shopOptions.value.map((shop) => ({
|
||||
label: shop.shop_name,
|
||||
value: shop.id
|
||||
})),
|
||||
config: {
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
remote: true,
|
||||
remoteMethod: (query: string) => searchShops(query)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '支付方式',
|
||||
@@ -454,7 +468,7 @@
|
||||
options: [
|
||||
{ label: '钱包支付', value: 'wallet' },
|
||||
{ label: '微信支付', value: 'wechat' },
|
||||
{ label: '支付宝支付', value: 'alipay' },
|
||||
// { label: '支付宝支付', value: 'alipay' },
|
||||
{ label: '线下支付', value: 'offline' }
|
||||
],
|
||||
config: {
|
||||
@@ -502,23 +516,6 @@
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '所属代理商',
|
||||
prop: 'seller_shop_id',
|
||||
type: 'select',
|
||||
placeholder: '请选择所属代理商',
|
||||
options: () =>
|
||||
shopOptions.value.map((shop) => ({
|
||||
label: shop.shop_name,
|
||||
value: shop.id
|
||||
})),
|
||||
config: {
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
remote: true,
|
||||
remoteMethod: (query: string) => searchShops(query)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '订单渠道',
|
||||
prop: 'purchase_role',
|
||||
@@ -526,9 +523,9 @@
|
||||
placeholder: '请选择订单渠道',
|
||||
options: [
|
||||
{ label: '自己购买', value: 'self_purchase' },
|
||||
{ label: '上级代理购买', value: 'purchased_by_parent' },
|
||||
{ label: '平台代购', value: 'purchased_by_platform' },
|
||||
{ label: '给下级购买', value: 'purchase_for_subordinate' }
|
||||
// { label: '上级代理购买', value: 'purchased_by_parent' },
|
||||
{ label: '平台代购', value: 'purchased_by_platform' }
|
||||
// { label: '给下级购买', value: 'purchase_for_subordinate' }
|
||||
],
|
||||
config: {
|
||||
clearable: true
|
||||
@@ -565,6 +562,7 @@
|
||||
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||
{ label: '订单渠道', prop: 'purchase_role' },
|
||||
{ label: '购买备注', prop: 'purchase_remark' },
|
||||
{ label: '下单时间', prop: 'created_at' },
|
||||
{ label: '操作者', prop: 'operator_name' },
|
||||
{ label: '支付状态', prop: 'payment_status' },
|
||||
{ label: '佣金状态', prop: 'commission_status_name' },
|
||||
@@ -914,6 +912,7 @@
|
||||
prop: 'asset_identifier',
|
||||
label: '资产标识符',
|
||||
width: 180,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => row.asset_identifier || '-'
|
||||
},
|
||||
{
|
||||
@@ -942,6 +941,12 @@
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => row.purchase_remark || '-'
|
||||
},
|
||||
{
|
||||
prop: 'created_at',
|
||||
label: '下单时间',
|
||||
width: 180,
|
||||
formatter: (row: Order) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operator_name',
|
||||
label: '操作者',
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { h, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { PackageManageService, PackageSeriesService } from '@/api/modules'
|
||||
import { ElMessage, ElMessageBox, ElTag, ElSwitch, ElInputNumber } from 'element-plus'
|
||||
@@ -922,6 +922,13 @@
|
||||
getTableData()
|
||||
})
|
||||
|
||||
// 监听对话框打开,加载系列选项
|
||||
watch(dialogVisible, (visible) => {
|
||||
if (visible) {
|
||||
loadSeriesOptions()
|
||||
}
|
||||
})
|
||||
|
||||
// 加载系列选项(用于新增/编辑对话框,默认加载10条)
|
||||
const loadSeriesOptions = async (seriesName?: string) => {
|
||||
seriesLoading.value = true
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增套餐系列' : '编辑套餐系列'"
|
||||
width="30%"
|
||||
width="40%"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
@@ -291,7 +291,13 @@
|
||||
<span class="title-text">强充配置</span>
|
||||
</div>
|
||||
|
||||
<ElFormItem label="强充金额">
|
||||
<ElFormItem label="启用强充">
|
||||
<ElSwitch
|
||||
v-model="form.one_time_commission_config.enable_force_recharge"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="强充金额" v-if="form.one_time_commission_config.enable_force_recharge">
|
||||
<ElInputNumber
|
||||
v-model="form.one_time_commission_config.force_amount"
|
||||
:min="0"
|
||||
@@ -305,7 +311,7 @@
|
||||
>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="强充计算类型">
|
||||
<ElFormItem label="强充计算类型" v-if="form.one_time_commission_config.enable_force_recharge">
|
||||
<ElRadioGroup v-model="form.one_time_commission_config.force_calc_type">
|
||||
<ElRadio value="fixed">固定</ElRadio>
|
||||
<ElRadio value="dynamic">动态</ElRadio>
|
||||
@@ -483,7 +489,8 @@
|
||||
{
|
||||
label: '强充状态',
|
||||
prop: 'enable_force_recharge',
|
||||
formatter: (row: any) => (row.one_time_commission_config?.enable_force_recharge ? '启用' : '未启用')
|
||||
formatter: (row: any) =>
|
||||
row.one_time_commission_config?.enable_force_recharge ? '启用' : '未启用'
|
||||
},
|
||||
{
|
||||
label: '强充金额',
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增代理系列授权' : '编辑代理系列授权'"
|
||||
width="40%"
|
||||
width="50%"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleDialogClosed"
|
||||
>
|
||||
@@ -973,13 +973,11 @@
|
||||
loading: shopLoading.value,
|
||||
placeholder: '请选择或搜索分配者店铺'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '平台', value: 0 },
|
||||
...searchAllocatorShopOptions.value.map((s) => ({
|
||||
options: () =>
|
||||
searchAllocatorShopOptions.value.map((s) => ({
|
||||
label: s.shop_name,
|
||||
value: s.id
|
||||
}))
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
@@ -1337,10 +1335,6 @@
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSeriesOptions()
|
||||
loadSearchSeriesOptions()
|
||||
loadSearchShopOptions()
|
||||
loadSearchAllocatorShopOptions()
|
||||
getTableData()
|
||||
})
|
||||
|
||||
@@ -1647,7 +1641,7 @@
|
||||
// 搜索系列(用于搜索栏)
|
||||
const handleSearchSeries = async (query: string) => {
|
||||
if (!query) {
|
||||
loadSearchSeriesOptions()
|
||||
await loadSearchSeriesOptions()
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -1668,7 +1662,7 @@
|
||||
// 搜索店铺(用于搜索栏-被分配的店铺)
|
||||
const handleSearchShop = async (query: string) => {
|
||||
if (!query) {
|
||||
loadSearchShopOptions()
|
||||
await loadSearchShopOptions()
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -1688,7 +1682,7 @@
|
||||
// 搜索分配者店铺(用于搜索栏)
|
||||
const handleSearchAllocatorShop = async (query: string) => {
|
||||
if (!query) {
|
||||
loadSearchAllocatorShopOptions()
|
||||
await loadSearchAllocatorShopOptions()
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -1767,6 +1761,8 @@
|
||||
|
||||
// 每次打开对话框时重新加载店铺列表,确保获取最新的店铺数据
|
||||
await loadShopListForDialog()
|
||||
// 加载系列选项
|
||||
await loadSeriesOptions()
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
// 编辑模式:先调用详情接口获取完整数据
|
||||
|
||||
@@ -245,6 +245,7 @@
|
||||
return actions
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
@@ -247,9 +247,9 @@
|
||||
return sections
|
||||
})
|
||||
|
||||
// 返回列表
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.push(RoutesAlias.WechatConfig)
|
||||
router.back()
|
||||
}
|
||||
|
||||
// 获取详情数据
|
||||
|
||||
@@ -1104,7 +1104,7 @@
|
||||
|
||||
.role-info {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.role-name {
|
||||
|
||||
Reference in New Issue
Block a user