Просмотр исходного кода

fix: 后台管理列表页统一显示用户名而非用户ID

- OperationLogs/VirtualTeamDetail/FamilyEarnings/ActivityRegistrationReview/Activities: 删除冗余userId列(已有用户名数据)
- CouponGrantLog/DanExecution/CfValueManage/MembershipCenter/PendingRefund/ButlerAudit/ServiceRoleApplications/supply-system/Detail/HealthReportAudit: 新增resolveUserNames方法,通过searchUsers API将userId解析为用户名显示
Xiaogang Liao 1 месяц назад
Родитель
Сommit
12ee1bf8e4

+ 3 - 5
cfc-web/src/views/OperationLogs.vue

@@ -3,8 +3,8 @@
     <!-- 筛选条件 -->
     <el-card class="filter-card">
       <el-form :inline="true" :model="filterForm" class="filter-form">
-        <el-form-item label="用户ID">
-          <el-input v-model="filterForm.userId" placeholder="请输入用户ID" clearable></el-input>
+        <el-form-item label="用户名/ID">
+          <el-input v-model="filterForm.userId" placeholder="请输入用户名或ID" clearable></el-input>
         </el-form-item>
         <el-form-item label="操作类型">
           <el-select v-model="filterForm.operationType" placeholder="请选择" clearable>
@@ -37,7 +37,6 @@
     <el-card>
       <el-table :max-height="tableHeight" :data="logs" v-loading="loading" stripe>
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
-        <el-table-column prop="userId" label="用户ID" width="100"></el-table-column>
         <el-table-column prop="nickname" label="用户名" width="120"></el-table-column>
         <el-table-column prop="operationType" label="操作类型" width="100">
           <template slot-scope="scope">
@@ -84,8 +83,7 @@
     <el-dialog title="操作日志详情" :visible.sync="detailVisible" width="600px">
       <el-descriptions :column="1" border v-if="currentLog">
         <el-descriptions-item label="ID">{{ currentLog.id }}</el-descriptions-item>
-        <el-descriptions-item label="用户ID">{{ currentLog.userId }}</el-descriptions-item>
-        <el-descriptions-item label="用户名">{{ currentLog.nickname }}</el-descriptions-item>
+        <el-descriptions-item label="用户名">{{ currentLog.nickname || currentLog.userName || '-' }}</el-descriptions-item>
         <el-descriptions-item label="操作类型">{{ currentLog.operationType }}</el-descriptions-item>
         <el-descriptions-item label="操作描述">{{ currentLog.description }}</el-descriptions-item>
         <el-descriptions-item label="请求路径">{{ currentLog.requestPath }}</el-descriptions-item>

+ 1 - 2
cfc-web/src/views/admin/Activities.vue

@@ -134,8 +134,7 @@
     >
       <el-table :data="registrations" v-loading="regLoading" border stripe :max-height="300">
         <el-table-column prop="id" label="ID" width="60" />
-        <el-table-column prop="userId" label="用户ID" width="70" />
-        <el-table-column prop="childId" label="孩子ID" width="70" />
+        <el-table-column prop="registrantName" label="报名人" width="100" />
         <el-table-column label="状态" width="90">
           <template slot-scope="{ row }">
             <el-tag :type="row.status === 'approved' ? 'success' : row.status === 'rejected' ? 'danger' : 'warning'">

+ 0 - 2
cfc-web/src/views/admin/ActivityRegistrationReview.vue

@@ -19,8 +19,6 @@
     <div class="table-scroll-wrap-sm">
       <el-table :max-height="tableHeight" :data="filteredList" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="60" />
-      <el-table-column prop="userId" label="用户ID" width="70" />
-      <el-table-column prop="childId" label="孩子ID" width="70" />
       <el-table-column prop="registrantName" label="报名人" width="100" />
       <el-table-column prop="registrantPhone" label="手机号" width="120" />
       <el-table-column prop="status" label="状态" width="90">

+ 51 - 17
cfc-web/src/views/admin/CfValueManage.vue

@@ -3,7 +3,7 @@
     <div class="header">
       <h2>CF值(平台积分)管理</h2>
       <div class="filters">
-        <el-input v-model="searchUserId" placeholder="用户ID搜索" style="width: 200px;" clearable @keyup.enter.native="searchBalance" />
+        <el-input v-model="searchUserId" placeholder="用户ID/用户名搜索" style="width: 200px;" clearable @keyup.enter.native="searchBalance" />
         <el-button type="primary" @click="searchBalance">查询余额</el-button>
       </div>
     </div>
@@ -12,7 +12,9 @@
       <el-tab-pane label="余额列表" name="balances">
         <el-table :data="balanceList" v-loading="loadingBalance" border stripe>
           <el-table-column prop="id" label="ID" width="70" />
-          <el-table-column prop="userId" label="用户ID" width="100" />
+          <el-table-column label="用户" width="140">
+            <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+          </el-table-column>
           <el-table-column prop="familyId" label="家庭ID" width="100" />
           <el-table-column prop="totalEarned" label="累计获得" width="100" />
           <el-table-column prop="available" label="可用" width="80" />
@@ -35,7 +37,9 @@
         </div>
         <el-table :data="logList" v-loading="loadingLog" border stripe>
           <el-table-column prop="id" label="ID" width="70" />
-          <el-table-column prop="userId" label="用户ID" width="80" />
+          <el-table-column label="用户" width="120">
+            <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+          </el-table-column>
           <el-table-column prop="type" label="类型" width="100">
             <template slot-scope="{ row }">
               <el-tag :type="typeTag(row.type)" size="small">{{ row.type }}</el-tag>
@@ -55,7 +59,7 @@
 </template>
 
 <script>
-import { getCfValueBalance, getCfValueLogs, getCfValueAllBalances } from '@/api/admin'
+import { getCfValueBalance, getCfValueLogs, getCfValueAllBalances, searchUsers } from '@/api/admin'
 
 export default {
   data() {
@@ -81,16 +85,44 @@ export default {
     this.loadBalances()
   },
   methods: {
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      var self = this
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
     typeTag(type) {
       const map = { earn: 'success', spend: 'warning', freeze: 'info', unfreeze: 'info', withdraw: 'primary' }
       return map[type] || 'info'
     },
     loadBalances() {
       this.loadingBalance = true
-      getCfValueAllBalances({ page: this.balancePage, size: this.balanceSize }).then(res => {
+      getCfValueAllBalances({ page: this.balancePage, size: this.balanceSize }).then(async res => {
         if (res.code === 200) {
           this.balanceList = res.data.records || []
           this.balanceTotal = res.data.total || 0
+          await this.resolveUserNames(this.balanceList, 'userId')
         }
       }).finally(() => { this.loadingBalance = false })
     },
@@ -101,12 +133,13 @@ export default {
     searchBalance() {
       if (!this.searchUserId) { this.loadBalances(); return }
       this.loadingBalance = true
-      getCfValueBalance({ userId: this.searchUserId }).then(res => {
-        if (res.code === 200) {
-          this.balanceList = [res.data]
-          this.balanceTotal = 1
-        }
-      }).finally(() => { this.loadingBalance = false })
+getCfValueBalance({ userId: this.searchUserId }).then(async res => {
+          if (res.code === 200) {
+            this.balanceList = [res.data]
+            this.balanceTotal = 1
+            await this.resolveUserNames(this.balanceList, 'userId')
+          }
+        }).finally(() => { this.loadingBalance = false })
     },
     viewLogs(userId) {
       this.logUserId = String(userId)
@@ -117,12 +150,13 @@ export default {
       if (!this.logUserId) return
       this.loadingLog = true
       this.logPage = 1
-      getCfValueLogs({ userId: this.logUserId, page: 1, size: this.logSize }).then(res => {
-        if (res.code === 200) {
-          this.logList = res.data.records || []
-          this.logTotal = res.data.total || 0
-        }
-      }).finally(() => { this.loadingLog = false })
+getCfValueLogs({ userId: this.logUserId, page: 1, size: this.logSize }).then(async res => {
+          if (res.code === 200) {
+            this.logList = res.data.records || []
+            this.logTotal = res.data.total || 0
+            await this.resolveUserNames(this.logList, 'userId')
+          }
+        }).finally(() => { this.loadingLog = false })
     },
     pageLogs(page) {
       this.logPage = page

+ 30 - 2
cfc-web/src/views/admin/CouponGrantLog.vue

@@ -4,7 +4,7 @@
       <div slot="header" class="admin-page-header">
         <span class="admin-page-title">发券记录</span>
         <div class="admin-page-actions">
-          <el-input v-model="userId" placeholder="用户ID" clearable style="width: 140px" @keyup.enter.native="handleSearch" @clear="handleSearch" />
+          <el-input v-model="userId" placeholder="用户ID/用户名" clearable style="width: 140px" @keyup.enter.native="handleSearch" @clear="handleSearch" />
           <el-input v-model="couponId" placeholder="优惠券ID" clearable style="width: 140px" @keyup.enter.native="handleSearch" @clear="handleSearch" />
           <el-button type="primary" size="small" icon="el-icon-search" @click="handleSearch">查询</el-button>
         </div>
@@ -13,7 +13,9 @@
       <div class="table-scroll-wrap-sm">
         <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
           <el-table-column prop="id" label="ID" width="80" />
-          <el-table-column prop="userId" label="用户ID" width="100" />
+          <el-table-column label="用户" width="140">
+            <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+          </el-table-column>
           <el-table-column prop="couponId" label="优惠券ID" width="110" />
           <el-table-column label="发放类型" width="120">
             <template slot-scope="{ row }">
@@ -45,6 +47,7 @@
 
 <script>
 import { getCouponGrantLog } from '@/api/coupon'
+import { searchUsers } from '@/api/admin'
 
 export default {
   name: 'CouponGrantLog',
@@ -88,6 +91,7 @@ export default {
         if (res.data) {
           this.list = res.data
           this.total = this.list.length
+          await this.resolveUserNames(this.list, 'userId')
         }
       } catch (e) {
         console.error(e)
@@ -95,6 +99,30 @@ export default {
         this.loading = false
       }
     },
+    async resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      for (var i = 0; i < ids.length; i++) {
+        try {
+          var res = await searchUsers({ keyword: String(ids[i]) })
+          if (res.data && res.data.length > 0) {
+            var u = res.data[0]
+            nameMap[ids[i]] = u.nickname || u.realName || '用户' + ids[i]
+          } else {
+            nameMap[ids[i]] = '用户' + ids[i]
+          }
+        } catch (e) {
+          nameMap[ids[i]] = '用户' + ids[i]
+        }
+      }
+      items.forEach(function(item) {
+        item.userName = nameMap[item[idField]] || '用户' + item[idField]
+      })
+    },
     handleSearch() {
       this.page = 1
       this.loadData()

+ 38 - 4
cfc-web/src/views/admin/DanExecution.vue

@@ -19,7 +19,9 @@
         <el-table-column label="订单ID" prop="assessmentOrderId" width="100" />
         <el-table-column label="家庭ID" prop="familyId" width="100" />
         <el-table-column label="孩子ID" prop="childId" width="100" />
-        <el-table-column label="评估师ID" prop="assessorId" width="100" />
+        <el-table-column label="评估师" width="120">
+          <template slot-scope="{ row }">{{ row.assessorName || (row.assessorId ? 'ID:' + row.assessorId : '—') }}</template>
+        </el-table-column>
         <el-table-column label="评估师状态" width="130">
           <template slot-scope="{ row }">
             <div class="status-cell">
@@ -30,7 +32,9 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="规划师ID" prop="plannerId" width="100" />
+        <el-table-column label="规划师" width="120">
+          <template slot-scope="{ row }">{{ row.plannerName || (row.plannerId ? 'ID:' + row.plannerId : '—') }}</template>
+        </el-table-column>
         <el-table-column label="规划师状态" width="130">
           <template slot-scope="{ row }">
             <div v-if="row.plannerId" class="status-cell">
@@ -83,7 +87,7 @@
           <el-descriptions-item label="订单ID">{{ currentRecord.assessmentOrderId }}</el-descriptions-item>
           <el-descriptions-item label="家庭ID">{{ currentRecord.familyId }}</el-descriptions-item>
           <el-descriptions-item label="孩子ID">{{ currentRecord.childId }}</el-descriptions-item>
-          <el-descriptions-item label="评估师ID">{{ currentRecord.assessorId }}</el-descriptions-item>
+          <el-descriptions-item label="评估师">{{ currentRecord.assessorName || (currentRecord.assessorId ? 'ID:' + currentRecord.assessorId : '-') }}</el-descriptions-item>
           <el-descriptions-item label="评估师状态">
             <div class="status-cell">
               <i :class="executionIconClass(currentRecord.assessorStatus)" :style="{ color: executionIconColor(currentRecord.assessorStatus) }"></i>
@@ -94,7 +98,7 @@
           </el-descriptions-item>
           <el-descriptions-item label="评估师完成时间">{{ formatTime(currentRecord.assessorCompletedAt) }}</el-descriptions-item>
           <el-descriptions-item label="评估师备注">{{ currentRecord.assessorNotes || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="规划师ID">{{ currentRecord.plannerId || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="规划师">{{ currentRecord.plannerName || (currentRecord.plannerId ? 'ID:' + currentRecord.plannerId : '-') }}</el-descriptions-item>
           <el-descriptions-item label="规划师状态">
             <div v-if="currentRecord.plannerId" class="status-cell">
               <i :class="executionIconClass(currentRecord.plannerStatus)" :style="{ color: executionIconColor(currentRecord.plannerStatus) }"></i>
@@ -147,6 +151,8 @@
 </template>
 
 <script>
+import { searchUsers } from '@/api/admin'
+
 export default {
   name: 'DanExecution',
   data() {
@@ -186,6 +192,34 @@ export default {
         this.loading = false
       }
     },
+    async resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      items.forEach(function(item) {
+        if (item.assessorId && ids.indexOf(item.assessorId) === -1) ids.push(item.assessorId)
+      })
+      var nameMap = {}
+      for (var i = 0; i < ids.length; i++) {
+        try {
+          var res = await searchUsers({ keyword: String(ids[i]) })
+          if (res.data && res.data.length > 0) {
+            var u = res.data[0]
+            nameMap[ids[i]] = u.nickname || u.realName || '用户' + ids[i]
+          } else {
+            nameMap[ids[i]] = '用户' + ids[i]
+          }
+        } catch (e) {
+          nameMap[ids[i]] = '用户' + ids[i]
+        }
+      }
+      items.forEach(function(item) {
+        item.plannerName = nameMap[item.plannerId] || ''
+        item.assessorName = nameMap[item.assessorId] || ''
+      })
+    },
     async viewDetail(row) {
       this.detailDialogVisible = true
       this.detailLoading = true

+ 0 - 1
cfc-web/src/views/admin/FamilyEarnings.vue

@@ -62,7 +62,6 @@
 
         <h4 style="margin:20px 0 10px">成员贡献</h4>
         <el-table :data="detail.members || []" border stripe size="small" :max-height="300" v-if="detail && detail.members && detail.members.length > 0">
-          <el-table-column label="用户ID" prop="userId" width="80" />
           <el-table-column label="昵称" prop="nickname" min-width="120" />
           <el-table-column label="已分配" width="120">
             <template slot-scope="{ row }">{{ fenToYuan(row.allocatedAmount) }}</template>

+ 36 - 5
cfc-web/src/views/admin/MembershipCenter.vue

@@ -91,7 +91,9 @@
     </div>
     <el-table :data="upgradeRecords" v-loading="recordsLoading" border stripe :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="70" />
-        <el-table-column prop="userId" label="用户ID" width="80" />
+        <el-table-column label="用户" width="120">
+          <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+        </el-table-column>
         <el-table-column label="从" width="100">
           <template slot-scope="{ row }">
             <el-tag size="mini">{{ row.fromLevel || 'FREE' }}</el-tag>
@@ -195,6 +197,8 @@
 </template>
 
 <script>
+import { searchUsers } from '@/api/admin'
+
 export default {
   name: 'MembershipCenter',
   computed: {
@@ -306,10 +310,11 @@ export default {
           page: this.recordFilter.page,
           size: this.recordFilter.size
         })
-        if (res.data && res.data.code === 200) {
-          this.upgradeRecords = res.data.data.records || []
-          this.recordsTotal = res.data.data.total || 0
-        }
+if (res.data && res.data.code === 200) {
+            this.upgradeRecords = res.data.data.records || []
+            this.recordsTotal = res.data.data.total || 0
+            await this.resolveUserNames(this.upgradeRecords, 'userId')
+          }
       } catch (e) {
         this.$message && this.$message.error && this.$message.error('加载升级记录失败')
       } finally {
@@ -335,6 +340,32 @@ export default {
           this.featuresVisible = true
           break
       }
+    },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
     }
   }
 }

+ 31 - 4
cfc-web/src/views/admin/PendingRefund.vue

@@ -47,9 +47,9 @@
             {{ row.withdrawalRequestId || '-' }}
           </template>
         </el-table-column>
-        <el-table-column label="用户ID" width="80">
+        <el-table-column label="用户" width="120">
           <template slot-scope="{ row }">
-            {{ row.userId }}
+            {{ row.userName || '用户' + row.userId }}
           </template>
         </el-table-column>
         <el-table-column label="退款金额" width="120">
@@ -111,7 +111,7 @@
 </template>
 
 <script>
-import { getPendingRefundList, getPendingRefundSummary, cancelPendingRefund } from '@/api/admin'
+import { getPendingRefundList, getPendingRefundSummary, cancelPendingRefund, searchUsers } from '@/api/admin'
 
 export default {
   name: 'PendingRefund',
@@ -150,7 +150,7 @@ export default {
         console.error('加载待退款摘要失败', e)
       }
     },
-    async loadList() {
+async loadList() {
       this.loading = true
       try {
         const res = await getPendingRefundList({
@@ -162,6 +162,7 @@ export default {
         if (res.code === 200) {
           this.list = res.data.records || []
           this.total = res.data.total || 0
+          await this.resolveUserNames(this.list, 'userId')
         }
       } catch (e) {
         console.error('加载待退款列表失败', e)
@@ -169,6 +170,32 @@ export default {
         this.loading = false
       }
     },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
     handlePageChange(page) {
       this.currentPage = page
       this.loadList()

+ 0 - 1
cfc-web/src/views/admin/VirtualTeamDetail.vue

@@ -22,7 +22,6 @@
       </div>
       <el-table :max-height="tableHeight" :data="members" stripe v-loading="membersLoading">
         <el-table-column prop="id" label="关系ID" width="80"></el-table-column>
-        <el-table-column prop="userId" label="用户ID" width="100"></el-table-column>
         <el-table-column label="用户昵称" width="140">
           <template slot-scope="scope">
             <div style="display: flex; align-items: center; gap: 6px">

+ 31 - 1
cfc-web/src/views/admin/review/ButlerAudit.vue

@@ -11,7 +11,9 @@
     </div>
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="档案ID" width="80" />
-      <el-table-column prop="userId" label="用户ID" width="80" />
+      <el-table-column label="用户" width="120">
+          <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+        </el-table-column>
       <el-table-column prop="tier" label="等级" width="80" />
       <el-table-column prop="description" label="申请说明" min-width="200" show-overflow-tooltip />
       <el-table-column label="审核状态" width="100">
@@ -42,6 +44,7 @@
 
 <script>
 import { getButlerList, reviewButler } from '@/api/butler'
+import { searchUsers } from '@/api/admin'
 
 export default {
   name: 'ButlerAudit',
@@ -65,6 +68,7 @@ export default {
       try {
         const res = await getButlerList({ status: this.statusFilter || undefined, page: 1, size: 50 })
         this.list = res.data && res.data.records || res.data && res.data.list || res.data || []
+        await this.resolveUserNames(this.list, 'userId')
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },
@@ -77,6 +81,32 @@ export default {
       } catch (e) { this.$message.error('操作失败') }
     },
     showReject(row) { this.rejectTarget = row; this.rejectReason = ''; this.rejectDialogVisible = true },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
     async confirmReject() {
       if (!this.rejectReason) return this.$message.warning('请填写拒绝原因')
       try {

+ 32 - 3
cfc-web/src/views/admin/supply-system/Detail.vue

@@ -36,7 +36,9 @@
           </div>
           <el-table :data="members" stripe :max-height="tableHeight">
             <el-table-column prop="id" label="ID" width="60"></el-table-column>
-            <el-table-column prop="userId" label="用户ID" width="100"></el-table-column>
+            <el-table-column label="用户" width="140">
+              <template slot-scope="scope">{{ scope.row.userName || (scope.row.nickname || '用户' + scope.row.userId) }}</template>
+            </el-table-column>
             <el-table-column prop="role" label="角色" width="100">
               <template slot-scope="scope">
                 <el-tag :type="scope.row.role === 'admin' ? 'warning' : 'default'" size="small">
@@ -81,7 +83,7 @@
             <el-table-column label="指定收款方" width="140">
               <template slot-scope="scope">
                 <el-select v-model="scope.row.supplierId" size="mini" placeholder="选择供应商" @change="handleSelectPayee(scope.row)">
-                  <el-option v-for="m in members" :key="m.userId" :label="m.userId" :value="m.userId"></el-option>
+                  <el-option v-for="m in members" :key="m.userId" :label="(m.userName || m.nickname || '用户' + m.userId)" :value="m.userId"></el-option>
                 </el-select>
               </template>
             </el-table-column>
@@ -206,7 +208,7 @@ import { getSupplySystemDetail, getSupplySystemMembers, addSupplySystemMember,
     removeSupplySystemMember, changeSupplySystemMemberRole,
     getSupplySettlementList, getSupplySettlementDetailItems,
     createSupplySettlement, confirmSupplySettlement, getSupplyHierarchyTree,
-    setSupplyHierarchy, removeSupplyHierarchy } from '@/api/admin'
+    setSupplyHierarchy, removeSupplyHierarchy, searchUsers } from '@/api/admin'
 
 export default {
   name: 'SupplySystemDetail',
@@ -276,6 +278,7 @@ export default {
         const res = await getSupplySystemMembers(systemId)
         if (res.data) {
           this.members = res.data
+          await this.resolveUserNames(this.members, 'userId')
         }
       } catch (e) {
         console.error(e)
@@ -470,6 +473,32 @@ export default {
             ])
         ])
     },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
 }
 }
 </script>

+ 32 - 3
cfc-web/src/views/health/HealthReportAudit.vue

@@ -6,8 +6,8 @@
 
     <el-table :data="list" v-loading="loading" border stripe :max-height="tableHeight">
       <el-table-column prop="id" label="草稿ID" width="80" />
-      <el-table-column label="上传用户" width="120">
-        <template slot-scope="{ row }">{{ row.userId }}</template>
+      <el-table-column label="上传用户" width="140">
+        <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
       </el-table-column>
       <el-table-column prop="reportType" label="报告类型" width="120" />
       <el-table-column prop="originalFilename" label="文件名" min-width="200" show-overflow-tooltip />
@@ -46,7 +46,7 @@
       <div v-if="detail">
         <el-descriptions :column="2" border>
           <el-descriptions-item label="草稿ID">{{ detail.id }}</el-descriptions-item>
-          <el-descriptions-item label="上传用户ID">{{ detail.userId }}</el-descriptions-item>
+          <el-descriptions-item label="上传用户">{{ detail.userName || '用户' + detail.userId }}</el-descriptions-item>
           <el-descriptions-item label="报告类型">{{ detail.reportType }}</el-descriptions-item>
           <el-descriptions-item label="文件名">{{ detail.originalFilename }}</el-descriptions-item>
           <el-descriptions-item label="上传时间">{{ formatTime(detail.createdAt) }}</el-descriptions-item>
@@ -110,6 +110,8 @@
 </template>
 
 <script>
+import { searchUsers } from '@/api/admin'
+
 export default {
   name: 'HealthReportAudit',
   data() {
@@ -162,6 +164,7 @@ export default {
         const records = res.data?.data || []
         this.list = records
         this.total = records.length
+        await this.resolveUserNames(records, 'userId')
       } catch (e) {
         this.$message?.error?.('加载草稿列表失败')
       } finally {
@@ -177,6 +180,32 @@ export default {
       this.activeTab = 'indicators'
       this.detailVisible = true
     },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers({ keyword: String(id) })
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
     async handleConfirm(row) {
       try {
         await this.$confirm(`确认草稿 #${row.id}?确认后将生成正式报告。`, '提示', { type: 'warning' })

+ 34 - 1
cfc-web/src/views/service/ServiceRoleApplications.vue

@@ -6,7 +6,9 @@
       </div>
       <el-table :data="applications" v-loading="loading" style="width: 100%">
         <el-table-column prop="id" label="ID" width="60" />
-        <el-table-column prop="userId" label="用户ID" width="80" />
+        <el-table-column label="用户" width="120">
+          <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
+        </el-table-column>
         <el-table-column label="申请角色" width="200">
           <template slot-scope="{ row }">
             <el-tag v-for="role in parseRoles(row.roles)" :key="role" style="margin-right: 4px" size="mini">
@@ -77,6 +79,10 @@
 <script>
 import request from '@/utils/request'
 
+async function searchUsers(keyword) {
+    return await request({ url: '/api/admin/user/search', method: 'post', data: { keyword } })
+  }
+
 export default {
   data() {
     return {
@@ -104,6 +110,7 @@ export default {
       try {
         const res = await request({ url: '/api/service-role/admin/list', method: 'post', data: { status: 0 } })
         this.applications = res.data || []
+        await this.resolveUserNames(this.applications, 'userId')
       } catch (e) {
         this.$message.error('获取列表失败')
       } finally {
@@ -116,6 +123,32 @@ export default {
     parseImages(images) {
       try { return JSON.parse(images) } catch (e) { return [] }
     },
+    resolveUserNames(items, idField) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+      })
+      var nameMap = {}
+      return Promise.all(ids.map(function(id) {
+        return searchUsers(String(id))
+          .then(function(res) {
+            if (res.data && res.data.length > 0) {
+              var u = res.data[0]
+              nameMap[id] = u.nickname || u.realName || '用户' + id
+            } else {
+              nameMap[id] = '用户' + id
+            }
+          })
+          .catch(function() {
+            nameMap[id] = '用户' + id
+          })
+      })).then(function() {
+        items.forEach(function(item) {
+          item.userName = nameMap[item[idField]] || '用户' + item[idField]
+        })
+      })
+    },
     statusType(status) {
       return { 0: 'warning', 1: 'success', 2: 'danger' }[status] || 'info'
     },