diff --git a/model/user.go b/model/user.go index 1dc633b156..6f30811ba0 100644 --- a/model/user.go +++ b/model/user.go @@ -10,6 +10,7 @@ import ( "github.com/songquanpeng/one-api/common/random" "gorm.io/gorm" "strings" + "time" ) const ( @@ -28,24 +29,25 @@ const ( // User if you add sensitive fields, don't forget to clean them in setupLogin function. // Otherwise, the sensitive information will be saved on local storage in plain text! type User struct { - Id int `json:"id"` - Username string `json:"username" gorm:"unique;index" validate:"max=12"` - Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"` - DisplayName string `json:"display_name" gorm:"index" validate:"max=20"` - Role int `json:"role" gorm:"type:int;default:1"` // admin, util - Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled - Email string `json:"email" gorm:"index" validate:"max=50"` - GitHubId string `json:"github_id" gorm:"column:github_id;index"` - WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"` - LarkId string `json:"lark_id" gorm:"column:lark_id;index"` - VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! - AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management - Quota int64 `json:"quota" gorm:"bigint;default:0"` - UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0;column:used_quota"` // used quota - RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number - Group string `json:"group" gorm:"type:varchar(32);default:'default'"` - AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"` - InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"` + Id int `json:"id"` + Username string `json:"username" gorm:"unique;index" validate:"max=12"` + Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"` + DisplayName string `json:"display_name" gorm:"index" validate:"max=20"` + Role int `json:"role" gorm:"type:int;default:1"` // admin, util + Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled + Email string `json:"email" gorm:"index" validate:"max=50"` + GitHubId string `json:"github_id" gorm:"column:github_id;index"` + WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"` + LarkId string `json:"lark_id" gorm:"column:lark_id;index"` + VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! + AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management + Quota int64 `json:"quota" gorm:"bigint;default:0"` + UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0;column:used_quota"` // used quota + RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number + Group string `json:"group" gorm:"type:varchar(32);default:'default'"` + AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"` + InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"` + ExpirationDate time.Time `json:"expiration_date" gorm:"type:datetime;column:expiration_date"` // Expiration date of the user's subscription or account. } func GetMaxUserId() int { diff --git a/web/berry/src/views/User/component/EditModal.js b/web/berry/src/views/User/component/EditModal.js index f6b533e2d1..6b6788c3d1 100644 --- a/web/berry/src/views/User/component/EditModal.js +++ b/web/berry/src/views/User/component/EditModal.js @@ -17,7 +17,8 @@ import { Select, MenuItem, IconButton, - FormHelperText + FormHelperText, + TextField } from '@mui/material'; import Visibility from '@mui/icons-material/Visibility'; @@ -44,6 +45,11 @@ const validationSchema = Yup.object().shape({ is: false, then: Yup.number().min(0, '额度 不能小于 0'), otherwise: Yup.number() + }), + expiration_date: Yup.date().when('group', { + is: (val) => val !== 'default', + then: Yup.date().required('到期时间 不能为空').nullable(), + otherwise: Yup.date().nullable() }) }); @@ -53,7 +59,8 @@ const originInputs = { display_name: '', password: '', group: 'default', - quota: 0 + quota: 0, + expiration_date: null }; const EditModal = ({ open, userId, onCancel, onOk }) => { @@ -125,156 +132,188 @@ const EditModal = ({ open, userId, onCancel, onOk }) => { }, [userId]); return ( - - - {userId ? '编辑用户' : '新建用户'} - - - - - {({ errors, handleBlur, handleChange, handleSubmit, touched, values, isSubmitting }) => ( -
- - 用户名 - - {touched.username && errors.username && ( - - {errors.username} - - )} - - - - 显示名称 - - {touched.display_name && errors.display_name && ( - - {errors.display_name} - - )} - - - - 密码 - - - {showPassword ? : } - - - } - aria-describedby="helper-text-channel-password-label" - /> - {touched.password && errors.password && ( - - {errors.password} - - )} - - - {values.is_edit && ( - <> - - 额度 + + + {userId ? '编辑用户' : '新建用户'} + + + + + {({ errors, handleBlur, handleChange, handleSubmit, setFieldValue, touched, values, isSubmitting }) => ( + + + 用户名 {renderQuotaWithPrompt(values.quota)}} - onBlur={handleBlur} - onChange={handleChange} - aria-describedby="helper-text-channel-quota-label" - disabled={values.unlimited_quota} + id="channel-username-label" + label="用户名" + type="text" + value={values.username} + name="username" + onBlur={handleBlur} + onChange={handleChange} + inputProps={{ autoComplete: 'username' }} + aria-describedby="helper-text-channel-username-label" /> + {touched.username && errors.username && ( + + {errors.username} + + )} + - {touched.quota && errors.quota && ( - - {errors.quota} - + + 显示名称 + + {touched.display_name && errors.display_name && ( + + {errors.display_name} + )} - - 分组 - - {touched.group && errors.group && ( - - {errors.group} - + aria-describedby="helper-text-channel-password-label" + /> + {touched.password && errors.password && ( + + {errors.password} + )} - - )} - - - - - - )} - - - + + {values.is_edit && ( + <> + + 额度 + {renderQuotaWithPrompt(values.quota)}} + onBlur={handleBlur} + onChange={handleChange} + aria-describedby="helper-text-channel-quota-label" + disabled={values.unlimited_quota} + /> + + {touched.quota && errors.quota && ( + + {errors.quota} + + )} + + + + 分组 + + {touched.group && errors.group && ( + + {errors.group} + + )} + + + )} + + {values.group !== 'default' && ( + + + {touched.expiration_date && errors.expiration_date && ( + + {errors.expiration_date} + + )} + + )} + + + + + + + )} +
+
+
); }; @@ -285,4 +324,4 @@ EditModal.propTypes = { userId: PropTypes.number, onCancel: PropTypes.func, onOk: PropTypes.func -}; +}; \ No newline at end of file