From 3ac02879dedd591d13e52068c68de8ddd9238212 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Mon, 16 Jun 2025 03:20:54 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20admin-only=20"remark"?= =?UTF-8?q?=20support=20for=20users?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * backend - model: add `Remark` field (varchar 255, `json:"remark,omitempty"`); AutoMigrate handles schema change automatically - controller: * accept `remark` on user create/update endpoints * hide remark from regular users (`GetSelf`) by zero-ing the field before JSON marshalling * clarify inline comment explaining the omitempty behaviour * frontend (React / Semi UI) - AddUser.js & EditUser.js: add “Remark” input for admins - UsersTable.js: * remove standalone “Remark” column * show remark as a truncated Tag next to username with Tooltip for full text * import Tooltip component - i18n: reuse existing translations where applicable This commit enables administrators to label users with private notes while ensuring those notes are never exposed to the users themselves. --- controller/user.go | 3 +++ model/user.go | 2 ++ web/src/components/table/UsersTable.js | 22 ++++++++++++++++++++++ web/src/i18n/locales/en.json | 3 ++- web/src/pages/User/AddUser.js | 18 +++++++++++++++++- web/src/pages/User/EditUser.js | 17 +++++++++++++++++ 6 files changed, 63 insertions(+), 2 deletions(-) diff --git a/controller/user.go b/controller/user.go index d7eb42d7..ecaf2583 100644 --- a/controller/user.go +++ b/controller/user.go @@ -459,6 +459,9 @@ func GetSelf(c *gin.Context) { }) return } + // Hide admin remarks: set to empty to trigger omitempty tag, ensuring the remark field is not included in JSON returned to regular users + user.Remark = "" + c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", diff --git a/model/user.go b/model/user.go index 1b3a04b6..6a695457 100644 --- a/model/user.go +++ b/model/user.go @@ -41,6 +41,7 @@ type User struct { DeletedAt gorm.DeletedAt `gorm:"index"` LinuxDOId string `json:"linux_do_id" gorm:"column:linux_do_id;index"` Setting string `json:"setting" gorm:"type:text;column:setting"` + Remark string `json:"remark,omitempty" gorm:"type:varchar(255)" validate:"max=255"` } func (user *User) ToBaseUser() *UserBase { @@ -366,6 +367,7 @@ func (user *User) Edit(updatePassword bool) error { "display_name": newUser.DisplayName, "group": newUser.Group, "quota": newUser.Quota, + "remark": newUser.Remark, } if updatePassword { updates["password"] = newUser.Password diff --git a/web/src/components/table/UsersTable.js b/web/src/components/table/UsersTable.js index a027af59..d245c56f 100644 --- a/web/src/components/table/UsersTable.js +++ b/web/src/components/table/UsersTable.js @@ -26,6 +26,7 @@ import { Space, Table, Tag, + Tooltip, Typography } from '@douyinfe/semi-ui'; import { @@ -110,6 +111,27 @@ const UsersTable = () => { { title: t('用户名'), dataIndex: 'username', + render: (text, record) => { + const remark = record.remark; + if (!remark) { + return {text}; + } + const maxLen = 10; + const displayRemark = remark.length > maxLen ? remark.slice(0, maxLen) + '…' : remark; + return ( + + {text} + + +
+
+ {displayRemark} +
+ + + + ); + }, }, { title: t('分组'), diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index ba23ca5c..358c86bb 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1658,5 +1658,6 @@ "清除失效兑换码": "Clear invalid redemption codes", "确定清除所有失效兑换码?": "Are you sure you want to clear all invalid redemption codes?", "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。": "This will delete all used, disabled, and expired redemption codes, this operation cannot be undone.", - "选择过期时间(可选,留空为永久)": "Select expiration time (optional, leave blank for permanent)" + "选择过期时间(可选,留空为永久)": "Select expiration time (optional, leave blank for permanent)", + "请输入备注(仅管理员可见)": "Please enter a remark (only visible to administrators)" } \ No newline at end of file diff --git a/web/src/pages/User/AddUser.js b/web/src/pages/User/AddUser.js index 99620cfe..259a7750 100644 --- a/web/src/pages/User/AddUser.js +++ b/web/src/pages/User/AddUser.js @@ -16,6 +16,7 @@ import { IconClose, IconKey, IconUserAdd, + IconEdit, } from '@douyinfe/semi-icons'; import { useTranslation } from 'react-i18next'; @@ -27,10 +28,11 @@ const AddUser = (props) => { username: '', display_name: '', password: '', + remark: '', }; const [inputs, setInputs] = useState(originInputs); const [loading, setLoading] = useState(false); - const { username, display_name, password } = inputs; + const { username, display_name, password, remark } = inputs; const handleInputChange = (name, value) => { setInputs((inputs) => ({ ...inputs, [name]: value })); @@ -175,6 +177,20 @@ const AddUser = (props) => { required />
+ +
+ {t('备注')} + handleInputChange('remark', value)} + value={remark} + autoComplete="off" + size="large" + className="!rounded-lg" + prefix={} + showClear + /> +
diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js index dceb670a..8c028d74 100644 --- a/web/src/pages/User/EditUser.js +++ b/web/src/pages/User/EditUser.js @@ -22,6 +22,7 @@ import { IconLink, IconUserGroup, IconPlus, + IconEdit, } from '@douyinfe/semi-icons'; import { useTranslation } from 'react-i18next'; @@ -42,6 +43,7 @@ const EditUser = (props) => { email: '', quota: 0, group: 'default', + remark: '', }); const [groupOptions, setGroupOptions] = useState([]); const { @@ -55,6 +57,7 @@ const EditUser = (props) => { email, quota, group, + remark, } = inputs; const handleInputChange = (name, value) => { setInputs((inputs) => ({ ...inputs, [name]: value })); @@ -247,6 +250,20 @@ const EditUser = (props) => { showClear /> + +
+ {t('备注')} + handleInputChange('remark', value)} + value={remark} + autoComplete="off" + size="large" + className="!rounded-lg" + prefix={} + showClear + /> +