refactor: migrate wechat to user attributes and enhance users list

Migrate the hardcoded wechat field to the new extensible user
attributes system and improve the users management UI.

Migration:
- Add migration 019 to move wechat data to user_attribute_values
- Remove wechat field from User entity, DTOs, and API contracts
- Clean up wechat-related code from backend and frontend

UsersView enhancements:
- Add text labels to action buttons (Filter Settings, Column Settings,
  Attributes Config) for better UX
- Change status column to show colored dot + Chinese text instead of
  English text
- Add dynamic attribute columns support with batch loading
- Add column visibility settings with localStorage persistence
- Add filter settings modal for search and filter preferences
- Update i18n translations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Edric Li
2026-01-01 18:59:38 +08:00
parent f44cf642bc
commit 404bf0f8d2
30 changed files with 1390 additions and 462 deletions

View File

@@ -434,9 +434,7 @@ export default {
administrator: 'Administrator',
user: 'User',
username: 'Username',
wechat: 'WeChat ID',
enterUsername: 'Enter username',
enterWechat: 'Enter WeChat ID',
editProfile: 'Edit Profile',
updateProfile: 'Update Profile',
updating: 'Updating...',
@@ -565,12 +563,10 @@ export default {
email: 'Email',
password: 'Password',
username: 'Username',
wechat: 'WeChat ID',
notes: 'Notes',
enterEmail: 'Enter email',
enterPassword: 'Enter password',
enterUsername: 'Enter username (optional)',
enterWechat: 'Enter WeChat ID (optional)',
enterNotes: 'Enter notes (admin only)',
notesHint: 'This note is only visible to administrators',
enterNewPassword: 'Enter new password (optional)',
@@ -582,7 +578,6 @@ export default {
columns: {
user: 'User',
username: 'Username',
wechat: 'WeChat ID',
notes: 'Notes',
role: 'Role',
subscriptions: 'Subscriptions',
@@ -653,7 +648,67 @@ export default {
failedToDeposit: 'Failed to deposit',
failedToWithdraw: 'Failed to withdraw',
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal'
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal',
// Settings Dropdowns
filterSettings: 'Filter Settings',
columnSettings: 'Column Settings',
filterValue: 'Enter value',
// User Attributes
attributes: {
title: 'User Attributes',
description: 'Configure custom user attribute fields',
configButton: 'Attributes',
addAttribute: 'Add Attribute',
editAttribute: 'Edit Attribute',
deleteAttribute: 'Delete Attribute',
deleteConfirm: "Are you sure you want to delete attribute '{name}'? All user values for this attribute will be deleted.",
noAttributes: 'No custom attributes',
noAttributesHint: 'Click the button above to add custom attributes',
key: 'Attribute Key',
keyHint: 'For programmatic reference, only letters, numbers and underscores',
name: 'Display Name',
nameHint: 'Name shown in forms',
type: 'Attribute Type',
fieldDescription: 'Description',
fieldDescriptionHint: 'Description text for the attribute',
placeholder: 'Placeholder',
placeholderHint: 'Placeholder text for input field',
required: 'Required',
enabled: 'Enabled',
options: 'Options',
optionsHint: 'For select/multi-select types',
addOption: 'Add Option',
optionValue: 'Option Value',
optionLabel: 'Display Text',
validation: 'Validation Rules',
minLength: 'Min Length',
maxLength: 'Max Length',
min: 'Min Value',
max: 'Max Value',
pattern: 'Regex Pattern',
patternMessage: 'Validation Error Message',
types: {
text: 'Text',
textarea: 'Textarea',
number: 'Number',
email: 'Email',
url: 'URL',
date: 'Date',
select: 'Select',
multi_select: 'Multi-Select'
},
created: 'Attribute created successfully',
updated: 'Attribute updated successfully',
deleted: 'Attribute deleted successfully',
reordered: 'Attribute order updated successfully',
failedToLoad: 'Failed to load attributes',
failedToCreate: 'Failed to create attribute',
failedToUpdate: 'Failed to update attribute',
failedToDelete: 'Failed to delete attribute',
failedToReorder: 'Failed to update order',
keyExists: 'Attribute key already exists',
dragToReorder: 'Drag to reorder'
}
},
// Groups

View File

@@ -430,9 +430,7 @@ export default {
administrator: '管理员',
user: '用户',
username: '用户名',
wechat: '微信号',
enterUsername: '输入用户名',
enterWechat: '输入微信号',
editProfile: '编辑个人资料',
updateProfile: '更新资料',
updating: '更新中...',
@@ -583,12 +581,10 @@ export default {
email: '邮箱',
password: '密码',
username: '用户名',
wechat: '微信号',
notes: '备注',
enterEmail: '请输入邮箱',
enterPassword: '请输入密码',
enterUsername: '请输入用户名(选填)',
enterWechat: '请输入微信号(选填)',
enterNotes: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见',
enterNewPassword: '请输入新密码(选填)',
@@ -601,7 +597,6 @@ export default {
user: '用户',
email: '邮箱',
username: '用户名',
wechat: '微信号',
notes: '备注',
role: '角色',
subscriptions: '订阅分组',
@@ -655,8 +650,6 @@ export default {
emailPlaceholder: '请输入邮箱',
usernameLabel: '用户名',
usernamePlaceholder: '请输入用户名(选填)',
wechatLabel: '微信号',
wechatPlaceholder: '请输入微信号(选填)',
notesLabel: '备注',
notesPlaceholder: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见',
@@ -711,7 +704,67 @@ export default {
failedToDeposit: '充值失败',
failedToWithdraw: '退款失败',
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
insufficientBalance: '余额不足,退款后余额不能为负数'
insufficientBalance: '余额不足,退款后余额不能为负数',
// Settings Dropdowns
filterSettings: '筛选设置',
columnSettings: '列设置',
filterValue: '输入值',
// User Attributes
attributes: {
title: '用户属性配置',
description: '配置用户的自定义属性字段',
configButton: '属性配置',
addAttribute: '添加属性',
editAttribute: '编辑属性',
deleteAttribute: '删除属性',
deleteConfirm: "确定要删除属性 '{name}' 吗?所有用户的该属性值将被删除。",
noAttributes: '暂无自定义属性',
noAttributesHint: '点击上方按钮添加自定义属性',
key: '属性键',
keyHint: '用于程序引用,只能包含字母、数字和下划线',
name: '显示名称',
nameHint: '在表单中显示的名称',
type: '属性类型',
fieldDescription: '描述',
fieldDescriptionHint: '属性的说明文字',
placeholder: '占位符',
placeholderHint: '输入框的提示文字',
required: '必填',
enabled: '启用',
options: '选项配置',
optionsHint: '用于单选/多选类型',
addOption: '添加选项',
optionValue: '选项值',
optionLabel: '显示文本',
validation: '验证规则',
minLength: '最小长度',
maxLength: '最大长度',
min: '最小值',
max: '最大值',
pattern: '正则表达式',
patternMessage: '验证失败提示',
types: {
text: '单行文本',
textarea: '多行文本',
number: '数字',
email: '邮箱',
url: '链接',
date: '日期',
select: '单选',
multi_select: '多选'
},
created: '属性创建成功',
updated: '属性更新成功',
deleted: '属性删除成功',
reordered: '属性排序更新成功',
failedToLoad: '加载属性列表失败',
failedToCreate: '创建属性失败',
failedToUpdate: '更新属性失败',
failedToDelete: '删除属性失败',
failedToReorder: '更新排序失败',
keyExists: '属性键已存在',
dragToReorder: '拖拽排序'
}
},
// Groups Management