Procházet zdrojové kódy

用户下单修改,判断有无企业

“陈博论” před 9 měsíci
rodič
revize
27d0645c23

+ 3 - 1
web/src/views/frontend/Me.vue

@@ -40,11 +40,12 @@
 <script setup lang="ts">
 import { computed, markRaw, onMounted, reactive, ref, watch, type Component } from 'vue'
 import { useRoute } from 'vue-router'
-import { Goods, HomeFilled, OfficeBuilding, ShoppingCartFull, User } from '@element-plus/icons-vue'
+import { Goods, HomeFilled,Tickets, OfficeBuilding, ShoppingCartFull, User } from '@element-plus/icons-vue'
 import Header from './components/header.vue'
 // 导入各个功能组件
 import Info from './components/Me/info.vue'
 import PurchaseHistory from './components/Me/purchaseHistory.vue'
+import withdrawList from './components/Me/withdrawList.vue'
 import PurchaseProduct from './components/Me/purchaseProduct.vue'
 import Subordinates from './components/Me/subordinates.vue'
 import Enterprise from './components/Me/enterprise.vue'
@@ -58,6 +59,7 @@ const activeMenu = ref('home')
 const menuList = reactive<MenuItem[]>([
   { label: '我的主页', value: 'home', component: markRaw(Info), icon: markRaw(HomeFilled) },
   { label: '购买记录', value: 'purchase-history', component: markRaw(PurchaseHistory), icon: markRaw(ShoppingCartFull) },
+  { label: '提现记录', value: 'withdraw-list', component: markRaw(withdrawList), icon: markRaw(Tickets) },
   { label: '我的产品', value: 'purchase-product', component: markRaw(PurchaseProduct), icon: markRaw(Goods) },
   { label: '我的下级', value: 'subordinates', component: markRaw(Subordinates), icon: markRaw(User) },
   { label: '我的企业', value: 'enterprise', component: markRaw(Enterprise), icon: markRaw(OfficeBuilding) }

+ 34 - 28
web/src/views/frontend/Order.vue

@@ -68,8 +68,8 @@
                 <span>购买席位</span>
               </div>
               <div class="field-content quantity-control">
-                <el-input-number v-model="seatCount" :min="2" :step="1" size="small"
-                  controls-position="right" class="qty-input-number" />
+                <el-input-number v-model="seatCount" :min="2" :step="1" size="small" controls-position="right"
+                  class="qty-input-number" />
                 <span class="stock-hint">
                   最少2个席位,当前默认 {{ seatCount }} 个
                 </span>
@@ -114,17 +114,9 @@
       <CreateEnterpriseDialog v-model:visible="createEnterpriseVisible" @success="handleEnterpriseCreated" />
 
       <!-- 支付二维码弹窗 -->
-      <PayQrCodeDialog
-        v-model:visible="payDialogVisible"
-        :order-info="currentOrderInfo"
-        :qr-code-url="qrCodeUrl"
-        :pay-method="selectedPayMethod"
-        :loading="qrCodeLoading"
-        :pay-status="payStatus"
-        @close="handlePayDialogClose"
-        @confirm="handlePaySuccess"
-        @expired="handlePayExpired"
-      />
+      <PayQrCodeDialog v-model:visible="payDialogVisible" :order-info="currentOrderInfo" :qr-code-url="qrCodeUrl"
+        :pay-method="selectedPayMethod" :loading="qrCodeLoading" :pay-status="payStatus" @close="handlePayDialogClose"
+        @confirm="handlePaySuccess" @expired="handlePayExpired" />
 
       <!-- 下半部分:宝贝详情 -->
       <el-card class="detail-panel" shadow="never">
@@ -146,12 +138,13 @@
 import { computed, onMounted, onBeforeUnmount, ref } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
-import { ChatDotRound ,Coin} from '@element-plus/icons-vue'
+import { ChatDotRound, Coin } from '@element-plus/icons-vue'
 import Header from './components/header.vue'
 import CreateEnterpriseDialog from './components/CreateEnterpriseDialog.vue'
 import PayQrCodeDialog from './components/PayQrCodeDialog.vue'
 import { detail } from '/@/api/frontend/home'
 import { createOrder, createQrCode, checkOrderStatus } from '/@/api/frontend/order'
+import { checkCompany } from '/@/api/frontend/me'
 
 defineOptions({ name: 'ClientOrder' })
 
@@ -216,7 +209,7 @@ const displayPrice = computed(() => {
 // 获取商品详情
 const getGoodsInfo = () => {
   goodsId.value = route.params?.goodsId
-  detail({id:goodsId.value}).then(res =>{
+  detail({ id: goodsId.value }).then(res => {
     goodsInfo.value = res.data
   })
 }
@@ -233,14 +226,14 @@ const createOrderAndQrCode = async () => {
 
     qrCodeLoading.value = true
     const orderRes = await createOrder(orderData)
-    
+
     // 验证订单创建结果
     if (!orderRes || !orderRes.data) {
       ElMessage.error(orderRes?.msg || '创建订单失败,请重试')
       qrCodeLoading.value = false
       return
     }
-    
+
     if (!orderRes.data.order_id || !orderRes.data.order_no) {
       ElMessage.error(orderRes.data.msg || '订单信息不完整,请重试')
       qrCodeLoading.value = false
@@ -270,14 +263,14 @@ const createOrderAndQrCode = async () => {
     }
 
     const qrCodeRes = await createQrCode(qrCodeData)
-    
+
     // 验证二维码生成结果
     if (!qrCodeRes || !qrCodeRes.data) {
       ElMessage.error(qrCodeRes?.msg || '生成支付二维码失败,请重试')
       qrCodeLoading.value = false
       return
     }
-    
+
     // 根据接口返回数据结构,使用 qr_code_url 或 qr_code_content
     const qrCodeValue = qrCodeRes.data.qr_code_url || qrCodeRes.data.qr_code_content
     if (!qrCodeValue) {
@@ -305,7 +298,7 @@ const createOrderAndQrCode = async () => {
     qrCodeUrl.value = qrCodeValue
     qrCodeLoading.value = false
     payStatus.value = ''
-    
+
     // 3. 显示支付弹窗
     payDialogVisible.value = true
 
@@ -325,7 +318,7 @@ const startPayStatusPolling = (orderId, orderNo) => {
   if (payCheckTimer.value) {
     clearInterval(payCheckTimer.value)
   }
-  
+
   // 重置轮询计数
   payCheckCount.value = 0
 
@@ -333,7 +326,7 @@ const startPayStatusPolling = (orderId, orderNo) => {
   payCheckTimer.value = setInterval(async () => {
     try {
       payCheckCount.value++
-      
+
       // 超过最大轮询次数,停止轮询
       if (payCheckCount.value > MAX_PAY_CHECK_COUNT) {
         stopPayStatusPolling()
@@ -347,16 +340,16 @@ const startPayStatusPolling = (orderId, orderNo) => {
         order_no: orderNo
       }
       const res = await checkOrderStatus(checkData)
-      
+
       // 支付状态:1-已支付,0-待支付,2-支付失败,3-已取消(兼容数字/字符串)
       const rawStatus = res.data?.order?.pay_status
       const status = rawStatus != null ? String(rawStatus) : ''
-      
+
       if (status === '1') {
         // 支付成功
         payStatus.value = 'success'
         stopPayStatusPolling()
-        
+
         // 更新订单信息(可能包含最新的支付信息)
         if (res.data) {
           currentOrderInfo.value = {
@@ -365,7 +358,7 @@ const startPayStatusPolling = (orderId, orderNo) => {
             pay_status: status
           }
         }
-        
+
         // 3秒后自动关闭弹窗并跳转成功页
         setTimeout(() => {
           handlePaySuccess()
@@ -474,8 +467,10 @@ const openCreateEnterpriseDialog = () => {
   createEnterpriseVisible.value = true
 }
 
-const handleEnterpriseCreated = () => {
-  hasEnterprise.value = true
+const handleEnterpriseCreated = async () => {
+  // 企业创建成功后,重新检查企业状态
+  await checkUserCompany()
+  ElMessage.success('企业创建成功!')
 }
 
 const handleSubmit = () => {
@@ -504,8 +499,19 @@ const handleSubmit = () => {
   createOrderAndQrCode()
 }
 
+// 检查用户是否拥有企业
+const checkUserCompany = async () => {
+  try {
+    const res = await checkCompany()
+    hasEnterprise.value = res.data.has_company
+  } catch (error) {
+    hasEnterprise.value = false
+  }
+}
+
 onMounted(() => {
   getGoodsInfo()
+  checkUserCompany()
 })
 
 // 组件卸载前清除定时器

+ 7 - 2
web/src/views/frontend/components/Me/info.vue

@@ -33,9 +33,9 @@
           <div class="commission-icon icon-current">
             <el-icon><Wallet /></el-icon>
           </div>
-          <div class="commission-content">
+          <div class="commission-content" style="cursor: pointer;">
             <div class="commission-label">当前佣金</div>
-            <div class="commission-value">¥{{ formatMoney(uersInfo?.commission_info?.current_commission) }}</div>
+            <div class="commission-value">¥{{ formatMoney(uersInfo?.commission_info?.current_commission) }} <span>去提现</span></div>
             <div class="commission-desc">可提现的佣金余额</div>
           </div>
         </div>
@@ -771,6 +771,11 @@ onMounted(() => {
             color: #303133;
             margin-bottom: 6px;
             word-break: break-all;
+
+            span{
+              color: #409EFF;
+              font-size: 10px;
+            }
           }
 
           .commission-desc {

+ 793 - 0
web/src/views/frontend/components/Me/withdrawList.vue

@@ -0,0 +1,793 @@
+<template>
+    <div class="me-withdraw-list">
+        <div class="withdraw-header">
+            <div class="header-text">
+                <h2 class="withdraw-title">提现记录</h2>
+                <p class="withdraw-subtitle">查看您的提现申请记录和处理状态</p>
+            </div>
+            <el-button type="primary" :icon="Plus" @click="handleApplyWithdraw">申请提现</el-button>
+        </div>
+
+        <div class="stats-section">
+            <div class="stat-card stat-total">
+                <div class="stat-icon">
+                    <el-icon>
+                        <Money />
+                    </el-icon>
+                </div>
+                <div class="stat-content">
+                    <div class="stat-label">累计提现金额</div>
+                    <div class="stat-value">¥{{ formatMoney(stats.total_amount) }}</div>
+                    <div class="stat-desc">所有已完成提现的总金额</div>
+                </div>
+            </div>
+            <div class="stat-card stat-pending">
+                <div class="stat-icon">
+                    <el-icon>
+                        <Clock />
+                    </el-icon>
+                </div>
+                <div class="stat-content">
+                    <div class="stat-label">待审核金额</div>
+                    <div class="stat-value">¥{{ formatMoney(stats.pending_amount) }}</div>
+                    <div class="stat-desc">正在审核中的提现金额</div>
+                </div>
+            </div>
+            <div class="stat-card stat-success">
+                <div class="stat-icon">
+                    <el-icon>
+                        <CircleCheck />
+                    </el-icon>
+                </div>
+                <div class="stat-content">
+                    <div class="stat-label">成功提现次数</div>
+                    <div class="stat-value">{{ stats.success_count }}</div>
+                    <div class="stat-desc">已成功完成的提现次数</div>
+                </div>
+            </div>
+        </div>
+
+        <div class="withdraw-filters">
+            <el-input v-model="searchForm.withdrawNo" placeholder="请输入提现单号" clearable
+                class="filter-item withdraw-input">
+                <template #prepend>提现单号</template>
+            </el-input>
+
+            <el-date-picker v-model="searchForm.dateRange" class="filter-item date-picker" type="daterange"
+                unlink-panels start-placeholder="申请开始时间" end-placeholder="申请结束时间" range-separator="~"
+                value-format="YYYY-MM-DD" clearable />
+
+            <el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
+            <el-button @click="handleReset">重置</el-button>
+        </div>
+
+        <div class="withdraw-tabs">
+            <el-tabs v-model="activeStatus" @tab-click="handleTabClick">
+                <el-tab-pane v-for="tab in statusTabs" :key="tab.value"
+                    :label="`${tab.label} (${getWithdrawCount(tab.value)})`" :name="tab.value" />
+            </el-tabs>
+        </div>
+
+        <div v-loading="loading" class="withdraw-list-wrapper">
+            <template v-if="filteredWithdraws.length">
+                <div class="withdraw-list">
+                    <div v-for="item in filteredWithdraws" :key="item.id" class="withdraw-card">
+                        <div class="withdraw-card__header">
+                            <div class="header-left">
+                                <span class="withdraw-no">提现单号:{{ item.withdraw_no }}</span>
+                                <el-tag size="small" :type="getStatusType(item.status)" class="status-tag">
+                                    {{ item.status_text }}
+                                </el-tag>
+                            </div>
+                            <div class="header-right">
+                                <span class="withdraw-time">申请时间:{{ item.create_time }}</span>
+                            </div>
+                        </div>
+
+                        <div class="withdraw-card__body">
+                            <div class="withdraw-item">
+                                <div class="item-label">提现金额</div>
+                                <div class="item-value amount">¥{{ formatMoney(item.amount) }}</div>
+                            </div>
+
+                            <div class="withdraw-item">
+                                <div class="item-label">手续费</div>
+                                <div class="item-value fee">¥{{ formatMoney(item.fee) }}</div>
+                            </div>
+
+                            <div class="withdraw-item">
+                                <div class="item-label">实际到账</div>
+                                <div class="item-value actual">¥{{ formatMoney(item.actual_amount) }}</div>
+                            </div>
+
+                            <div class="withdraw-item">
+                                <div class="item-label">提现方式</div>
+                                <div class="item-value">{{ item.withdraw_type_text }}</div>
+                            </div>
+
+                            <div class="withdraw-item">
+                                <div class="item-label">收款账户</div>
+                                <div class="item-value account">{{ item.account }}</div>
+                            </div>
+
+                            <div class="withdraw-item">
+                                <div class="item-label">处理时间</div>
+                                <div class="item-value">{{ item.process_time || '-' }}</div>
+                            </div>
+
+                            <div v-if="item.remark" class="withdraw-item full-width">
+                                <div class="item-label">备注说明</div>
+                                <div class="item-value note">{{ item.remark }}</div>
+                            </div>
+
+                            <div v-if="item.reject_reason" class="withdraw-item full-width">
+                                <div class="item-label">拒绝原因</div>
+                                <div class="item-value reject-reason">{{ item.reject_reason }}</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </template>
+            <template v-else>
+                <el-empty class="withdraw-empty" description="暂无提现记录" />
+            </template>
+        </div>
+
+        <el-dialog v-model="applyDialogVisible" title="申请提现" width="500px" :close-on-click-modal="false">
+            <el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="100px">
+                <el-form-item label="可提现金额">
+                    <div class="available-amount">¥{{ formatMoney(availableAmount) }}</div>
+                </el-form-item>
+
+                <el-form-item label="提现金额" prop="amount">
+                    <el-input v-model="applyForm.amount" placeholder="请输入提现金额" type="number">
+                        <template #append>元</template>
+                    </el-input>
+                    <div class="form-tip">最低提现金额:¥100,手续费率:2%</div>
+                </el-form-item>
+
+                <el-form-item label="提现方式" prop="withdraw_type">
+                    <el-select v-model="applyForm.withdraw_type" placeholder="请选择提现方式">
+                        <el-option label="支付宝" value="alipay" />
+                        <el-option label="微信" value="wechat" />
+                        <el-option label="银行卡" value="bank" />
+                    </el-select>
+                </el-form-item>
+
+                <el-form-item label="收款账户" prop="account">
+                    <el-input v-model="applyForm.account" placeholder="请输入收款账户" />
+                </el-form-item>
+
+                <el-form-item label="账户姓名" prop="account_name">
+                    <el-input v-model="applyForm.account_name" placeholder="请输入账户姓名" />
+                </el-form-item>
+
+                <el-form-item label="备注" prop="remark">
+                    <el-input v-model="applyForm.remark" type="textarea" :rows="3" placeholder="请输入备注信息(选填)" />
+                </el-form-item>
+            </el-form>
+
+            <template #footer>
+                <el-button @click="applyDialogVisible = false">取消</el-button>
+                <el-button type="primary" :loading="submitting" @click="handleSubmitApply">
+                    提交申请
+                </el-button>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue'
+import { Search, Plus, Money, Clock, CircleCheck } from '@element-plus/icons-vue'
+import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
+
+defineOptions({ name: 'MeWithdrawList' })
+
+type WithdrawStatus = 'pending' | 'approved' | 'rejected' | 'processing' | 'completed'
+type WithdrawTabStatus = 'all' | WithdrawStatus
+
+type WithdrawRecord = {
+    id: number
+    withdraw_no: string
+    amount: number
+    fee: number
+    actual_amount: number
+    status: WithdrawStatus
+    status_text: string
+    withdraw_type: string
+    withdraw_type_text: string
+    account: string
+    account_name: string
+    create_time: string
+    process_time: string
+    remark: string
+    reject_reason: string
+}
+
+type WithdrawStats = {
+    total_amount: number
+    pending_amount: number
+    success_count: number
+}
+
+const activeStatus = ref<WithdrawTabStatus>('all')
+const loading = ref(false)
+const submitting = ref(false)
+const applyDialogVisible = ref(false)
+const applyFormRef = ref<FormInstance>()
+
+const searchForm = reactive<{ withdrawNo: string; dateRange: string[] }>({
+    withdrawNo: '',
+    dateRange: []
+})
+
+const appliedFilters = reactive<{ withdrawNo: string; dateRange: string[] }>({
+    withdrawNo: '',
+    dateRange: []
+})
+
+const applyForm = reactive({
+    amount: '',
+    withdraw_type: '',
+    account: '',
+    account_name: '',
+    remark: ''
+})
+
+const availableAmount = ref(0)
+
+const applyRules: FormRules = {
+    amount: [
+        { required: true, message: '请输入提现金额', trigger: 'blur' },
+        {
+            validator: (rule, value, callback) => {
+                if (value < 100) {
+                    callback(new Error('最低提现金额为100元'))
+                } else if (value > availableAmount.value) {
+                    callback(new Error('提现金额不能超过可提现金额'))
+                } else {
+                    callback()
+                }
+            },
+            trigger: 'blur'
+        }
+    ],
+    withdraw_type: [{ required: true, message: '请选择提现方式', trigger: 'change' }],
+    account: [{ required: true, message: '请输入收款账户', trigger: 'blur' }],
+    account_name: [{ required: true, message: '请输入账户姓名', trigger: 'blur' }]
+}
+
+const statusTabs: { label: string; value: WithdrawTabStatus }[] = [
+    { label: '全部', value: 'all' },
+    { label: '待审核', value: 'pending' },
+    { label: '已通过', value: 'approved' },
+    { label: '处理中', value: 'processing' },
+    { label: '已完成', value: 'completed' },
+    { label: '已拒绝', value: 'rejected' }
+]
+
+const withdraws = ref<WithdrawRecord[]>([])
+const stats = ref<WithdrawStats>({
+    total_amount: 0,
+    pending_amount: 0,
+    success_count: 0
+})
+
+const loadWithdrawRecords = async () => {
+    try {
+        loading.value = true
+        await new Promise(resolve => setTimeout(resolve, 500))
+        withdraws.value = [
+            {
+                id: 1,
+                withdraw_no: 'WD202501200001',
+                amount: 1000,
+                fee: 20,
+                actual_amount: 980,
+                status: 'completed',
+                status_text: '已完成',
+                withdraw_type: 'alipay',
+                withdraw_type_text: '支付宝',
+                account: '138****8888',
+                account_name: '张三',
+                create_time: '2025-01-15 10:30:00',
+                process_time: '2025-01-15 14:20:00',
+                remark: '正常提现',
+                reject_reason: ''
+            },
+            {
+                id: 2,
+                withdraw_no: 'WD202501200002',
+                amount: 500,
+                fee: 10,
+                actual_amount: 490,
+                status: 'pending',
+                status_text: '待审核',
+                withdraw_type: 'wechat',
+                withdraw_type_text: '微信',
+                account: 'wx_test123',
+                account_name: '李四',
+                create_time: '2025-01-18 09:15:00',
+                process_time: '',
+                remark: '',
+                reject_reason: ''
+            },
+            {
+                id: 3,
+                withdraw_no: 'WD202501200003',
+                amount: 2000,
+                fee: 40,
+                actual_amount: 1960,
+                status: 'rejected',
+                status_text: '已拒绝',
+                withdraw_type: 'bank',
+                withdraw_type_text: '银行卡',
+                account: '6222****1234',
+                account_name: '王五',
+                create_time: '2025-01-10 16:45:00',
+                process_time: '2025-01-11 10:00:00',
+                remark: '',
+                reject_reason: '账户信息不符,请核对后重新申请'
+            }
+        ]
+
+        stats.value = {
+            total_amount: 15680,
+            pending_amount: 500,
+            success_count: 12
+        }
+
+        availableAmount.value = 3580.50
+    } catch (error) {
+        console.error('加载提现记录失败:', error)
+        ElMessage.error('加载提现记录失败,请稍后重试')
+    } finally {
+        loading.value = false
+    }
+}
+
+const baseFilteredWithdraws = computed(() => {
+    let filtered = withdraws.value
+
+    if (appliedFilters.withdrawNo) {
+        const withdrawNo = appliedFilters.withdrawNo.trim()
+        filtered = filtered.filter(w => w.withdraw_no.includes(withdrawNo))
+    }
+
+    if (appliedFilters.dateRange && appliedFilters.dateRange.length === 2) {
+        filtered = filtered.filter(w => {
+            return isWithinRange(w.create_time, appliedFilters.dateRange)
+        })
+    }
+
+    return filtered
+})
+
+const filteredWithdraws = computed(() => {
+    let filtered = baseFilteredWithdraws.value
+
+    if (activeStatus.value !== 'all') {
+        filtered = filtered.filter(w => w.status === activeStatus.value)
+    }
+
+    return filtered
+})
+
+const filteredStats = computed(() => {
+    const base = baseFilteredWithdraws.value
+    return {
+        all: base.length,
+        pending: base.filter(w => w.status === 'pending').length,
+        approved: base.filter(w => w.status === 'approved').length,
+        processing: base.filter(w => w.status === 'processing').length,
+        completed: base.filter(w => w.status === 'completed').length,
+        rejected: base.filter(w => w.status === 'rejected').length
+    }
+})
+
+const formatMoney = (amount: number | undefined) => {
+    if (amount === undefined || amount === null) return '0.00'
+    return Number(amount).toFixed(2)
+}
+
+const getStatusType = (status: WithdrawStatus) => {
+    const typeMap: Record<WithdrawStatus, 'success' | 'warning' | 'danger' | 'info' | 'primary'> = {
+        pending: 'warning',
+        approved: 'success',
+        processing: 'primary',
+        completed: 'success',
+        rejected: 'danger'
+    }
+    return typeMap[status] || 'info'
+}
+
+const handleSearch = () => {
+    appliedFilters.withdrawNo = searchForm.withdrawNo || ''
+    if (searchForm.dateRange && Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
+        appliedFilters.dateRange = [...searchForm.dateRange]
+    } else {
+        appliedFilters.dateRange = []
+    }
+}
+
+const handleReset = () => {
+    searchForm.withdrawNo = ''
+    searchForm.dateRange = []
+    appliedFilters.withdrawNo = ''
+    appliedFilters.dateRange = []
+}
+
+const handleTabClick = (tab: { paneName?: string | number; name?: string | number }) => {
+    activeStatus.value = (tab.paneName || tab.name || 'all') as WithdrawTabStatus
+}
+
+const getWithdrawCount = (status: WithdrawTabStatus) => {
+    if (status === 'all') return filteredStats.value.all || 0
+    return filteredStats.value[status] || 0
+}
+
+const isWithinRange = (time: string, range: string[]) => {
+    if (!range || range.length !== 2 || !range[0] || !range[1]) return true
+    const [start, end] = range
+    const startDate = new Date(start + ' 00:00:00')
+    const endDate = new Date(end + ' 23:59:59')
+    const targetDate = new Date(time.replace(/-/g, '/'))
+    if (isNaN(startDate.getTime()) || isNaN(endDate.getTime()) || isNaN(targetDate.getTime())) {
+        return true
+    }
+    return targetDate >= startDate && targetDate <= endDate
+}
+
+const handleApplyWithdraw = () => {
+    applyDialogVisible.value = true
+    applyForm.amount = ''
+    applyForm.withdraw_type = ''
+    applyForm.account = ''
+    applyForm.account_name = ''
+    applyForm.remark = ''
+}
+
+const handleSubmitApply = async () => {
+    if (!applyFormRef.value) return
+
+    await applyFormRef.value.validate((valid) => {
+        if (valid) {
+            submitting.value = true
+            setTimeout(() => {
+                submitting.value = false
+                applyDialogVisible.value = false
+                ElMessage.success('提现申请已提交,请等待审核')
+                loadWithdrawRecords()
+            }, 1000)
+        }
+    })
+}
+
+onMounted(() => {
+    loadWithdrawRecords()
+})
+</script>
+
+<style scoped lang="scss">
+.me-withdraw-list {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+}
+
+.withdraw-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex-wrap: wrap;
+    gap: 12px;
+
+    .header-text {
+        .withdraw-title {
+            margin: 0;
+            font-size: 24px;
+            font-weight: 600;
+            color: #1f2f3d;
+        }
+
+        .withdraw-subtitle {
+            margin: 6px 0 0;
+            font-size: 14px;
+            color: #909399;
+        }
+    }
+}
+
+.stats-section {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 20px;
+
+    @media (max-width: 1024px) {
+        grid-template-columns: repeat(2, 1fr);
+    }
+
+    @media (max-width: 768px) {
+        grid-template-columns: 1fr;
+    }
+
+    .stat-card {
+        display: flex;
+        align-items: flex-start;
+        padding: 20px;
+        background: #ffffff;
+        border-radius: 12px;
+        box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
+        transition: all 0.3s ease;
+        border: 1px solid #d9ecff;
+
+        &:hover {
+            box-shadow: 0 6px 20px rgba(64, 158, 255, 0.18);
+            transform: translateY(-3px);
+        }
+
+        .stat-icon {
+            width: 56px;
+            height: 56px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            border-radius: 12px;
+            margin-right: 16px;
+            flex-shrink: 0;
+            font-size: 28px;
+            color: #fff;
+        }
+
+        &.stat-total .stat-icon {
+            background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
+        }
+
+        &.stat-pending .stat-icon {
+            background: linear-gradient(135deg, #E6A23C 0%, #ebb563 100%);
+        }
+
+        &.stat-success .stat-icon {
+            background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%);
+        }
+
+        .stat-content {
+            flex: 1;
+
+            .stat-label {
+                font-size: 14px;
+                color: #909399;
+                margin-bottom: 8px;
+                font-weight: 500;
+            }
+
+            .stat-value {
+                font-size: 26px;
+                font-weight: 700;
+                color: #303133;
+                margin-bottom: 6px;
+            }
+
+            .stat-desc {
+                font-size: 12px;
+                color: #C0C4CC;
+                line-height: 1.4;
+            }
+        }
+    }
+}
+
+.withdraw-filters {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 12px;
+    padding: 16px;
+    background: linear-gradient(135deg, #ecf5ff 0%, #f5f9ff 100%);
+    border-radius: 8px;
+    border: 1px solid #c6e2ff;
+
+    .filter-item {
+        flex: 1;
+        min-width: 200px;
+    }
+
+    .withdraw-input {
+        max-width: 320px;
+    }
+
+    .date-picker {
+        max-width: 360px;
+    }
+}
+
+.withdraw-tabs {
+    background: #fff;
+    border-radius: 8px;
+    padding: 0 16px;
+    box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
+
+    ::v-deep .el-tabs__item {
+        font-weight: 500;
+    }
+
+    ::v-deep .el-tabs__active-bar {
+        height: 3px;
+        background: linear-gradient(90deg, #409EFF, #66b1ff);
+    }
+
+    ::v-deep .el-tabs__item.is-active {
+        color: #409EFF;
+    }
+}
+
+.withdraw-list-wrapper {
+    min-height: 200px;
+}
+
+.withdraw-list {
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+}
+
+.withdraw-card {
+    background: #fff;
+    border-radius: 12px;
+    border: 1px solid #d9ecff;
+    box-shadow: 0 6px 18px rgba(64, 158, 255, 0.12);
+    overflow: hidden;
+    transition: all 0.3s ease;
+
+    &:hover {
+        box-shadow: 0 8px 24px rgba(64, 158, 255, 0.18);
+        transform: translateY(-2px);
+    }
+
+    &__header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: 10px;
+        padding: 16px 20px;
+        background: linear-gradient(135deg, #ecf5ff, #ffffff);
+    }
+
+    .header-left {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+    }
+
+    .withdraw-no {
+        font-weight: 600;
+        color: #1f2d3d;
+        font-size: 15px;
+    }
+
+    .status-tag {
+        font-weight: 500;
+    }
+
+    .header-right {
+        color: #606266;
+        font-size: 13px;
+    }
+
+    .withdraw-time {
+        white-space: nowrap;
+    }
+
+    .withdraw-card__body {
+        padding: 20px;
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(180px, max-content));
+        gap: 16px 24px;
+        justify-content: flex-start;
+    }
+}
+
+.withdraw-item {
+    display: inline-flex;
+    flex-direction: column;
+    gap: 6px;
+    font-size: 14px;
+    width: max-content;
+    min-width: 0;
+
+    .item-label {
+        color: #909399;
+    }
+
+    .item-value {
+        color: #303133;
+        font-weight: 500;
+
+        &.amount {
+            font-size: 20px;
+            color: #409EFF;
+            font-weight: 700;
+        }
+
+        &.fee {
+            color: #E6A23C;
+            font-weight: 600;
+        }
+
+        &.actual {
+            font-size: 18px;
+            color: #67C23A;
+            font-weight: 700;
+        }
+
+        &.account {
+            font-family: 'Courier New', Courier, monospace;
+        }
+
+        &.note {
+            padding: 10px;
+            background: #f0f5ff;
+            border-radius: 6px;
+            color: #606266;
+            line-height: 1.6;
+        }
+
+        &.reject-reason {
+            padding: 10px;
+            background: #fef0f0;
+            border-radius: 6px;
+            color: #F56C6C;
+            line-height: 1.6;
+        }
+    }
+
+    &.full-width {
+        grid-column: 1 / -1;
+        width: 100%;
+    }
+}
+
+.withdraw-empty {
+    padding: 60px 0;
+    background: #fff;
+    border-radius: 12px;
+    border: 1px dashed #c6e2ff;
+}
+
+.available-amount {
+    font-size: 24px;
+    font-weight: 700;
+    color: #409EFF;
+}
+
+.form-tip {
+    font-size: 12px;
+    color: #909399;
+    margin-top: 4px;
+}
+
+@media (max-width: 768px) {
+    .withdraw-header {
+        flex-direction: column;
+        align-items: flex-start;
+    }
+
+    .withdraw-filters {
+        flex-direction: column;
+
+        .filter-item,
+        .withdraw-input,
+        .date-picker {
+            width: 100%;
+            max-width: none;
+        }
+    }
+
+    .withdraw-card__body {
+        grid-template-columns: 1fr;
+    }
+}
+</style>