This commit is contained in:
@@ -9,6 +9,7 @@ import { get, post, put } from '@/utils/request'
|
||||
export interface LoginParams {
|
||||
username: string
|
||||
password: string
|
||||
device?: 'h5'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,6 +19,7 @@ export interface LoginResponse {
|
||||
access_token: string
|
||||
refresh_token: string
|
||||
expires_in: number
|
||||
buttons?: string[]
|
||||
user: {
|
||||
id: number
|
||||
username: string
|
||||
@@ -47,6 +49,7 @@ export interface UserInfo {
|
||||
status?: 'active' | 'inactive'
|
||||
create_time?: string
|
||||
last_login_time?: string
|
||||
buttons?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +65,12 @@ export interface ChangePasswordParams {
|
||||
* POST /api/auth/login
|
||||
*/
|
||||
export function login(data: LoginParams) {
|
||||
return post<LoginResponse>('/api/auth/login', { data })
|
||||
return post<LoginResponse>('/api/auth/login', {
|
||||
data: {
|
||||
...data,
|
||||
device: data.device ?? 'h5',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,30 +15,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/my-packages/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的套餐",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/commission/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的佣金",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/packages/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "套餐列表",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/assets/index",
|
||||
"style": {
|
||||
@@ -47,22 +23,6 @@
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/tags/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "标签管理",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/asset-search/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "资产详情",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agent-system/asset-detail/index",
|
||||
"style": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -71,6 +71,14 @@
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
const sessionButtonCodes = ref<string[]>([])
|
||||
|
||||
const ASSET_ACTION_BUTTON_CODES = {
|
||||
cardStop: 'asset:card_stop_h5',
|
||||
cardStart: 'asset:card_start_h5',
|
||||
deviceStop: 'asset:device_stop_h5',
|
||||
deviceStart: 'asset:device_start_h5',
|
||||
} as const
|
||||
|
||||
// 获取单卡列表(代理端)
|
||||
function getStandaloneCards(params : any) {
|
||||
@@ -130,10 +138,36 @@
|
||||
const carrierList = ref<CarrierInfo[]>([])
|
||||
const isAgent = computed(() => userInfo.value?.user_type === 3)
|
||||
const searchPlaceholder = computed(() => activeTab.value === 'card' ? '搜索ICCID' : '搜索设备号')
|
||||
const canShowDeviceStopButton = computed(() => hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.deviceStop))
|
||||
const canShowDeviceStartButton = computed(() => hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.deviceStart))
|
||||
|
||||
function hasStatusCode(error : unknown) {
|
||||
return typeof error === 'object' && error !== null && 'statusCode' in error
|
||||
}
|
||||
|
||||
function getSessionButtonCodes() {
|
||||
const cachedUserInfo = uni.getStorageSync('user_info')
|
||||
|
||||
if (!cachedUserInfo || typeof cachedUserInfo !== 'object' || !Array.isArray((cachedUserInfo as UserInfo).buttons)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return (cachedUserInfo as UserInfo).buttons!.filter((code) => {
|
||||
return typeof code === 'string' && code.trim().length > 0
|
||||
})
|
||||
}
|
||||
|
||||
function hasActionButtonPermission(code : string) {
|
||||
return sessionButtonCodes.value.includes(code)
|
||||
}
|
||||
|
||||
function canShowCardStopButton(card : CardInfo) {
|
||||
return card.network_status !== 0 && hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.cardStop)
|
||||
}
|
||||
|
||||
function canShowCardStartButton(card : CardInfo) {
|
||||
return card.network_status !== 1 && hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.cardStart)
|
||||
}
|
||||
// 运营商选择器选项
|
||||
const carrierOptions = computed(() => [
|
||||
{ label: '全部运营商', value: undefined },
|
||||
@@ -410,12 +444,12 @@
|
||||
if (asset.type === 'card') {
|
||||
const card = asset.raw as CardInfo
|
||||
uni.navigateTo({
|
||||
url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`,
|
||||
url: `/pages/agent-system/asset-detail/index?iccid=${card.iccid}`,
|
||||
})
|
||||
} else {
|
||||
const device = asset.raw as DeviceInfo
|
||||
uni.navigateTo({
|
||||
url: `/pages/agent-system/asset-search/index?virtual_no=${device.virtual_no}`,
|
||||
url: `/pages/agent-system/asset-detail/index?virtual_no=${device.virtual_no}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -575,6 +609,7 @@
|
||||
onMounted(async () => {
|
||||
// 1. 获取状态栏高度
|
||||
statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight || 20
|
||||
sessionButtonCodes.value = getSessionButtonCodes()
|
||||
|
||||
// 2. 加载用户信息(判断是企业端还是代理端)
|
||||
try {
|
||||
@@ -646,8 +681,7 @@
|
||||
<view v-if="isAgent"
|
||||
class="flex items-center gap-1.5 px-3 py-1.5 bg-[#f8fafc] rounded-full flex-shrink-0 max-w-36"
|
||||
@click="showShopPicker = true">
|
||||
<text class="text-12px truncate"
|
||||
:class="selectedShopFilter ? 'text-brand font-medium' : 'text-[#64748b]'">
|
||||
<text class="text-12px truncate" :class="selectedShopFilter ? 'text-brand font-medium' : 'text-[#64748b]'">
|
||||
{{ selectedShopName }}
|
||||
</text>
|
||||
<i class="i-mdi-chevron-down text-12px text-[#94a3b8] flex-shrink-0" />
|
||||
@@ -716,42 +750,30 @@
|
||||
<view class="flex items-center gap-2">
|
||||
<!-- IoT卡操作按钮 -->
|
||||
<template v-if="asset.type === 'card'">
|
||||
<template v-if="(asset.raw as CardInfo).network_status === 0">
|
||||
<view
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#52c41a] text-white shadow-sm shadow-[#52c41a]/30 active:scale-95 transition-transform"
|
||||
@click="handleStartCard(asset, $event)">
|
||||
复机
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="(asset.raw as CardInfo).network_status === 1">
|
||||
<view
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#ff4d4f] text-white shadow-sm shadow-[#ff4d4f]/30 active:scale-95 transition-transform"
|
||||
@click="handleStopCard(asset, $event)">
|
||||
停机
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#ff4d4f] text-white shadow-sm shadow-[#ff4d4f]/30 active:scale-95 transition-transform"
|
||||
@click="handleStopCard(asset, $event)">
|
||||
停机
|
||||
</view>
|
||||
<view
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#52c41a] text-white shadow-sm shadow-[#52c41a]/30 active:scale-95 transition-transform"
|
||||
@click="handleStartCard(asset, $event)">
|
||||
复机
|
||||
</view>
|
||||
</template>
|
||||
<view
|
||||
v-if="canShowCardStopButton(asset.raw as CardInfo)"
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#ff4d4f] text-white shadow-sm shadow-[#ff4d4f]/30 active:scale-95 transition-transform"
|
||||
@click="handleStopCard(asset, $event)">
|
||||
停机
|
||||
</view>
|
||||
<view
|
||||
v-if="canShowCardStartButton(asset.raw as CardInfo)"
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#52c41a] text-white shadow-sm shadow-[#52c41a]/30 active:scale-95 transition-transform"
|
||||
@click="handleStartCard(asset, $event)">
|
||||
复机
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 设备操作按钮 -->
|
||||
<template v-else>
|
||||
<view
|
||||
v-if="canShowDeviceStopButton"
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#ff4d4f] text-white shadow-sm shadow-[#ff4d4f]/30 active:scale-95 transition-transform"
|
||||
@click="handleStopDevice(asset, $event)">
|
||||
停机
|
||||
</view>
|
||||
<view
|
||||
v-if="canShowDeviceStartButton"
|
||||
class="px-3 py-1.5 rounded-10px text-12px font-medium bg-[#52c41a] text-white shadow-sm shadow-[#52c41a]/30 active:scale-95 transition-transform"
|
||||
@click="handleStartDevice(asset, $event)">
|
||||
复机
|
||||
@@ -766,11 +788,10 @@
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="activeTabHasMore" class="py-4 flex justify-center">
|
||||
<view
|
||||
class="min-w-32 px-5 py-2.5 rounded-full text-sm font-medium transition-all"
|
||||
class="min-w-32 px-5 py-2.5 rounded-full text-sm font-medium transition-all flex items-center justify-center"
|
||||
:class="loading
|
||||
? 'bg-[#f1f5f9] text-[#94a3b8]'
|
||||
: 'bg-brand text-white shadow-sm shadow-brand/30 active:scale-95'"
|
||||
@click="!loading && loadMore()">
|
||||
: 'bg-brand text-white shadow-sm shadow-brand/30 active:scale-95'" @click="!loading && loadMore()">
|
||||
<template v-if="loading">
|
||||
<i class="i-mdi-loading animate-spin text-base mr-1.5" />
|
||||
<text>加载中...</text>
|
||||
|
||||
@@ -261,7 +261,6 @@ function loadMore() {
|
||||
</view>
|
||||
<view class="flex items-center justify-between text-12px text-[#999]">
|
||||
<text>{{ formatTime(record.created_at) }}</text>
|
||||
<text v-if="record.order_id">订单 {{ record.order_id }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
// 直接跳转到佣金中心
|
||||
onMounted(() => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/agent-system/commission-center/index',
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bg-[#fafafa] min-h-screen flex items-center justify-center">
|
||||
<text class="text-14px text-[#999]">跳转中...</text>
|
||||
</view>
|
||||
</template>
|
||||
@@ -54,7 +54,7 @@ const stats = computed(() => {
|
||||
|
||||
async function loadCarriers() {
|
||||
try {
|
||||
const response = await getCarriers({ page: 1, size: 50 })
|
||||
const response = await getCarriers({ page: 1, size: 100 })
|
||||
carrierList.value = response?.items || []
|
||||
} catch (error) {
|
||||
console.error('加载运营商列表失败:', error)
|
||||
@@ -128,7 +128,7 @@ async function loadEnterpriseCards(isRefresh = false) {
|
||||
|
||||
function viewCardDetail(card: any) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`,
|
||||
url: `/pages/agent-system/asset-detail/index?iccid=${card.iccid}`,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ async function loadEnterpriseDevices(isRefresh = false) {
|
||||
|
||||
function viewDeviceDetail(device: any) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/agent-system/asset-search/index?virtual_no=${device.virtual_no}`,
|
||||
url: `/pages/agent-system/asset-detail/index?virtual_no=${device.virtual_no}`,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,6 @@
|
||||
shopId.value = user.shop_id || loginUserInfo?.shop_id || 0
|
||||
const fundSummaryUsername = loginUserInfo?.username || user.username
|
||||
|
||||
console.log('首页数据加载成功:', { user })
|
||||
|
||||
// 如果是代理账号,加载佣金数据
|
||||
if (user.user_type === 3) {
|
||||
await loadCommissionData(fundSummaryUsername)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
function goBack() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bg-[#fafafa] min-h-screen flex items-center justify-center pb-safe">
|
||||
<view class="px-6 text-center">
|
||||
<i class="i-mdi-hammer-wrench text-64px text-[#ccc] block mb-4" />
|
||||
<text class="text-18px font-600 text-[#333] block mb-2">功能开发中</text>
|
||||
<text class="text-14px text-[#999] block mb-6">
|
||||
我的套餐功能正在开发中,敬请期待
|
||||
</text>
|
||||
<view
|
||||
class="bg-[#212121] text-white rounded-full px-8 py-3 inline-block"
|
||||
@click="goBack"
|
||||
>
|
||||
<text class="text-15px">返回</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -1,245 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getEnterpriseCards } from '@/api/enterprise'
|
||||
import { getUserInfo } from '@/api/auth'
|
||||
|
||||
interface CardInfo {
|
||||
id: number
|
||||
iccid: string
|
||||
msisdn: string
|
||||
virtual_no?: string
|
||||
carrier_id: number
|
||||
carrier_name?: string
|
||||
package_name?: string
|
||||
status: number
|
||||
status_name: string
|
||||
network_status: number
|
||||
network_status_name: string
|
||||
}
|
||||
|
||||
// 企业ID
|
||||
const enterpriseId = ref<number>(0)
|
||||
// IoT卡列表
|
||||
const cardList = ref<CardInfo[]>([])
|
||||
// 加载状态
|
||||
const loading = ref(true)
|
||||
|
||||
// 套餐统计
|
||||
const packageStats = computed(() => {
|
||||
const total = cardList.value.length
|
||||
const hasPackage = cardList.value.filter(c => c.package_name).length
|
||||
const noPackage = total - hasPackage
|
||||
|
||||
return { total, hasPackage, noPackage }
|
||||
})
|
||||
|
||||
// 按套餐分组
|
||||
const packageGroups = computed(() => {
|
||||
const groups = new Map<string, CardInfo[]>()
|
||||
|
||||
cardList.value.forEach(card => {
|
||||
const pkg = card.package_name || '无套餐'
|
||||
if (!groups.has(pkg)) {
|
||||
groups.set(pkg, [])
|
||||
}
|
||||
groups.get(pkg)!.push(card)
|
||||
})
|
||||
|
||||
return Array.from(groups.entries()).map(([name, cards]) => ({
|
||||
name,
|
||||
count: cards.length,
|
||||
cards,
|
||||
}))
|
||||
})
|
||||
|
||||
// 加载数据
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
// 获取企业ID
|
||||
if (!enterpriseId.value) {
|
||||
const userInfo = uni.getStorageSync('user_info')
|
||||
if (userInfo?.enterprise_id) {
|
||||
enterpriseId.value = userInfo.enterprise_id
|
||||
} else {
|
||||
const apiUserInfo = await getUserInfo()
|
||||
if (!apiUserInfo.enterprise_id) {
|
||||
throw new Error('未找到企业ID')
|
||||
}
|
||||
enterpriseId.value = apiUserInfo.enterprise_id
|
||||
}
|
||||
}
|
||||
|
||||
// 获取企业卡列表
|
||||
const response = await getEnterpriseCards(enterpriseId.value)
|
||||
cardList.value = response?.items || []
|
||||
} catch (error: any) {
|
||||
console.error('加载数据失败:', error)
|
||||
if (error instanceof Error && error.message && !error.statusCode) {
|
||||
uni.$u.toast(error.message)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 查看IoT卡详情
|
||||
function viewCardDetail(card: CardInfo) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取卡片状态颜色
|
||||
function getStatusColor(status: number) {
|
||||
const map: Record<number, { bg: string, text: string }> = {
|
||||
1: { bg: '#f5f5f5', text: '#999' }, // 在库
|
||||
2: { bg: '#e8f5e9', text: '#2e7d32' }, // 已分销
|
||||
3: { bg: '#ffebee', text: '#c62828' }, // 已停用
|
||||
}
|
||||
return map[status] || { bg: '#f5f5f5', text: '#999' }
|
||||
}
|
||||
|
||||
// 获取网络状态颜色
|
||||
function getNetworkStatusColor(status: number) {
|
||||
return status === 1
|
||||
? { bg: '#e8f5e9', text: '#2e7d32' }
|
||||
: { bg: '#ffebee', text: '#c62828' }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bg-[#fafafa] min-h-screen pb-safe">
|
||||
<!-- 顶部统计 -->
|
||||
<view class="px-4 pt-4 pb-3">
|
||||
<view class="bg-white rounded-16px shadow-[0_2px_12px_rgba(0,0,0,0.06)] p-4">
|
||||
<text class="text-16px font-700 text-[#212121] block mb-3">套餐统计</text>
|
||||
<view class="grid grid-cols-3 gap-3">
|
||||
<view class="text-center">
|
||||
<text class="text-24px font-700 text-[#212121] block">{{ packageStats.total }}</text>
|
||||
<text class="text-11px text-[#999] mt-1">总IoT卡</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<text class="text-24px font-700 text-[var(--brand-primary)] block">{{ packageStats.hasPackage }}</text>
|
||||
<text class="text-11px text-[#999] mt-1">已订购</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<text class="text-24px font-700 text-[#999] block">{{ packageStats.noPackage }}</text>
|
||||
<text class="text-11px text-[#999] mt-1">未订购</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading" class="pt-20 flex flex-col items-center">
|
||||
<i class="i-mdi-loading animate-spin text-40px text-[#999]" />
|
||||
</view>
|
||||
|
||||
<!-- 套餐分组列表 -->
|
||||
<view v-else class="px-4 pb-4">
|
||||
<view class="space-y-3">
|
||||
<view
|
||||
v-for="group in packageGroups"
|
||||
:key="group.name"
|
||||
class="bg-white rounded-16px shadow-[0_2px_12px_rgba(0,0,0,0.06)] overflow-hidden"
|
||||
>
|
||||
<!-- 套餐头部 -->
|
||||
<view class="px-4 py-3 bg-[#f8fafc] border-b-1px border-[#f1f5f9]">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center gap-2">
|
||||
<i class="i-mdi-package-variant text-20px text-[var(--brand-primary)]" />
|
||||
<text class="text-15px font-600 text-[#212121]">{{ group.name }}</text>
|
||||
</view>
|
||||
<text class="text-12px text-[#999]">{{ group.count }} 张</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- IoT卡列表 -->
|
||||
<view class="px-4 py-2">
|
||||
<view
|
||||
v-for="(card, index) in group.cards"
|
||||
:key="card.id"
|
||||
class="py-3 active:bg-[#f8fafc] transition-all"
|
||||
:class="{ 'border-t-1px border-[#f1f5f9]': index > 0 }"
|
||||
@click="viewCardDetail(card)"
|
||||
>
|
||||
<view class="flex items-start justify-between">
|
||||
<view class="flex-1 min-w-0">
|
||||
<!-- ICCID -->
|
||||
<text class="text-13px text-[#212121] block mb-1 font-500">
|
||||
{{ card.iccid }}
|
||||
</text>
|
||||
|
||||
<!-- 电话号码 -->
|
||||
<view class="flex items-center gap-2 mb-1">
|
||||
<i class="i-mdi-phone text-14px text-[#999]" />
|
||||
<text class="text-12px text-[#666]">
|
||||
{{ card.msisdn }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 状态标签 -->
|
||||
<view class="flex items-center gap-2">
|
||||
<!-- 卡片状态 -->
|
||||
<view
|
||||
:style="{
|
||||
backgroundColor: getStatusColor(card.status).bg,
|
||||
color: getStatusColor(card.status).text,
|
||||
}"
|
||||
class="px-2 py-0.5 rounded text-10px font-600"
|
||||
>
|
||||
{{ card.status_name }}
|
||||
</view>
|
||||
|
||||
<!-- 网络状态 -->
|
||||
<view
|
||||
:style="{
|
||||
backgroundColor: getNetworkStatusColor(card.network_status).bg,
|
||||
color: getNetworkStatusColor(card.network_status).text,
|
||||
}"
|
||||
class="px-2 py-0.5 rounded text-10px font-600"
|
||||
>
|
||||
{{ card.network_status_name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧箭头 -->
|
||||
<view class="flex items-center ml-3">
|
||||
<i class="i-mdi-chevron-right text-20px text-[#cbd5e1]" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="packageGroups.length === 0" class="bg-white rounded-16px py-16 flex flex-col items-center">
|
||||
<i class="i-mdi-package-variant-closed text-64px text-[#ddd] mb-3" />
|
||||
<text class="text-15px text-[#999]">暂无套餐数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.space-y-3 > view:not(:last-child) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
function goBack() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bg-[#fafafa] min-h-screen flex items-center justify-center pb-safe">
|
||||
<view class="px-6 text-center">
|
||||
<i class="i-mdi-hammer-wrench text-64px text-[#ccc] block mb-4" />
|
||||
<text class="text-18px font-600 text-[#333] block mb-2">功能开发中</text>
|
||||
<text class="text-14px text-[#999] block mb-6">
|
||||
标签管理功能正在开发中,敬请期待
|
||||
</text>
|
||||
<view
|
||||
class="bg-[#212121] text-white rounded-full px-8 py-3 inline-block"
|
||||
@click="goBack"
|
||||
>
|
||||
<text class="text-15px">返回</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -43,7 +43,6 @@ async function loadUserInfo() {
|
||||
try {
|
||||
// 调用 API 获取用户信息
|
||||
const data = await getUserInfo()
|
||||
console.log('获取用户信息成功:', data)
|
||||
userInfo.value = data
|
||||
}
|
||||
catch (error: any) {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
// 套餐状态枚举
|
||||
export const packageStatus = {
|
||||
0: { name: '待生效', color: '#666', bgColor: '#f5f5f5' },
|
||||
1: { name: '生效中', color: '#212121', bgColor: '#e8e8e8' },
|
||||
2: { name: '已用完', color: '#999', bgColor: '#f5f5f5' },
|
||||
3: { name: '已过期', color: '#999', bgColor: '#f5f5f5' },
|
||||
4: { name: '已失效', color: '#999', bgColor: '#f5f5f5' },
|
||||
}
|
||||
|
||||
// 套餐类型
|
||||
export const packageTypeMap = {
|
||||
formal: '正式套餐',
|
||||
addon: '加油包',
|
||||
}
|
||||
|
||||
// 佣金来源映射
|
||||
export const commissionSourceMap = {
|
||||
cost_diff: '成本差价',
|
||||
one_time: '一次性佣金',
|
||||
tier_bonus: '层级分成',
|
||||
}
|
||||
|
||||
// 格式化流量 (MB to GB)
|
||||
export function formatDataSize(mb: number): string {
|
||||
if (mb < 1024)
|
||||
return `${mb}MB`
|
||||
return `${(mb / 1024).toFixed(1)}GB`
|
||||
}
|
||||
|
||||
// 计算使用百分比
|
||||
export function calcUsagePercent(used: number, total: number): number {
|
||||
if (total === 0)
|
||||
return 0
|
||||
return Math.min(Math.round((used / total) * 100), 100)
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
export function formatDate(dateStr: string | null): string {
|
||||
if (!dateStr)
|
||||
return '--'
|
||||
const date = new Date(dateStr)
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
export function formatDateTime(dateStr: string | null): string {
|
||||
if (!dateStr)
|
||||
return '--'
|
||||
const date = new Date(dateStr)
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 计算剩余天数
|
||||
export function calcRemainingDays(expiresAt: string | null): number | null {
|
||||
if (!expiresAt)
|
||||
return null
|
||||
const now = new Date()
|
||||
const expireDate = new Date(expiresAt)
|
||||
const diff = expireDate.getTime() - now.getTime()
|
||||
return Math.ceil(diff / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// 格式化金额 (分转元)
|
||||
export function formatAmount(cents: number): string {
|
||||
return (cents / 100).toFixed(2)
|
||||
}
|
||||
|
||||
// 获取剩余天数颜色
|
||||
export function getRemainingDaysColor(days: number | null): string {
|
||||
if (days === null)
|
||||
return '#bdbdbd'
|
||||
if (days <= 7)
|
||||
return '#f44336' // 红色
|
||||
if (days <= 30)
|
||||
return '#ff9800' // 橙色
|
||||
return '#4caf50' // 绿色
|
||||
}
|
||||
@@ -106,6 +106,10 @@ async function submit() {
|
||||
username: account.value.trim(),
|
||||
password: password.value.trim(),
|
||||
});
|
||||
const sessionUser = {
|
||||
...res.user,
|
||||
buttons: Array.isArray(res.buttons) ? res.buttons : [],
|
||||
};
|
||||
|
||||
// 保存 access_token
|
||||
setToken(res.access_token);
|
||||
@@ -113,12 +117,6 @@ async function submit() {
|
||||
// 保存 refresh_token 到本地存储
|
||||
uni.setStorageSync('refresh_token', res.refresh_token);
|
||||
|
||||
// 保存用户信息到本地存储
|
||||
uni.setStorageSync('user_info', res.user);
|
||||
|
||||
// 调试日志:查看用户信息
|
||||
console.log('登录成功,用户信息:', res.user);
|
||||
|
||||
// 验证用户类型 (只允许 3=代理 和 4=企业 登录)
|
||||
if (res.user.user_type !== 3 && res.user.user_type !== 4) {
|
||||
const typeNames: Record<number, string> = {
|
||||
@@ -139,9 +137,12 @@ async function submit() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存用户信息到本地存储
|
||||
uni.setStorageSync('user_info', sessionUser);
|
||||
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
icon: 'none',
|
||||
duration: 1500,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { providerType, UserState } from './types';
|
||||
import type { LoginReq } from '@/api/user/types';
|
||||
import { defineStore } from 'pinia';
|
||||
import { UserApi } from '@/api';
|
||||
import { refreshToken } from '@/api/auth';
|
||||
import { refreshToken, type UserInfo } from '@/api/auth';
|
||||
|
||||
import { clearToken, setToken } from '@/utils/auth';
|
||||
|
||||
@@ -82,12 +82,22 @@ const useUserStore = defineStore('user', {
|
||||
|
||||
try {
|
||||
const res = await refreshToken(refresh_token);
|
||||
const rawUserInfo = uni.getStorageSync('user_info');
|
||||
const cachedUserInfo = rawUserInfo && typeof rawUserInfo === 'object'
|
||||
? rawUserInfo as Partial<UserInfo>
|
||||
: {};
|
||||
const cachedButtons = Array.isArray(cachedUserInfo.buttons) ? cachedUserInfo.buttons : [];
|
||||
const sessionUser = {
|
||||
...cachedUserInfo,
|
||||
...res.user,
|
||||
buttons: Array.isArray(res.buttons) ? res.buttons : cachedButtons,
|
||||
};
|
||||
// 更新 access_token
|
||||
setToken(res.access_token);
|
||||
// 更新 refresh_token
|
||||
uni.setStorageSync('refresh_token', res.refresh_token);
|
||||
// 更新用户信息
|
||||
uni.setStorageSync('user_info', res.user);
|
||||
uni.setStorageSync('user_info', sessionUser);
|
||||
return res;
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export function setupRequest() {
|
||||
export function request<T = any>(config: HttpRequestConfig): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
http.request(config).then((res: HttpResponse<IResponse<T>>) => {
|
||||
console.log('[ res ] >', res);
|
||||
// console.log('[ res ] >', res);
|
||||
const { data } = res.data;
|
||||
resolve(data as T);
|
||||
}).catch((err: any) => {
|
||||
|
||||
Reference in New Issue
Block a user