Compare commits

..

22 Commits

Author SHA1 Message Date
sepakeloudest 70f4f97d53 修改首页图片
site-dist-deploy / build-and-deploy (push) Successful in 57s
2026-08-31 00:28:13 +08:00
sepakeloudest 013dfb4bee 修改首页图片
site-dist-deploy / build-and-deploy (push) Successful in 1m8s
2026-08-29 10:45:08 +08:00
sepakeloudest f02f3f5c04 进入页面检查提现审核状态
site-dist-deploy / build-and-deploy (push) Failing after 15m13s
2026-07-02 10:10:15 +08:00
sepakeloudest adde209c57 增加提现审核中拦截 2026-07-02 10:06:08 +08:00
sepakeloudest 104e04a8cc 提现记录和退费数据展示
site-dist-deploy / build-and-deploy (push) Failing after 11m50s
2026-06-18 11:23:09 +08:00
sepakeloudest 11bc49d1e0 修改文案,分佣比例
site-dist-deploy / build-and-deploy (push) Failing after 15m10s
2026-06-10 10:25:32 +08:00
sepakeloudest 3639e10a11 修改文案
site-dist-deploy / build-and-deploy (push) Successful in 1m44s
2026-05-10 20:10:24 +03:00
sepakeloudest 3f0be7575c 新增提示文案
site-dist-deploy / build-and-deploy (push) Successful in 58s
2026-05-08 18:14:00 +03:00
sepakeloudest 63d40b3e76 更改未完成工单查询条件
site-dist-deploy / build-and-deploy (push) Successful in 58s
2026-05-08 14:09:25 +03:00
sepakeloudest 19d27194e2 字段调整
site-dist-deploy / build-and-deploy (push) Successful in 1m4s
2026-05-08 11:18:13 +03:00
sepakeloudest 4776d22aeb 增加代理邀请码 2026-05-06 18:27:16 +03:00
sepakeloudest 20b0b0483f 返佣比例ui 优化
site-dist-deploy / build-and-deploy (push) Successful in 1m8s
2026-05-03 18:32:24 +03:00
sepakeloudest 0f882af81e 返佣比例
site-dist-deploy / build-and-deploy (push) Has been cancelled
2026-05-03 18:23:30 +03:00
sepakeloudest f0140d85e6 提现功能
site-dist-deploy / build-and-deploy (push) Has been cancelled
2026-05-01 17:10:00 +03:00
sepakeloudest 9343b288ec 更改接口
site-dist-deploy / build-and-deploy (push) Has been cancelled
2026-05-01 12:02:16 +03:00
sepakeloudest 7c452ce42f 更改接口
site-dist-deploy / build-and-deploy (push) Has been cancelled
2026-05-01 07:43:11 +03:00
sepakeloudest 828bb8a93e 替换x链接
site-dist-deploy / build-and-deploy (push) Successful in 1m34s
2026-02-26 10:45:05 +02:00
sepakeloudest b566d66c50 样式修改
site-dist-deploy / build-and-deploy (push) Successful in 1m47s
2026-02-26 09:00:06 +02:00
sepakeloudest 3223287418 增加销售数据时间戳
site-dist-deploy / build-and-deploy (push) Successful in 1m42s
2026-02-10 11:12:19 +02:00
sepakeloudest 5e4354d654 增加销售数据时间戳 2026-02-09 06:09:49 +02:00
sepakeloudest cc9f0298f7 修改文案
site-dist-deploy / build-and-deploy (push) Successful in 1m22s
2026-02-06 09:43:34 +02:00
sepakeloudest d618042960 修改文案
site-dist-deploy / build-and-deploy (push) Successful in 1m29s
2026-02-06 08:58:18 +02:00
17 changed files with 693 additions and 90 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="overflow-x-auto md:min-w-[1220px]">
<main> <main>
<RouterView /> <RouterView />
</main> </main>
+18 -21
View File
@@ -1,37 +1,34 @@
import type { VariantProps } from "class-variance-authority" import type { VariantProps } from 'class-variance-authority'
import { cva } from "class-variance-authority" import { cva } from 'class-variance-authority'
export { default as Button } from "./Button.vue" export { default as Button } from './Button.vue'
export const buttonVariants = cva( export const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "inline-flex items-center justify-center cursor-pointer gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{ {
variants: { variants: {
variant: { variant: {
default: default: 'bg-primary text-primary-foreground hover:bg-primary/90',
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive: destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline: outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
secondary: secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
"bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
ghost: link: 'text-primary underline-offset-4 hover:underline',
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
"default": "h-9 px-4 py-2 has-[>svg]:px-3", default: 'h-9 px-4 py-2 has-[>svg]:px-3',
"sm": "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
"lg": "h-10 rounded-md px-6 has-[>svg]:px-4", lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
"icon": "size-9", icon: 'size-9',
"icon-sm": "size-8", 'icon-sm': 'size-8',
"icon-lg": "size-10", 'icon-lg': 'size-10',
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: 'default',
size: "default", size: 'default',
}, },
}, },
) )
+1 -1
View File
@@ -176,7 +176,7 @@ const videoRef = ref<HTMLVideoElement | null>(null)
const openVideoModal = () => { const openVideoModal = () => {
videoModalVisible.value = true videoModalVisible.value = true
} }
// console.log('11')
const handleVideoModalClose = () => { const handleVideoModalClose = () => {
if (videoRef.value) { if (videoRef.value) {
videoRef.value.pause() videoRef.value.pause()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 176 KiB

+16 -2
View File
@@ -1,6 +1,6 @@
<template> <template>
<!-- Main Neon Green Card --> <!-- Main Neon Green Card -->
<div class="flex h-[668px] justify-center gap-[20px]"> <div class="mt-4 flex flex-col justify-center gap-[20px] md:mt-0 md:h-[668px] md:flex-row">
<!-- Left Column --> <!-- 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">
@@ -17,12 +17,23 @@
<!-- Right Column --> <!-- Right Column -->
<div class="flex h-full w-[345px] flex-col gap-[20px]"> <div class="flex h-full w-[345px] flex-col gap-[20px]">
<ProxyData /> <ProxyData />
<div class="flex-1"> <div class="">
<SalesData @show-history="orderDetailsModalRef?.show()" /> <SalesData @show-history="orderDetailsModalRef?.show()" />
</div> </div>
<div class="">
<RefundData @show-history="refundDetailsModalRef?.show()" />
</div>
</div> </div>
<OrderDetailsModal ref="orderDetailsModalRef" /> <OrderDetailsModal ref="orderDetailsModalRef" />
<WithdrawalLogDialog
ref="refundDetailsModalRef"
title="退费记录"
biz-type="commission_refund"
amount-label="退费金额"
empty-text="暂无退费记录"
show-content
/>
</div> </div>
</template> </template>
@@ -33,9 +44,12 @@ import DownloadStats from '@/pages/UserCenter/components/DownloadStats/index.vue
import QuickTools from '@/pages/UserCenter/components/QuickTools/index.vue' import QuickTools from '@/pages/UserCenter/components/QuickTools/index.vue'
import ProxyData from '@/pages/UserCenter/components/ProxyData/index.vue' import ProxyData from '@/pages/UserCenter/components/ProxyData/index.vue'
import SalesData from '@/pages/UserCenter/components/SalesData/index.vue' import SalesData from '@/pages/UserCenter/components/SalesData/index.vue'
import RefundData from '@/pages/UserCenter/components/RefundData/index.vue'
import OrderDetailsModal from '@/pages/UserCenter/components/OrderDetails/index.vue' import OrderDetailsModal from '@/pages/UserCenter/components/OrderDetails/index.vue'
import WithdrawalLogDialog from '@/pages/UserCenter/components/UserInfo/components/WithdrawalLogDialog.vue'
const orderDetailsModalRef = ref<InstanceType<typeof OrderDetailsModal> | null>(null) const orderDetailsModalRef = ref<InstanceType<typeof OrderDetailsModal> | null>(null)
const refundDetailsModalRef = ref<InstanceType<typeof WithdrawalLogDialog> | null>(null)
</script> </script>
<style scoped></style> <style scoped></style>
@@ -3,7 +3,7 @@
class="lucid-glass-bar flex w-full flex-col justify-between rounded-4xl! border-1 border-white px-6 py-7" 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="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="relative ml-1 text-base font-bold text-white">本月下载量</div>
<div class="mr-1 text-2xl font-bold text-white tabular-nums">{{ info.total }}</div> <div class="mr-1 text-2xl font-bold text-white tabular-nums">{{ info.total }}</div>
</div> </div>
@@ -39,7 +39,7 @@
<!-- Time --> <!-- Time -->
<div class="col-span-2 pl-4"> <div class="col-span-2 pl-4">
<div class="text-xs text-gray-500">支付时间</div> <div class="text-xs text-gray-500">支付时间</div>
<div class="whitespace-nowrap">{{ formatTime(order.update_at) }}</div> <div class="whitespace-nowrap">{{ formatTime(order.updated_at) }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -139,7 +139,7 @@ function statusText(status: number) {
function formatTime(timestamp: number) { function formatTime(timestamp: number) {
if (!timestamp) return '-' if (!timestamp) return '-'
const date = new Date(timestamp > 10000000000 ? timestamp : timestamp * 1000) const date = new Date(timestamp > 10000000000 ? timestamp : timestamp * 1000)
return date.toLocaleString('en-US', { return date.toLocaleString('zh-CN', {
month: 'numeric', month: 'numeric',
day: 'numeric', day: 'numeric',
year: 'numeric', year: 'numeric',
@@ -3,10 +3,10 @@
class="lucid-glass-bar flex w-full flex-col justify-between rounded-4xl! border-1 border-white px-6 py-7" 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="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 class="relative ml-1 pb-2 text-base font-bold text-white">本月链接转化</div>
</div> </div>
<div class="mt-2 flex flex-col gap-2"> <div class="flex flex-col">
<div <div
v-for="item in data" v-for="item in data"
:key="item.label" :key="item.label"
@@ -17,7 +17,7 @@
</div> </div>
</div> </div>
<div class="mt-2 text-xs text-white/40">相比前一个月 {{ growthRate }}</div> <div class="mt-1 text-xs text-white/40">相比前一个月 {{ growthRate }}</div>
</div> </div>
</template> </template>
@@ -28,7 +28,7 @@ import request from '@/utils/request'
const data = ref([ const data = ref([
{ label: '点击量', value: 0, key: 'clicks' }, { label: '点击量', value: 0, key: 'clicks' },
{ label: '浏览量', value: 0, key: 'views' }, { label: '浏览量', value: 0, key: 'views' },
{ label: '付费数', value: 0, key: 'paid_count' }, { label: '付费用户数', value: 0, key: 'paid_count' },
]) ])
const growthRate = ref('N/A') const growthRate = ref('N/A')
@@ -26,9 +26,9 @@ import { toast } from 'vue-sonner'
const tools = [ const tools = [
{ label: 'iOS外区账号教程', url: 'http://getsapp.net/F6Lmev' }, { label: 'iOS外区账号教程', url: 'http://getsapp.net/F6Lmev' },
{ label: '官方邮箱', url: 'hifastvpn1@gmail.com' }, { label: '软件防丢邮箱(秒回)', url: 'hifastvpn1@gmail.com' },
{ label: '软件防丢名片', url: 'https://getsapp.net/PTHZVp' }, { label: '软件防丢名片', url: 'https://getsapp.net/PTHZVp' },
{ label: '官方X账号', url: 'https://x.com/hifasttech' }, { label: '官方X账号', url: 'https://getsapp.net/xcom' },
] ]
function copy(text: string) { function copy(text: string) {
@@ -0,0 +1,94 @@
<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-base font-bold text-white">退费数据</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 v-if="loading" class="flex h-10 items-center justify-center">
<div
class="h-6 w-6 animate-spin rounded-full border-2 border-[#ADFF5B] border-t-transparent"
></div>
</div>
<div v-else class="">
<div
v-for="(item, index) in salesPage.list"
:key="index"
class="mb-3 flex items-center justify-between last:mb-0"
>
<div class="flex flex-col">
<span class="text-sm font-semibold text-white">{{ item.id }}</span>
<span class="text-[10px] text-white/40">{{ formatTime(item.created_at) }}</span>
</div>
<div class="text-lg font-bold text-white tabular-nums">
$ {{ formatAmount(item.amount) }}
</div>
</div>
<div v-if="!salesPage.list.length">暂无数据</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { Button } from '@/components/ui/button'
import request from '@/utils/request'
defineEmits(['show-history'])
const salesPage = ref({
total: 0,
list: [] as any[],
})
function formatAmount(amount: number) {
return ((amount || 0) / 100).toFixed(2)
}
function formatTime(timestamp: number) {
if (!timestamp) return '-'
const date = new Date(timestamp > 10000000000 ? timestamp : timestamp * 1000)
return date.toLocaleString('zh-CN', {
month: 'numeric',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: false,
})
}
const loading = ref(false)
async function fetchSales() {
loading.value = true
try {
const res: any = await request.get('/api/v1/public/user/withdrawal_log', {
page: 1,
size: 2,
biz_type: 'commission_refund',
})
salesPage.value = res
} catch (error) {
console.error('Fetch sales error:', error)
} finally {
loading.value = false
}
}
onMounted(() => {
fetchSales()
})
</script>
@@ -2,10 +2,10 @@
<div <div
class="lucid-glass-bar flex h-full w-full flex-col rounded-4xl! border-1 border-white px-6 py-7" 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 class="mb-[10px] flex justify-between border-b-1 border-dashed pb-4">
<div> <div>
<div class="mb-1 text-xl font-bold text-white">最近销售数据</div> <div class="mb-1 text-base font-bold text-white">本月销售数据</div>
<div class="text-sm text-white/40">累计成交订单{{ salesPage.total }}/</div> <div class="text-sm text-white/40">本月已成交{{ salesPage.total }}</div>
</div> </div>
<Button <Button
variant="link" variant="link"
@@ -17,10 +17,7 @@
</div> </div>
<div class="flex-1 overflow-y-auto pr-1"> <div class="flex-1 overflow-y-auto pr-1">
<div <div v-if="loading" class="flex h-[100px] items-center justify-center">
v-if="loading && salesPage.list.length === 0"
class="flex h-20 items-center justify-center"
>
<div <div
class="h-6 w-6 animate-spin rounded-full border-2 border-[#ADFF5B] border-t-transparent" class="h-6 w-6 animate-spin rounded-full border-2 border-[#ADFF5B] border-t-transparent"
></div> ></div>
@@ -29,14 +26,15 @@
<div <div
v-for="(item, index) in salesPage.list" v-for="(item, index) in salesPage.list"
:key="index" :key="index"
class="mb-5 flex items-center justify-between last:mb-0" class="mb-3 flex items-center justify-between last:mb-0"
> >
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-sm font-semibold text-white">{{ item.user_hash }}</span> <span class="text-sm font-semibold text-white">{{ item.user_hash }}</span>
<span class="text-[10px] text-white/40">{{ formatTime(item.update_at) }}</span> <span class="text-[10px] text-white/40">{{ formatTime(item.updated_at) }}</span>
</div> </div>
<div class="text-lg font-bold text-white tabular-nums">$ {{ item.amount }}</div> <div class="text-lg font-bold text-white tabular-nums">$ {{ item.amount }}</div>
</div> </div>
<div v-if="!salesPage.list.length">暂无数据</div>
</div> </div>
</div> </div>
</div> </div>
@@ -59,9 +57,19 @@ const loading = ref(false)
async function fetchSales() { async function fetchSales() {
loading.value = true loading.value = true
try { try {
const now = new Date()
// 2. 获取当月 1 号 0 点 0 分 0 秒
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0)
const start_time = Math.floor(startOfMonth.getTime() / 1000)
// 3. 获取下个月的第 0 天(即本月的最后一天)的 23:59:59
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59)
const end_time = Math.floor(endOfMonth.getTime() / 1000)
const res: any = await request.get('/api/v1/public/user/invite/sales', { const res: any = await request.get('/api/v1/public/user/invite/sales', {
page: 1, page: 1,
size: 8, size: 3,
start_time,
end_time,
}) })
salesPage.value = res salesPage.value = res
} catch (error) { } catch (error) {
@@ -12,8 +12,8 @@
<div class="my-4 text-left text-sm font-medium text-white/30"> <div class="my-4 text-left text-sm font-medium text-white/30">
{{ {{
values.type === 'USDT(TRC20)' values.type === 'USDT(TRC20)'
? '将佣金提现至您的个人数字钱包,手续费' ? '提现至您的数字钱包,手续费1USDT,该费率由支付平台收取'
: '该方式涉及平台手续费,预计 1-3 个工作日到账' : '该提现方式需5%手续费,该费率由支付平台收取'
}} }}
</div> </div>
@@ -36,7 +36,7 @@
:value="t" :value="t"
class="py-3 text-sm focus:bg-[#ADFF5B] focus:text-black" class="py-3 text-sm focus:bg-[#ADFF5B] focus:text-black"
> >
{{ t === 'USDT(TRC20)' ? 'USDT' : t }} {{ t }}
</SelectItem> </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
@@ -63,7 +63,9 @@
<div <div
class="group relative flex h-[46px] items-center overflow-hidden rounded-[32px] bg-[#ADFF5B] p-[3px]" class="group relative flex h-[46px] items-center overflow-hidden rounded-[32px] bg-[#ADFF5B] p-[3px]"
> >
<div class="flex h-full items-center px-6 text-xl font-bold text-black">¥</div> <div class="flex h-full items-center px-6 text-xl font-bold text-black">
{{ values.type === 'USDT(TRC20)' ? '₮' : '$' }}
</div>
<FormControl> <FormControl>
<Input <Input
@@ -99,11 +101,13 @@
} }
" "
class="h-full flex-1 rounded-[28px]! border-none bg-[#222222] px-4 text-center text-sm font-bold text-white placeholder:text-white/20 focus:ring-0! focus-visible:ring-0!" class="h-full flex-1 rounded-[28px]! border-none bg-[#222222] px-4 text-center text-sm font-bold text-white placeholder:text-white/20 focus:ring-0! focus-visible:ring-0!"
placeholder="不小于200RMB" :placeholder="values.type === 'USDT(TRC20)' ? '不小于30USDT' : '不小于30$'"
/> />
</FormControl> </FormControl>
<div class="flex h-full items-center px-6 text-sm font-bold text-black">RMB</div> <div class="flex h-full items-center px-6 text-sm font-bold text-black">
{{ values.type === 'USDT(TRC20)' ? 'USDT' : '$' }}
</div>
</div> </div>
<FormMessage class="ml-4 text-red-400" /> <FormMessage class="ml-4 text-red-400" />
</FormItem> </FormItem>
@@ -112,11 +116,40 @@
<FormField v-if="values.type !== 'USDT(TRC20)'" name="avatar"> <FormField v-if="values.type !== 'USDT(TRC20)'" name="avatar">
<FormItem> <FormItem>
<FormLabel class="mb-4 block text-sm font-bold text-white">收款码</FormLabel> <FormLabel class="mb-4 block text-sm font-bold text-white">收款码</FormLabel>
<input
ref="fileInputRef"
type="file"
accept="image/*"
class="hidden"
@change="onFileChange"
/>
<div <div
class="flex cursor-pointer flex-col items-center justify-center rounded-[32px] border-2 border-dashed border-white/10 bg-[#222222] p-10 transition-all hover:border-[#ADFF5B]/50 hover:bg-[#2a2a2a]" class="group relative flex cursor-pointer flex-col items-center justify-center overflow-hidden rounded-[32px] border-2 border-dashed border-white/10 bg-[#222222] transition-all hover:border-[#ADFF5B]/50 hover:bg-[#2a2a2a]"
:class="[values.avatar ? 'h-auto min-h-[160px] p-2' : 'p-10']"
@click="fileInputRef?.click()"
> >
<Upload class="mb-3 h-12 w-12 text-white/20" /> <template v-if="values.avatar">
<span class="text-base font-medium text-white/30">点击上传高清收款二维码</span> <img
:src="receiptPreviewUrl || values.avatar"
class="max-h-[300px] w-full rounded-2xl object-contain"
/>
<div
class="absolute inset-0 flex items-center justify-center bg-black/60 opacity-0 transition-opacity group-hover:opacity-100"
>
<span class="text-sm font-bold text-white">点击更换图片</span>
</div>
</template>
<template v-else>
<Upload class="mb-3 h-12 w-12 text-white/20" />
<span class="text-base font-medium text-white/30">点击上传高清收款二维码</span>
</template>
<div
v-if="isUploadingReceipt"
class="absolute inset-0 flex items-center justify-center bg-black/70"
>
<Loader2 class="mr-2 h-6 w-6 animate-spin text-[#ADFF5B]" />
<span class="text-sm font-bold text-white">上传中</span>
</div>
</div> </div>
<FormMessage class="ml-4 text-red-400" /> <FormMessage class="ml-4 text-red-400" />
</FormItem> </FormItem>
@@ -125,7 +158,7 @@
<div class="flex justify-center pt-2"> <div class="flex justify-center pt-2">
<Button <Button
type="submit" type="submit"
:disabled="isPending" :disabled="isPending || isUploadingReceipt"
class="h-[30px] w-[100px] rounded-full bg-[#ADFF5B] text-sm font-black text-black shadow-[0_10px_30px_rgba(173,255,91,0.3)] transition-all hover:scale-105 hover:bg-[#9ded4e] active:scale-95 disabled:opacity-50" class="h-[30px] w-[100px] rounded-full bg-[#ADFF5B] text-sm font-black text-black shadow-[0_10px_30px_rgba(173,255,91,0.3)] transition-all hover:scale-105 hover:bg-[#9ded4e] active:scale-95 disabled:opacity-50"
> >
<Loader2 v-if="isPending" class="mr-2 h-7 w-7 animate-spin" /> <Loader2 v-if="isPending" class="mr-2 h-7 w-7 animate-spin" />
@@ -166,7 +199,94 @@ const emit = defineEmits(['confirm'])
const open = ref(false) const open = ref(false)
const isPending = ref(false) // 手动管理加载状态 const isPending = ref(false) // 手动管理加载状态
const isUploadingReceipt = ref(false)
const receiptPreviewUrl = ref('')
const fileInputRef = ref<HTMLInputElement | null>(null)
const ACCOUNT_TYPE = ['USDT(TRC20)', '微信', '支付宝'] as const const ACCOUNT_TYPE = ['USDT(TRC20)', '微信', '支付宝'] as const
const RECEIPT_UPLOAD_BIZ_TYPE = 'app-package'
const WITHDRAW_METHOD = {
支付宝: 1,
微信: 2,
'USDT(TRC20)': 3,
} as const
type FileUploadResponse =
| string
| {
url?: string
file_url?: string
full_url?: string
path?: string
uri?: string
file?: string
src?: string
}
const revokeReceiptPreview = () => {
if (receiptPreviewUrl.value) {
URL.revokeObjectURL(receiptPreviewUrl.value)
receiptPreviewUrl.value = ''
}
}
const getUploadedFileUrl = (data: FileUploadResponse) => {
if (typeof data === 'string') return data
return (
data.url ||
data.file_url ||
data.full_url ||
data.path ||
data.uri ||
data.file ||
data.src ||
''
)
}
const uploadReceiptCode = async (file: File) => {
const formData = new FormData()
formData.append('biz_type', RECEIPT_UPLOAD_BIZ_TYPE)
formData.append('file', file, file.name)
const data = await request.post<FormData, FileUploadResponse>(
'/api/v1/public/file/upload',
formData,
)
return getUploadedFileUrl(data)
}
const onFileChange = async (e: Event) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (!file) return
if (!file.type.startsWith('image/')) {
toast.error('请上传图片文件')
return
}
try {
isUploadingReceipt.value = true
const uploadedUrl = await uploadReceiptCode(file)
if (!uploadedUrl) {
toast.error('上传失败,请重试')
return
}
revokeReceiptPreview()
receiptPreviewUrl.value = URL.createObjectURL(file)
setFieldValue('avatar', uploadedUrl)
} catch (err) {
console.error('收款码上传失败:', err)
toast.error('上传失败,请重试')
} finally {
isUploadingReceipt.value = false
// 重置 input,允许重新选择同一张图
if (e.target) {
;(e.target as HTMLInputElement).value = ''
}
}
}
// --- 表单验证 Schema --- // --- 表单验证 Schema ---
const formSchema = toTypedSchema( const formSchema = toTypedSchema(
@@ -196,6 +316,7 @@ const { handleSubmit, resetForm, values, setFieldValue } = useForm({
// --- 暴露给父组件的方法 --- // --- 暴露给父组件的方法 ---
const show = () => { const show = () => {
revokeReceiptPreview()
resetForm() resetForm()
open.value = true open.value = true
} }
@@ -206,39 +327,35 @@ const onSubmit = handleSubmit(async (val) => {
const amount = parseFloat(val.money) const amount = parseFloat(val.money)
// 1. 基础校验 // 1. 基础校验
if (amount > props.commission / 100) return toast.error('佣金不足') const minAmount = 30
if (amount < 200) return toast.error('金额不能小于200') if (amount < minAmount)
return toast.error(`金额不能小于${minAmount}${val.type === 'USDT(TRC20)' ? 'USDT' : '$'}`)
try { try {
isPending.value = true isPending.value = true
// 2. 检查是否有未完成工单 // 2. 获取最新佣金进行校验
const data = await request.get<any>('/v1/public/ticket/list', { const userInfo = await request.get<any>('/api/v1/public/user/info')
page: 1, const latestCommission = userInfo?.commission || 0
size: 1, if (Math.round(amount * 100) > latestCommission) {
issue_type: 1, toast.error('佣金不足')
})
if (data?.list?.length > 0) {
toast.info('已有待处理申请')
return return
} }
// 3. 执行提现请求 // 3. 执行提现请求
const description = await request.post('/api/v1/public/user/commission_withdraw', {
val.type === 'USDT(TRC20)' ? `${val.type}-${val.account}` : `${val.type}-${val.avatar}` amount: Math.round(amount * 100),
method: WITHDRAW_METHOD[val.type],
await request.post('/v1/public/ticket/', { account: val.type === 'USDT(TRC20)' ? val.account : '',
title: `提现-${val.money}`, qr_code_url: val.type === 'USDT(TRC20)' ? '' : val.avatar,
description,
issue_type: 1,
}) })
// 4. 成功后处理 // 4. 成功后处理
toast.success('提交成功') toast.success('提现申请已提交,请等待审核')
open.value = false open.value = false
emit('confirm') // 触发父组件刷新 info 接口 emit('confirm') // 触发父组件刷新 info 接口
} catch (error) { } catch (error) {
console.error('提现失败:', error) console.error('提现失败1:', error)
toast.error('操作失败,请重试') toast.error('操作失败,请重试')
} finally { } finally {
isPending.value = false isPending.value = false
@@ -0,0 +1,86 @@
<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-[628px] w-full flex-col rounded-[32px] bg-[#DDDDDD] px-4 py-6">
<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">{{ title }}</h2>
<WithdrawalLogList
ref="logListRef"
:biz-type="bizType"
:amount-label="amountLabel"
:empty-text="emptyText"
:show-content="showContent"
/>
</div>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { nextTick, ref } from 'vue'
import { Dialog, DialogContent } from '@/components/ui/dialog'
import WithdrawalLogList from './WithdrawalLogList.vue'
withDefaults(
defineProps<{
title?: string
bizType?: string
amountLabel?: string
emptyText?: string
showContent?: boolean
}>(),
{
title: '提现记录',
bizType: '',
amountLabel: '提现金额',
emptyText: '暂无提现记录',
showContent: false,
},
)
const isOpen = ref(false)
const logListRef = ref<InstanceType<typeof WithdrawalLogList> | null>(null)
const setOpen = (value: boolean) => {
isOpen.value = value
}
const show = () => {
isOpen.value = true
nextTick(() => {
logListRef.value?.refresh()
})
}
const hide = () => {
isOpen.value = false
}
defineExpose({
show,
hide,
})
</script>
@@ -0,0 +1,213 @@
<template>
<div class="flex flex-1 flex-col overflow-hidden">
<div class="h-[430px] overflow-y-auto pr-1">
<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"
>
{{ emptyText }}
</div>
<div v-else class="space-y-[10px]">
<div
v-for="item in list"
:key="item.id"
class="rounded-[20px] bg-[#CECECF] py-2 text-[14px] font-normal text-black"
>
<div v-if="showContent" class="grid grid-cols-2 gap-y-2">
<div class="pl-4">
<div class="text-xs text-gray-500">{{ amountLabel }}</div>
<div class="font-bold text-[#222]">${{ formatAmount(item.amount) }}</div>
</div>
<div>
<div class="text-xs text-gray-500">退费时间</div>
<div class="whitespace-nowrap">{{ formatTime(item.created_at) }}</div>
</div>
<div class="col-span-2 px-4">
<div class="text-xs text-gray-500">说明</div>
<div class="break-words leading-snug">{{ item.content || '-' }}</div>
</div>
</div>
<div v-else class="grid grid-cols-2 gap-y-1">
<div class="pl-4">
<div class="text-xs text-gray-500">{{ amountLabel }}</div>
<div class="font-bold text-[#222]">${{ formatAmount(item.amount) }}</div>
</div>
<div>
<div class="text-xs text-gray-500">状态</div>
<div class="font-medium">{{ statusText(item.status) }}</div>
</div>
<div class="pl-4">
<div class="text-xs text-gray-500">提现方式</div>
<div>{{ methodText(item.method) }}</div>
</div>
<div>
<div class="text-xs text-gray-500">申请时间</div>
<div class="whitespace-nowrap">{{ formatTime(item.created_at) }}</div>
</div>
<div v-if="item.reason" class="col-span-2 pl-4">
<div class="text-xs text-gray-500">拒绝原因</div>
<div class="break-all">{{ item.reason }}</div>
</div>
</div>
</div>
</div>
</div>
<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 disabled:opacity-30"
>
<span class="text-lg font-bold text-[#848484]">&lt;&lt;</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 disabled:opacity-30"
>
<span class="text-lg font-bold text-[#848484]">&lt;</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 disabled:opacity-30"
>
<span class="text-lg font-bold text-[#848484]">&gt;</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 disabled:opacity-30"
>
<span class="text-lg font-bold text-[#848484]">&gt;&gt;</span>
</button>
</div>
<div class="text-xs font-[300] text-[#848484]"> {{ page }} / {{ totalPages }} </div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import request from '@/utils/request'
const props = withDefaults(
defineProps<{
bizType?: string
amountLabel?: string
emptyText?: string
showContent?: boolean
}>(),
{
bizType: '',
amountLabel: '提现金额',
emptyText: '暂无提现记录',
showContent: false,
},
)
interface WithdrawalLog {
id: number
amount: number
status: number
method: number
content?: string
reason?: string
created_at: number
}
const page = ref(1)
const size = ref(4)
const total = ref(0)
const list = ref<WithdrawalLog[]>([])
const loading = ref(false)
const totalPages = computed(() => Math.ceil(total.value / size.value) || 1)
async function fetchLogs() {
loading.value = true
try {
const res: any = await request.get('/api/v1/public/user/withdrawal_log', {
page: page.value,
size: size.value,
...(props.bizType ? { biz_type: props.bizType } : {}),
})
list.value = res.list || []
total.value = res.total || 0
} catch (error) {
console.error('Fetch withdrawal logs error:', error)
} finally {
loading.value = false
}
}
function refresh() {
page.value = 1
fetchLogs()
}
function changePage(p: number) {
if (p < 1 || p > totalPages.value) return
page.value = p
fetchLogs()
}
function statusText(status: number) {
const map: Record<number, string> = {
0: '待审核',
1: '已通过',
2: '已拒绝',
3: '已取消',
}
return map[status] || '未知'
}
function methodText(method: number) {
const map: Record<number, string> = {
0: '其他',
1: '支付宝',
2: '微信',
3: 'USDT(TRC20)',
}
return map[method] || '未知'
}
function formatAmount(amount: number) {
return ((amount || 0) / 100).toFixed(2)
}
function formatTime(timestamp: number) {
if (!timestamp) return '-'
const date = new Date(timestamp > 10000000000 ? timestamp : timestamp * 1000)
return date.toLocaleString('zh-CN', {
month: 'numeric',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: false,
})
}
defineExpose({
refresh,
})
</script>
@@ -1,4 +1,4 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.14678 6.25189H11.9369C12.5063 6.25189 13.0523 6.47268 13.4549 6.86569C13.8575 7.2587 14.0837 7.79174 14.0837 8.34754V17.9046C14.0837 18.4604 13.8575 18.9934 13.4549 19.3864C13.0523 19.7794 12.5063 20.0002 11.9369 20.0003H2.14682C1.57745 20.0003 1.03139 19.7795 0.628788 19.3864C0.226182 18.9934 4.67869e-10 18.4604 4.67869e-10 17.9046V8.34758C-5.88518e-06 8.07237 0.055518 7.79986 0.163401 7.5456C0.271285 7.29133 0.429415 7.06031 0.628762 6.8657C0.82811 6.6711 1.06477 6.51673 1.32523 6.41141C1.58569 6.30609 1.86486 6.25189 2.14678 6.25189Z" fill="black"/> <path d="M2.14678 6.25189H11.9369C12.5063 6.25189 13.0523 6.47268 13.4549 6.86569C13.8575 7.2587 14.0837 7.79174 14.0837 8.34754V17.9046C14.0837 18.4604 13.8575 18.9934 13.4549 19.3864C13.0523 19.7794 12.5063 20.0002 11.9369 20.0003H2.14682C1.57745 20.0003 1.03139 19.7795 0.628788 19.3864C0.226182 18.9934 4.67869e-10 18.4604 4.67869e-10 17.9046V8.34758C-5.88518e-06 8.07237 0.055518 7.79986 0.163401 7.5456C0.271285 7.29133 0.429415 7.06031 0.628762 6.8657C0.82811 6.6711 1.06477 6.51673 1.32523 6.41141C1.58569 6.30609 1.86486 6.25189 2.14678 6.25189Z" fill="currentColor"/>
<path d="M18.3412 0H8.55111C7.98218 0.00138285 7.43695 0.222623 7.03465 0.615344C6.63235 1.00807 6.40571 1.54031 6.4043 2.0957V5.00011H11.9369C12.8457 5.00221 13.7166 5.35556 14.3592 5.98286C15.0018 6.61016 15.3638 7.46036 15.366 8.3475V13.7484H18.3412C18.9101 13.747 19.4553 13.5257 19.8576 13.133C20.2599 12.7403 20.4866 12.2081 20.488 11.6527V2.09572C20.4866 1.54033 20.2599 1.00808 19.8576 0.615355C19.4553 0.222629 18.9101 0.00138455 18.3412 0Z" fill="black"/> <path d="M18.3412 0H8.55111C7.98218 0.00138285 7.43695 0.222623 7.03465 0.615344C6.63235 1.00807 6.40571 1.54031 6.4043 2.0957V5.00011H11.9369C12.8457 5.00221 13.7166 5.35556 14.3592 5.98286C15.0018 6.61016 15.3638 7.46036 15.366 8.3475V13.7484H18.3412C18.9101 13.747 19.4553 13.5257 19.8576 13.133C20.2599 12.7403 20.4866 12.2081 20.488 11.6527V2.09572C20.4866 1.54033 20.2599 1.00808 19.8576 0.615355C19.4553 0.222629 18.9101 0.00138455 18.3412 0Z" fill="currentColor"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -12,11 +12,21 @@
<div class="ml-2 flex flex-col justify-center text-white"> <div class="ml-2 flex flex-col justify-center text-white">
<div class="text-base font-semibold">{{ userInfo.email }}</div> <div class="text-base font-semibold">{{ userInfo.email }}</div>
<div class="flex items-center text-base font-semibold"> <div class="flex items-center text-base font-semibold">
<span class="mr-0.5 text-3xl">🌞</span> 恒星合伙人 <span class="mr-0.5 text-3xl">🌞</span> {{ referralText }}
</div> </div>
</div> </div>
</div> </div>
<div class="mt-7 mb-1 ml-[18px] font-semibold text-[#ADFF5B]">专属代理链接</div> <div class="mt-7 mb-1 ml-[18px] flex justify-between font-semibold text-[#ADFF5B]">
专属代理链接
<Button
variant="link"
class="mr-6 h-auto p-0 text-sm font-bold text-[#999] underline outline-none hover:text-[#666]"
@click="withdrawalLogDialogRef?.show()"
>
提现记录
</Button>
</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"
> >
@@ -27,26 +37,38 @@
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"
> >
<div> <div>
<div class="text-xl font-semibold">佣金账户余额</div> <div class="flex items-center text-xl font-semibold">佣金账户余额</div>
<div class="text-3xl font-black"> <div class="text-3xl font-black">
$ {{ (userInfo.commission / 100 || 0).toFixed(2) }} $ {{ (userInfo.commission / 100 || 0).toFixed(2) }}
</div> </div>
</div> </div>
<Button variant="ghost" class="hover:bg-transparent" @click="walletDialogRef?.show()"> <Button
点击提现 variant="ghost"
class="hover:bg-transparent"
:disabled="isCheckingWithdrawal"
@click="handleWithdrawClick"
>
{{ hasPendingWithdrawal ? '审核中' : '点击提现' }}
</Button> </Button>
</div> </div>
</div> </div>
</Transition> </Transition>
</div> </div>
</div> </div>
<WalletDialog <WalletDialog ref="walletDialogRef" :commission="userInfo.commission || 0" @confirm="init" />
ref="walletDialogRef" <WithdrawalLogDialog ref="withdrawalLogDialogRef" />
:commission="userInfo.commission || 0"
@confirm="init"
/>
<div> <div>
<div class="flex flex-col gap-[10px] px-6 pt-8 pb-9"> <div class="flex flex-col gap-[10px] px-6 pt-8 pb-4">
<div
class="flex h-[50px] w-full items-center justify-between rounded-[32px] bg-[#222222] px-4 pr-6 leading-[50px] font-medium"
>
我的邀请码{{ userInfo.refer_code }}
<CopyIcon class="size-5 cursor-pointer text-white" @click="copy(userInfo.refer_code)" />
</div>
<div class="mt-[-2px] mb-1 px-2 text-[11px] leading-snug text-[#ADFF5B]">
*
提醒如果客户没有海外苹果ID使用官方提供的ID会导致绑定失败用户可以手动输入此邀请码绑定代理
</div>
<div class="h-[50px] w-full rounded-[32px] bg-[#222222] px-4 leading-[50px] font-medium"> <div class="h-[50px] w-full rounded-[32px] bg-[#222222] px-4 leading-[50px] font-medium">
历史佣金总计$ {{ (inviteStats.friendly_count / 100).toFixed(2) }} 历史佣金总计$ {{ (inviteStats.friendly_count / 100).toFixed(2) }}
</div> </div>
@@ -70,6 +92,7 @@
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue' import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import WalletDialog from './components/WalletDialog.vue' import WalletDialog from './components/WalletDialog.vue'
import WithdrawalLogDialog from './components/WithdrawalLogDialog.vue'
import CopyIcon from './copy.svg?component' 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'
@@ -78,17 +101,21 @@ import { toast } from 'vue-sonner'
const router = useRouter() const router = useRouter()
const walletDialogRef = ref<InstanceType<typeof WalletDialog> | null>(null) const walletDialogRef = ref<InstanceType<typeof WalletDialog> | null>(null)
const withdrawalLogDialogRef = ref<InstanceType<typeof WithdrawalLogDialog> | null>(null)
const userInfo = ref({ const userInfo = ref({
email: '', email: '',
created_at: '', created_at: '',
share_link: '', share_link: '',
commission: 0, commission: 0,
referral_percentage: 0,
}) })
const inviteStats = ref({ const inviteStats = ref({
friendly_count: 0, friendly_count: 0,
history_count: 0, history_count: 0,
}) })
const isUserLoading = ref(true) const isUserLoading = ref(true)
const isCheckingWithdrawal = ref(false)
const hasPendingWithdrawal = ref(false)
async function init() { async function init() {
// 1. 用户信息 & 设备列表 // 1. 用户信息 & 设备列表
isUserLoading.value = true isUserLoading.value = true
@@ -110,6 +137,8 @@ async function init() {
request.get('/api/v1/public/user/invite/stats').then((res: any) => { request.get('/api/v1/public/user/invite/stats').then((res: any) => {
inviteStats.value = res inviteStats.value = res
}) })
checkPendingWithdrawal()
} }
onMounted(() => { onMounted(() => {
@@ -133,6 +162,51 @@ const formattedDate = computed(() => {
}).format(date) }).format(date)
}) })
const referralText = computed(() => {
const percentage = Number(userInfo.value.referral_percentage || 0)
if (percentage <= 0) return '暂未激活比例,请联系客服激活'
if (percentage <= 20) return `初级合伙人 返佣比例${percentage}%`
if (percentage <= 39) return `高级合伙人 返佣比例${percentage}%`
return `超级合伙人 返佣比例${percentage}%`
})
async function handleWithdrawClick() {
if (hasPendingWithdrawal.value) {
toast.info('已有提现申请审核中,请勿重复提交')
return
}
if (isCheckingWithdrawal.value) return
const canWithdraw = await checkPendingWithdrawal(true)
if (canWithdraw) {
walletDialogRef.value?.show()
}
}
async function checkPendingWithdrawal(showErrorToast = false) {
try {
isCheckingWithdrawal.value = true
const res: any = await request.get('/api/v1/public/user/withdrawal_log', {
page: 1,
size: 20,
})
hasPendingWithdrawal.value = (res?.list || []).some((item: any) => item.status === 0)
if (hasPendingWithdrawal.value) {
toast.info('已有提现申请审核中,请勿重复提交')
return false
}
return true
} catch (error) {
console.error('Check withdrawal logs error:', error)
if (showErrorToast) {
toast.error('提现记录检查失败,请稍后重试')
}
return false
} finally {
isCheckingWithdrawal.value = false
}
}
function copy(text: string) { function copy(text: string) {
navigator.clipboard.writeText(text).then(() => { navigator.clipboard.writeText(text).then(() => {
toast.success('已复制到剪贴板') toast.success('已复制到剪贴板')
+4 -4
View File
@@ -8,10 +8,10 @@
html, body { html, body {
background: #000; background: #000;
} }
body { /*body {*/
min-width: 1220px; /* min-width: 1220px;*/
overflow-x: auto; /* 当窗口小于1024px时显示横向滚动条 */ /* overflow-x: auto; !* 当窗口小于1024px时显示横向滚动条 *!*/
} /*}*/
.lucid-glass-bar { .lucid-glass-bar {
/* 基础背景:Figma 中通常是叠加的,这里取中值确保通透度 */ /* 基础背景:Figma 中通常是叠加的,这里取中值确保通透度 */
background: rgb(255 255 255 / 6%); background: rgb(255 255 255 / 6%);