diff --git a/code/link56/link56-backend/src/main/java/cn/link56/auth/code/model/AuthCodePurpose.java b/code/link56/link56-backend/src/main/java/cn/link56/auth/code/model/AuthCodePurpose.java
index d28262e1..f66c33a3 100644
--- a/code/link56/link56-backend/src/main/java/cn/link56/auth/code/model/AuthCodePurpose.java
+++ b/code/link56/link56-backend/src/main/java/cn/link56/auth/code/model/AuthCodePurpose.java
@@ -6,5 +6,6 @@ package cn.link56.auth.code.model;
public enum AuthCodePurpose {
REGISTER, // 注册
LOGIN, // 登录
- RESET_PASSWORD // 找回密码
+ RESET_PASSWORD, // 找回密码
+ UPDATE_EMAIL // 更新邮箱
}
diff --git a/code/link56/link56-backend/src/main/java/cn/link56/auth/code/sender/EmailAuthCodeSender.java b/code/link56/link56-backend/src/main/java/cn/link56/auth/code/sender/EmailAuthCodeSender.java
index 0209d6ed..fca13229 100644
--- a/code/link56/link56-backend/src/main/java/cn/link56/auth/code/sender/EmailAuthCodeSender.java
+++ b/code/link56/link56-backend/src/main/java/cn/link56/auth/code/sender/EmailAuthCodeSender.java
@@ -23,12 +23,15 @@ public class EmailAuthCodeSender {
if (purpose.equals(AuthCodePurpose.LOGIN)){
subject = "登录";
templateHtml = "Login.html";
- }else if(purpose.equals(AuthCodePurpose.REGISTER)){
+ } else if (purpose.equals(AuthCodePurpose.REGISTER)){
subject = "注册";
templateHtml = "Register.html";
- }else if(purpose.equals(AuthCodePurpose.RESET_PASSWORD)){
+ } else if (purpose.equals(AuthCodePurpose.RESET_PASSWORD)){
subject = "找回密码";
templateHtml = "ForgetPassword.html";
+ } else if (purpose.equals(AuthCodePurpose.UPDATE_EMAIL)){
+ subject = "更新认证邮箱";
+ templateHtml = "UpdateEmail.html";
}
payload.put("Code",code);
diff --git a/code/link56/link56-backend/src/main/resources/templates/Login.html b/code/link56/link56-backend/src/main/resources/templates/Login.html
index 769bc125..80719e6c 100644
--- a/code/link56/link56-backend/src/main/resources/templates/Login.html
+++ b/code/link56/link56-backend/src/main/resources/templates/Login.html
@@ -480,7 +480,11 @@
您好
- 您正在登录我们的 link56 平台,系统已为您生成了登录验证码。该验证码仅在 10 分钟内有效。
+
+ 您正在 link56 平台进行登录操作,
+ 系统已为您生成邮箱验证码。
+ 该验证码仅在 10 分钟内有效。
+
[[${Code}]]
diff --git a/code/link56/link56-backend/src/main/resources/templates/Register.html b/code/link56/link56-backend/src/main/resources/templates/Register.html
index 32c0dd6c..20494120 100644
--- a/code/link56/link56-backend/src/main/resources/templates/Register.html
+++ b/code/link56/link56-backend/src/main/resources/templates/Register.html
@@ -480,7 +480,12 @@
您好
- 您正在注册我们的 link56 平台,系统已为您生成了注册验证码。该验证码仅在 10 分钟内有效。
+
+ 您正在 link56 平台进行注册操作,
+ 系统已为您生成邮箱验证码。
+ 该验证码仅在 10 分钟内有效。
+
+
[[${Code}]]
diff --git a/code/link56/link56-backend/src/main/resources/templates/UpdateEmail.html b/code/link56/link56-backend/src/main/resources/templates/UpdateEmail.html
new file mode 100644
index 00000000..ca5725c3
--- /dev/null
+++ b/code/link56/link56-backend/src/main/resources/templates/UpdateEmail.html
@@ -0,0 +1,526 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ link56
+
+ |
+
+
+
+
+
+
+
+
+
+ 您好
+
+ 您正在 link56 平台进行认证邮箱修改操作,
+ 系统已为您生成邮箱验证码。
+ 该验证码仅在 10 分钟内有效。
+
+
+
+ [[${Code}]]
+
+ 如果不是您本人操作,请忽略此邮件,或如有疑问,请 联系客服。
+
+
+ 感谢,
+ link56 团队
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+
+
\ No newline at end of file
diff --git a/code/link56/link56-frontend-new/src/components/EmailCodeInput.vue b/code/link56/link56-frontend-new/src/components/EmailCodeInput.vue
new file mode 100644
index 00000000..9fa81b9c
--- /dev/null
+++ b/code/link56/link56-frontend-new/src/components/EmailCodeInput.vue
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+ 发送中...
+
+
+ {{ countdown }}s
+
+
+ {{ buttonText }}
+
+
+
+
+
+
+
diff --git a/code/link56/link56-frontend-new/src/request/api/company/user.ts b/code/link56/link56-frontend-new/src/request/api/company/user.ts
index 5fd205d5..7993b2b1 100644
--- a/code/link56/link56-frontend-new/src/request/api/company/user.ts
+++ b/code/link56/link56-frontend-new/src/request/api/company/user.ts
@@ -1,6 +1,9 @@
-import type { CompanyUser, QueryCompanyUserParams } from '@/types/company/user'
+import type { AddCompanyUserParams, CompanyUser, QueryCompanyUserParams } from '@/types/company/user'
import { httpGet, httpPostJson } from '@/request/utils'
export function queryCompanyUserApi(params: QueryCompanyUserParams) {
return httpPostJson >('/company/user/query', params)
}
+export function addCompanyUserApi(params: AddCompanyUserParams) {
+ return httpPostJson>('/company/user/add', params)
+}
diff --git a/code/link56/link56-frontend-new/src/router/index.ts b/code/link56/link56-frontend-new/src/router/index.ts
index 0c187dba..190f681d 100644
--- a/code/link56/link56-frontend-new/src/router/index.ts
+++ b/code/link56/link56-frontend-new/src/router/index.ts
@@ -32,7 +32,7 @@ const router = createRouter({
},
{
path: '/settings/info/user',
- component: () => import('@/views/settings/info/user.vue'),
+ component: () => import('@/views/settings/info/user/index.vue'),
},
],
},
diff --git a/code/link56/link56-frontend-new/src/types/auth/code.ts b/code/link56/link56-frontend-new/src/types/auth/code.ts
index b0e3c709..94406834 100644
--- a/code/link56/link56-frontend-new/src/types/auth/code.ts
+++ b/code/link56/link56-frontend-new/src/types/auth/code.ts
@@ -7,6 +7,7 @@ export enum AuthCodePurpose {
REGISTER = 'REGISTER',
RESET_PASSWORD = 'RESET_PASSWORD',
LOGIN = 'LOGIN',
+ UPDATE_EMAIL = 'UPDATE_EMAIL',
}
export interface AuthCodeSendParams {
diff --git a/code/link56/link56-frontend-new/src/types/auto-import.d.ts b/code/link56/link56-frontend-new/src/types/auto-import.d.ts
index 67fe5ab3..5fc8fcde 100644
--- a/code/link56/link56-frontend-new/src/types/auto-import.d.ts
+++ b/code/link56/link56-frontend-new/src/types/auto-import.d.ts
@@ -14,6 +14,7 @@ declare module 'vue' {
AnchorMenu: typeof import('./../components/anchor-menu/index.vue')['default']
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
Directory: typeof import('./../components/wang-editor/modules/directory.vue')['default']
+ EmailCodeInput: typeof import('./../components/EmailCodeInput.vue')['default']
HelloWorld: typeof import('./../components/HelloWorld.vue')['default']
ICarbonAccessibility: typeof import('~icons/carbon/accessibility')['default']
'IFa7Solid:userShield': typeof import('~icons/fa7-solid/user-shield')['default']
diff --git a/code/link56/link56-frontend-new/src/types/company/user.ts b/code/link56/link56-frontend-new/src/types/company/user.ts
index 7dbe50b6..63a38cd0 100644
--- a/code/link56/link56-frontend-new/src/types/company/user.ts
+++ b/code/link56/link56-frontend-new/src/types/company/user.ts
@@ -19,3 +19,19 @@ export interface CompanyUser {
phone: string
canuse: string
}
+
+export interface CompanyUserForm extends CompanyUser {
+ password: string
+ emailCode: string
+}
+
+export interface AddCompanyUserParams {
+ companyId: number
+ email: string
+ password: string
+
+ name: string
+ position: string
+ phone: string
+ canuse: string
+}
diff --git a/code/link56/link56-frontend-new/src/views/auth/login.vue b/code/link56/link56-frontend-new/src/views/auth/login.vue
index e4ae3ee8..ecdfa427 100644
--- a/code/link56/link56-frontend-new/src/views/auth/login.vue
+++ b/code/link56/link56-frontend-new/src/views/auth/login.vue
@@ -3,6 +3,7 @@ import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
+import EmailCodeInput from '@/components/EmailCodeInput.vue'
import { loginByEmailCodeApi, loginByPasswordApi } from '@/request/api/auth'
import { authCodeSendApi } from '@/request/api/auth/code'
import { useUserStore } from '@/store/user'
@@ -52,22 +53,6 @@ const carouselList = [
},
]
-// --- 发送验证码状态 & 倒计时 ---
-const sending = ref(false)
-const countdown = ref(0)
-let timer: number | undefined
-
-function startCountdown(seconds = 60) {
- countdown.value = seconds
- timer = window.setInterval(() => {
- countdown.value -= 1
- if (countdown.value <= 0) {
- clearInterval(timer)
- timer = undefined
- }
- }, 1000)
-}
-
const form = reactive<{
email: string
password: string
@@ -116,39 +101,8 @@ const formRules = {
],
}
-const isEmailValid = computed(() => validateEmail(form.email))
-
const submitLoading = ref(false)
-function handleSendEmailCode() {
- if (sending.value || countdown.value > 0 || !isEmailValid.value)
- return
-
- sending.value = true
-
- authCodeSendApi({
- channel: AuthCodeChannel.EMAIL,
- purpose: AuthCodePurpose.LOGIN,
- target: form.email,
- }).then((res) => {
- if (res.isSuccess) {
- ElMessage.success('发送成功')
- startCountdown()
- }
- else {
- sending.value = false
- ElMessage.error(res.message || '发送失败,请检查邮箱是否正确或稍后再试!')
- }
- }).catch((err) => {
- console.log(err)
-
- ElMessage.error('发送失败,请稍后再试')
- sending.value = false
- }).finally(() => {
- sending.value = false
- })
-}
-
async function submitForm(formEl: FormInstance | undefined) {
if (!formEl)
return
@@ -301,30 +255,14 @@ function navigateTo(path: string) {
忘记密码?
-
-
-
-
- 发送中...
-
-
- {{ countdown }}s
-
-
- 发送验证码
-
-
-
-
+ :email="form.email"
+ :channel="AuthCodeChannel.EMAIL"
+ :purpose="AuthCodePurpose.LOGIN"
+ placeholder="请输入邮箱验证码"
+ />
diff --git a/code/link56/link56-frontend-new/src/views/auth/register/components/AdminForm.vue b/code/link56/link56-frontend-new/src/views/auth/register/components/AdminForm.vue
index 51a9a964..3a02afa0 100644
--- a/code/link56/link56-frontend-new/src/views/auth/register/components/AdminForm.vue
+++ b/code/link56/link56-frontend-new/src/views/auth/register/components/AdminForm.vue
@@ -2,10 +2,12 @@
import type { FormInstance } from 'element-plus'
import type { RegisterAdminForm } from '@/types/auth'
import { ElMessage } from 'element-plus'
-import { computed, onUnmounted, reactive, ref } from 'vue'
-import { authCodeSendApi, authCodeVerifyApi } from '@/request/api/auth/code'
+import { reactive, ref } from 'vue'
+import EmailCodeInput from '@/components/EmailCodeInput.vue'
+
+import { authCodeVerifyApi } from '@/request/api/auth/code'
import { AuthCodeChannel, AuthCodePurpose } from '@/types/auth/code'
-import { validateEmail, validatePhone } from '@/utils/validate'
+import { validatePhone } from '@/utils/validate'
const emit = defineEmits<{
(e: 'prev'): void
@@ -73,61 +75,6 @@ function phoneValidator(rule: any, value: any, callback: any) {
}
}
-const isEmailValid = computed(() => validateEmail(form.email))
-
-// --- 发送验证码状态 & 倒计时 ---
-const sending = ref(false)
-const countdown = ref(0)
-let timer: number | undefined
-
-function startCountdown(seconds = 60) {
- countdown.value = seconds
- timer = window.setInterval(() => {
- countdown.value -= 1
- if (countdown.value <= 0) {
- clearInterval(timer)
- timer = undefined
- }
- }, 1000)
-}
-
-function handleSendEmailCode() {
- if (sending.value || countdown.value > 0 || !isEmailValid.value)
- return
-
- sending.value = true
-
- authCodeSendApi({
- channel: AuthCodeChannel.EMAIL,
- purpose: AuthCodePurpose.REGISTER,
- target: form.email,
- }).then((res) => {
- if (res.isSuccess) {
- ElMessage.success('发送成功')
- startCountdown()
- }
- else {
- sending.value = false
- ElMessage.error(res.message || '发送失败,请检查邮箱是否正确或稍后再试!')
- }
- }).catch((err) => {
- console.log(err)
-
- ElMessage.error('发送失败,请稍后再试')
- sending.value = false
- }).finally(() => {
- sending.value = false
- })
-}
-
-// 清理
-onUnmounted(() => {
- if (timer) {
- clearInterval(timer)
- timer = undefined
- }
-})
-
async function handleSubmit(formEl: FormInstance | undefined) {
if (!formEl)
return
@@ -206,28 +153,13 @@ function handlePrev() {
-
-
-
-
- 发送中...
-
-
- {{ countdown }}s
-
-
- 发送验证码
-
-
-
-
+
@@ -274,21 +206,6 @@ function handlePrev() {
overflow-x: hidden;
}
- .email-code {
- :deep(.el-input-group__append) {
- background-color: #409eff;
- color: #fff;
- cursor: pointer;
- }
-
- &.disabled {
- :deep(.el-input-group__append) {
- background-color: #f5f7fa;
- color: #c0c4cc;
- }
- }
- }
-
.section-title {
font-size: 17px;
font-weight: 600;
diff --git a/code/link56/link56-frontend-new/src/views/auth/register/components/CompanyForm.vue b/code/link56/link56-frontend-new/src/views/auth/register/components/CompanyForm.vue
index ce503b52..ab156cd9 100644
--- a/code/link56/link56-frontend-new/src/views/auth/register/components/CompanyForm.vue
+++ b/code/link56/link56-frontend-new/src/views/auth/register/components/CompanyForm.vue
@@ -95,12 +95,12 @@ async function handleSubmit(formEl: FormInstance | undefined) {
-
+
-
+
+import type { FormInstance } from 'element-plus'
+import type { PropType } from 'vue'
+import type { CompanyUser, CompanyUserForm } from '@/types/company/user'
+import { ElMessage } from 'element-plus'
+import { computed, ref, watch } from 'vue'
+import EmailCodeInput from '@/components/EmailCodeInput.vue'
+import { authCodeVerifyApi } from '@/request/api/auth/code'
+import { addCompanyUserApi } from '@/request/api/company/user'
+import { useUserStore } from '@/store/user'
+import { AuthCodeChannel, AuthCodePurpose } from '@/types/auth/code'
+import { validatePhone } from '@/utils/validate'
+
+const props = defineProps({
+ user: {
+ type: Object as PropType,
+ default: null,
+ },
+})
+
+const userStore = useUserStore()
+const roleOptions = [
+ {
+ value: 0,
+ label: '系统管理员',
+ },
+ {
+ value: 1,
+ label: '普通成员',
+ },
+]
+
+const originEmail = ref('')
+
+const form = ref({
+ id: 0,
+ role: 1,
+ name: '',
+ position: '',
+ email: '',
+ phone: '',
+ canuse: '1',
+ password: '',
+ emailCode: '',
+})
+watch(
+ () => props.user,
+ (user) => {
+ if (!user)
+ return
+
+ form.value = { ...user, password: '', emailCode: '' }
+ originEmail.value = user.email
+ },
+ { immediate: true },
+)
+
+const isUpdate = computed(() => !!props.user && !!props.user.id)
+
+const isEmailChanged = computed(() => {
+ return form.value.email !== originEmail.value
+})
+
+const formRef = ref()
+
+const formRules = {
+ name: [
+ { required: true, message: '名称不能为空', trigger: 'blur' },
+ ],
+ email: [
+ { required: true, message: '邮箱不能为空', trigger: 'blur' },
+ { type: 'email', message: '请输入正确的邮箱格式', trigger: ['blur', 'change'] },
+ ],
+ emailCode: [
+ {
+ validator: (rule: any, value: any, callback: any) => {
+ if (!isEmailChanged.value) {
+ callback()
+ return
+ }
+
+ if (!value) {
+ callback(new Error('邮箱验证码不能为空'))
+ return
+ }
+ callback()
+ },
+ trigger: ['blur', 'change'],
+ },
+ ],
+ password: [
+ {
+ validator: (rule: any, value: any, callback: any) => {
+ // 更新时允许不填
+ if (isUpdate.value && !value) {
+ callback()
+ return
+ }
+
+ if (!value) {
+ callback(new Error('密码不能为空'))
+ }
+ else {
+ callback()
+ }
+ },
+ trigger: 'blur',
+ },
+ ],
+ role: [
+ { required: true, message: '角色不能为空', trigger: 'blur' },
+ ],
+ canuse: [
+ { required: true, message: '启用不能为空', trigger: 'blur' },
+ ],
+ phone: [{ validator: phoneValidator, trigger: 'blur' }],
+}
+const submitLoading = ref(false)
+
+function phoneValidator(rule: any, value: any, callback: any) {
+ // 未填写:直接通过
+ if (!value) {
+ callback()
+ return
+ }
+
+ if (!validatePhone(value)) {
+ callback(new Error('手机号格式不正确'))
+ }
+ else {
+ callback()
+ }
+}
+
+async function handleSubmit(formEl: FormInstance | undefined) {
+ if (!formEl)
+ return
+
+ await formEl.validate((valid) => {
+ if (valid) {
+ if (isEmailChanged.value) {
+ submitLoading.value = true
+
+ authCodeVerifyApi({
+ channel: AuthCodeChannel.EMAIL,
+ purpose: isUpdate.value ? AuthCodePurpose.UPDATE_EMAIL : AuthCodePurpose.REGISTER,
+ target: form.value.email,
+ code: form.value.emailCode,
+ }).then((res) => {
+ if (res.isSuccess) {
+ addCompanyUserApi({
+ companyId: userStore.userInfo.company.id,
+ email: form.value.email,
+ password: form.value.password,
+ name: form.value.name,
+ position: form.value.position,
+ phone: form.value.phone,
+ canuse: form.value.canuse,
+ }).then((res) => {
+ if (res.isSuccess) {
+ ElMessage.success('保存成功')
+ }
+ else {
+ ElMessage.error(res.message || '新增失败,请稍后再试')
+ }
+ }).catch((err) => {
+ console.log(err)
+ ElMessage.error('新增失败,请稍后再试')
+ }).finally(() => {
+ submitLoading.value = false
+ })
+ }
+ else {
+ submitLoading.value = false
+
+ ElMessage.error(res.message || '验证码无效或已过期,请重新获取')
+ }
+ }).catch((err) => {
+ submitLoading.value = false
+
+ console.log(err)
+ ElMessage.error('邮箱验证码校验失败,请稍后再试')
+ })
+ }
+ }
+ else {
+ console.log('===验证失败===')
+ }
+ })
+}
+
+
+
+
+
+
+ • 修改邮箱需要进行邮箱验证码验证
+ • 出于安全考虑,密码仅支持重置修改,无法查看原密码
+ • 角色无法更改
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+
+ 确认
+
+
+
+
+
+
+
diff --git a/code/link56/link56-frontend-new/src/views/settings/info/user.vue b/code/link56/link56-frontend-new/src/views/settings/info/user/index.vue
similarity index 89%
rename from code/link56/link56-frontend-new/src/views/settings/info/user.vue
rename to code/link56/link56-frontend-new/src/views/settings/info/user/index.vue
index 5f1b98b1..58676faa 100644
--- a/code/link56/link56-frontend-new/src/views/settings/info/user.vue
+++ b/code/link56/link56-frontend-new/src/views/settings/info/user/index.vue
@@ -5,6 +5,7 @@ import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import { queryCompanyUserApi } from '@/request/api/company/user'
import { useUserStore } from '@/store/user'
+import EditDrawer from './components/EditDrawer.vue'
const userStore = useUserStore()
const activeCollapseNames = ref(['1'])
@@ -43,6 +44,9 @@ const canuseOptions = [
]
const tableData = ref([])
+const showEdit = ref(false)
+const editUser = ref(null)
+
onMounted(() => {
getCompanyUser()
})
@@ -65,6 +69,11 @@ function getCompanyUser() {
ElMessage.error('查询失败,请稍后再试')
})
}
+
+function handleShowEditDrawer(user: CompanyUser | null) {
+ editUser.value = user
+ showEdit.value = true
+}
@@ -121,12 +130,8 @@ function getCompanyUser() {
@@ -207,7 +226,6 @@ function getCompanyUser() {
.table-container {
flex: 1;
- margin-top: 16px;
display: flex;
flex-direction: column;
min-height: 0;
@@ -227,10 +245,6 @@ function getCompanyUser() {
align-items: center;
justify-content: space-between;
height: 56px;
- .table-title {
- font-size: 18px;
- font-weight: 500;
- }
}
:deep(.el-table__header) {
.el-table__cell {
| |