This commit is contained in:
2025-11-27 20:08:02 +08:00
parent a188dd3734
commit 339a21827c
10 changed files with 368 additions and 47 deletions
+1
View File
@@ -6,6 +6,7 @@
"userNameDup":-4, "userNameDup":-4,
"userNameNoFund":-41, "userNameNoFund":-41,
"userPassIncorrect":-42, "userPassIncorrect":-42,
"userEmailFormatError":-43,
"userCookieError":-44, "userCookieError":-44,
"userCookieNotFund":-44, "userCookieNotFund":-44,
"userCookieExpired":-44 "userCookieExpired":-44
+9
View File
@@ -4,6 +4,7 @@ import (
"crypto/md5" "crypto/md5"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"regexp"
"time" "time"
) )
@@ -81,3 +82,11 @@ func CheckCookiesAndUpdate(cookie *TabCookie_) bool {
} }
//return false //return false
} }
// 判断邮箱是否合法
func IsEmailValid(email string) bool {
// 正则表达式(覆盖 99% 常见邮箱格式)
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
regex := regexp.MustCompile(pattern)
return regex.MatchString(email)
}
+67
View File
@@ -71,6 +71,15 @@ type From_user_updateinfo struct {
Birthday string `json:"birthday"` Birthday string `json:"birthday"`
} }
type From_user_changeemail struct {
Newemail string `json:"newemail"`
}
type From_user_changepass struct {
Oldpass string `json:"oldpass"`
Newpass string `json:"newpass"`
}
func AuthenticationAuthority(ctx *gin.Context) (bool, models.TabUser_, map[string]interface{}) { func AuthenticationAuthority(ctx *gin.Context) (bool, models.TabUser_, map[string]interface{}) {
var user models.TabUser_ var user models.TabUser_
@@ -118,6 +127,64 @@ func ApiUser(r *gin.RouterGroup) {
r.POST("/test", func(ctx *gin.Context) { r.POST("/test", func(ctx *gin.Context) {
ReturnJson(ctx, "apiOK", nil) ReturnJson(ctx, "apiOK", nil)
}) })
//修改用户密码
r.POST("/changePassword", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if isAuth {
var jsonData From_user_changepass
if err := mapstructure.Decode(data, &jsonData); err == nil {
//验证旧密码
fmt.Println(user)
//转换旧密码
olduser := models.TabUser_{
Pass: jsonData.Oldpass,
Salt: user.Salt,
}
models.HashUserPass(&olduser)
if olduser.Pass == user.Pass {
//旧密码正确,更新新密码
var userupdate models.TabUser_
userupdate.Pass = jsonData.Newpass
userupdate.Salt = models.RandStr32()
models.HashUserPass(&userupdate)
models.DB.Model(&user).Updates(&userupdate)
ReturnJson(ctx, "apiOK", nil)
} else {
//旧密码错误
ReturnJson(ctx, "userPassIncorrect", nil)
}
} else {
ReturnJson(ctx, "jsonErr", nil)
}
}
})
//更新用户邮箱
r.POST("/changeEmail", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx)
if isAuth {
var jsonData From_user_changeemail
if err := mapstructure.Decode(data, &jsonData); err == nil {
//判断新邮箱格式
if models.IsEmailValid(jsonData.Newemail) {
var userupdate models.TabUser_
userupdate.Email = jsonData.Newemail
models.DB.Model(&user).Updates(&userupdate)
ReturnJson(ctx, "apiOK", nil)
} else {
ReturnJson(ctx, "userEmailFormatError", nil)
}
} else {
ReturnJson(ctx, "jsonErr", nil)
}
}
})
//更新用户info //更新用户info
r.POST("/updateInfo", func(ctx *gin.Context) { r.POST("/updateInfo", func(ctx *gin.Context) {
isAuth, user, data := AuthenticationAuthority(ctx) isAuth, user, data := AuthenticationAuthority(ctx)
+8 -1
View File
@@ -6,6 +6,7 @@
"register": "Register" "register": "Register"
}, },
"message": { "message": {
"functionality_not_yet_developed":"Functionality not yet developed",
"hello": "Hello", "hello": "Hello",
"welcome": "Welcome", "welcome": "Welcome",
"dark_mode": "Enable dark mode", "dark_mode": "Enable dark mode",
@@ -47,7 +48,13 @@
"preferences": "Preferences", "preferences": "Preferences",
"administrator": "Administrator", "administrator": "Administrator",
"select_date":"Select a date", "select_date":"Select a date",
"save_ok":"Saved successfully" "save_ok":"Saved successfully",
"change_ok":"Changed successfully",
"type_old_pass":"Enter old password",
"type_new_pass":"Enter new password",
"type_cof_pass":"Confirm new password",
"old_pass_incorrect":"Old password is incorrect",
"confirm_password_incorrect":"Confirm password is incorrect"
}, },
"settings": { "settings": {
"account_settings": "Account Settings", "account_settings": "Account Settings",
+8 -1
View File
@@ -6,6 +6,7 @@
"register": "注册" "register": "注册"
}, },
"message": { "message": {
"functionality_not_yet_developed":"功能未开发",
"hello": "你好", "hello": "你好",
"welcome": "欢迎", "welcome": "欢迎",
"dark_mode": "深色模式", "dark_mode": "深色模式",
@@ -47,7 +48,13 @@
"preferences": "偏好设置", "preferences": "偏好设置",
"administrator": "管理员", "administrator": "管理员",
"select_date":"选择日期", "select_date":"选择日期",
"save_ok":"保存成功" "save_ok":"保存成功",
"change_ok":"更改成功",
"type_old_pass":"输入旧密码",
"type_new_pass":"输入新密码",
"type_cof_pass":"确认新密码",
"old_pass_incorrect":"旧密码不正确",
"confirm_password_incorrect":"确认密码不正确"
}, },
"settings": { "settings": {
"account_settings": "个人设置", "account_settings": "个人设置",
+3
View File
@@ -86,6 +86,8 @@ export const useUserStore = defineStore("user", () => {
getUserInfoFromCookie(); getUserInfoFromCookie();
}; };
const cookieUpdata = (cookie) => { const cookieUpdata = (cookie) => {
userCookie.value = cookie; userCookie.value = cookie;
myfuncs.saveJsonT("userCookie", cookie); myfuncs.saveJsonT("userCookie", cookie);
@@ -118,6 +120,7 @@ export const useUserStore = defineStore("user", () => {
isLoggedIn, isLoggedIn,
getUserAvatarPath, getUserAvatarPath,
getUserBirthday, getUserBirthday,
getUserInfoFromCookie,
logout, logout,
login, login,
loginFromStoreCookie, loginFromStoreCookie,
+2 -23
View File
@@ -7,32 +7,11 @@ import imageCropper from "@/components/imageCropper.vue";
const user = useUserStore(); const user = useUserStore();
const mos = ref(); const mos = ref();
function t() {
console.log("test");
my_network_func.postJson(
"/users/test",
{
a: "1",
},
(a) => {
console.log(a);
}
);
}
function tt(){
mos.value?.showAlert("danger", "123123", 5000);
}
</script> </script>
<template> <template>
<main>
1112
<button @click="t">222</button>
{{ user.userCookie }}
<button @click="tt">333</button>
{{ user.userInfo }}
</main>
<imageCropper />
<MyOffcanvas ref="mos" /> <MyOffcanvas ref="mos" />
</template> </template>
@@ -117,6 +117,9 @@ function functionupdataTitle() {
} }
onMounted(() => { onMounted(() => {
functionupdataTitle(); functionupdataTitle();
if (userStore.isLoggedIn) {
router.push("/");
}
}); });
// 监听语言变化,更新标题 // 监听语言变化,更新标题
watch(locale, () => { watch(locale, () => {
@@ -167,7 +167,11 @@ watch(locale, () => {
<span class="input-group-text"> <span class="input-group-text">
<div <div
class="link-secondary" class="link-secondary"
title="Show password" :title="
isShowPassword
? t('message.hidden_Password')
: t('message.show_password')
"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
> >
<!-- Download SVG icon from http://tabler-icons.io/i/eye --> <!-- Download SVG icon from http://tabler-icons.io/i/eye -->
@@ -7,16 +7,32 @@ import imageCropper from "@/components/imageCropper.vue";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import { my_network_func } from "@/my_network_func"; import { my_network_func } from "@/my_network_func";
import MyOffcanvas from "@/components/MyOffcanvas.vue"; import MyOffcanvas from "@/components/MyOffcanvas.vue";
import { myfuncs } from "@/myfunc";
import { useRouter } from "vue-router";
const mos = ref(); const mos = ref();
const { t } = useI18n(); const { t, locale } = useI18n();
const router = useRouter();
const birthday = ref(); const birthday = ref();
const username = ref(); const username = ref();
const userremark = ref(); const userremark = ref();
const emailInput = ref();
const userStore = useUserStore(); const userStore = useUserStore();
const oldPassInput = ref();
const newPassInput = ref();
const cnfPassInput = ref();
const isShowPassword = ref(false);
function togglePasswordVisibility() {
isShowPassword.value = !isShowPassword.value;
}
function updataInfo() { function updataInfo() {
let isDataErr = false; let isDataErr = false;
@@ -44,13 +60,13 @@ function updataInfo() {
} }
if (isDataErr) { if (isDataErr) {
console.log("用户信息有误,无法保存"); //console.log("用户信息有误,无法保存");
return; return;
} }
console.log("保存用户信息"); // console.log("保存用户信息");
console.log("用户名:", usernameValue); // console.log("用户名:", usernameValue);
console.log("备注:", userremarkValue); // console.log("备注:", userremarkValue);
console.log("生日:", birthdayValue); // console.log("生日:", birthdayValue);
my_network_func.postJson( my_network_func.postJson(
"/users/updateInfo", "/users/updateInfo",
{ {
@@ -59,16 +75,14 @@ function updataInfo() {
birthday: birthdayValue, birthday: birthdayValue,
}, },
(r) => { (r) => {
console.log(r); //console.log(r);
switch (r.statusCode) { switch (r.statusCode) {
case 200: case 200:
switch (r.data.err_code) { switch (r.data.err_code) {
case 0: case 0:
mos.value?.showAlert( mos.value?.showAlert("success", t("message.save_ok"), 1000);
"success", // 更新用户信息到store
t("message.save_ok"), userStore.getUserInfoFromCookie();
1000
);
break; break;
default: default:
mos.value?.showAlert("danger", t("message.server_error"), 5000); mos.value?.showAlert("danger", t("message.server_error"), 5000);
@@ -83,10 +97,167 @@ function updataInfo() {
); );
} }
function changeEmail() {
if (emailInput.value.value == "") {
emailInput.value.classList.add("is-invalid");
mos.value?.showAlert("warning", t("message.please_enter_your_email"), 3000);
return;
} else {
emailInput.value.classList.remove("is-invalid");
}
//判断是否是合法邮箱
if (myfuncs.isValidEmail(emailInput.value.value) == false) {
emailInput.value.classList.add("is-invalid");
mos.value?.showAlert("danger", t("message.this_not_email"), 3000);
return;
} else {
emailInput.value.classList.remove("is-invalid");
}
my_network_func.postJson(
"/users/changeEmail",
{
newemail: emailInput.value.value,
},
(r) => {
switch (r.statusCode) {
case 200:
switch (r.data.err_code) {
case 0:
mos.value?.showAlert("success", t("message.change_ok"), 5000);
// 更新用户信息到store
userStore.getUserInfoFromCookie();
break;
case -43:
emailInput.value.classList.add("is-invalid");
mos.value?.showAlert("danger", t("message.this_not_email"), 3000);
break;
default:
mos.value?.showAlert("danger", t("message.server_error"), 5000);
break;
}
break;
default:
mos.value?.showAlert("danger", t("message.network_err"), 5000);
break;
}
}
);
}
function changePassword() {
let isDataErr = false;
let oldPass = oldPassInput.value.value;
let newPass = newPassInput.value.value;
let cnfPass = cnfPassInput.value.value;
oldPassInput.value.classList.remove("is-invalid");
newPassInput.value.classList.remove("is-invalid");
cnfPassInput.value.classList.remove("is-invalid");
if (!oldPass) {
isDataErr = true;
oldPassInput.value.classList.add("is-invalid");
}
if (!newPass) {
isDataErr = true;
newPassInput.value.classList.add("is-invalid");
}
if (!cnfPass) {
isDataErr = true;
cnfPassInput.value.classList.add("is-invalid");
}
if (newPass !== cnfPass) {
isDataErr = true;
newPassInput.value.classList.add("is-invalid");
cnfPassInput.value.classList.add("is-invalid");
mos.value?.showAlert(
"warning",
t("message.confirm_password_incorrect"),
3000
);
}
if (isDataErr) {
return;
}
my_network_func.postJson(
"/users/changePassword",
{
oldpass: oldPass,
newpass: newPass,
},
(r) => {
switch (r.statusCode) {
case 200:
switch (r.data.err_code) {
case 0:
// 清空输入框
oldPassInput.value.value = "";
newPassInput.value.value = "";
cnfPassInput.value.value = "";
mos.value?.showAlert(
"success",
t("message.change_ok"),
2000,
() => {
userStore.logout();
router.push("/");
}
);
break;
case -42:
oldPassInput.value.classList.add("is-invalid");
mos.value?.showAlert(
"danger",
t("message.old_pass_incorrect"),
3000
);
break;
default:
mos.value?.showAlert("danger", t("message.server_error"), 5000);
break;
}
break;
default:
mos.value?.showAlert("danger", t("message.network_err"), 5000);
break;
}
}
);
}
function changeAvatar() {
mos.value?.showAlert(
"info",
t("message.functionality_not_yet_developed"),
5000
);
}
function functionupdataTitle() {
document.title = "Operations." + t("settings.account_settings");
}
// 监听语言变化,更新标题
watch(locale, () => {
functionupdataTitle();
});
onMounted(() => { onMounted(() => {
//console.log("account mounted"); //console.log("account mounted");
//username.value.value="Kevin"; //username.value.value="Kevin";
functionupdataTitle();
if (!userStore.isLoggedIn) {
router.push("/login");
}
}); });
</script> </script>
@@ -119,7 +290,7 @@ onMounted(() => {
</div> </div>
<!-- <imageCropper /> --> <!-- <imageCropper /> -->
<div class="col-auto"> <div class="col-auto">
<button class="btn"> <button class="btn" @click="changeAvatar">
{{ t("settings.change_avatar") }} {{ t("settings.change_avatar") }}
</button> </button>
</div> </div>
@@ -150,7 +321,10 @@ onMounted(() => {
</div> </div>
<div class="col-md"> <div class="col-md">
<div class="form-label">{{ t("settings.birthday") }}</div> <div class="form-label">{{ t("settings.birthday") }}</div>
<datePicker ref="birthday" :setdef="userStore.getUserBirthday()"/> <datePicker
ref="birthday"
:setdef="userStore.getUserBirthday()"
/>
</div> </div>
<div> <div>
<button class="btn" @click="updataInfo"> <button class="btn" @click="updataInfo">
@@ -162,22 +336,89 @@ onMounted(() => {
<div> <div>
<div class="row g-2"> <div class="row g-2">
<div class="col-auto"> <div class="col-auto">
<input type="text" class="form-control w-auto" /> <input
ref="emailInput"
type="text"
class="form-control w-auto"
:value="userStore.user.Email"
:placeholder="t('message.your_email_address')"
/>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button class="btn"> <button class="btn" @click="changeEmail">
{{ t("settings.change_email") }} {{ t("settings.change_email") }}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<h3 class="card-title mt-4">{{ t("settings.password") }}</h3> <h3 class="card-title mt-4">
<input type="password" class="form-control w-auto mb-1" /> {{ t("settings.password") }}
<input type="password" class="form-control w-auto mb-1" /> <!-- Download SVG icon from http://tabler-icons.io/i/eye -->
<input type="password" class="form-control w-auto mb-1" /> <svg
v-if="!isShowPassword"
@click="togglePasswordVisibility"
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6"
/>
</svg>
<svg
v-if="isShowPassword"
@click="togglePasswordVisibility"
xmlns="http://www.w3.org/2000/svg"
class="icon"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10.585 10.587a2 2 0 0 0 2.829 2.828" />
<path
d="M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87"
/>
<path d="M3 3l18 18" />
</svg>
</h3>
<input
ref="oldPassInput"
:type="isShowPassword ? 'text' : 'password'"
class="form-control w-auto mb-1"
:placeholder="t('message.type_old_pass')"
/>
<input
ref="newPassInput"
:type="isShowPassword ? 'text' : 'password'"
class="form-control w-auto mb-1"
:placeholder="t('message.type_new_pass')"
/>
<input
ref="cnfPassInput"
:type="isShowPassword ? 'text' : 'password'"
class="form-control w-auto mb-1"
:placeholder="t('message.type_cof_pass')"
/>
<div> <div>
<button class="btn"> <button class="btn" @click="changePassword">
{{ t("settings.set_new_password") }} {{ t("settings.set_new_password") }}
</button> </button>
</div> </div>