构建提现
site-dist-deploy / build-and-deploy (push) Successful in 1m8s

This commit is contained in:
2026-01-30 19:50:14 -08:00
parent de0c9ca198
commit 2a7e851cda
29 changed files with 1965 additions and 85 deletions
@@ -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() {