DESKTOP-7E9LH09\admin 2 недель назад
Родитель
Сommit
59a21e8e5f

Разница между файлами не показана из-за своего большого размера
+ 476 - 488
src/api/data.js


Разница между файлами не показана из-за своего большого размера
+ 330 - 367
src/views/agent/index.vue


+ 101 - 139
src/views/agent/loginLog/index.vue

@@ -1,14 +1,6 @@
 <template>
   <div class="agent-login-log-page">
     <div class="login-log-panel">
-      <el-alert
-        class="ip-alert"
-        type="success"
-        title="点击IP可在线查询归属地"
-        :closable="true"
-        show-icon
-      />
-
       <el-form
         ref="queryForm"
         :model="query"
@@ -16,11 +8,20 @@
         inline
         size="small"
       >
-        <el-form-item label="会员账号" prop="member_account">
+        <el-form-item label="代理ID" prop="agent_id">
+          <el-input
+            v-model="query.agent_id"
+            placeholder="代理ID"
+            clearable
+            @keyup.enter.native="handleSearch"
+          />
+        </el-form-item>
+        <el-form-item label="代理账号" prop="username">
           <el-input
-            v-model="query.member_account"
-            placeholder="会员账号"
+            v-model="query.username"
+            placeholder="代理账号"
             clearable
+            @keyup.enter.native="handleSearch"
           />
         </el-form-item>
         <el-form-item label="IP地址" prop="ip">
@@ -28,8 +29,15 @@
             v-model="query.ip"
             placeholder="IP地址"
             clearable
+            @keyup.enter.native="handleSearch"
           />
         </el-form-item>
+        <el-form-item label="设备" prop="device">
+          <el-select v-model="query.device" placeholder="设备" clearable>
+            <el-option label="手机端" value="mobile" />
+            <el-option label="电脑端" value="pc" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="登录时间" prop="date_range">
           <el-date-picker
             v-model="query.date_range"
@@ -51,31 +59,48 @@
         v-loading="loading"
         :data="tableData"
         border
-        height="calc(100vh - 290px)"
+        height="calc(100vh - 255px)"
         class="login-log-table"
       >
-        <el-table-column prop="id" label="ID" width="116" align="center" />
-        <el-table-column prop="member_account" label="会员账号" min-width="180" align="center" />
-        <el-table-column prop="login_time" label="登录时间" min-width="210" align="center" />
-        <el-table-column prop="domain" label="访问域名" min-width="360" align="center" />
-        <el-table-column prop="ip" label="IP地址" min-width="210" align="center">
+        <el-table-column prop="id" label="ID" width="90" align="center" />
+        <el-table-column prop="agent_id" label="代理ID" width="90" align="center" />
+        <el-table-column prop="username" label="代理账号" min-width="150" align="center" />
+        <el-table-column prop="login_time" label="登录时间" min-width="190" align="center">
+          <template slot-scope="{ row }">
+            {{ row.login_time || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="domain" label="访问域名" min-width="220" align="center">
+          <template slot-scope="{ row }">
+            <div class="domain-cell">{{ row.domain || '--' }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="ip" label="IP地址" min-width="150" align="center">
+          <template slot-scope="{ row }">
+            {{ row.ip || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="location" label="登录地区" min-width="190" align="center">
           <template slot-scope="{ row }">
-            <span class="ip-link" @click="openIpLookup(row.ip)">
-              {{ row.ip }}
-            </span>
+            {{ row.location || '--' }}
           </template>
         </el-table-column>
-        <el-table-column prop="device" label="设备" min-width="180" align="center">
+        <el-table-column prop="device_text" label="设备" min-width="110" align="center">
           <template slot-scope="{ row }">
             <el-tag
               size="small"
-              :type="row.device === '手机端' ? 'success' : ''"
+              :type="row.device === 'mobile' ? 'success' : ''"
               effect="light"
             >
-              {{ row.device }}
+              {{ row.device_text || getDeviceText(row.device) }}
             </el-tag>
           </template>
         </el-table-column>
+        <el-table-column prop="user_agent" label="User Agent" min-width="320" align="center">
+          <template slot-scope="{ row }">
+            <div class="user-agent-cell">{{ row.user_agent || '--' }}</div>
+          </template>
+        </el-table-column>
       </el-table>
 
       <el-pagination
@@ -94,122 +119,73 @@
 </template>
 
 <script>
+import { getAgentLoginLogs } from '@/api/data'
 import { disableFutureDate } from '@/utils/index'
+
 export default {
   name: 'AgentLoginLog',
   data() {
     return {
       loading: false,
       query: {
-        member_account: '',
+        agent_id: '',
+        username: '',
         ip: '',
+        device: '',
         date_range: []
       },
       page: 1,
       limit: 10,
       total: 0,
       tableData: [],
-      disableFutureDate,
+      disableFutureDate
     }
   },
   created() {
     this.getList()
   },
   methods: {
-    getMockRows() {
-      return [
-        {
-          id: 1,
-          member_account: 'qwe775825',
-          login_time: '2026-07-28 09:23:13',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '103.43.78.192',
-          device: '电脑端'
-        },
-        {
-          id: 2,
-          member_account: 'agent',
-          login_time: '2026-07-28 09:17:17',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '103.43.78.192',
-          device: '电脑端'
-        },
-        {
-          id: 3,
-          member_account: 'agent',
-          login_time: '2026-07-27 19:32:34',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '103.95.34.4',
-          device: '电脑端'
-        },
-        {
-          id: 4,
-          member_account: 'agent',
-          login_time: '2026-07-26 14:12:22',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '61.140.220.87',
-          device: '手机端'
-        },
-        {
-          id: 5,
-          member_account: 'agent',
-          login_time: '2026-07-26 04:04:26',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '188.253.12.37',
-          device: '电脑端'
-        },
-        {
-          id: 6,
-          member_account: 'agent',
-          login_time: '2026-07-25 23:16:13',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '95.41.10.57',
-          device: '手机端'
-        },
-        {
-          id: 7,
-          member_account: 'agent',
-          login_time: '2026-07-25 11:44:46',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '63.140.1.198',
-          device: '手机端'
-        },
-        {
-          id: 8,
-          member_account: 'agent',
-          login_time: '2026-07-25 03:08:45',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '61.140.220.87',
-          device: '手机端'
-        },
-        {
-          id: 9,
-          member_account: 'agent',
-          login_time: '2026-07-24 02:56:15',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '23.249.17.194',
-          device: '电脑端'
-        },
-        {
-          id: 10,
-          member_account: 'agent',
-          login_time: '2026-07-24 02:52:13',
-          domain: 'http://api-agent.ng-demo.xyz',
-          ip: '154.92.130.88',
-          device: '电脑端'
-        }
-      ]
+    normalizeRow(row = {}) {
+      return Object.assign({}, row, {
+        login_time: row.login_at || row.created_at || '',
+        device_text: row.device_text || this.getDeviceText(row.device),
+        location: row.location || this.formatLocation(row)
+      })
+    },
+    getDeviceText(device) {
+      const map = {
+        mobile: '手机端',
+        pc: '电脑端'
+      }
+      return map[device] || device || '--'
+    },
+    formatLocation(row) {
+      return [row.country, row.region, row.city].filter(Boolean).join(' ')
     },
     getList() {
       this.loading = true
-      const rows = this.getMockRows().filter(item => {
-        const accountMatched = !this.query.member_account || item.member_account.indexOf(this.query.member_account) > -1
-        const ipMatched = !this.query.ip || item.ip.indexOf(this.query.ip) > -1
-        return accountMatched && ipMatched
-      })
-      this.total = 546
-      this.tableData = rows.slice((this.page - 1) * this.limit, this.page * this.limit)
-      this.loading = false
+      const [startDate, endDate] = this.query.date_range || []
+      const params = {
+        page: this.page,
+        limit: this.limit,
+        agent_id: this.query.agent_id || undefined,
+        username: this.query.username || undefined,
+        ip: this.query.ip || undefined,
+        device: this.query.device || undefined,
+        start_date: startDate || undefined,
+        end_date: endDate || undefined
+      }
+      getAgentLoginLogs(params)
+        .then(res => {
+          const data = res.data || {}
+          this.total = Number(data.total) || 0
+          this.page = Number(data.page) || this.page
+          this.limit = Number(data.limit) || this.limit
+          this.tableData = (data.list || []).map(this.normalizeRow)
+        })
+        .finally(() => {
+          this.loading = false
+        })
     },
     handleSearch() {
       this.page = 1
@@ -223,9 +199,6 @@ export default {
     handleCurrentChange(val) {
       this.page = val
       this.getList()
-    },
-    openIpLookup(ip) {
-      this.$message.info(`查询IP归属地:${ip}`)
     }
   }
 }
@@ -250,20 +223,6 @@ export default {
   box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08);
 }
 
-.ip-alert {
-  flex-shrink: 0;
-  margin-bottom: 0;
-  border: 0;
-  border-radius: 3px;
-  background: #f1fbec;
-
-  ::v-deep .el-alert__title {
-    color: #31c323;
-    font-size: 18px;
-    font-weight: 700;
-  }
-}
-
 .login-log-search {
   flex-shrink: 0;
   padding-top: 0;
@@ -279,7 +238,8 @@ export default {
     font-weight: 500;
   }
 
-  ::v-deep .el-input {
+  ::v-deep .el-input,
+  ::v-deep .el-select {
     width: 236px;
   }
 
@@ -309,10 +269,11 @@ export default {
   }
 }
 
-.ip-link {
-  color: #20bfb2;
-  font-weight: 700;
-  cursor: pointer;
+.domain-cell,
+.user-agent-cell {
+  white-space: normal;
+  word-break: break-all;
+  line-height: 1.55;
 }
 
 ::v-deep .el-tag {
@@ -339,7 +300,8 @@ export default {
 
 @media (max-width: 1280px) {
   .login-log-search {
-    ::v-deep .el-input {
+    ::v-deep .el-input,
+    ::v-deep .el-select {
       width: 190px;
     }
 

+ 125 - 105
src/views/agent/rebateLog/index.vue

@@ -2,80 +2,96 @@
   <div class="agent-rebate-page">
     <div class="rebate-panel">
       <el-form ref="queryForm" :model="query" inline size="small" class="rebate-search">
-        <el-form-item label="代理账号" prop="agent_account">
-          <el-input v-model="query.agent_account" placeholder="代理账号" clearable />
+        <el-form-item label="代理ID" prop="agent_id">
+          <el-input
+            v-model="query.agent_id"
+            placeholder="代理ID"
+            clearable
+            @keyup.enter.native="handleSearch"
+          />
         </el-form-item>
-        <el-form-item label="流水佣金状态" prop="turnover_status">
-          <el-select v-model="query.turnover_status" placeholder="流水佣金状态" clearable>
-            <el-option label="待返佣" value="pending" />
-            <el-option label="已返佣" value="settled" />
+        <el-form-item label="佣金类型" prop="type">
+          <el-select v-model="query.type" placeholder="佣金类型" clearable>
+            <el-option label="流水佣金" value="flow" />
+            <el-option label="盈亏佣金" value="profit" />
           </el-select>
         </el-form-item>
-        <el-form-item label="盈亏佣金状态" prop="profit_status">
-          <el-select v-model="query.profit_status" placeholder="盈亏佣金状态" clearable>
-            <el-option label="待返佣" value="pending" />
-            <el-option label="已返佣" value="settled" />
+        <el-form-item label="入账状态" prop="status">
+          <el-select v-model="query.status" placeholder="入账状态" clearable>
+            <el-option label="待入账" value="pending" />
+            <el-option label="已入账" value="credited" />
           </el-select>
         </el-form-item>
-        <el-form-item label="创建日期" prop="date_range">
+        <el-form-item label="结算日期" prop="date_range">
           <el-date-picker
             v-model="query.date_range"
-            type="datetimerange"
+            type="daterange"
             range-separator="-"
             start-placeholder="开始日期"
             end-placeholder="结束日期"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            :default-time="['00:00:00', '23:59:59']"
+            value-format="yyyy-MM-dd"
+            :default-value="[new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1), new Date()]"
+            :picker-options="disableFutureDate"
             class="date-range"
           />
         </el-form-item>
         <el-form-item class="search-actions">
           <el-button @click="handleSearch">查询</el-button>
-          <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchRebate">批量流水返佣</el-button>
         </el-form-item>
       </el-form>
 
       <el-table
-        ref="rebateTable"
         v-loading="loading"
-        :data="pagedRows"
+        :data="tableData"
         border
         height="calc(100vh - 275px)"
         class="rebate-table"
-        @selection-change="handleSelectionChange"
       >
-        <el-table-column type="selection" width="100" align="center" />
-        <el-table-column prop="agent_account" label="代理账号" width="150" align="center" />
-        <el-table-column prop="rebate_no" label="代理返佣单号" width="200" align="center" />
-        <el-table-column prop="platform" label="游戏平台" width="150" align="center" />
-        <el-table-column prop="turnover_commission" label="流水佣金" width="150" align="center">
+        <el-table-column prop="id" label="ID" width="90" align="center" />
+        <el-table-column prop="agent_id" label="代理ID" width="90" align="center" />
+        <el-table-column prop="agent_account" label="代理账号" min-width="140" align="center" />
+        <el-table-column prop="settlement_date_text" label="所属账期" min-width="170" align="center" />
+        <el-table-column prop="type" label="佣金类型" min-width="120" align="center">
+          <template slot-scope="{ row }">
+            <el-tag size="mini" :type="row.type === 'flow' ? 'success' : ''" effect="light">
+              {{ typeTextMap[row.type] || row.type || '--' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="base_amount" label="结算基数" min-width="140" align="center">
           <template slot="header">
-            流水佣金
-            <el-tooltip content="根据代理流水和流水佣金比例计算" placement="top">
+            结算基数
+            <el-tooltip content="流水佣金为有效投注额,盈亏佣金为公司正盈利" placement="top">
               <i class="el-icon-question table-help" />
             </el-tooltip>
           </template>
-          <template slot-scope="{ row }">{{ formatMoney(row.turnover_commission) }}</template>
+          <template slot-scope="{ row }">{{ formatMoney(row.base_amount) }}</template>
         </el-table-column>
-        <el-table-column prop="turnover_ratio" label="流水佣金比例" width="150" align="center">
-          <template slot-scope="{ row }">{{ formatRate(row.turnover_ratio) }}</template>
+        <el-table-column prop="rate" label="佣金比例" min-width="120" align="center">
+          <template slot-scope="{ row }">{{ formatRate(row.rate) }}</template>
         </el-table-column>
-        <el-table-column prop="profit_commission" label="盈亏佣金" width="150" align="center">
-          <template slot-scope="{ row }">{{ formatMoney(row.profit_commission) }}</template>
+        <el-table-column prop="amount" label="返佣金额" min-width="140" align="center">
+          <template slot-scope="{ row }">{{ formatMoney(row.amount) }}</template>
         </el-table-column>
-        <el-table-column prop="profit_ratio" label="盈亏佣金比例" width="150" align="center">
-          <template slot-scope="{ row }">{{ formatRate(row.profit_ratio) }}</template>
-        </el-table-column>
-        <el-table-column prop="bet_count" label="注单数量" width="130" align="center" />
-        <el-table-column prop="bet_amount" label="总投注金额" width="150" align="center">
-          <template slot-scope="{ row }">{{ formatMoney(row.bet_amount) }}</template>
+        <el-table-column prop="bet_count" label="注单数量" min-width="110" align="center" />
+        <el-table-column prop="status" label="入账状态" min-width="110" align="center">
+          <template slot-scope="{ row }">
+            <el-tag size="mini" :type="row.status === 'credited' ? 'success' : 'warning'" effect="light">
+              {{ statusTextMap[row.status] || row.status || '--' }}
+            </el-tag>
+          </template>
         </el-table-column>
-        <el-table-column label="操作" width="150" fixed="right" align="center">
+        <el-table-column prop="snapshot_agent_ids" label="覆盖代理树" min-width="180" align="center">
           <template slot-scope="{ row }">
-            <el-button v-if="row.turnover_status === 'pending'" type="text" size="mini" @click="handleRebate(row)">流水返佣</el-button>
-            <span v-else class="empty-action">--</span>
+            <div class="snapshot-cell">{{ row.snapshot_agent_ids || '--' }}</div>
           </template>
         </el-table-column>
+        <el-table-column prop="credited_at" label="入账时间" min-width="180" align="center">
+          <template slot-scope="{ row }">{{ row.credited_at || '--' }}</template>
+        </el-table-column>
+        <el-table-column prop="created_at" label="创建时间" min-width="170" align="center">
+          <template slot-scope="{ row }">{{ row.created_at || '--' }}</template>
+        </el-table-column>
       </el-table>
 
       <el-pagination
@@ -85,7 +101,7 @@
         :current-page="page"
         :page-size="limit"
         :page-sizes="[10, 20, 50, 100]"
-        :total="filteredRows.length"
+        :total="total"
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
       />
@@ -94,12 +110,8 @@
 </template>
 
 <script>
-const getTodayRange = () => {
-  const now = new Date()
-  const pad = value => String(value).padStart(2, '0')
-  const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
-  return [`${date} 00:00:00`, `${date} 23:59:59`]
-}
+import { getAgentCommissionList } from '@/api/data'
+import { disableFutureDate } from '@/utils/index'
 
 export default {
   name: 'AgentRebateLog',
@@ -108,75 +120,87 @@ export default {
       loading: false,
       page: 1,
       limit: 10,
+      total: 0,
       query: {
-        agent_account: '',
-        turnover_status: '',
-        profit_status: '',
-        date_range: getTodayRange()
+        agent_id: '',
+        type: '',
+        status: '',
+        date_range: []
       },
-      rows: [],
-      selectedRows: []
+      tableData: [],
+      disableFutureDate,
+      typeTextMap: {
+        flow: '流水佣金',
+        profit: '盈亏佣金'
+      },
+      statusTextMap: {
+        pending: '待入账',
+        credited: '已入账'
+      }
     }
   },
-  computed: {
-    filteredRows() {
-      return this.rows.filter(row => {
-        const accountMatched = !this.query.agent_account || row.agent_account.indexOf(this.query.agent_account) > -1
-        const turnoverMatched = !this.query.turnover_status || row.turnover_status === this.query.turnover_status
-        const profitMatched = !this.query.profit_status || row.profit_status === this.query.profit_status
-        return accountMatched && turnoverMatched && profitMatched
-      })
-    },
-    pagedRows() {
-      const start = (this.page - 1) * this.limit
-      return this.filteredRows.slice(start, start + this.limit)
-    }
+  created() {
+    this.getList()
   },
   methods: {
-    handleSearch() {
-      this.page = 1
-      this.selectedRows = []
+    normalizeRow(row = {}) {
+      const snapshot = row.snapshot || {}
+      return Object.assign({}, row, {
+        agent_account: row.agent && row.agent.username ? row.agent.username : '',
+        settlement_date_text: this.formatDate(row.settlement_date),
+        snapshot_agent_ids: Array.isArray(snapshot.agent_ids) ? snapshot.agent_ids.join(', ') : ''
+      })
     },
-    handleSelectionChange(rows) {
-      this.selectedRows = rows
+    formatDate(value) {
+      if (!value) return '--'
+      return String(value).replace('T', ' ').replace(/\.\d+Z$/, '').slice(0, 19)
     },
-    handleRebate(row) {
-      this.$confirm('确定对该代理进行流水返佣吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        row.turnover_status = 'settled'
-        this.$message.success('流水返佣成功')
-        this.selectedRows = []
-        this.$refs.rebateTable.clearSelection()
-      }).catch(() => {})
+    getList() {
+      this.loading = true
+      const [startDate, endDate] = this.query.date_range || []
+      const params = {
+        page: this.page,
+        limit: this.limit,
+        agent_id: this.query.agent_id || undefined,
+        type: this.query.type || undefined,
+        status: this.query.status || undefined,
+        start_date: startDate || undefined,
+        end_date: endDate || undefined
+      }
+      getAgentCommissionList(params)
+        .then(res => {
+          const data = res.data || {}
+          this.total = Number(data.total) || 0
+          this.page = Number(data.page) || this.page
+          this.limit = Number(data.limit) || this.limit
+          this.tableData = (data.list || []).map(this.normalizeRow)
+        })
+        .finally(() => {
+          this.loading = false
+        })
     },
-    handleBatchRebate() {
-      if (!this.selectedRows.length) return
-      this.$confirm(`确定对选中的 ${this.selectedRows.length} 条记录进行流水返佣吗?`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(() => {
-        this.selectedRows.forEach(row => { row.turnover_status = 'settled' })
-        this.$message.success('批量流水返佣成功')
-        this.selectedRows = []
-        this.$refs.rebateTable.clearSelection()
-      }).catch(() => {})
+    handleSearch() {
+      this.page = 1
+      this.getList()
     },
     handleSizeChange(value) {
       this.limit = value
       this.page = 1
+      this.getList()
     },
     handleCurrentChange(value) {
       this.page = value
+      this.getList()
     },
     formatMoney(value) {
-      return Number(value || 0).toFixed(2)
+      const numberValue = Number(value)
+      if (Number.isNaN(numberValue)) return value || 0
+      return numberValue.toFixed(2)
     },
     formatRate(value) {
-      return `${Number(value || 0).toFixed(2).replace(/\.00$/, '')}%`
+      const numberValue = Number(value)
+      if (Number.isNaN(numberValue)) return value || 0
+      return `${numberValue.toFixed(4).replace(/0+$/, '').replace(/\.$/, '')}%`
     }
   }
 }
@@ -233,12 +257,6 @@ export default {
     margin-left: 0;
     margin-right: 14px;
   }
-
-  ::v-deep .el-button--danger.is-disabled {
-    color: #fff;
-    background: #ffb3b5;
-    border-color: #ffb3b5;
-  }
 }
 
 .rebate-table {
@@ -264,16 +282,18 @@ export default {
   cursor: help;
 }
 
+.snapshot-cell {
+  white-space: normal;
+  word-break: break-all;
+  line-height: 1.55;
+}
+
 .rebate-pagination {
   flex-shrink: 0;
   padding-top: 12px;
   text-align: right;
 }
 
-.empty-action {
-  color: #909399;
-}
-
 @media (max-width: 1280px) {
   .rebate-search {
     ::v-deep .el-input,

+ 214 - 298
src/views/agent/report/index.vue

@@ -8,7 +8,7 @@
         show-icon
       >
         <template slot="title">
-          公司盈亏计算方式:【总存款 减去 总提款 减去 总加送金额 减去 总优惠 减去 总红包 减去 总活动彩金 减去 总返水】 其中未包含【手动加/扣款】
+          公司盈亏计算方式:【总存款 减去 总提款 减去 优惠/加送 减去 返水】 其中未包含【手动加/扣款】
         </template>
       </el-alert>
 
@@ -24,13 +24,7 @@
             v-model="query.agent_account"
             placeholder="代理账号"
             clearable
-          />
-        </el-form-item>
-        <el-form-item label="真实姓名" prop="real_name">
-          <el-input
-            v-model="query.real_name"
-            placeholder="真实姓名"
-            clearable
+            @keyup.enter.native="handleSearch"
           />
         </el-form-item>
         <el-form-item label="时间" prop="date_range">
@@ -43,11 +37,12 @@
             end-placeholder="结束日期"
             value-format="yyyy-MM-dd"
             :picker-options="disableFutureDate"
+            :clearable="false"
           />
         </el-form-item>
         <el-form-item class="search-actions">
           <el-button @click="handleSearch">查询</el-button>
-          <el-button type="primary" plain @click="handleExport">导出Excel</el-button>
+          <el-button type="primary" plain :loading="exportLoading" @click="handleExport">导出Excel</el-button>
         </el-form-item>
       </el-form>
 
@@ -58,23 +53,28 @@
         height="calc(100vh - 305px)"
         class="report-table"
       >
-        <el-table-column prop="id" label="ID" width="54" align="center" fixed="left" />
+        <el-table-column prop="id" label="ID" width="70" align="center" fixed="left" />
         <el-table-column label="代理自身" align="center">
-          <el-table-column prop="agent_account" label="账号" min-width="82" align="center" />
-          <el-table-column prop="real_name" label="真实姓名" min-width="82" align="center" />
-          <el-table-column prop="balance" label="账户余额" min-width="82" align="center">
+          <el-table-column prop="agent_account" label="账号" min-width="110" align="center" />
+          <el-table-column prop="real_name" label="真实姓名" min-width="100" align="center">
+            <template slot-scope="{ row }">{{ row.real_name || '--' }}</template>
+          </el-table-column>
+          <el-table-column prop="parent_username" label="上级代理" min-width="110" align="center">
+            <template slot-scope="{ row }">{{ row.parent_username || '--' }}</template>
+          </el-table-column>
+          <el-table-column prop="balance" label="账户余额" min-width="100" align="center">
             <template slot-scope="{ row }">
               {{ formatMoney(row.balance) }}
             </template>
           </el-table-column>
-          <el-table-column prop="locked_wallet" label="锁定钱包" min-width="82" align="center">
+          <el-table-column prop="frozen_balance" label="冻结余额" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.locked_wallet) }}
+              {{ formatMoney(row.frozen_balance) }}
             </template>
           </el-table-column>
         </el-table-column>
         <el-table-column label="下级数量" align="center">
-          <el-table-column prop="sub_count" label="代理数/会员数" min-width="90" align="center">
+          <el-table-column prop="sub_count" label="代理数/会员数" min-width="110" align="center">
             <template slot-scope="{ row }">
               <span class="count-blue">({{ row.agent_count }})</span>
               <span> / </span>
@@ -83,85 +83,76 @@
           </el-table-column>
         </el-table-column>
         <el-table-column label="下级金额" align="center">
-          <el-table-column prop="member_balance" label="会员余额" min-width="82" align="center">
+          <el-table-column prop="member_balance" label="会员余额" min-width="100" align="center">
             <template slot-scope="{ row }">
               {{ formatMoney(row.member_balance) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_deposit" label="总存款" min-width="82" align="center">
+          <el-table-column prop="deposit_amount" label="总存款" min-width="100" align="center">
             <template slot-scope="{ row }">
-              <span class="amount-red">{{ formatMoney(row.total_deposit) }}</span>
+              <span class="amount-red">{{ formatMoney(row.deposit_amount) }}</span>
             </template>
           </el-table-column>
-          <el-table-column prop="total_bonus" label="总加送金额" min-width="82" align="center">
+          <el-table-column prop="withdraw_amount" label="总提款" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_bonus) }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="total_withdraw" label="总提款" min-width="82" align="center">
-            <template slot-scope="{ row }">
-              {{ formatMoney(row.total_withdraw) }}
+              {{ formatMoney(row.withdraw_amount) }}
             </template>
           </el-table-column>
         </el-table-column>
         <el-table-column label="加/扣款" align="center">
-          <el-table-column prop="total_add" label="总加款" min-width="82" align="center">
+          <el-table-column prop="manual_credit" label="人工充值" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_add) }}
+              {{ formatMoney(row.manual_credit) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_subtract" label="总扣款" min-width="82" align="center">
+          <el-table-column prop="manual_debit" label="人工扣款" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_subtract) }}
+              {{ formatMoney(row.manual_debit) }}
             </template>
           </el-table-column>
         </el-table-column>
-        <el-table-column label="下级红利" align="center">
-          <el-table-column prop="total_discount" label="总优惠" min-width="82" align="center">
-            <template slot-scope="{ row }">
-              {{ formatMoney(row.total_discount) }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="total_red_packet" label="总红包" min-width="82" align="center">
+        <el-table-column label="优惠返水" align="center">
+          <el-table-column prop="bonus_amount" label="优惠金额" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_red_packet) }}
+              {{ formatMoney(row.bonus_amount) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_activity_bonus" label="总活动彩金" min-width="82" align="center">
+          <el-table-column prop="rebate_amount" label="返水金额" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_activity_bonus) }}
+              {{ formatMoney(row.rebate_amount) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_rebate" label="总返水" min-width="82" align="center">
+          <el-table-column prop="commission_amount" label="代理佣金" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_rebate) }}
+              {{ formatMoney(row.commission_amount) }}
             </template>
           </el-table-column>
         </el-table-column>
         <el-table-column label="下级投注" align="center">
-          <el-table-column prop="total_bet" label="总投注" min-width="82" align="center">
+          <el-table-column prop="bet_count" label="注单数" min-width="90" align="center" />
+          <el-table-column prop="bet_amount" label="总投注" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_bet) }}
+              {{ formatMoney(row.bet_amount) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_valid_bet" label="总有效投注" min-width="82" align="center">
+          <el-table-column prop="valid_bet_amount" label="有效投注" min-width="100" align="center">
             <template slot-scope="{ row }">
-              {{ formatMoney(row.total_valid_bet) }}
+              {{ formatMoney(row.valid_bet_amount) }}
             </template>
           </el-table-column>
-          <el-table-column prop="total_win" label="总输赢" min-width="82" align="center">
+          <el-table-column prop="win_loss" label="会员输赢" min-width="100" align="center">
             <template slot-scope="{ row }">
               <el-tag
                 size="mini"
-                :type="Number(row.total_win) < 0 ? 'danger' : 'info'"
+                :type="Number(row.win_loss) < 0 ? 'danger' : 'info'"
                 effect="plain"
               >
-                {{ formatMoney(row.total_win) }}
+                {{ formatMoney(row.win_loss) }}
               </el-tag>
             </template>
           </el-table-column>
         </el-table-column>
-        <el-table-column prop="company_profit" label="公司盈亏" min-width="82" align="center" fixed="right">
+        <el-table-column prop="company_profit" label="公司盈亏" min-width="110" align="center" fixed="right">
           <template slot-scope="{ row }">
             {{ formatMoney(row.company_profit) }}
           </template>
@@ -174,7 +165,7 @@
         layout="total, sizes, prev, pager, next, jumper"
         :current-page="page"
         :page-size="limit"
-        :page-sizes="[10, 20, 50, 100]"
+        :page-sizes="[10, 20]"
         :total="total"
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
@@ -184,279 +175,204 @@
 </template>
 
 <script>
+import exportExcel from '@/components/Table2Excel/exportExcel'
+import { getAgentReportList } from '@/api/data'
 import { disableFutureDate } from '@/utils/index'
+
+const pad = value => String(value).padStart(2, '0')
+const formatDate = date => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
+const EXPORT_LIMIT = 20
+
 export default {
   name: 'AgentReport',
   data() {
     return {
       loading: false,
+      exportLoading: false,
       query: {
         agent_account: '',
-        real_name: '',
-        date_range: []
+        date_range: this.getDefaultDateRange()
       },
       page: 1,
       limit: 10,
       total: 0,
       tableData: [],
-      disableFutureDate,
+      disableFutureDate
     }
   },
   created() {
     this.getList()
   },
   methods: {
-    getMockRows() {
-      return [
-        {
-          id: 1,
-          agent_account: 'agent',
-          real_name: 'agent',
-          balance: 1234,
-          locked_wallet: 0,
-          agent_count: 14,
-          member_count: 3,
-          member_balance: 6105645.33,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 999800,
-          total_add: 7127344,
-          total_subtract: 21899,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0.1115,
-          total_bet: 398.8,
-          total_valid_bet: 398.8,
-          total_win: -138,
-          company_profit: -999800.11
-        },
-        {
-          id: 2,
-          agent_account: 'zxc123',
-          real_name: 'zxc123',
-          balance: 0.33,
-          locked_wallet: 0,
-          agent_count: 1,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 3,
-          agent_account: 'a112411',
-          real_name: '',
-          balance: 0.03,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 4,
-          agent_account: '112411',
-          real_name: '',
-          balance: 0,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 5,
-          agent_account: '112400',
-          real_name: '',
-          balance: 0,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 6,
-          agent_account: 'zxc1122',
-          real_name: '',
-          balance: 122122,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 1,
-          member_balance: 5000,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 100000,
-          total_subtract: 95000,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 7,
-          agent_account: 'zxc123456',
-          real_name: '',
-          balance: 100,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 8,
-          agent_account: 'admin',
-          real_name: '',
-          balance: 9999999,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 9,
-          agent_account: 'abc123',
-          real_name: '',
-          balance: 1222,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        },
-        {
-          id: 10,
-          agent_account: 'abc1233',
-          real_name: '',
-          balance: 1999,
-          locked_wallet: 0,
-          agent_count: 0,
-          member_count: 0,
-          member_balance: 0,
-          total_deposit: 0,
-          total_bonus: 0,
-          total_withdraw: 0,
-          total_add: 0,
-          total_subtract: 0,
-          total_discount: 0,
-          total_red_packet: 0,
-          total_activity_bonus: 0,
-          total_rebate: 0,
-          total_bet: 0,
-          total_valid_bet: 0,
-          total_win: 0,
-          company_profit: 0
-        }
-      ]
+    getDefaultDateRange() {
+      const end = new Date()
+      const start = new Date()
+      start.setDate(start.getDate() - 29)
+      return [formatDate(start), formatDate(end)]
+    },
+    normalizeRow(row = {}) {
+      return Object.assign({}, row, {
+        agent_account: row.username || ''
+      })
+    },
+    getReportParams(page = this.page, limit = this.limit) {
+      const [startDate, endDate] = this.query.date_range || []
+      return {
+        page,
+        limit,
+        username: this.query.agent_account || undefined,
+        start_date: startDate,
+        end_date: endDate
+      }
+    },
+    validateDateRange() {
+      const [startDate, endDate] = this.query.date_range || []
+      if (!startDate || !endDate) {
+        this.$message.error('请选择统计时间')
+        return false
+      }
+      return true
     },
     getList() {
+      if (!this.validateDateRange()) return
+
       this.loading = true
-      const rows = this.getMockRows().filter(item => {
-        const accountMatched = !this.query.agent_account || item.agent_account.indexOf(this.query.agent_account) > -1
-        const realNameMatched = !this.query.real_name || item.real_name.indexOf(this.query.real_name) > -1
-        return accountMatched && realNameMatched
-      })
-      this.total = rows.length
-      this.tableData = rows.slice((this.page - 1) * this.limit, this.page * this.limit)
-      this.loading = false
+      getAgentReportList(this.getReportParams())
+        .then(res => {
+          const data = res.data || {}
+          this.total = Number(data.total) || 0
+          this.page = Number(data.page) || this.page
+          this.limit = Number(data.limit) || this.limit
+          this.tableData = (data.list || []).map(this.normalizeRow)
+        })
+        .finally(() => {
+          this.loading = false
+        })
     },
     handleSearch() {
       this.page = 1
       this.getList()
     },
-    handleExport() {
-      this.$message.success('导出任务已创建')
+    async handleExport() {
+      if (!this.validateDateRange() || this.exportLoading) return
+
+      this.exportLoading = true
+      try {
+        const rows = await this.fetchAllReportRows()
+        if (!rows.length) {
+          this.$message.warning('暂无可导出的数据')
+          return
+        }
+        exportExcel({
+          theadMap: [
+            {
+              ID: { r: 1, c: 1 },
+              账号: { r: 1, c: 1 },
+              真实姓名: { r: 1, c: 1 },
+              上级代理: { r: 1, c: 1 },
+              账户余额: { r: 1, c: 1 },
+              冻结余额: { r: 1, c: 1 },
+              下级代理数: { r: 1, c: 1 },
+              会员数: { r: 1, c: 1 },
+              会员余额: { r: 1, c: 1 },
+              总存款: { r: 1, c: 1 },
+              总提款: { r: 1, c: 1 },
+              人工充值: { r: 1, c: 1 },
+              人工扣款: { r: 1, c: 1 },
+              优惠金额: { r: 1, c: 1 },
+              返水金额: { r: 1, c: 1 },
+              注单数: { r: 1, c: 1 },
+              总投注: { r: 1, c: 1 },
+              有效投注: { r: 1, c: 1 },
+              会员输赢: { r: 1, c: 1 },
+              代理佣金: { r: 1, c: 1 },
+              公司盈亏: { r: 1, c: 1 }
+            }
+          ],
+          theadProps: this.getExportTheadProps(),
+          tableData: rows.map(this.normalizeExportRow),
+          tfootData: [],
+          baseApi: '',
+          dataName: this.getExportFileName()
+        })
+        this.$message.success('导出成功')
+      } finally {
+        this.exportLoading = false
+      }
+    },
+    fetchAllReportRows() {
+      return getAgentReportList(this.getReportParams(1, EXPORT_LIMIT)).then(res => {
+        const data = res.data || {}
+        const total = Number(data.total) || 0
+        const firstRows = data.list || []
+        const pageCount = Math.ceil(total / EXPORT_LIMIT)
+        if (pageCount <= 1) return firstRows.map(this.normalizeRow)
+
+        const requests = []
+        for (let page = 2; page <= pageCount; page += 1) {
+          requests.push(getAgentReportList(this.getReportParams(page, EXPORT_LIMIT)))
+        }
+        return Promise.all(requests).then(results => {
+          return results.reduce((rows, item) => {
+            const pageData = item.data || {}
+            return rows.concat(pageData.list || [])
+          }, firstRows).map(this.normalizeRow)
+        })
+      })
+    },
+    getExportTheadProps() {
+      return {
+        id: {},
+        agent_account: {},
+        real_name: {},
+        parent_username: {},
+        balance: {},
+        frozen_balance: {},
+        agent_count: {},
+        member_count: {},
+        member_balance: {},
+        deposit_amount: {},
+        withdraw_amount: {},
+        manual_credit: {},
+        manual_debit: {},
+        bonus_amount: {},
+        rebate_amount: {},
+        bet_count: {},
+        bet_amount: {},
+        valid_bet_amount: {},
+        win_loss: {},
+        commission_amount: {},
+        company_profit: {}
+      }
+    },
+    normalizeExportRow(row) {
+      return {
+        id: row.id,
+        agent_account: row.agent_account,
+        real_name: row.real_name || '',
+        parent_username: row.parent_username || '',
+        balance: this.formatMoney(row.balance),
+        frozen_balance: this.formatMoney(row.frozen_balance),
+        agent_count: row.agent_count || 0,
+        member_count: row.member_count || 0,
+        member_balance: this.formatMoney(row.member_balance),
+        deposit_amount: this.formatMoney(row.deposit_amount),
+        withdraw_amount: this.formatMoney(row.withdraw_amount),
+        manual_credit: this.formatMoney(row.manual_credit),
+        manual_debit: this.formatMoney(row.manual_debit),
+        bonus_amount: this.formatMoney(row.bonus_amount),
+        rebate_amount: this.formatMoney(row.rebate_amount),
+        bet_count: row.bet_count || 0,
+        bet_amount: this.formatMoney(row.bet_amount),
+        valid_bet_amount: this.formatMoney(row.valid_bet_amount),
+        win_loss: this.formatMoney(row.win_loss),
+        commission_amount: this.formatMoney(row.commission_amount),
+        company_profit: this.formatMoney(row.company_profit)
+      }
+    },
+    getExportFileName() {
+      const [startDate, endDate] = this.query.date_range || []
+      return `代理报表_${startDate}_${endDate}.xls`
     },
     handleSizeChange(val) {
       this.limit = val
@@ -470,7 +386,7 @@ export default {
     formatMoney(value) {
       const numberValue = Number(value)
       if (Number.isNaN(numberValue)) return value || 0
-      return Number.isInteger(numberValue) ? numberValue : numberValue.toFixed(2)
+      return numberValue.toFixed(2)
     }
   }
 }

+ 219 - 173
src/views/agent/withdrawLog/index.vue

@@ -8,11 +8,12 @@
         inline
         size="small"
       >
-        <el-form-item label="会员账号" prop="member_account">
+        <el-form-item label="代理ID" prop="agent_id">
           <el-input
-            v-model="query.member_account"
-            placeholder="会员账号"
+            v-model="query.agent_id"
+            placeholder="代理ID"
             clearable
+            @keyup.enter.native="handleSearch"
           />
         </el-form-item>
         <el-form-item label="订单号" prop="order_no">
@@ -20,13 +21,15 @@
             v-model="query.order_no"
             placeholder="订单号"
             clearable
+            @keyup.enter.native="handleSearch"
           />
         </el-form-item>
-        <el-form-item label="启用状态" prop="status">
-          <el-select v-model="query.status" placeholder="启用状态" clearable>
+        <el-form-item label="提现状态" prop="status">
+          <el-select v-model="query.status" placeholder="提现状态" clearable>
             <el-option label="待处理" value="pending" />
-            <el-option label="成功" value="success" />
-            <el-option label="失败" value="failed" />
+            <el-option label="已审核" value="approved" />
+            <el-option label="已驳回" value="rejected" />
+            <el-option label="已付款" value="paid" />
           </el-select>
         </el-form-item>
         <el-form-item label="申请日期" prop="date_range">
@@ -54,41 +57,106 @@
         class="withdraw-table"
         :row-class-name="tableRowClassName"
       >
-        <el-table-column prop="order_no" label="订单号" min-width="200" align="center" />
-        <el-table-column prop="member_account" label="会员账号" min-width="150" align="center" />
-        <el-table-column prop="real_name" label="真实姓名" min-width="150" align="center" />
-        <el-table-column prop="withdraw_amount" label="提现金额" min-width="160" align="center">
+        <el-table-column prop="order_no" label="订单号" min-width="210" align="center" />
+        <el-table-column prop="agent_id" label="代理ID" width="90" align="center" />
+        <el-table-column prop="agent_username" label="代理账号" min-width="130" align="center" />
+        <el-table-column prop="real_name" label="真实姓名" min-width="120" align="center">
           <template slot-scope="{ row }">
-            {{ formatMoney(row.withdraw_amount) }}
+            {{ row.real_name || '--' }}
           </template>
         </el-table-column>
-        <el-table-column prop="withdraw_fee" label="提现手续费" min-width="150" align="center">
+        <el-table-column prop="amount" label="提现金额" min-width="120" align="center">
           <template slot-scope="{ row }">
-            {{ formatMoney(row.withdraw_fee) }}
+            {{ formatMoney(row.amount) }}
           </template>
         </el-table-column>
-        <el-table-column prop="usdt_amount" label="出款USDT数量" min-width="150" align="center">
+        <el-table-column prop="fee" label="提现手续费" min-width="120" align="center">
           <template slot-scope="{ row }">
-            {{ formatMoney(row.usdt_amount) }}
+            {{ formatMoney(row.fee) }}
           </template>
         </el-table-column>
-        <el-table-column prop="withdraw_method" label="取款方式" min-width="180" align="center" />
-        <el-table-column prop="withdraw_type" label="取款类型" min-width="150" align="center">
+        <el-table-column prop="actual_amount" label="实际到账" min-width="120" align="center">
           <template slot-scope="{ row }">
-            <el-tag size="mini" type="success" effect="light">
-              {{ row.withdraw_type }}
+            {{ formatMoney(row.actual_amount) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="account_type_text" label="收款方式" min-width="110" align="center" />
+        <el-table-column prop="account_name" label="收款姓名" min-width="120" align="center" />
+        <el-table-column prop="account_network" label="网络/银行" min-width="120" align="center">
+          <template slot-scope="{ row }">
+            {{ row.account_network || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="account_no" label="收款账号" min-width="220" align="center">
+          <template slot-scope="{ row }">
+            <div class="account-cell">{{ row.account_no || '--' }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="100" align="center">
+          <template slot-scope="{ row }">
+            <el-tag size="mini" :type="statusTypeMap[row.status]" effect="light">
+              {{ statusTextMap[row.status] || row.status || '--' }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="withdraw_address" label="取款地址" min-width="220" align="center" />
+        <el-table-column prop="remark" label="申请备注" min-width="150" align="center">
+          <template slot-scope="{ row }">
+            {{ row.remark || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="audit_remark" label="审核备注" min-width="150" align="center">
+          <template slot-scope="{ row }">
+            {{ row.audit_remark || '--' }}
+          </template>
+        </el-table-column>
         <el-table-column prop="created_at" label="申请时间" min-width="170" align="center">
           <template slot-scope="{ row }">
             {{ row.created_at || '--' }}
           </template>
         </el-table-column>
-        <el-table-column label="操作" width="100" fixed="right" align="center">
-          <template>
-            <span class="empty-action">--</span>
+        <el-table-column prop="audited_at" label="审核时间" min-width="170" align="center">
+          <template slot-scope="{ row }">
+            {{ row.audited_at || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="paid_at" label="付款时间" min-width="170" align="center">
+          <template slot-scope="{ row }">
+            {{ row.paid_at || '--' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="160" fixed="right" align="center">
+          <template slot-scope="{ row }">
+            <div v-if="canOperate(row)" class="row-actions">
+              <el-button
+                v-if="row.status === 'pending'"
+                type="text"
+                size="mini"
+                :loading="auditLoadingId === `${row.id}-approve`"
+                @click="handleAudit(row, 'approve')"
+              >
+                通过
+              </el-button>
+              <el-button
+                v-if="row.status === 'pending' || row.status === 'approved'"
+                type="text"
+                size="mini"
+                class="reject-button"
+                :loading="auditLoadingId === `${row.id}-reject`"
+                @click="handleAudit(row, 'reject')"
+              >
+                驳回
+              </el-button>
+              <el-button
+                v-if="row.status === 'approved'"
+                type="text"
+                size="mini"
+                :loading="auditLoadingId === `${row.id}-paid`"
+                @click="handleAudit(row, 'paid')"
+              >
+                确认付款
+              </el-button>
+            </div>
+            <span v-else class="empty-action">--</span>
           </template>
         </el-table-column>
       </el-table>
@@ -109,14 +177,17 @@
 </template>
 
 <script>
+import { auditAgentWithdrawal, getAgentWithdrawalList } from '@/api/data'
 import { disableFutureDate } from '@/utils/index'
+
 export default {
   name: 'AgentWithdrawLog',
   data() {
     return {
       loading: false,
+      auditLoadingId: '',
       query: {
-        member_account: '',
+        agent_id: '',
         order_no: '',
         status: '',
         date_range: []
@@ -126,157 +197,73 @@ export default {
       total: 0,
       tableData: [],
       disableFutureDate,
+      statusTextMap: {
+        pending: '待处理',
+        approved: '已审核',
+        rejected: '已驳回',
+        paid: '已付款'
+      },
+      statusTypeMap: {
+        pending: 'warning',
+        approved: '',
+        rejected: 'danger',
+        paid: 'success'
+      },
+      actionTextMap: {
+        approve: '通过',
+        reject: '驳回',
+        paid: '确认付款'
+      }
     }
   },
   created() {
     this.getList()
   },
   methods: {
-    getMockRows() {
-      return [
-        {
-          order_no: '26041907503087021681',
-          member_account: 'zxc123',
-          real_name: 'zxc123',
-          withdraw_amount: 9800,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: '支付宝',
-          withdraw_type: '即时取款',
-          withdraw_address: '18408708806',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '26041907444978896357',
-          member_account: 'zxc123',
-          real_name: 'zxc123',
-          withdraw_amount: 90000,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: '支付宝',
-          withdraw_type: '即时取款',
-          withdraw_address: '18408708806',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '26041907441669305898',
-          member_account: 'zxc123',
-          real_name: 'zxc123',
-          withdraw_amount: 900000,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: '支付宝',
-          withdraw_type: '即时取款',
-          withdraw_address: '18408708806',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '26031112595929507936',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 1234,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: 'K豆钱包(代付)',
-          withdraw_type: '即时取款',
-          withdraw_address: 'a',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '26020109555433162264',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 1234,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: 'K豆钱包(代付)',
-          withdraw_type: '即时取款',
-          withdraw_address: 'a',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '25082602570070976086',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 2500,
-          withdraw_fee: 0,
-          usdt_amount: 347.22,
-          withdraw_method: 'USDT提币',
-          withdraw_type: '即时取款',
-          withdraw_address: '演示数据',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '25082602560856350122',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 10000,
-          withdraw_fee: 0,
-          usdt_amount: 1388.89,
-          withdraw_method: 'USDT提币',
-          withdraw_type: '即时取款',
-          withdraw_address: '演示数据',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '25082602555492512945',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 10000,
-          withdraw_fee: 0,
-          usdt_amount: 1388.89,
-          withdraw_method: 'USDT提币',
-          withdraw_type: '即时取款',
-          withdraw_address: '演示数据',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '25073020481092019401',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 34917,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: '支付宝',
-          withdraw_type: '即时取款',
-          withdraw_address: '演示数据',
-          status: 'success',
-          created_at: ''
-        },
-        {
-          order_no: '25073020463230869159',
-          member_account: 'agent',
-          real_name: 'agent',
-          withdraw_amount: 5000,
-          withdraw_fee: 0,
-          usdt_amount: 0,
-          withdraw_method: '支付宝',
-          withdraw_type: '即时取款',
-          withdraw_address: '演示数据',
-          status: 'success',
-          created_at: ''
-        }
-      ]
+    normalizeRow(row = {}) {
+      const agent = row.agent || {}
+      const snapshot = row.account_snapshot || {}
+      return Object.assign({}, row, {
+        agent_username: agent.username || '',
+        real_name: agent.real_name || snapshot.name || '',
+        account_type_text: this.getAccountTypeText(row.account_type || snapshot.type),
+        account_name: snapshot.name || '--',
+        account_no: snapshot.account || '',
+        account_network: snapshot.network || snapshot.bank_name || ''
+      })
+    },
+    getAccountTypeText(type) {
+      const map = {
+        usdt: 'USDT',
+        bank: '银行卡',
+        alipay: '支付宝',
+        wechat: '微信'
+      }
+      return map[type] || type || '--'
     },
     getList() {
       this.loading = true
-      const rows = this.getMockRows().filter(item => {
-        const accountMatched = !this.query.member_account || item.member_account.indexOf(this.query.member_account) > -1
-        const orderMatched = !this.query.order_no || item.order_no.indexOf(this.query.order_no) > -1
-        const statusMatched = !this.query.status || item.status === this.query.status
-        return accountMatched && orderMatched && statusMatched
-      })
-      this.total = rows.length
-      this.tableData = rows.slice((this.page - 1) * this.limit, this.page * this.limit)
-      this.loading = false
+      const [startDate, endDate] = this.query.date_range || []
+      const params = {
+        page: this.page,
+        limit: this.limit,
+        agent_id: this.query.agent_id || undefined,
+        order_no: this.query.order_no || undefined,
+        status: this.query.status || undefined,
+        start_date: startDate || undefined,
+        end_date: endDate || undefined
+      }
+      getAgentWithdrawalList(params)
+        .then(res => {
+          const data = res.data || {}
+          this.total = Number(data.total) || 0
+          this.page = Number(data.page) || this.page
+          this.limit = Number(data.limit) || this.limit
+          this.tableData = (data.list || []).map(this.normalizeRow)
+        })
+        .finally(() => {
+          this.loading = false
+        })
     },
     handleSearch() {
       this.page = 1
@@ -292,12 +279,54 @@ export default {
       this.getList()
     },
     tableRowClassName({ rowIndex }) {
-      return rowIndex === 5 ? 'withdraw-row-tint' : ''
+      return rowIndex % 2 === 1 ? 'withdraw-row-tint' : ''
     },
     formatMoney(value) {
       const numberValue = Number(value)
       if (Number.isNaN(numberValue)) return value || 0
-      return Number.isInteger(numberValue) ? numberValue : numberValue.toFixed(2)
+      return numberValue.toFixed(2)
+    },
+    canOperate(row) {
+      return row.status === 'pending' || row.status === 'approved'
+    },
+    handleAudit(row, action) {
+      const actionText = this.actionTextMap[action]
+      const title = `${actionText}提现`
+      if (action === 'reject') {
+        this.$prompt('请输入驳回备注', title, {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          inputType: 'textarea',
+          inputValidator: value => Boolean(value && value.trim()),
+          inputErrorMessage: '请输入驳回备注'
+        }).then(({ value }) => {
+          this.submitAudit(row, action, value)
+        }).catch(() => {})
+        return
+      }
+
+      this.$confirm(`确认${actionText}该提现吗?`, title, {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: action === 'paid' ? 'warning' : 'info'
+      }).then(() => {
+        this.submitAudit(row, action, '')
+      }).catch(() => {})
+    },
+    submitAudit(row, action, remark) {
+      this.auditLoadingId = `${row.id}-${action}`
+      auditAgentWithdrawal({
+        id: row.id,
+        action,
+        remark
+      })
+        .then(() => {
+          this.$message.success(`${this.actionTextMap[action]}成功`)
+          this.getList()
+        })
+        .finally(() => {
+          this.auditLoadingId = ''
+        })
     }
   }
 }
@@ -369,11 +398,28 @@ export default {
   ::v-deep .withdraw-row-tint td {
     background: #e9fbfb;
   }
+}
+
+.account-cell {
+  white-space: normal;
+  word-break: break-all;
+  line-height: 1.55;
+}
+
+.row-actions {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  gap: 4px 10px;
+
+  .el-button {
+    margin: 0;
+    color: #05a58f;
+    font-weight: 600;
+  }
 
-  ::v-deep .el-tag--success {
-    color: #18c80a;
-    border-color: #c8efbf;
-    background-color: #effceb;
+  .reject-button {
+    color: #f56c6c;
   }
 }
 

Некоторые файлы не были показаны из-за большого количества измененных файлов