Jelajahi Sumber

feat: 用户管理列表新增会员级别和推荐人列,编辑支持修改推荐人

- 列表新增 memberLevel 列(免费/家庭会员/服务商彩色标签)
- 列表新增 referrerName 列(通过searchUsers解析referrerId显示推荐人名称)
- 编辑弹窗新增推荐人下拉选择(远程搜索昵称/手机号/ID)
- 新增 resolveReferrerNames 批量解析方法,loadUsers后自动调用
Xiaogang Liao 2 bulan lalu
induk
melakukan
ec78e05881
1 mengubah file dengan 90 tambahan dan 7 penghapusan
  1. 90 7
      cfc-web/src/views/Users.vue

+ 90 - 7
cfc-web/src/views/Users.vue

@@ -93,6 +93,14 @@
           </template>
         </el-table-column>
         <el-table-column prop="familyId" label="家庭ID" width="100"></el-table-column>
+        <el-table-column label="会员级别" width="100">
+          <template slot-scope="{ row }">
+            <el-tag :type="memberLevelType(row.memberLevel)" size="small">{{ memberLevelLabel(row.memberLevel) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="推荐人" width="140">
+          <template slot-scope="{ row }">{{ row.referrerName || '—' }}</template>
+        </el-table-column>
         <el-table-column prop="createdAt" label="创建时间" width="180">
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
@@ -146,6 +154,26 @@
         <el-form-item label="手机号" prop="phone">
           <el-input v-model="editForm.phone" placeholder="请输入手机号"></el-input>
         </el-form-item>
+        <el-form-item label="推荐人">
+          <el-select
+            v-model="editForm.referrerId"
+            filterable
+            remote
+            reserve-keyword
+            clearable
+            placeholder="搜索推荐人(昵称/手机号/ID)"
+            :remote-method="searchUsersForReferrer"
+            :loading="userSearchLoading"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="u in userSearchResults"
+              :key="u.id"
+              :label="(u.nickname || u.realName || '') + ' (ID:' + u.id + ')'"
+              :value="u.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
         <el-form-item label="角色">
           <el-checkbox-group v-model="editForm.roles">
             <el-checkbox
@@ -200,7 +228,7 @@
 
 <script>
 import SortMixin from '@/mixins/SortMixin'
-import { getUserList, updateUser, deleteUser, resetUserPassword } from '@/api/admin'
+import { getUserList, updateUser, deleteUser, resetUserPassword, searchUsers } from '@/api/admin'
 
 import { getRoleLabel } from '@/utils/permissions'
 
@@ -241,7 +269,8 @@ export default {
         nickname: '',
         realName: '',
         phone: '',
-        roles: []
+        roles: [],
+        referrerId: null
       },
       editRules: {
         nickname: [
@@ -258,7 +287,9 @@ export default {
         { value: 'nutritionist', label: '营养师', disabled: false },
         { value: 'supplier', label: '供应商', disabled: false },
         { value: 'activity_manager', label: '活动管理员', disabled: false },
-      ]
+      ],
+      userSearchResults: [],
+      userSearchLoading: false,
     }
   },
   mounted() {
@@ -286,9 +317,10 @@ export default {
         }
         if (this.sortState.length > 0) params.sort = this.sortState
 
-        const res = await getUserList(params)
-        this.tableData = res.data.records || res.data || []
-        this.pagination.total = res.data.total || this.tableData.length
+const res = await getUserList(params)
+          this.tableData = res.data.records || res.data || []
+          this.pagination.total = res.data.total || this.tableData.length
+          await this.resolveReferrerNames(this.tableData)
       } catch (error) {
         this.$message.error(error.message || '加载用户列表失败')
       } finally {
@@ -325,7 +357,8 @@ export default {
         nickname: row.nickname,
         realName: row.realName || '',
         phone: row.phone || '',
-        roles: row.roles ? [...row.roles] : (row.role ? [row.role] : [])
+        roles: row.roles ? [...row.roles] : (row.role ? [row.role] : []),
+        referrerId: row.referrerId || null
       }
       this.editDialogVisible = true
     },
@@ -412,6 +445,56 @@ export default {
     formatDate(dateStr) {
       if (!dateStr) return '-'
       return String(dateStr).replace('T', ' ').substring(0, 19)
+    },
+    memberLevelLabel(level) {
+      const map = { FREE: '免费', FAMILY: '家庭会员', PROVIDER: '服务商' }
+      return map[level] || '免费'
+    },
+    memberLevelType(level) {
+      const map = { FREE: 'info', FAMILY: 'success', PROVIDER: 'warning' }
+      return map[level] || 'info'
+    },
+    async searchUsersForReferrer(query) {
+      if (!query) {
+        this.userSearchResults = []
+        return
+      }
+      this.userSearchLoading = true
+      try {
+        const res = await searchUsers({ keyword: query })
+        this.userSearchResults = res.data || []
+      } catch (e) {
+        this.userSearchResults = []
+      } finally {
+        this.userSearchLoading = false
+      }
+    },
+    resolveReferrerNames(items) {
+      if (!items || items.length === 0) return Promise.resolve()
+      var ids = []
+      items.forEach(function(item) {
+        if (item.referrerId && ids.indexOf(item.referrerId) === -1) ids.push(item.referrerId)
+      })
+      if (ids.length === 0) return Promise.resolve()
+      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.referrerName = nameMap[item.referrerId] || ''
+        })
+      })
     }
   }
 }