@@ -25,7 +25,7 @@ import CopyIcon from '../UserInfo/copy.svg?component'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const tools = [
|
||||
{ label: 'iOS外区账号教程', url: 'hifastvpn.com/tutorial' },
|
||||
{ label: 'iOS外区账号教程', url: 'http://getsapp.net/F6Lmev' },
|
||||
{ label: 'app视频使用教程', url: 'hifastvpn.com/tutorialvids' },
|
||||
{ label: '软件防丢名片', url: 'hifastvpn.com/alwaysfindus' },
|
||||
{ label: '官方Telegram群', url: 't.me/hifastvpnofficial' },
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<Dialog v-model:open="open">
|
||||
<DialogContent
|
||||
class="gap-0 rounded-[40px]! border-none bg-[#111111] px-12 py-10 text-white shadow-2xl! ring-0! outline-hidden! sm:max-w-[500px]"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle class="text-left text-2xl font-bold tracking-tight text-white"
|
||||
>佣金提现</DialogTitle
|
||||
>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="my-4 text-left text-sm font-medium text-white/30">
|
||||
{{
|
||||
values.type === 'USDT(TRC20)'
|
||||
? '将佣金提现至您的个人数字钱包,无手续费'
|
||||
: '该方式涉及平台手续费,预计 1-3 个工作日到账'
|
||||
}}
|
||||
</div>
|
||||
|
||||
<form @submit="onSubmit" class="space-y-[20px]">
|
||||
<FormField v-slot="{ componentField }" name="type">
|
||||
<FormItem>
|
||||
<FormLabel class="block text-sm font-bold text-white">提现方式</FormLabel>
|
||||
<Select v-bind="componentField">
|
||||
<FormControl>
|
||||
<SelectTrigger
|
||||
class="!h-[46px] w-full rounded-[32px] border-[4px] border-[#ADFF5B] bg-transparent px-6 text-sm font-medium text-[#ADFF5B] outline-hidden! focus:ring-0! [&_svg]:size-6 [&_svg]:text-[#ADFF5B] [&_svg]:opacity-100"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent class="rounded-2xl border-white/10 bg-[#222222] text-white">
|
||||
<SelectItem
|
||||
v-for="t in ACCOUNT_TYPE"
|
||||
:key="t"
|
||||
:value="t"
|
||||
class="py-3 text-sm focus:bg-[#ADFF5B] focus:text-black"
|
||||
>
|
||||
{{ t === 'USDT(TRC20)' ? 'USDT' : t }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-if="values.type === 'USDT(TRC20)'" v-slot="{ componentField }" name="account">
|
||||
<FormItem>
|
||||
<FormLabel class="block text-sm font-bold text-white">提现地址</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
v-bind="componentField"
|
||||
class="h-[46px] rounded-[32px] border-none bg-[#222222] px-6 text-sm text-white transition-colors placeholder:text-white/20 focus:bg-[#2a2a2a]"
|
||||
placeholder="输入地址"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage class="ml-4 text-red-400" />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="money">
|
||||
<FormItem>
|
||||
<FormLabel class="block text-sm font-bold text-white">提现金额</FormLabel>
|
||||
<div
|
||||
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>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
v-bind="componentField"
|
||||
type="number"
|
||||
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"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<div class="flex h-full items-center px-6 text-sm font-bold text-black">RMB</div>
|
||||
</div>
|
||||
<FormMessage class="ml-4 text-red-400" />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-if="values.type !== 'USDT(TRC20)'" name="avatar">
|
||||
<FormItem>
|
||||
<FormLabel class="mb-4 block text-sm font-bold text-white">收款码</FormLabel>
|
||||
<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]"
|
||||
>
|
||||
<Upload class="mb-3 h-12 w-12 text-white/20" />
|
||||
<span class="text-base font-medium text-white/30">点击上传高清收款二维码</span>
|
||||
</div>
|
||||
<FormMessage class="ml-4 text-red-400" />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<div class="flex justify-center pt-2">
|
||||
<Button
|
||||
type="submit"
|
||||
:disabled="isPending"
|
||||
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" />
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { z } from 'zod'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { Upload, Loader2 } from 'lucide-vue-next'
|
||||
|
||||
// Shadcn UI 组件
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
|
||||
// 业务 API
|
||||
import request from '@/utils/request'
|
||||
|
||||
const props = defineProps<{ commission: number }>()
|
||||
const emit = defineEmits(['confirm'])
|
||||
|
||||
const open = ref(false)
|
||||
const isPending = ref(false) // 手动管理加载状态
|
||||
const ACCOUNT_TYPE = ['USDT(TRC20)', '微信', '支付宝'] as const
|
||||
|
||||
// --- 表单验证 Schema ---
|
||||
const formSchema = toTypedSchema(
|
||||
z
|
||||
.object({
|
||||
type: z.enum(ACCOUNT_TYPE),
|
||||
account: z.string().optional(),
|
||||
money: z
|
||||
.string()
|
||||
.min(1, '金额不能为空')
|
||||
.regex(/^\d+(\.\d+)?$/, '格式错误'),
|
||||
avatar: z.string().optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.type === 'USDT(TRC20)' && !data.account) {
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: '地址不能为空', path: ['account'] })
|
||||
} else if (data.type !== 'USDT(TRC20)' && !data.avatar) {
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: '请上传收款码', path: ['avatar'] })
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
const { handleSubmit, resetForm, values } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: { type: 'USDT(TRC20)', account: '', money: '', avatar: '' },
|
||||
})
|
||||
|
||||
// --- 暴露给父组件的方法 ---
|
||||
const show = () => {
|
||||
resetForm()
|
||||
open.value = true
|
||||
}
|
||||
defineExpose({ show })
|
||||
|
||||
// --- 提交逻辑 ---
|
||||
const onSubmit = handleSubmit(async (val) => {
|
||||
const amount = parseFloat(val.money)
|
||||
|
||||
// 1. 基础校验
|
||||
if (amount > props.commission / 100) return toast.error('超过最大佣金')
|
||||
if (amount < 200) return toast.error('金额不能小于200')
|
||||
|
||||
try {
|
||||
isPending.value = true
|
||||
|
||||
// 2. 检查是否有未完成工单
|
||||
const data = await request.get<any>('/v1/public/ticket/list', {
|
||||
page: 1,
|
||||
size: 1,
|
||||
issue_type: 1,
|
||||
})
|
||||
if (data?.list?.length > 0) {
|
||||
toast.info('已有待处理申请')
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 执行提现请求
|
||||
const description =
|
||||
val.type === 'USDT(TRC20)' ? `${val.type}-${val.account}` : `${val.type}-${val.avatar}`
|
||||
|
||||
await request.post('/v1/public/ticket/', {
|
||||
title: `提现-${val.money}`,
|
||||
description,
|
||||
issue_type: 1,
|
||||
})
|
||||
|
||||
// 4. 成功后处理
|
||||
toast.success('提交成功')
|
||||
open.value = false
|
||||
emit('confirm') // 触发父组件刷新 info 接口
|
||||
} catch (error) {
|
||||
console.error('提现失败:', error)
|
||||
toast.error('操作失败,请重试')
|
||||
} finally {
|
||||
isPending.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -28,14 +28,23 @@
|
||||
>
|
||||
<div>
|
||||
<div class="text-xl font-semibold">佣金账户余额</div>
|
||||
<div class="text-3xl font-black">$12344</div>
|
||||
<div class="text-3xl font-black">
|
||||
$ {{ (userInfo.commission / 100 || 0).toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="ghost" class="hover:bg-transparent">点击提现</Button>
|
||||
<Button variant="ghost" class="hover:bg-transparent" @click="walletDialogRef?.show()">
|
||||
点击提现
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
<WalletDialog
|
||||
ref="walletDialogRef"
|
||||
:commission="userInfo.commission || 0"
|
||||
@confirm="init"
|
||||
/>
|
||||
<div>
|
||||
<div class="flex flex-col gap-[10px] px-6 pt-8 pb-9">
|
||||
<div class="h-[50px] w-full rounded-[32px] bg-[#222222] px-4 leading-[50px] font-medium">
|
||||
@@ -60,6 +69,7 @@
|
||||
<script setup lang="ts">
|
||||
import UserCenterSkeleton from '@/components/user-center/UserCenterSkeleton.vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import WalletDialog from './components/WalletDialog.vue'
|
||||
import CopyIcon from './copy.svg?component'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import request from '@/utils/request'
|
||||
@@ -67,9 +77,12 @@ import { useRouter } from 'vue-router'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const router = useRouter()
|
||||
const walletDialogRef = ref<InstanceType<typeof WalletDialog> | null>(null)
|
||||
const userInfo = ref({
|
||||
email: '',
|
||||
created_at: '',
|
||||
share_link: '',
|
||||
commission: 0,
|
||||
})
|
||||
const isUserLoading = ref(true)
|
||||
async function init() {
|
||||
|
||||
Reference in New Issue
Block a user