Jelajahi Sumber

我的下级列表编写

“陈博论” 9 bulan lalu
induk
melakukan
f8e83144e3

+ 6 - 3
web/src/api/frontend/me.ts

@@ -9,10 +9,11 @@ export function dashboard() {
 }
 
 //用户购买记录
-export function purchaseRecordsSimple() {
+export function purchaseRecordsSimple(data:anyObj) {
   return createAxios({
       url: '/api/invitation.order/purchaseRecordsSimple',
       method: 'get',
+      params:data
   })
 }
 
@@ -34,17 +35,19 @@ export function purchasedList() {
 }
 
 //产品详情
-export function productDetail() {
+export function productDetail(data:anyObj) {
   return createAxios({
       url: '/api/invitation.product/detail',
       method: 'get',
+      params:data,
   })
 }
 
 //获取下级用户列表
-export function subordinateList() {
+export function subordinateList(data:anyObj) {
   return createAxios({
       url: '/api/user/subordinateList',
       method: 'get',
+      params:data,
   })
 }

+ 1 - 1
web/src/router/static/memberCenterBase.ts

@@ -11,7 +11,7 @@ export const memberCenterBaseRoutePath = '/user'
 const memberCenterBaseRoute: RouteRecordRaw = {
     path: memberCenterBaseRoutePath,
     name: 'user',
-    component: () => import('/@/layouts/frontend/user.vue'),
+    component: () => import('/@/views/frontend/Me.vue'),
     // 重定向到 loading 路由
     redirect: memberCenterBaseRoutePath + '/loading',
     meta: {

+ 244 - 160
web/src/views/frontend/components/Me/purchaseHistory.vue

@@ -12,16 +12,8 @@
         <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"
-      />
+      <el-date-picker v-model="searchForm.dateRange" class="filter-item date-picker" style="width: 100px !important;" type="daterange" unlink-panels
+        start-placeholder="下单开始时间" end-placeholder="下单结束时间" range-separator="~" value-format="YYYY-MM-DD" clearable />
 
       <el-button size="small" type="primary" :icon="Search" @click="handleSearch">查询</el-button>
       <el-button size="small" @click="handleReset">重置</el-button>
@@ -29,115 +21,119 @@
 
     <div class="history-tabs">
       <el-tabs v-model="activeStatus" @tab-click="handleTabClick">
-        <el-tab-pane
-          v-for="tab in statusTabs"
-          :key="tab.value"
-          :label="`${tab.label} (${getOrderCount(tab.value)})`"
-          :name="tab.value"
-        />
+        <el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="`${tab.label} (${getOrderCount(tab.value)})`"
+          :name="tab.value" />
       </el-tabs>
     </div>
 
-    <div v-if="filteredOrders.length" class="order-list">
-      <div v-for="order in filteredOrders" :key="order.orderId" class="order-card">
-        <div class="order-card__header">
-          <div class="header-left">
-            <span class="order-id">订单号:{{ order.orderId }}</span>
-          </div>
-          <div class="header-right">
-            <span>下单时间:{{ order.orderTime }}</span>
-          </div>
-        </div>
-
-        <div class="order-card__body">
-          <div class="order-item product-info">
-            <div class="item-label">商品</div>
-            <div class="item-content">
-              <div class="product-name">{{ order.productName }}</div>
+    <div v-loading="loading" class="order-list-wrapper">
+      <div v-if="filteredOrders.length" class="order-list">
+        <div v-for="order in filteredOrders" :key="order.order_no" class="order-card">
+          <div class="order-card__header">
+            <div class="header-left">
+              <span class="order-id">订单号:{{ order.order_no }}</span>
+              <el-tag size="small" :type="order.pay_status_text === '已支付' ? 'success' : 'warning'" class="status-tag">
+                {{ order.pay_status_text }}
+              </el-tag>
+            </div>
+            <div class="header-right">
+              <span class="order-time">下单时间:{{ order.order_time_formatted }}</span>
             </div>
           </div>
 
-          <div v-if="order.type === 1" class="order-item">
-            <div class="item-label">数量</div>
-            <div class="item-value">x{{ order.quantity }}</div>
-          </div>
-          <div v-if="order.type === 2" class="order-item">
-            <div class="item-label">席位</div>
-            <div class="item-value">{{ order.quantity }}个</div>
-          </div>
+          <div class="order-card__body">
+            <div class="order-item product-info">
+              <div class="item-label">商品名称</div>
+              <div class="item-content">
+                <div class="product-name">{{ order.product_name }}</div>
+              </div>
+            </div>
 
-          <div class="order-item">
-            <div class="item-label">金额</div>
-            <div class="item-value amount">¥{{ order.amount }}</div>
-          </div>
+            <div class="order-item">
+              <div class="item-label">购买数量</div>
+              <div class="item-value">x{{ order.quantity }}</div>
+            </div>
 
-          <div class="order-item">
-            <div class="item-label">支付方式</div>
-            <div class="item-value">{{ order.payMethod }}</div>
-          </div>
+            <div class="order-item">
+              <div class="item-label">订单金额</div>
+              <div class="item-value amount">{{ order.amount }}</div>
+            </div>
 
-          <div class="order-item">
-            <div class="item-label">支付状态</div>
-            <el-tag size="small" :type="order.payStatus === '已支付' ? 'success' : 'warning'">
-              {{ order.payStatus }}
-            </el-tag>
-          </div>
+            <div class="order-item">
+              <div class="item-label">支付方式</div>
+              <div class="item-value pay-method">{{ order.pay_method_text }}</div>
+            </div>
 
-          <div class="order-item">
-            <div class="item-label">发货方式</div>
-            <div class="item-value">自动发货</div>
-          </div>
+            <div class="order-item">
+              <div class="item-label">发货方式</div>
+              <div class="item-value">自动开通</div>
+            </div>
 
-          <div class="order-item">
-            <div class="item-label">套餐类型</div>
-            <div class="item-value">{{ order.type === 1 ? '个人套餐' : '企业套餐' }}</div>
-          </div>
+            <div class="order-item">
+              <div class="item-label">付款时间</div>
+              <div class="item-value">{{ order.pay_time_formatted || '-' }}</div>
+            </div>
 
-          <div class="order-item full-width">
-            <div class="item-label">商家留言</div>
-            <div class="item-value note">
-              {{ order.merchantNote }}
+            <div class="order-item full-width">
+              <div class="item-label">商家留言</div>
+              <div class="item-value note">
+                请前往相对应的网站进行使用工具
+              </div>
             </div>
-          </div>
 
-          <div class="order-item full-width time-info">
-            <span>下单时间:{{ order.payTime || '-' }}</span>
-            <span>付款时间:{{ order.deliverTime || '-' }}</span>
+            <!-- <div class="order-item full-width time-info">
+              <div class="time-item">
+                <span class="time-label">付款时间:</span>
+                <span class="time-value">{{ order.pay_time_formatted || '-' }}</span>
+              </div>
+            </div> -->
           </div>
         </div>
       </div>
-    </div>
 
-    <el-empty v-else class="history-empty" description="暂无符合条件的订单" />
+      <el-empty v-else-if="!loading" class="history-empty" description="暂无符合条件的订单" />
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { computed, reactive, ref } from 'vue'
+import { computed, onMounted, reactive, ref } from 'vue'
 import { Search } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+import { purchaseRecordsSimple } from '/@/api/frontend/me'
 
 defineOptions({ name: 'MePurchaseHistory' })
 
 type OrderRecordStatus = 'paid' | 'unpaid'
 type OrderTabStatus = 'all' | OrderRecordStatus
+
+// 新的订单数据结构,匹配接口返回
 type Order = {
-  orderId: string
-  productName: string
-  sku: string
+  order_no: string
+  product_name: string
   quantity: number
-  type: number
-  amount: number
-  payMethod: string
-  payStatus: string
-  merchantNote: string
-  codes: string[]
-  orderTime: string
-  payTime: string
-  deliverTime: string
-  status: OrderRecordStatus
+  amount: string
+  pay_method_text: string
+  pay_status_text: string
+  order_time_formatted: string
+  pay_time_formatted: string
+}
+
+// API 返回的数据结构
+type PurchaseRecordsResponse = {
+  list: Order[]
+  total: number
+  page: number
+  page_size: number
+  stats: {
+    total_orders: number
+    paid_orders: number
+    unpaid_orders: number
+  }
 }
 
 const activeStatus = ref<OrderTabStatus>('all')
+const loading = ref(false)
 const searchForm = reactive<{ orderId: string; dateRange: string[] }>({
   orderId: '',
   dateRange: []
@@ -151,76 +147,103 @@ const statusTabs: { label: string; value: OrderTabStatus }[] = [
   { label: '已支付', value: 'paid' },
   { label: '未支付', value: 'unpaid' }
 ]
-const orders = ref<Order[]>([
-  {
-    orderId: '6952512020095823599',
-    productName: '望舒·月卡',
-    sku: '-',
-    quantity: 1,
-    type: 1,
-    amount: 52,
-    payMethod: '支付宝',
-    payStatus: '已支付',
-    merchantNote: '请前往相对应的网站进行使用工具,具体方法在文档里。激活前永久有效,激活后30天内有效。',
-    codes: ['XCCSYVIPJ7LM85R6IIOYXP7S6TO'],
-    orderTime: '2025-10-01 12:01:02',
-    payTime: '2025-10-01 12:01:02',
-    deliverTime: '2025-10-01 12:01:32',
-    status: 'paid'
-  },
-  {
-    orderId: '4502508151712325808',
-    productName: '清和·季卡',
-    sku: '-',
-    quantity: 1,
-    type: 2,
-    amount: 168,
-    payMethod: '微信',
-    payStatus: '待支付',
-    merchantNote: '如有疑问请联系在线客服,超过24小时自动关闭。',
-    codes: [],
-    orderTime: '2025-09-22 09:36:15',
-    payTime: '',
-    deliverTime: '',
-    status: 'unpaid'
-  },
-  {
-    orderId: '55665111144555',
-    productName: '聚德神笔日卡',
-    sku: 'JD-001-DAY',
-    quantity: 1,
-    type: 1,
-    amount: 6,
-    payMethod: '微信',
-    payStatus: '已支付',
-    merchantNote: '请前往相对应的网站进行使用工具,具体方法在文档里。激活前永久有效,激活后30天内有效。',
-    codes: ['JDSB20251001-0001'],
-    orderTime: '2025-10-01 12:01:02',
-    payTime: '2025-10-01 12:01:02',
-    deliverTime: '2025-10-01 12:01:05',
-    status: 'paid'
-  }
-])
-
-const filteredOrders = computed(() =>
-  orders.value.filter(order => {
-    const matchesStatus = activeStatus.value === 'all' ? true : order.status === activeStatus.value
-    const orderIdFilter = appliedFilters.orderId
-      ? order.orderId.includes(appliedFilters.orderId.trim())
-      : true
-    const dateFilter = isWithinRange(order.orderTime, appliedFilters.dateRange)
-    return matchesStatus && orderIdFilter && dateFilter
-  })
-)
+const orders = ref<Order[]>([])
+const stats = ref<PurchaseRecordsResponse['stats']>({
+  total_orders: 0,
+  paid_orders: 0,
+  unpaid_orders: 0
+})
+
+// 分页相关
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 加载购买记录
+const loadPurchaseRecords = async () => {
+  try {
+    loading.value = true
+    const res = await purchaseRecordsSimple({})
+    if (res.code === 1 && res.data) {
+      const data = res.data as PurchaseRecordsResponse
+      orders.value = data.list || []
+      stats.value = data.stats || { total_orders: 0, paid_orders: 0, unpaid_orders: 0 }
+      pagination.total = data.total || 0
+    } else {
+      ElMessage.error(res.msg || '获取购买记录失败')
+    }
+  } catch (error) {
+    console.error('加载购买记录失败:', error)
+    ElMessage.error('加载购买记录失败,请稍后重试')
+  } finally {
+    loading.value = false
+  }
+}
+
+// 先应用订单号和日期筛选,得到基础筛选结果
+const baseFilteredOrders = computed(() => {
+  let filtered = orders.value
+
+  // 订单号过滤
+  if (appliedFilters.orderId) {
+    const orderId = appliedFilters.orderId.trim()
+    filtered = filtered.filter(order => order.order_no.includes(orderId))
+  }
+
+  // 日期范围过滤
+  if (appliedFilters.dateRange && appliedFilters.dateRange.length === 2) {
+    filtered = filtered.filter(order => {
+      return isWithinRange(order.order_time_formatted, appliedFilters.dateRange)
+    })
+  }
+
+  return filtered
+})
+
+// 再应用状态筛选,得到最终筛选结果
+const filteredOrders = computed(() => {
+  let filtered = baseFilteredOrders.value
+
+  // 状态过滤
+  if (activeStatus.value !== 'all') {
+    if (activeStatus.value === 'paid') {
+      filtered = filtered.filter(order => order.pay_status_text === '已支付')
+    } else if (activeStatus.value === 'unpaid') {
+      filtered = filtered.filter(order => order.pay_status_text === '未支付')
+    }
+  }
+
+  return filtered
+})
+
+// 根据基础筛选结果计算各状态的订单数
+const filteredStats = computed(() => {
+  const base = baseFilteredOrders.value
+  return {
+    total_orders: base.length,
+    paid_orders: base.filter(order => order.pay_status_text === '已支付').length,
+    unpaid_orders: base.filter(order => order.pay_status_text === '未支付').length
+  }
+})
 
 const handleSearch = () => {
-  appliedFilters.orderId = searchForm.orderId
-  appliedFilters.dateRange = [...searchForm.dateRange]
+  // 更新应用的筛选条件
+  appliedFilters.orderId = searchForm.orderId || ''
+  // 确保日期范围格式正确
+  if (searchForm.dateRange && Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
+    appliedFilters.dateRange = [...searchForm.dateRange]
+  } else {
+    appliedFilters.dateRange = []
+  }
 }
 
 const handleReset = () => {
+  // 重置搜索表单
   searchForm.orderId = ''
   searchForm.dateRange = []
+  // 重置应用的筛选条件
   appliedFilters.orderId = ''
   appliedFilters.dateRange = []
 }
@@ -230,18 +253,36 @@ const handleTabClick = (tab: { paneName?: string | number; name?: string | numbe
 }
 
 const getOrderCount = (status: OrderTabStatus) => {
-  if (status === 'all') return orders.value.length
-  return orders.value.filter(order => order.status === status).length
+  // 使用筛选后的统计结果
+  if (status === 'all') return filteredStats.value.total_orders || 0
+  if (status === 'paid') return filteredStats.value.paid_orders || 0
+  if (status === 'unpaid') return filteredStats.value.unpaid_orders || 0
+  return 0
 }
 
 const isWithinRange = (orderTime: string, range: string[]) => {
-  if (!range || range.length !== 2) return true
+  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`)
+
+  // 确保日期格式正确,处理 YYYY-MM-DD 格式
+  const startDate = new Date(start + ' 00:00:00')
+  const endDate = new Date(end + ' 23:59:59')
+
+  // 订单时间格式通常是 "2025-06-11 15:53:35"
   const targetDate = new Date(orderTime.replace(/-/g, '/'))
+
+  // 检查日期是否有效
+  if (isNaN(startDate.getTime()) || isNaN(endDate.getTime()) || isNaN(targetDate.getTime())) {
+    return true
+  }
+
   return targetDate >= startDate && targetDate <= endDate
 }
+
+// 初始化加载数据
+onMounted(() => {
+  loadPurchaseRecords()
+})
 </script>
 
 <style scoped lang="scss">
@@ -280,6 +321,7 @@ const isWithinRange = (orderTime: string, range: string[]) => {
 .history-filters {
   display: flex;
   flex-wrap: wrap;
+  align-items: center;
   gap: 12px;
   padding: 16px;
   background: linear-gradient(135deg, #ecf5ff 0%, #f5f9ff 100%);
@@ -320,6 +362,10 @@ const isWithinRange = (orderTime: string, range: string[]) => {
   }
 }
 
+.order-list-wrapper {
+  min-height: 200px;
+}
+
 .order-list {
   display: flex;
   flex-direction: column;
@@ -343,10 +389,29 @@ const isWithinRange = (orderTime: string, range: string[]) => {
     background: linear-gradient(135deg, #ecf5ff, #ffffff);
   }
 
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+  }
+
   .order-id {
     font-weight: 600;
-    margin-right: 12px;
     color: #1f2d3d;
+    font-size: 15px;
+  }
+
+  .status-tag {
+    font-weight: 500;
+  }
+
+  .header-right {
+    color: #606266;
+    font-size: 13px;
+  }
+
+  .order-time {
+    white-space: nowrap;
   }
 
   .order-card__body {
@@ -391,6 +456,11 @@ const isWithinRange = (orderTime: string, range: string[]) => {
     font-weight: 600;
   }
 
+  .pay-method {
+    color: #606266;
+    font-weight: 500;
+  }
+
   .note {
     padding: 10px;
     background: #f0f5ff;
@@ -421,11 +491,26 @@ const isWithinRange = (orderTime: string, range: string[]) => {
   display: flex;
   justify-content: space-between;
   flex-wrap: wrap;
-  gap: 10px;
-  font-size: 13px;
-  color: #606266;
-  padding-top: 10px;
+  gap: 16px;
+  padding-top: 16px;
   border-top: 1px dashed #d9e2f0;
+  margin-top: 4px;
+
+  .time-item {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    font-size: 13px;
+  }
+
+  .time-label {
+    color: #909399;
+  }
+
+  .time-value {
+    color: #606266;
+    font-weight: 500;
+  }
 }
 
 .history-empty {
@@ -462,4 +547,3 @@ const isWithinRange = (orderTime: string, range: string[]) => {
   }
 }
 </style>
-

+ 52 - 52
web/src/views/frontend/components/Me/purchaseProduct.vue

@@ -4,19 +4,17 @@
       <h2 class="product-title">我的产品</h2>
     </div>
     <div class="product-content">
-      <div class="product-grid">
-        <div
-          v-for="item in products"
-          :key="item.id"
-          class="product-card"
-          @click="handleProductClick(item)"
-        >
+      <el-empty v-if="products.length == 0" description="暂无产品">
+        <el-button type="primary" @click="gotoBuy">去购买</el-button>
+      </el-empty>
+      <div v-else class="product-grid">
+        <div v-for="item in products" :key="item.id" class="product-card" @click="handleProductClick(item.id)">
           <div class="product-image-wrapper">
-            <img src="https://picsum.photos/200" alt="">
+            <img :src="item.image" alt="">
           </div>
           <div class="product-info">
             <div class="product-name">{{ item.name }}</div>
-            <div class="product-desc">{{ item.description }}</div>
+            <!-- <div class="product-desc">{{ item.description }}</div>
             <div v-if="item.expireAt" class="product-expire">
               <span class="expire-label">有效期到:</span>
               <span class="expire-text">{{ item.expireAt }}</span>
@@ -24,7 +22,7 @@
             <div class="product-meta">
               <span class="product-type">{{ item.typeLabel }}</span>
               <span v-if="item.tag" class="product-tag">{{ item.tag }}</span>
-            </div>
+            </div> -->
           </div>
         </div>
       </div>
@@ -33,11 +31,14 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
-
+import { onMounted, ref } from 'vue'
+import { purchasedList, productDetail } from '/@/api/frontend/me'
+import { useRouter } from 'vue-router'
 defineOptions({ name: 'MePurchaseProduct' })
 
-type ProductType = 'web' | 'download'
+const router = useRouter()
+
+type ProductType = 1 | 2 //产品类型(1:网页2:软件)
 type Product = {
   id: string
   name: string
@@ -47,48 +48,48 @@ type Product = {
   typeLabel: string
   link: string
   tag?: string
+  image: string
 }
 
-const products = ref<Product[]>([
-  {
-    id: 'jude-pen',
-    name: '聚德神笔',
-    description: '聚德 AI 写作/办公套件,可在线使用多种智能工具。',
-    expireAt: '2025-12-30',
-    type: 'web',
-    typeLabel: '在线工具',
-    link: 'https://www.example-tools.com/jude-pen',
-    tag: '网页'
-  },
-  {
-    id: 'lens-tool',
-    name: '棱镜',
-    description: '图像/视频增强助手,需要下载安装客户端使用。',
-    expireAt: '2025-12-30',
-    type: 'download',
-    typeLabel: '软件下载',
-    link: 'https://www.example-tools.com/download/lens-setup.exe',
-    tag: 'Windows'
-  }
-])
-
-const handleProductClick = (item: Product) => {
-  if (!item?.link) return
-
-  if (item.type === 'download') {
-    const a = document.createElement('a')
-    a.href = item.link
-    a.target = '_blank'
-    a.rel = 'noopener noreferrer'
-    a.download = ''
-    document.body.appendChild(a)
-    a.click()
-    document.body.removeChild(a)
-    return
-  }
+// 产品列表
+const products = ref<Product[]>([])
+
+
+// 获取列表
+const getList = async () => {
+  const res = await purchasedList()
+  products.value = res.data as Product[]
+}
 
-  window.open(item.link, '_blank', 'noopener,noreferrer')
+//跳转购买
+const gotoBuy = () =>{
+  router.push('/')
 }
+
+// 点击跳转
+const handleProductClick = (id: string) => {
+  productDetail({ id }).then(res => {
+    const data = res.data.product
+    if (!data?.link) return
+    if (data.type == 2) {
+      const a = document.createElement('a')
+      a.href = data.link
+      a.target = '_blank'
+      a.rel = 'noopener noreferrer'
+      a.download = ''
+      document.body.appendChild(a)
+      a.click()
+      document.body.removeChild(a)
+      return
+    }
+
+    window.open(data.link, '_blank', 'noopener,noreferrer')
+  })
+}
+
+onMounted(() => {
+  getList()
+})
 </script>
 
 <style scoped lang="scss">
@@ -202,4 +203,3 @@ const handleProductClick = (item: Product) => {
   }
 }
 </style>
-

+ 120 - 120
web/src/views/frontend/components/Me/subordinates.vue

@@ -2,137 +2,178 @@
   <div class="me-subordinates">
     <div class="subordinates-header">
       <h2 class="subordinates-title">我的下级</h2>
+      <p class="subordinates-subtitle">邀请人数:{{ total }}</p>
     </div>
 
     <div class="subordinates-content">
-      <!-- 过滤/搜索区域 + 汇总指标 -->
+      <!-- 过滤/搜索区域 -->
       <div class="filter-section">
         <div class="filter-left">
-          <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"
-          />
-
-          <el-input
-            v-model="searchForm.name"
-            placeholder="请输入名称"
-            clearable
-            class="filter-item name-input"
-            @keyup.enter="handleSearch"
-          />
+          <el-input v-model="searchForm.username" placeholder="请输入用户名" clearable class="filter-item name-input"
+            @keyup.enter="handleSearch" />
+
+          <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" size="small" class="search-btn" @click="handleSearch">
             搜索
           </el-button>
-        </div>
-
-        <!-- 汇总指标 -->
-        <div class="summary-item">
-          <span class="summary-label">分拥余额:</span>
-          <span class="summary-value">
-            {{ totalCommission }}
-          </span>
+          <el-button size="small" @click="handleReset" class="search-btn">重置</el-button>
         </div>
       </div>
 
       <!-- 数据表格 -->
       <div class="table-section">
-        <el-table
-          :data="tableData"
-          style="width: 100%"
-          :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: '500' }"
-        >
+        <el-table v-if="tableData.length > 0" v-loading="loading" :data="tableData" style="width: 100%"
+          :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: '500' }">
           <el-table-column prop="id" label="ID" width="80" align="center" />
-          <el-table-column prop="userName" label="用户名称" min-width="120" align="center" />
-          <el-table-column prop="email" label="邮箱" min-width="180" align="center" />
-          <el-table-column prop="commissionAmount" label="已得分佣金额" width="140" align="center" />
-          <el-table-column prop="registerTime" label="注册时间" width="180" align="center" />
+          <el-table-column prop="username" label="用户名" min-width="120" align="center" />
+          <el-table-column prop="email" label="邮箱" min-width="200" align="center">
+            <template #default="{ row }">
+              <span>{{ row.email || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="invite_time" label="邀请时间" width="180" align="center">
+            <template #default="{ row }">
+              <span>{{ row.invite_time || '-' }}</span>
+            </template>
+          </el-table-column>
           <el-table-column prop="status" label="状态" width="100" align="center">
             <template #default="{ row }">
-              <span class="status-text">{{ row.status }}</span>
+              <el-tag :type="row.status === 'enable' ? 'success' : 'danger'" size="small">
+                {{ getStatusText(row.status) }}
+              </el-tag>
             </template>
           </el-table-column>
-          <el-table-column prop="operation" label="操作" width="100" align="center">
-            <template #default>
-              <span class="status-text">详情</span>
+          <el-table-column prop="last_login_time" label="最后登录时间" width="180" align="center">
+            <template #default="{ row }">
+              <span>{{ row.last_login_time ? formatTime(row.last_login_time) : '-' }}</span>
             </template>
           </el-table-column>
         </el-table>
+        <!-- 空状态 -->
+        <el-empty v-if="!loading && tableData.length === 0" description="暂无下级用户" />
       </div>
 
       <!-- 分页控件 -->
-      <div class="pagination-section">
-        <el-pagination
-          :current-page="currentPage"
-          :page-size="pageSize"
-          :page-sizes="pageSizes"
-          :total="total"
-          layout="total, sizes, prev, pager, next, jumper"
-          background
-          @size-change="handlePageSizeChange"
-          @current-change="handleCurrentPageChange"
-        />
+      <div v-if="total > 0" class="pagination-section">
+        <el-pagination :current-page="currentPage" :page-size="pageSize" :page-sizes="pageSizes" :total="total"
+          layout="total, sizes, prev, pager, next, jumper" background @size-change="handlePageSizeChange"
+          @current-change="handleCurrentPageChange" />
       </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { reactive, ref } from 'vue'
+import { onMounted, reactive, ref } from 'vue'
 import { Search } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+import { subordinateList } from '/@/api/frontend/me'
+import { timeFormat } from '/@/utils/common'
 
 defineOptions({ name: 'MeSubordinates' })
 
-type TableRow = {
-  id: number | string
-  userName: string
-  email: string
-  commissionAmount: number
-  registerTime: string
-  status: string
+// 接口返回的数据结构
+interface SubordinateUser {
+  id: number // id
+  username: string // 用户名
+  email: string // 电子邮箱
+  last_login_time: number // 最后登录时间(时间戳)
+  status: 'enable' | 'disable' // 状态:enable=启用,disable=禁用
+  invite_time: string // 邀请时间
 }
 
-const searchForm = reactive<{ dateRange: string[]; name: string }>({
-  dateRange: ['2025-10-01', '2025-10-01'],
-  name: ''
+const searchForm = reactive<{ dateRange?: string[]; username: string }>({
+  dateRange: undefined,
+  username: ''
 })
-const totalCommission = ref(30)
-const tableData = ref<TableRow[]>([
-  { id: '-', userName: '汇总', email: '-', commissionAmount: 30, registerTime: '-', status: '-' },
-  { id: 1, userName: '张三', email: 'XXX.qq.com', commissionAmount: 10, registerTime: '2025-10-01 14:32:11', status: '正常' },
-  { id: 10, userName: '李四', email: 'XXX.qq.com', commissionAmount: 20, registerTime: '2025-10-01 14:32:11', status: '正常' }
-])
+
+const tableData = ref<SubordinateUser[]>([])
+const loading = ref(false)
+
+// 分页数据
 const currentPage = ref(1)
 const pageSize = ref(10)
 const pageSizes = [10, 20, 30, 50]
-const total = ref(1)
+const total = ref(0)
 
-const loadData = () => {
-  // TODO: 接入实际 API 获取数据
-  console.log('搜索条件:', { ...searchForm })
+// 格式化时间戳
+const formatTime = (timestamp: number): string => {
+  if (!timestamp) return '-'
+  return timeFormat(timestamp, 'yyyy-mm-dd hh:MM:ss')
+}
+
+// 获取状态文本
+const getStatusText = (status: string): string => {
+  return status === 'enable' ? '启用' : '禁用'
+}
+
+// 获取数据
+const loadData = async () => {
+  loading.value = true
+  try {
+    const params: Record<string, any> = {
+      page: currentPage.value,
+      page_size: pageSize.value
+    }
+
+    // 处理搜索条件
+    if (searchForm.username) {
+      params.username = searchForm.username
+    }
+
+    // 处理日期范围
+    if (searchForm.dateRange && searchForm.dateRange.length === 2) {
+      params.start_date = searchForm.dateRange[0]
+      params.end_date = searchForm.dateRange[1]
+    }
+
+    const res = await subordinateList(params)
+    if (res.data) {
+      tableData.value = res.data.list || []
+      total.value = res.data.total || 0
+    }
+  } catch (error: any) {
+    ElMessage.error(error.message || '获取下级列表失败')
+    tableData.value = []
+    total.value = 0
+  } finally {
+    loading.value = false
+  }
 }
 
+// 搜索
 const handleSearch = () => {
   currentPage.value = 1
   loadData()
 }
 
+// 重置
+const handleReset = () => {
+  searchForm.dateRange = undefined
+  searchForm.username = ''
+  currentPage.value = 1
+  loadData()
+}
+
+// 分页大小改变
 const handlePageSizeChange = (val: number) => {
   pageSize.value = val
   currentPage.value = 1
   loadData()
 }
 
+// 当前页改变
 const handleCurrentPageChange = (val: number) => {
   currentPage.value = val
   loadData()
 }
+
+// 初始化加载数据
+onMounted(() => {
+  loadData()
+})
 </script>
 
 <style scoped lang="scss">
@@ -145,11 +186,17 @@ const handleCurrentPageChange = (val: number) => {
     border-bottom: 1px solid #e4e7ed;
 
     .subordinates-title {
-      margin: 0;
+      margin: 0 0 8px 0;
       font-size: 20px;
       font-weight: 500;
       color: #303133;
     }
+
+    .subordinates-subtitle {
+      margin: 0;
+      font-size: 14px;
+      color: #909399;
+    }
   }
 
   .subordinates-content {
@@ -208,49 +255,7 @@ const handleCurrentPageChange = (val: number) => {
       }
     }
 
-    .summary-item {
-      display: flex;
-      align-items: center;
-      padding: 8px 10px;
-      border-radius: 10px;
-      box-shadow: 0 2px 6px rgba(104, 151, 238, 0.15);
-      white-space: nowrap;
-      flex-shrink: 0;
-
-      .summary-label {
-        color: #8c6e1f;
-        font-size: 14px;
-        font-weight: 500;
-      }
 
-      .summary-value {
-        position: relative;
-        color: #d48806;
-        font-weight: 600;
-        font-size: 20px;
-        letter-spacing: 0.5px;
-
-        .summary-badge {
-          position: absolute;
-          top: -10px;
-          right: -20px;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-          min-width: 20px;
-          height: 20px;
-          padding: 0 5px;
-          background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
-          color: #fff;
-          border-radius: 10px;
-          font-size: 11px;
-          font-weight: 600;
-          line-height: 1;
-          box-shadow: 0 2px 4px rgba(255, 152, 0, 0.4);
-          border: 2px solid #fff;
-        }
-      }
-    }
   }
 
   .table-section {
@@ -344,11 +349,6 @@ const handleCurrentPageChange = (val: number) => {
         width: 100%;
         margin-top: 8px;
       }
-
-      .summary-item {
-        width: 100%;
-        justify-content: center;
-      }
     }
 
     .pagination-section {