Files
device-voice-h5/pages/package-order/package-order.vue
2026-04-11 14:42:14 +08:00

131 lines
4.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<view v-if="packageList.length === 0 && !loading" class="empty-state">
<view class="empty-icon">📦</view>
<view class="empty-title">暂无可购套餐</view>
<view class="empty-desc">当前资产暂无适用的套餐</view>
</view>
<view class="card package-card" v-for="item in packageList" :key="item.package_id">
<view class="package-header flex-row-sb">
<view class="package-name">{{ item.package_name }}</view>
<view class="tag-apple" :class="item.is_addon ? 'tag-warning' : 'tag-primary'">
{{ item.is_addon ? '加油包' : '正式套餐' }}
</view>
</view>
<view class="package-price">¥{{ formatMoney(item.retail_price) }}</view>
<view class="package-desc">{{ item.description }}</view>
<view class="package-info">
<view class="info-item">流量: {{ formatData(item.data_allowance, item.data_unit) }}</view>
<view class="info-item">有效: {{ item.validity_days }}</view>
</view>
<view class="btn">
<up-button type="primary" @click="buyPackage(item)">立即订购</up-button>
</view>
</view>
<up-modal title="确认购买" :show="showModal" showCancelButton @confirm="confirmBuy" @cancel="showModal=false">
<view style="padding: 40rpx; text-align: center;">
<view>套餐名称{{ currentPackage?.package_name }}</view>
<view class="mt-20">价格¥{{ formatMoney(currentPackage?.retail_price) }}</view>
</view>
</up-modal>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { assetApi, orderApi } from '@/api/index.js';
import { useUserStore } from '@/store/index.js';
const userStore = useUserStore();
let showModal = ref(false);
let currentPackage = ref(null);
let packageList = reactive([]);
let loading = ref(false);
const formatMoney = (amount) => {
if (!amount && amount !== 0) return '0.00';
return (amount / 100).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
const formatData = (allowance, unit) => {
if (unit === 'MB') {
return allowance >= 1024 ? (allowance / 1024).toFixed(0) + ' GB' : allowance + ' MB';
}
return allowance + ' ' + unit;
};
const loadPackages = async () => {
loading.value = true;
try {
const data = await assetApi.getPackages(userStore.state.identifier);
packageList.splice(0, packageList.length, ...(data.packages || []));
} catch (e) {
console.error('加载套餐列表失败', e);
}
loading.value = false;
};
const buyPackage = (item) => {
currentPackage.value = item;
showModal.value = true;
};
const confirmBuy = async () => {
try {
await orderApi.create(userStore.state.identifier, [currentPackage.value.package_id]);
uni.showToast({ title: '购买成功', icon: 'success' });
} catch (e) {
console.error('购买套餐失败', e);
}
showModal.value = false;
};
onMounted(() => {
loadPackages();
});
</script>
<style lang="scss" scoped>
.container {
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
min-height: 400rpx;
.empty-icon { font-size: 120rpx; margin-bottom: 30rpx; opacity: 0.6; }
.empty-title { font-size: 32rpx; font-weight: 600; color: var(--text-primary); margin-bottom: 16rpx; }
.empty-desc { font-size: 26rpx; color: var(--text-tertiary); text-align: center; }
}
.package-card {
margin-bottom: var(--space-md);
.package-header {
margin-bottom: var(--space-sm);
.package-name { font-size: 32rpx; font-weight: 600; color: var(--text-primary); }
}
.package-price {
font-size: 40rpx;
font-weight: bold;
color: var(--warning);
margin-bottom: var(--space-sm);
}
.package-desc {
font-size: 26rpx;
color: var(--text-secondary);
margin-bottom: var(--space-sm);
}
.package-info {
display: flex;
gap: var(--space-lg);
margin-bottom: var(--space-md);
.info-item { font-size: 24rpx; color: var(--text-tertiary); }
}
.btn { margin-top: var(--space-sm); }
}
}
</style>