This commit is contained in:
parent
b532ba5406
commit
de0c9ca198
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="relative min-h-screen overflow-hidden bg-black bg-[url('@/pages/Home/bg-mobile.webp')] bg-cover bg-center bg-no-repeat pb-[calc(1rem+env(safe-area-inset-bottom))] font-sans text-white md:flex md:flex-col md:bg-[url('@/pages/Home/bg-desktop.webp')] md:pb-0"
|
class="relative min-h-screen overflow-hidden bg-black bg-cover bg-center bg-no-repeat pb-[calc(1rem+env(safe-area-inset-bottom))] font-sans text-white md:flex md:flex-col md:pb-0"
|
||||||
>
|
>
|
||||||
<!-- Full Width Header -->
|
<!-- Full Width Header -->
|
||||||
<div class="h-[60px] md:h-[125px]">
|
<div class="h-[60px] md:h-[125px]">
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<router-link to="/" class="flex items-center gap-2">
|
<router-link to="/" class="flex items-center gap-2">
|
||||||
<!-- Desktop Logo -->
|
<!-- Desktop Logo -->
|
||||||
<Logo alt="Hi快VPN" class="h-[18px] w-auto md:ml-8 md:h-[29px]" />
|
<Logo alt="Hi快VPN" class="h-[18px] w-auto md:ml-8 md:h-[29px]" />
|
||||||
<span class="font-black text-2xl ml-3">高能合伙人</span>
|
<span class="ml-3 text-2xl font-black">高能合伙人</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div v-if="isLoggedIn" class="flex items-center">
|
<div v-if="isLoggedIn" class="flex items-center">
|
||||||
<router-link
|
<router-link
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main Content Container -->
|
<!-- Main Content Container -->
|
||||||
<div class="container mx-auto flex flex-col">
|
<div class="container mx-auto flex flex-col">
|
||||||
<main class="pt-10">
|
<main class="pt-10">
|
||||||
<!-- Left Column: Text & Downloads -->
|
<!-- Left Column: Text & Downloads -->
|
||||||
<div class="md:flex md:w-[432px] md:flex-col md:justify-center">
|
<div class="md:flex md:w-[432px] md:flex-col md:justify-center">
|
||||||
@ -48,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<div class="container justify-center flex flex-col items-center">
|
<div class="container flex flex-col items-center justify-center">
|
||||||
<div class="mb-1 h-[20px] w-[352px] md:ml-[17px]">
|
<div class="mb-1 h-[20px] w-[352px] md:ml-[17px]">
|
||||||
<img src="./image-1.png" alt="image" />
|
<img src="./image-1.png" alt="image" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,33 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- Main Neon Green Card -->
|
<!-- Main Neon Green Card -->
|
||||||
<div class="flex h-[678px] justify-center">
|
<div class="flex h-[668px] justify-center gap-[20px]">
|
||||||
|
<!-- Left Column -->
|
||||||
<div class="h-full w-[345px]">
|
<div class="h-full w-[345px]">
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<UserInfo />
|
<UserInfo />
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<!-- Middle Column -->
|
||||||
class="ml-2.5 flex items-center overflow-hidden rounded-4xl pt-[32px] pb-[22px]"
|
<div class="flex h-full w-[345px] flex-col gap-[20px]">
|
||||||
>
|
<DownloadStats />
|
||||||
<div class="h-full w-[345px]">
|
<QuickTools />
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx-[5px] h-[624px]"
|
|
||||||
></div>
|
|
||||||
<div class="h-full w-[345px]">
|
|
||||||
|
|
||||||
|
<!-- Right Column -->
|
||||||
|
<div class="flex h-full w-[345px] flex-col gap-[20px]">
|
||||||
|
<ProxyData />
|
||||||
|
<div class="flex-1">
|
||||||
|
<SalesData @show-history="orderDetailsModalRef?.show()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<OrderDetailsModal ref="orderDetailsModalRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import UserInfo from '@/pages/UserCenter/components/UserInfo/index.vue'
|
import UserInfo from '@/pages/UserCenter/components/UserInfo/index.vue'
|
||||||
|
import DownloadStats from '@/pages/UserCenter/components/DownloadStats/index.vue'
|
||||||
|
import QuickTools from '@/pages/UserCenter/components/QuickTools/index.vue'
|
||||||
|
import ProxyData from '@/pages/UserCenter/components/ProxyData/index.vue'
|
||||||
|
import SalesData from '@/pages/UserCenter/components/SalesData/index.vue'
|
||||||
|
import OrderDetailsModal from '@/pages/UserCenter/components/OrderDetails/index.vue'
|
||||||
|
|
||||||
|
const orderDetailsModalRef = ref<InstanceType<typeof OrderDetailsModal> | null>(null)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
34
src/pages/UserCenter/components/DownloadStats/index.vue
Normal file
34
src/pages/UserCenter/components/DownloadStats/index.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="lucid-glass-bar flex w-full flex-col justify-between rounded-4xl! border-1 border-white px-6 py-7"
|
||||||
|
>
|
||||||
|
<div class="mb-2 flex items-center justify-between border-b-1 border-dashed pb-3">
|
||||||
|
<div class="relative ml-1 text-base font-bold text-white">各端下载量</div>
|
||||||
|
<div class="mr-1 text-2xl font-bold text-white tabular-nums">62</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<div
|
||||||
|
v-for="item in stats"
|
||||||
|
:key="item.label"
|
||||||
|
class="flex items-center justify-between text-white/90"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="ml-1 text-xs font-medium">{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mr-1 text-base font-semibold tabular-nums">{{ item.value }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 text-xs text-white/40">相比前一个月+19%</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const stats = [
|
||||||
|
{ label: 'iPhone/iPad', value: 42 },
|
||||||
|
{ label: 'Windows', value: 7 },
|
||||||
|
{ label: 'Android', value: 8 },
|
||||||
|
{ label: 'Mac', value: 15 },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
163
src/pages/UserCenter/components/OrderDetails/OrderList.vue
Normal file
163
src/pages/UserCenter/components/OrderDetails/OrderList.vue
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-1 flex-col overflow-hidden">
|
||||||
|
<!-- List Container -->
|
||||||
|
<div class="h-[470px]">
|
||||||
|
<div v-if="loading" class="flex h-40 items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="h-8 w-8 animate-spin rounded-full border-4 border-[#ADFF5B] border-t-transparent"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="list.length === 0"
|
||||||
|
class="flex h-40 items-center justify-center text-gray-500"
|
||||||
|
>
|
||||||
|
暂无推荐数据
|
||||||
|
</div>
|
||||||
|
<div v-else class="space-y-[10px]">
|
||||||
|
<div
|
||||||
|
v-for="order in list"
|
||||||
|
:key="order.id"
|
||||||
|
class="rounded-[20px] bg-[#CECECF] py-2 text-[14px] font-normal text-black"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-2 gap-y-1">
|
||||||
|
<!-- Order No -->
|
||||||
|
<div class="col-span-2 pl-4">
|
||||||
|
<div class="">用户ID</div>
|
||||||
|
<div class="break-all">{{ order.order_no }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name and Status -->
|
||||||
|
<div class="pl-4">
|
||||||
|
<div class="text-gray-500">名称</div>
|
||||||
|
<div class="whitespace-nowrap">{{ order.quantity }}天VPN服务</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<div class="text-gray-500">状态</div>
|
||||||
|
<div>{{ statusText(order.status) }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Amount and Time -->
|
||||||
|
<div class="pl-4">
|
||||||
|
<div class="text-gray-500">支付金额</div>
|
||||||
|
<div>${{ order.amount / 100 }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<div class="text-gray-500">支付时间</div>
|
||||||
|
<div class="whitespace-nowrap">{{ formatTime(order.created_at) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<div v-if="total > 0" class="flex flex-col items-center pt-[18px]">
|
||||||
|
<div class="mb-2 flex items-center gap-[10px]">
|
||||||
|
<button
|
||||||
|
@click="changePage(1)"
|
||||||
|
:disabled="page === 1"
|
||||||
|
class="flex h-[30px] min-w-[30px] items-center justify-center rounded-full bg-[#EAEAEA] transition-opacity"
|
||||||
|
>
|
||||||
|
<span class="text-lg font-bold text-[#848484]"><<</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="changePage(page - 1)"
|
||||||
|
:disabled="page === 1"
|
||||||
|
class="flex h-[30px] min-w-[30px] items-center justify-center rounded-full bg-[#EAEAEA] transition-opacity"
|
||||||
|
>
|
||||||
|
<span class="text-lg font-bold text-[#848484]"><</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex h-[30px] min-w-[65px] items-center justify-center rounded-full bg-[#EAEAEA] px-2"
|
||||||
|
>
|
||||||
|
<span class="text-base text-[#848484]">{{ page }}</span>
|
||||||
|
<span class="ml-1 text-base text-[#848484]">v</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="changePage(page + 1)"
|
||||||
|
:disabled="page >= totalPages"
|
||||||
|
class="flex h-[30px] min-w-[30px] items-center justify-center rounded-full bg-[#EAEAEA] transition-opacity"
|
||||||
|
>
|
||||||
|
<span class="text-lg font-bold text-[#848484]">></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="changePage(totalPages)"
|
||||||
|
:disabled="page >= totalPages"
|
||||||
|
class="flex h-[30px] min-w-[30px] items-center justify-center rounded-full bg-[#EAEAEA] transition-opacity"
|
||||||
|
>
|
||||||
|
<span class="text-lg font-bold text-[#848484]">>></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs font-[300] text-[#848484]">第 {{ page }} / {{ totalPages }} 页</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, computed } from 'vue'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const size = ref(3)
|
||||||
|
const total = ref(0)
|
||||||
|
const list = ref<any[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const totalPages = computed(() => Math.ceil(total.value / size.value) || 1)
|
||||||
|
|
||||||
|
async function fetchOrders() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await request.get('/api/v1/public/order/list', {
|
||||||
|
page: page.value,
|
||||||
|
size: size.value,
|
||||||
|
status: 5,
|
||||||
|
})
|
||||||
|
list.value = res.list || []
|
||||||
|
total.value = res.total || 0
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fetch orders error:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage(p: number) {
|
||||||
|
if (p < 1 || p > totalPages.value) return
|
||||||
|
page.value = p
|
||||||
|
fetchOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusText(status: number) {
|
||||||
|
const map: Record<number, string> = {
|
||||||
|
1: '待支付',
|
||||||
|
2: '已支付',
|
||||||
|
3: '已关闭',
|
||||||
|
4: '支付失败',
|
||||||
|
5: '已完成',
|
||||||
|
}
|
||||||
|
return map[status] || '未知'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(timestamp: number) {
|
||||||
|
if (!timestamp) return '-'
|
||||||
|
const date = new Date(timestamp > 10000000000 ? timestamp : timestamp * 1000)
|
||||||
|
return date.toLocaleString('en-US', {
|
||||||
|
month: 'numeric',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchOrders()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
60
src/pages/UserCenter/components/OrderDetails/index.vue
Normal file
60
src/pages/UserCenter/components/OrderDetails/index.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :open="isOpen" @update:open="setOpen">
|
||||||
|
<DialogContent
|
||||||
|
class="top-[94px] flex h-[calc(100vh-94px-50px)] w-[386px] translate-y-0 flex-col items-center justify-center border-none bg-transparent p-0 shadow-none outline-none focus:ring-0"
|
||||||
|
:showCloseButton="false"
|
||||||
|
>
|
||||||
|
<div class="relative flex h-[678px] w-full flex-col rounded-[32px] bg-[#DDDDDD] px-4 py-6">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button
|
||||||
|
@click="hide"
|
||||||
|
class="absolute top-6 right-6 z-10 flex size-8 items-center justify-center rounded-lg transition-colors hover:bg-black/5"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="text-black"
|
||||||
|
>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<h2 class="mb-2 px-4 pt-8 text-center text-[20px] font-bold text-black">历史推荐数据</h2>
|
||||||
|
|
||||||
|
<OrderList />
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||||
|
import OrderList from './OrderList.vue'
|
||||||
|
|
||||||
|
const isOpen = ref(false)
|
||||||
|
|
||||||
|
const setOpen = (value: boolean) => {
|
||||||
|
isOpen.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
isOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
30
src/pages/UserCenter/components/ProxyData/index.vue
Normal file
30
src/pages/UserCenter/components/ProxyData/index.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="lucid-glass-bar flex w-full flex-col justify-between rounded-4xl! border-1 border-white px-6 py-7"
|
||||||
|
>
|
||||||
|
<div class="mb-2 flex items-center justify-between border-b-1 border-dashed pb-3">
|
||||||
|
<div class="relative ml-1 pb-2 text-base font-bold text-white">代理链接实时数据</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 flex flex-col gap-2">
|
||||||
|
<div
|
||||||
|
v-for="item in data"
|
||||||
|
:key="item.label"
|
||||||
|
class="flex items-center justify-between text-white/90"
|
||||||
|
>
|
||||||
|
<span class="text-xs font-medium">{{ item.label }}</span>
|
||||||
|
<span class="text-base font-bold tabular-nums">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 text-xs text-white/40">相比前一个月+32%</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const data = [
|
||||||
|
{ label: '点击量', value: 782 },
|
||||||
|
{ label: '浏览量', value: 699 },
|
||||||
|
{ label: '付费数量', value: 54 },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
39
src/pages/UserCenter/components/QuickTools/index.vue
Normal file
39
src/pages/UserCenter/components/QuickTools/index.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="lucid-glass-bar flex w-full flex-col rounded-4xl! border-1 border-white px-6 py-7">
|
||||||
|
<div class="mb-[20px] border-b-1 border-dashed pb-4">
|
||||||
|
<div class="mb-1 text-xl font-bold text-white">快捷工具</div>
|
||||||
|
<div class="text-sm text-white/40">帮您快速解决用户的常见问题</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2.5">
|
||||||
|
<div
|
||||||
|
v-for="tool in tools"
|
||||||
|
:key="tool.label"
|
||||||
|
class="flex h-[50px] w-full items-center justify-between rounded-[100px] bg-[#A9A9A9] px-4 text-black transition-colors"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col leading-[1.2]">
|
||||||
|
<span class="font-semibold">{{ tool.label }}</span>
|
||||||
|
<span class="underline">{{ tool.url }}</span>
|
||||||
|
</div>
|
||||||
|
<CopyIcon class="size-5 cursor-pointer" @click="copy(tool.url)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import CopyIcon from '../UserInfo/copy.svg?component'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
|
const tools = [
|
||||||
|
{ label: 'iOS外区账号教程', url: 'hifastvpn.com/tutorial' },
|
||||||
|
{ label: 'app视频使用教程', url: 'hifastvpn.com/tutorialvids' },
|
||||||
|
{ label: '软件防丢名片', url: 'hifastvpn.com/alwaysfindus' },
|
||||||
|
{ label: '官方Telegram群', url: 't.me/hifastvpnofficial' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function copy(text: string) {
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
toast.success('已复制到剪贴板')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
48
src/pages/UserCenter/components/SalesData/index.vue
Normal file
48
src/pages/UserCenter/components/SalesData/index.vue
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="lucid-glass-bar flex h-full w-full flex-col rounded-4xl! border-1 border-white px-6 py-7"
|
||||||
|
>
|
||||||
|
<div class="mb-[20px] flex justify-between border-b-1 border-dashed pb-4">
|
||||||
|
<div>
|
||||||
|
<div class="mb-1 text-xl font-bold text-white">最近销售数据</div>
|
||||||
|
<div class="text-sm text-white/40">本月已成交订单54人/次</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
class="h-auto p-0 text-sm font-medium text-white/60 underline hover:text-white"
|
||||||
|
@click="$emit('show-history')"
|
||||||
|
>
|
||||||
|
历史数据
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 overflow-y-auto pr-1">
|
||||||
|
<div class="border-t border-dashed border-white/20 pt-4">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in sales"
|
||||||
|
:key="index"
|
||||||
|
class="mb-5 flex items-center justify-between last:mb-0"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-sm font-semibold text-white">{{ item.email }}</span>
|
||||||
|
<span class="text-[10px] text-white/40">{{ item.time }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-lg font-bold text-white tabular-nums">$ {{ item.amount }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
||||||
|
defineEmits(['show-history'])
|
||||||
|
|
||||||
|
const sales = [
|
||||||
|
{ email: '321x***.com', time: '2026/1/10 22:32', amount: '2.99' },
|
||||||
|
{ email: 'fafax***.com', time: '2026/1/10 22:32', amount: '44.99' },
|
||||||
|
{ email: 'kjkljl***.com', time: '2026/1/10 22:32', amount: '19.79' },
|
||||||
|
{ email: '098f***.com', time: '2026/1/10 22:32', amount: '2.99' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
@ -16,12 +16,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-7 mb-1 font-semibold text-[#ADFF5B]">专属代理链接</div>
|
<div class="mt-7 mb-1 ml-[18px] font-semibold text-[#ADFF5B]">专属代理链接</div>
|
||||||
<div
|
<div
|
||||||
class="mb-[10px] flex h-[50px] w-full items-center justify-between rounded-[32px] bg-[#ADFF5B] px-4 font-medium text-black"
|
class="mb-[10px] flex h-[50px] w-full items-center justify-between rounded-[32px] bg-[#ADFF5B] px-4 font-medium text-black"
|
||||||
>
|
>
|
||||||
{{ userInfo.share_link }}
|
{{ userInfo.share_link }}
|
||||||
<CopyIcon />
|
<CopyIcon class="size-5 cursor-pointer" @click="copy(userInfo.share_link)" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex min-h-[90px] w-full items-center justify-between rounded-[25px] bg-[#ADFF5B] px-4 font-medium text-black"
|
class="flex min-h-[90px] w-full items-center justify-between rounded-[25px] bg-[#ADFF5B] px-4 font-medium text-black"
|
||||||
@ -64,6 +64,7 @@ import CopyIcon from './copy.svg?component'
|
|||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userInfo = ref({
|
const userInfo = ref({
|
||||||
@ -110,6 +111,12 @@ const formattedDate = computed(() => {
|
|||||||
}).format(date)
|
}).format(date)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function copy(text: string) {
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
toast.success('已复制到剪贴板')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
localStorage.removeItem('Authorization')
|
localStorage.removeItem('Authorization')
|
||||||
|
|||||||
@ -37,14 +37,7 @@
|
|||||||
</div>-->
|
</div>-->
|
||||||
<div class="hidden flex-1 items-center justify-center md:flex md:pb-[50px]">
|
<div class="hidden flex-1 items-center justify-center md:flex md:pb-[50px]">
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto">
|
||||||
<DesktopLayout
|
<DesktopLayout />
|
||||||
:selected-plan-id="selectedPlanId"
|
|
||||||
:is-paying="isPaying"
|
|
||||||
:is-user-loading="isUserLoading"
|
|
||||||
:is-plans-loading="isPlansLoading"
|
|
||||||
:is-payments-loading="isPaymentsLoading"
|
|
||||||
@show-order-details="orderDetailsModalRef?.show()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -53,17 +46,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import DesktopLayout from './DesktopLayout/index.vue'
|
import DesktopLayout from './DesktopLayout/index.vue'
|
||||||
import OrderStatusDialog from '@/components/user-center/OrderStatusDialog.vue'
|
|
||||||
import OrderDetailsModal from './components/OrderDetails/index.vue'
|
|
||||||
import Logo from '@/pages/Home/logo.svg?component'
|
import Logo from '@/pages/Home/logo.svg?component'
|
||||||
import request from '@/utils/request'
|
|
||||||
import { toast } from 'vue-sonner'
|
|
||||||
import { useLocalStorage } from '@vueuse/core'
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const token = useLocalStorage('Authorization', '')
|
const token = useLocalStorage('Authorization', '')
|
||||||
const userEmail = useLocalStorage('UserEmail', '')
|
const userEmail = useLocalStorage('UserEmail', '')
|
||||||
|
|
||||||
@ -72,14 +58,6 @@ const userLetter = computed(() => {
|
|||||||
if (!userEmail.value) return '?'
|
if (!userEmail.value) return '?'
|
||||||
return userEmail.value.charAt(0).toUpperCase()
|
return userEmail.value.charAt(0).toUpperCase()
|
||||||
})
|
})
|
||||||
// --- State ---
|
|
||||||
const selectedPlanId = ref('p2')
|
|
||||||
const isPaying = ref(false)
|
|
||||||
const isUserLoading = ref(true)
|
|
||||||
const isPlansLoading = ref(true)
|
|
||||||
const isPaymentsLoading = ref(true)
|
|
||||||
const orderStatusDialogRef = ref<InstanceType<typeof OrderStatusDialog> | null>(null)
|
|
||||||
const orderDetailsModalRef = ref<InstanceType<typeof OrderDetailsModal> | null>(null)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -23,13 +23,6 @@ export default defineConfig({
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
// rewrite: (path) => path.replace(/^\/api/, ''),
|
// rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
autoRewrite: true,
|
autoRewrite: true,
|
||||||
// 3. 关键:将路径重写,在前面补上 /api/v1
|
|
||||||
// 验证请求是否进入代理,可以在终端看到打印
|
|
||||||
configure: (proxy, _options) => {
|
|
||||||
proxy.on('proxyReq', (proxyReq, req, _res) => {
|
|
||||||
console.log('代理请求:', req.method, req.url, ' -> ', proxyReq.path)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user