fix(operator): fix operator edit issue
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m25s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m25s
This commit is contained in:
@@ -34,6 +34,12 @@
|
||||
<ElDescriptionsItem label="套餐总量">
|
||||
{{ formatDataSize(currentPackage.package_total_data || 0) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="套餐类型">
|
||||
{{ getPackageTypeName(currentPackage.package_type) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="虚流量比例">
|
||||
{{ currentPackage.virtual_ratio || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
<!-- 套餐时长 -->
|
||||
<ElDescriptionsItem v-if="currentPackage.duration_days" label="套餐时长" :span="3">
|
||||
@@ -165,6 +171,15 @@
|
||||
const handleShowRecharge = () => {
|
||||
emit('showRecharge')
|
||||
}
|
||||
|
||||
// 套餐类型名称
|
||||
const getPackageTypeName = (type?: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
formal: '正式套餐',
|
||||
addon: '加油包'
|
||||
}
|
||||
return typeMap[type || ''] || '-'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="package-table-wrapper">
|
||||
<ElTable
|
||||
v-if="packageList && packageList.length > 0"
|
||||
:data="packageList"
|
||||
class="package-table"
|
||||
border
|
||||
max-height="400"
|
||||
>
|
||||
<ElTableColumn label="套餐名称" min-width="150">
|
||||
<div v-if="packageList && packageList.length > 0" class="table-scroll-container">
|
||||
<ElTable
|
||||
:data="packageList"
|
||||
class="package-table"
|
||||
border
|
||||
>
|
||||
<ElTableColumn label="套餐名称" width="150">
|
||||
<template #default="scope">
|
||||
<ElButton
|
||||
type="primary"
|
||||
@@ -26,9 +25,9 @@
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="package_price" label="套餐价格" width="120">
|
||||
<ElTableColumn label="套餐价格" width="120">
|
||||
<template #default="scope">
|
||||
{{ formatAmount(scope.row.package_price) }}
|
||||
{{ formatAmount(scope.row.package_price || 0) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="status_name" label="套餐状态" width="100">
|
||||
@@ -111,13 +110,25 @@
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</ElTable>
|
||||
<ElPagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
class="pagination"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
<ElEmpty v-else description="暂无套餐数据" :image-size="100" />
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import {
|
||||
ElCard,
|
||||
ElTable,
|
||||
@@ -125,41 +136,55 @@
|
||||
ElTag,
|
||||
ElButton,
|
||||
ElProgress,
|
||||
ElEmpty
|
||||
ElEmpty,
|
||||
ElPagination
|
||||
} from 'element-plus'
|
||||
import { useAssetFormatters } from '../composables/useAssetFormatters'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import type { AssetPackageUsageRecord } from '@/types/api'
|
||||
|
||||
// Props
|
||||
interface PackageInfo {
|
||||
id: number
|
||||
package_id: number
|
||||
package_name: string
|
||||
package_price: number
|
||||
data_limit_mb: number
|
||||
data_usage_mb: number
|
||||
virtual_limit_mb: number
|
||||
virtual_used_mb: number
|
||||
virtual_remain_mb: number
|
||||
activated_at: string
|
||||
expires_at: string
|
||||
status: number
|
||||
status_name: string
|
||||
}
|
||||
// Props 使用 API 类型
|
||||
type PackageInfo = AssetPackageUsageRecord
|
||||
|
||||
interface Props {
|
||||
packageList: PackageInfo[]
|
||||
total?: number
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
// Emits
|
||||
interface Emits {
|
||||
(e: 'showDailyRecords', payload: { packageRow: PackageInfo }): void
|
||||
(e: 'page-change', page: number): void
|
||||
(e: 'size-change', size: number): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
// 分页状态
|
||||
const pagination = ref({
|
||||
page: props.page,
|
||||
pageSize: props.pageSize,
|
||||
total: props.total
|
||||
})
|
||||
|
||||
// 监听props变化更新分页
|
||||
watch(
|
||||
() => [props.page, props.pageSize, props.total],
|
||||
([newPage, newPageSize, newTotal]) => {
|
||||
pagination.value.page = newPage
|
||||
pagination.value.pageSize = newPageSize
|
||||
pagination.value.total = newTotal
|
||||
}
|
||||
)
|
||||
|
||||
// 使用格式化工具
|
||||
const {
|
||||
formatDataSize,
|
||||
@@ -173,12 +198,30 @@
|
||||
const handleShowDailyRecords = (packageRow: PackageInfo) => {
|
||||
emit('showDailyRecords', { packageRow })
|
||||
}
|
||||
|
||||
// 页码变化
|
||||
const handlePageChange = (page: number) => {
|
||||
emit('page-change', page)
|
||||
}
|
||||
|
||||
// 每页条数变化
|
||||
const handleSizeChange = (size: number) => {
|
||||
emit('size-change', size)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.info-card {
|
||||
&.package-info {
|
||||
width: 100%;
|
||||
overflow: hidden; // 防止内容溢出
|
||||
|
||||
.package-table-wrapper {
|
||||
.table-scroll-container {
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.package-table {
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
@@ -215,6 +258,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 16px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,6 +47,26 @@
|
||||
</template>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="form.payment_method === 'offline'"
|
||||
label="支付凭证"
|
||||
prop="payment_voucher_key"
|
||||
>
|
||||
<ElUpload
|
||||
ref="uploadRef"
|
||||
:auto-upload="false"
|
||||
:on-change="handleVoucherFileChange"
|
||||
:on-remove="handleRemoveVoucher"
|
||||
accept="image/*"
|
||||
list-type="picture-card"
|
||||
:limit="1"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">支持 jpg、png 格式,文件大小不超过 5MB</div>
|
||||
</template>
|
||||
</ElUpload>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -66,10 +86,13 @@
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElButton,
|
||||
ElMessage
|
||||
ElMessage,
|
||||
ElUpload,
|
||||
ElIcon
|
||||
} from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { OrderService, PackageManageService } from '@/api/modules'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules, UploadFile, UploadInstance } from 'element-plus'
|
||||
import { OrderService, PackageManageService, StorageService } from '@/api/modules'
|
||||
import type { CreateOrderRequest, PackageResponse } from '@/types/api'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
@@ -91,13 +114,15 @@
|
||||
|
||||
const userStore = useUserStore()
|
||||
const formRef = ref<FormInstance>()
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const loading = ref(false)
|
||||
const packageSearchLoading = ref(false)
|
||||
const packageOptions = ref<PackageResponse[]>([])
|
||||
|
||||
const form = reactive({
|
||||
package_id: undefined as number | undefined,
|
||||
payment_method: 'wallet' as 'wallet' | 'offline'
|
||||
payment_method: 'wallet' as 'wallet' | 'offline',
|
||||
payment_voucher_key: undefined as string | undefined
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
@@ -172,6 +197,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 处理支付凭证文件变化
|
||||
const handleVoucherFileChange = async (uploadFile: UploadFile) => {
|
||||
const file = uploadFile.raw
|
||||
if (!file) return
|
||||
|
||||
// 验证文件类型
|
||||
const isImage = file.type.startsWith('image/')
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件!')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小
|
||||
const isLt5M = file.size / 1024 / 1024 < 5
|
||||
if (!isLt5M) {
|
||||
ElMessage.error('图片大小不能超过 5MB!')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
|
||||
// 开始上传
|
||||
try {
|
||||
ElMessage.info('正在上传支付凭证...')
|
||||
|
||||
// 1. 获取上传 URL
|
||||
const uploadUrlRes = await StorageService.getUploadUrl({
|
||||
file_name: file.name,
|
||||
content_type: file.type,
|
||||
purpose: 'attachment'
|
||||
})
|
||||
|
||||
if (uploadUrlRes.code !== 0) {
|
||||
ElMessage.error(uploadUrlRes.msg || '获取上传地址失败')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
|
||||
const { upload_url, file_key } = uploadUrlRes.data
|
||||
|
||||
// 2. 上传文件到对象存储
|
||||
await StorageService.uploadFile(upload_url, file, file.type)
|
||||
|
||||
// 3. 保存 file_key
|
||||
form.payment_voucher_key = file_key
|
||||
ElMessage.success('支付凭证上传成功')
|
||||
} catch (error: any) {
|
||||
console.error('上传支付凭证失败:', error)
|
||||
ElMessage.error(error?.message || '上传失败')
|
||||
uploadRef.value?.clearFiles()
|
||||
}
|
||||
}
|
||||
|
||||
// 移除支付凭证
|
||||
const handleRemoveVoucher = () => {
|
||||
form.payment_voucher_key = undefined
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false
|
||||
}
|
||||
@@ -187,6 +270,12 @@
|
||||
return
|
||||
}
|
||||
|
||||
// 线下支付时验证支付凭证
|
||||
if (form.payment_method === 'offline' && !form.payment_voucher_key) {
|
||||
ElMessage.error('线下支付需要上传支付凭证')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 创建订单(后端接收数组,前端单选转为单元素数组)
|
||||
@@ -196,6 +285,11 @@
|
||||
payment_method: form.payment_method
|
||||
}
|
||||
|
||||
// 线下支付时,添加支付凭证
|
||||
if (form.payment_method === 'offline' && form.payment_voucher_key) {
|
||||
data.payment_voucher_key = form.payment_voucher_key
|
||||
}
|
||||
|
||||
await OrderService.createOrder(data)
|
||||
|
||||
// 根据支付方式显示不同的成功消息
|
||||
@@ -214,7 +308,6 @@
|
||||
return
|
||||
}
|
||||
console.error('创建订单失败:', error)
|
||||
ElMessage.error(error?.message || '创建订单失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -224,7 +317,9 @@
|
||||
formRef.value?.resetFields()
|
||||
form.package_id = undefined
|
||||
form.payment_method = 'wallet'
|
||||
form.payment_voucher_key = undefined
|
||||
packageOptions.value = []
|
||||
uploadRef.value?.clearFiles()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -50,11 +50,12 @@
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
cardNo?: string // 流量卡号(ICCID)
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm', data: { enabled: number; ssid: string; password: string }): void
|
||||
(e: 'confirm', data: { card_no: string; enabled: number; ssid: string; password: string }): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@@ -104,6 +105,7 @@
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
emit('confirm', {
|
||||
card_no: props.cardNo || '',
|
||||
enabled: form.enabled,
|
||||
ssid: form.ssid,
|
||||
password: form.password
|
||||
|
||||
Reference in New Issue
Block a user