This commit is contained in:
@@ -10,7 +10,11 @@
|
||||
</div>
|
||||
<div class="card-header-right">
|
||||
<ElButton
|
||||
v-if="cardInfo?.asset_type === 'card' && hasAuth(JULY_PERMISSIONS.speedTier.view)"
|
||||
v-if="
|
||||
cardInfo?.asset_type === 'card' &&
|
||||
hasAuth(JULY_PERMISSIONS.speedTier.view) &&
|
||||
hasAuth(JULY_PERMISSIONS.speedTier.update)
|
||||
"
|
||||
type="primary"
|
||||
link
|
||||
@click="emit('showSpeedLimit')"
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
v-model="createRefundDialogVisible"
|
||||
:initial-order-id="currentRefundOrderId"
|
||||
:initial-order-no="currentRefundOrderNo"
|
||||
:initial-package-usage-id="currentRefundPackageUsageId"
|
||||
@success="handleRefundSuccess"
|
||||
/>
|
||||
|
||||
@@ -279,12 +280,7 @@
|
||||
width="30%"
|
||||
@closed="resetUsedDataForm"
|
||||
>
|
||||
<ElForm
|
||||
ref="usedDataFormRef"
|
||||
:model="usedDataForm"
|
||||
:rules="usedDataRules"
|
||||
label-width="90px"
|
||||
>
|
||||
<ElForm ref="usedDataFormRef" :model="usedDataForm" :rules="usedDataRules" label-width="90px">
|
||||
<ElFormItem label="套餐名称">
|
||||
<span>{{ selectedPackage?.package_name || '-' }}</span>
|
||||
</ElFormItem>
|
||||
@@ -416,6 +412,7 @@
|
||||
const createRefundDialogVisible = ref(false)
|
||||
const currentRefundOrderId = ref<number | undefined>(undefined)
|
||||
const currentRefundOrderNo = ref<string | undefined>(undefined)
|
||||
const currentRefundPackageUsageId = ref<number | undefined>(undefined)
|
||||
const selectedPackage = ref<PackageInfo>()
|
||||
const usedDataDialogVisible = ref(false)
|
||||
const expiresAtDialogVisible = ref(false)
|
||||
@@ -474,10 +471,12 @@
|
||||
}
|
||||
currentRefundOrderId.value = row.order_id
|
||||
currentRefundOrderNo.value = row.order_no
|
||||
currentRefundPackageUsageId.value = row.package_usage_id ?? row.id
|
||||
createRefundDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleRefundSuccess = () => {
|
||||
currentRefundPackageUsageId.value = undefined
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,66 @@
|
||||
<template>
|
||||
<ElDialog v-model="visible" title="设置限速" width="500px">
|
||||
<ElDialog
|
||||
v-model="visible"
|
||||
title="设置限速"
|
||||
width="500px"
|
||||
:lock-scroll="true"
|
||||
:z-index="4000"
|
||||
modal-class="speed-limit-modal"
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<ElFormItem label="固定档位" prop="code">
|
||||
<ElSelect v-model="form.code" style="width: 100%">
|
||||
<ElOption v-for="tier in speedTiers" :key="tier.code" :label="tier.label" :value="tier.code" />
|
||||
<ElSelect
|
||||
v-model="form.code"
|
||||
style="width: 100%"
|
||||
:teleported="true"
|
||||
append-to="body"
|
||||
popper-class="speed-limit-select-popper"
|
||||
:popper-style="{ zIndex: 4001 }"
|
||||
>
|
||||
<ElOption
|
||||
v-for="tier in speedTiers"
|
||||
:key="tier.code"
|
||||
:label="tier.label"
|
||||
:value="tier.code"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="handleCancel">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleConfirm" :loading="loading"> 确认设置 </ElButton>
|
||||
<ElButton
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
:loading="confirmLoading"
|
||||
:disabled="confirmLoading"
|
||||
>
|
||||
确认设置
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { ref, reactive, computed, onUnmounted, watch } from 'vue'
|
||||
import { ElDialog, ElForm, ElFormItem, ElSelect, ElOption, ElButton } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
confirmLoading?: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm', data: { code: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 }): void
|
||||
(e: 'confirm', data: { code: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 }): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
confirmLoading: false
|
||||
})
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(false)
|
||||
|
||||
const speedTiers = [
|
||||
{ code: -1, label: '不限速' },
|
||||
{ code: 0, label: '0kbps' },
|
||||
@@ -59,10 +86,25 @@
|
||||
})
|
||||
|
||||
// 监听对话框打开,重置表单
|
||||
watch(visible, (newVal) => {
|
||||
if (newVal) {
|
||||
form.code = 3
|
||||
}
|
||||
const setLayoutScrollLock = (locked: boolean) => {
|
||||
document
|
||||
.querySelector<HTMLElement>('.layouts')
|
||||
?.classList.toggle('speed-limit-scroll-locked', locked)
|
||||
}
|
||||
|
||||
watch(
|
||||
visible,
|
||||
(newVal) => {
|
||||
setLayoutScrollLock(newVal)
|
||||
if (newVal) {
|
||||
form.code = 3
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
setLayoutScrollLock(false)
|
||||
})
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -70,6 +112,7 @@
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (props.confirmLoading) return
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
@@ -83,4 +126,28 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss">
|
||||
.speed-limit-modal .el-dialog__body {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.speed-limit-select-popper {
|
||||
z-index: 4001 !important;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.speed-limit-select-popper .el-select-dropdown__wrap,
|
||||
.speed-limit-select-popper .el-scrollbar__wrap {
|
||||
max-height: none !important;
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
|
||||
.speed-limit-modal {
|
||||
overflow: hidden;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.layouts.speed-limit-scroll-locked {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
@show-switch-card="showSwitchCardDialog"
|
||||
@show-switch-mode="showSwitchModeDialog"
|
||||
@show-set-wifi="showSetWiFiDialog"
|
||||
@show-realname-policy="showRealnamePolicyDialog"
|
||||
@show-speed-limit="speedLimitDialogVisible = true"
|
||||
@show-realname-policy="showRealnamePolicyDialog"
|
||||
@show-speed-limit="speedLimitDialogVisible = true"
|
||||
@show-update-realname-status="showUpdateRealnameStatusDialog"
|
||||
@enable-binding-card="handleEnableBindingCard"
|
||||
@disable-binding-card="handleDisableBindingCard"
|
||||
@@ -174,7 +174,11 @@
|
||||
:current-realname-status="bindingCardRealnameStatusValue"
|
||||
@success="handleBindingCardRealnameStatusSuccess"
|
||||
/>
|
||||
<SpeedLimitDialog v-model="speedLimitDialogVisible" @confirm="handleSpeedTierConfirm" />
|
||||
<SpeedLimitDialog
|
||||
v-model="speedLimitDialogVisible"
|
||||
:confirm-loading="speedTierSubmitting"
|
||||
@confirm="handleSpeedTierConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -183,7 +187,10 @@
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElCard, ElEmpty } from 'element-plus'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import { CardService, DeviceService } from '@/api/modules'
|
||||
import { CardService, DeviceService } from '@/api/modules'
|
||||
import { JULY_PERMISSIONS } from '@/config/constants'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import {
|
||||
formatRemainingTime,
|
||||
FrontendRateLimitError,
|
||||
@@ -204,9 +211,9 @@
|
||||
SwitchModeDialog,
|
||||
WiFiConfigDialog,
|
||||
PackageRechargeDialog,
|
||||
DailyRecordsDialog,
|
||||
OrderHistoryDialog,
|
||||
SpeedLimitDialog
|
||||
DailyRecordsDialog,
|
||||
OrderHistoryDialog,
|
||||
SpeedLimitDialog
|
||||
} from './components/dialogs'
|
||||
import SwitchCardDialog from '@/components/device/SwitchCardDialog.vue'
|
||||
import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue'
|
||||
@@ -235,6 +242,7 @@
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { hasAuth } = useAuth()
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const assetSearchCardRef = ref<InstanceType<typeof AssetSearchCard>>()
|
||||
@@ -294,6 +302,7 @@
|
||||
// 绑定卡实名状态更新
|
||||
const bindingCardRealnameStatusDialogVisible = ref(false)
|
||||
const speedLimitDialogVisible = ref(false)
|
||||
const speedTierSubmitting = ref(false)
|
||||
const bindingCardRealnameStatusIccid = ref('')
|
||||
const bindingCardRealnameStatusValue = ref<number>(0)
|
||||
|
||||
@@ -806,12 +815,34 @@
|
||||
}
|
||||
}
|
||||
const handleSpeedTierConfirm = async (data: { code: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 }) => {
|
||||
if (!cardInfo.value?.iccid) return
|
||||
const response = await CardService.setSpeedTier(cardInfo.value.iccid, data.code)
|
||||
if (response.code === 0) {
|
||||
if (!hasAuth(JULY_PERMISSIONS.speedTier.update)) {
|
||||
ElMessage.error('暂无设置限速权限')
|
||||
return
|
||||
}
|
||||
|
||||
const iccid = cardInfo.value?.iccid
|
||||
if (!iccid) {
|
||||
ElMessage.error('当前卡缺少 ICCID,无法设置限速')
|
||||
return
|
||||
}
|
||||
if (speedTierSubmitting.value) return
|
||||
|
||||
speedTierSubmitting.value = true
|
||||
try {
|
||||
const response = await CardService.setSpeedTier(iccid, data.code)
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.msg || '限速设置失败')
|
||||
return
|
||||
}
|
||||
|
||||
ElMessage.success(`限速设置成功:${response.data.speed_tier_name}`)
|
||||
speedLimitDialogVisible.value = false
|
||||
await handleRefresh()
|
||||
} catch (error) {
|
||||
console.error('设置限速失败:', error)
|
||||
ElMessage.error(normalizeApiError(error).message)
|
||||
} finally {
|
||||
speedTierSubmitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user