From 09469888b4bae66a2cc5891d218260db14510378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wei=5F=E4=BD=B3?= Date: Fri, 14 Nov 2025 18:05:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=AF=A6=E6=83=85=E5=BC=B9=E7=AA=97=E5=92=8C?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E9=A1=B5=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增UserDetailModal组件,支持用户基础信息、发票抬头和操作记录查看 - 完善用户管理API数据结构,添加用户类型字段和模拟数据优化 - 实现用户详情查看功能,支持完整信息展示 - 优化估值审核详情页样式,改进标签页和区块视觉效果 - 修复审核详情页面布局问题,提升用户体验 --- web/src/api/index.js | 211 +++++++++++------ .../user-list/UserDetailModal.vue | 222 ++++++++++++++++++ .../views/user-management/user-list/index.vue | 44 +++- .../audit/components/AuditDetail.vue | 40 +++- 4 files changed, 434 insertions(+), 83 deletions(-) create mode 100644 web/src/views/user-management/user-list/UserDetailModal.vue diff --git a/web/src/api/index.js b/web/src/api/index.js index c847945..875d817 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -276,6 +276,120 @@ const mockValuationDetails = valuationRecords.map((record) => ({ ...record, })) +const mockAppUsers = [ + { + id: 11111111, + phone: '15021982682', + wechat: 'f1498480844', + created_at: '2024-01-15T10:30:00Z', + notes: '测试用户1', + remaining_count: 1, + user_type: '体验用户', + }, + { + id: 11111112, + phone: '13800138002', + wechat: 'wx_limming2024', + created_at: '2024-02-20T14:20:00Z', + notes: '付费用户', + remaining_count: 5, + user_type: '付费用户', + }, + { + id: 11111113, + phone: '13800138003', + wechat: null, + created_at: '2024-03-10T08:45:00Z', + notes: null, + remaining_count: 0, + user_type: '体验用户', + }, + { + id: 11111114, + phone: '13800138004', + wechat: 'chenjun_vip', + created_at: '2024-04-05T11:30:00Z', + notes: 'VIP用户', + remaining_count: 10, + user_type: 'VIP', + }, + { + id: 11111115, + phone: '13800138005', + wechat: 'liuxia888', + created_at: '2024-05-12T16:15:00Z', + notes: '体验用户', + remaining_count: 3, + user_type: '体验用户', + }, + { + id: 11111116, + phone: '13800138006', + wechat: null, + created_at: '2024-06-18T09:00:00Z', + notes: '新注册用户', + remaining_count: 2, + user_type: '体验用户', + }, + { + id: 11111117, + phone: '13800138007', + wechat: 'zhaolei2024', + created_at: '2024-07-22T12:45:00Z', + notes: null, + remaining_count: 0, + user_type: '体验用户', + }, + { + id: 11111118, + phone: '13800138008', + wechat: 'sunmei_user', + created_at: '2024-08-30T15:20:00Z', + notes: '活跃用户', + remaining_count: 7, + user_type: 'VIP', + }, +] + +const defaultInvoiceHeaders = [ + { + company_name: '成都文创科技有限公司', + tax_number: '91510100MA7XYZ1234', + register_address: '四川省成都市高新区天府三街666号', + register_phone: '028-66666666', + bank_name: '招商银行成都分行', + bank_account: '6225 6666 8888 0000', + email: 'finance@scwenchuang.com', + }, + { + company_name: '天府文化发展有限公司', + tax_number: '91510100678912345K', + register_address: '四川省成都市武侯区科华北路88号', + register_phone: '028-12345678', + bank_name: '中国工商银行成都分行', + bank_account: '6212 8888 0000 9999', + email: 'invoice@tfculture.com', + }, +] + +const defaultOperationLogs = [ + { + time: '2025-10-31 18:30:30', + operator: 'admin', + records: ['剩余估值次数:0 -> 1', '类型:付费估值', '备注:新用户'], + }, + { + time: '2025-10-31 18:30:30', + operator: 'admin', + records: ['剩余估值次数:2 -> 1', '类型:付费估值', '备注:退款'], + }, + { + time: '2025-10-31 18:30:30', + operator: 'admin', + records: ['用户备注:111111111111 -> 22222222222222222222'], + }, +] + export default { login: (data) => request.post('/base/access_token', data, { noNeedToken: true }), getUserInfo: () => request.get('/base/userinfo'), @@ -317,76 +431,8 @@ export default { getAuditLogList: (params = {}) => request.get('/auditlog/list', { params }), // app users (客户端用户管理) - 使用现有的后端接口 getAppUserList: (params = {}) => { - // Mock 数据 - const mockUsers = [ - { - id: 11111111, - phone: '15021982682', - wechat: 'f1498480844', - created_at: '2024-01-15T10:30:00Z', - notes: '测试用户1', - remaining_count: 1 - }, - { - id: 11111112, - phone: '13800138002', - wechat: 'wx_limming2024', - created_at: '2024-02-20T14:20:00Z', - notes: '付费用户', - remaining_count: 5 - }, - { - id: 11111113, - phone: '13800138003', - wechat: null, - created_at: '2024-03-10T08:45:00Z', - notes: null, - remaining_count: 0 - }, - { - id: 11111114, - phone: '13800138004', - wechat: 'chenjun_vip', - created_at: '2024-04-05T11:30:00Z', - notes: 'VIP用户', - remaining_count: 10 - }, - { - id: 11111115, - phone: '13800138005', - wechat: 'liuxia888', - created_at: '2024-05-12T16:15:00Z', - notes: '体验用户', - remaining_count: 3 - }, - { - id: 11111116, - phone: '13800138006', - wechat: null, - created_at: '2024-06-18T09:00:00Z', - notes: '新注册用户', - remaining_count: 2 - }, - { - id: 11111117, - phone: '13800138007', - wechat: 'zhaolei2024', - created_at: '2024-07-22T12:45:00Z', - notes: null, - remaining_count: 0 - }, - { - id: 11111118, - phone: '13800138008', - wechat: 'sunmei_user', - created_at: '2024-08-30T15:20:00Z', - notes: '活跃用户', - remaining_count: 7 - } - ] - // 模拟分页和搜索 - let filteredUsers = [...mockUsers] + let filteredUsers = [...mockAppUsers] // 手机号搜索 if (params.phone) { @@ -415,13 +461,32 @@ export default { resolve({ data: paginatedUsers, total: filteredUsers.length, - page: page, - page_size: pageSize + page, + page_size: pageSize, }) - }, 300) // 模拟网络延迟 + }, 300) }) }, - getAppUserById: (params = {}) => request.get('/app-user/detail', { params }), + getAppUserById: (params = {}) => + new Promise((resolve) => { + const id = Number(params.id) + const user = mockAppUsers.find((item) => item.id === id) || {} + setTimeout(() => { + resolve({ + baseInfo: { + id: user.id, + phone: user.phone, + wechat: user.wechat, + register_time: user.created_at, + notes: user.notes, + remaining_count: user.remaining_count, + user_type: user.user_type || '体验用户', + }, + invoiceHeaders: defaultInvoiceHeaders, + operationLogs: defaultOperationLogs, + }) + }, 300) + }), createAppUser: (data = {}) => request.post('/app-user/register', data), updateAppUser: (data = {}) => request.post('/app-user/update', data), deleteAppUser: (params = {}) => request.delete('/app-user/delete', { params }), diff --git a/web/src/views/user-management/user-list/UserDetailModal.vue b/web/src/views/user-management/user-list/UserDetailModal.vue new file mode 100644 index 0000000..f287222 --- /dev/null +++ b/web/src/views/user-management/user-list/UserDetailModal.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/web/src/views/user-management/user-list/index.vue b/web/src/views/user-management/user-list/index.vue index 9b73279..750b4fc 100644 --- a/web/src/views/user-management/user-list/index.vue +++ b/web/src/views/user-management/user-list/index.vue @@ -16,6 +16,7 @@ import QueryBarItem from '@/components/query-bar/QueryBarItem.vue' import CrudModal from '@/components/table/CrudModal.vue' import CrudTable from '@/components/table/CrudTable.vue' import LimitSettingModal from './LimitSettingModal.vue' +import UserDetailModal from './UserDetailModal.vue' import { formatDate, renderIcon } from '@/utils' import { useCRUD } from '@/composables' @@ -32,6 +33,14 @@ const vPermission = resolveDirective('permission') const limitModalVisible = ref(false) const currentUser = ref(null) +const detailModalVisible = ref(false) +const detailLoading = ref(false) +const userDetail = ref({ + baseInfo: {}, + invoiceHeaders: [], + operationLogs: [], +}) + const { modalVisible, modalTitle, @@ -156,9 +165,30 @@ const columns = [ ] // 查看用户详情 -function handleViewDetail(row) { - // 这里可以跳转到详情页面或打开详情弹窗 - $message.info('查看用户详情功能待实现') +async function handleViewDetail(row) { + detailModalVisible.value = true + detailLoading.value = true + try { + const detail = await api.getAppUserById({ id: row.id }) + const baseInfoFromServer = detail?.baseInfo || {} + userDetail.value = { + baseInfo: { + ...baseInfoFromServer, + id: row.id, + phone: row.phone, + wechat: row.wechat, + register_time: row.created_at ? formatDate(row.created_at) : '-', + notes: row.notes, + remaining_count: row.remaining_count, + }, + invoiceHeaders: detail?.invoiceHeaders || [], + operationLogs: detail?.operationLogs || [], + } + } catch (error) { + $message.error('获取用户详情失败') + } finally { + detailLoading.value = false + } } // 次数设置 @@ -271,6 +301,12 @@ const validateForm = { :user-data="currentUser" @save="handleSaveLimitSetting" /> + + + - diff --git a/web/src/views/valuation/audit/components/AuditDetail.vue b/web/src/views/valuation/audit/components/AuditDetail.vue index 2bd6d5b..e498e69 100644 --- a/web/src/views/valuation/audit/components/AuditDetail.vue +++ b/web/src/views/valuation/audit/components/AuditDetail.vue @@ -236,7 +236,12 @@ const handleCertificateConfirm = (data) => { - +
@@ -387,19 +392,42 @@ const handleCertificateConfirm = (data) => { margin-bottom: 24px; } +.audit-tabs :deep(.n-tabs-nav) { + margin-bottom: 16px; +} + +.audit-tabs :deep(.n-tabs-tab) { + font-size: 16px; + font-weight: 500; + color: #1d2129; + padding: 0 24px; +} + +.audit-tabs :deep(.n-tabs-tab.n-tabs-tab--active) { + color: #ff6f3b; +} + +.audit-tabs :deep(.n-tabs-nav__line) { + background-color: #ff6f3b; + height: 3px; + border-radius: 999px; +} + .section-title { display: flex; align-items: center; - gap: 8px; + gap: 12px; font-weight: 600; margin-bottom: 12px; + font-size: 18px; + color: #1d2129; } .section-title .dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: #409eff; + width: 10px; + height: 18px; + border-radius: 4px; + background: #3b82f6; } .detail-section :deep(.n-data-table) {