Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
P
poc-fe
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
poc
poc-fe
Commits
0d05de2e
Commit
0d05de2e
authored
Nov 11, 2024
by
nick zheng
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://gitlab.gsstcloud.com/poc/poc-fe
into beta
parents
666fc2a6
d3157a29
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
439 additions
and
34 deletions
+439
-34
index.html
index.html
+1
-1
sidebar.vue
src/layout/components/sidebar/sidebar.vue
+32
-28
personal-settings.ts
src/router/modules/personal-settings.ts
+24
-0
system-language.ts
src/store/modules/system-language.ts
+4
-4
user.ts
src/store/modules/user.ts
+2
-0
user.ts
src/store/types/user.ts
+2
-0
login.vue
src/views/login/login.vue
+1
-1
personal-settings.vue
src/views/personal-settings/personal-settings.vue
+372
-0
locales.d.ts
types/locales.d.ts
+1
-0
No files found.
index.html
View file @
0d05de2e
...
...
@@ -8,7 +8,7 @@
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link
rel=
"stylesheet"
href=
"//at.alicdn.com/t/c/font_4711453_
vbf79y4vtuk
.css"
/>
<link
rel=
"stylesheet"
href=
"//at.alicdn.com/t/c/font_4711453_
a5ytfgvaagl
.css"
/>
<title>
Model Link
</title>
</head>
...
...
src/layout/components/sidebar/sidebar.vue
View file @
0d05de2e
...
...
@@ -2,9 +2,8 @@
import
{
computed
,
h
,
ref
,
watchEffect
}
from
'vue'
import
{
useRoute
,
useRouter
}
from
'vue-router'
import
{
useI18n
}
from
'vue-i18n'
import
{
Plus
}
from
'@icon-park/vue-next'
import
{
Plus
,
Logout
,
Me
}
from
'@icon-park/vue-next'
import
type
{
MenuOption
}
from
'naive-ui'
import
CustomIcon
from
'@/components/custom-icon/custom-icon.vue'
import
{
useUserStore
}
from
'@/store/modules/user'
// import LanguageSetting from '@/components/language-setting/language-setting.vue'
...
...
@@ -46,12 +45,17 @@ const menuOptions = computed<MenuOption[]>(() => {
]
})
const
avatar
Options
=
computed
(()
=>
{
const
userConfig
Options
=
computed
(()
=>
{
return
[
{
label
:
()
=>
h
(
'div'
,
{},
t
(
'common_module.logout'
)),
key
:
'logout'
,
icon
:
()
=>
h
(
CustomIcon
,
{
icon
:
'teenyicons:logout-solid'
}),
label
:
()
=>
h
(
'div'
,
'个人设置'
),
key
:
'PersonalSettings'
,
icon
:
()
=>
h
(
Me
,
{
theme
:
'outline'
,
size
:
14
,
strokeWidth
:
3
}),
},
{
label
:
()
=>
h
(
'div'
,
t
(
'common_module.logout'
)),
key
:
'Logout'
,
icon
:
()
=>
h
(
Logout
,
{
theme
:
'outline'
,
size
:
14
,
strokeWidth
:
3
}),
},
]
})
...
...
@@ -64,8 +68,8 @@ function handleToPersonAppSettingPage() {
router
.
push
({
name
:
'PersonalAppSetting'
})
}
function
handle
Dropdown
Select
(
key
:
string
)
{
if
(
key
===
'
l
ogout'
)
{
function
handle
UserConfigOptions
Select
(
key
:
string
)
{
if
(
key
===
'
L
ogout'
)
{
userStore
.
logout
().
then
(()
=>
{
router
.
push
({
name
:
'Login'
})
})
...
...
@@ -119,28 +123,28 @@ function handleMenuValueChange(key: string) {
<div
class=
"mb-[20px] mt-6"
>
<div>
<
ND
ropdown
<
n-d
ropdown
v-if=
"userStore.isLogin"
trigger=
"click"
placement=
"top"
:options=
"
avatar
Options"
@
select=
"handle
Dropdown
Select"
:options=
"
userConfig
Options"
@
select=
"handle
UserConfigOptions
Select"
>
<div
class=
"flex h-full cursor-pointer items-center rounded-[6px] px-[12px] py-[4px] transition hover:bg-[#F3F3F5]"
>
<
NA
vatar
round
:size=
"40"
object-fit=
"cover"
:src=
"userStore.userInfo.avatarUrl || defaultAvatar"
/>
<
n-a
vatar
round
:size=
"40"
object-fit=
"cover"
:src=
"userStore.userInfo.avatarUrl || defaultAvatar"
/>
<div
class=
"ml-3 line-clamp-1 max-w-[140px] select-none break-all text-base"
>
<div
class=
"ml-3 line-clamp-1 max-w-[140px]
flex-1
select-none break-all text-base"
>
{{
userStore
.
userInfo
.
nickName
||
t
(
'common_module.not_login_text'
)
}}
</div>
</div>
</
ND
ropdown>
</
n-d
ropdown>
<div
v-else
>
<
NB
utton
type=
"primary"
class=
"w-full! rounded-md!"
@
click=
"handleToLogin"
>
<
n-b
utton
type=
"primary"
class=
"w-full! rounded-md!"
@
click=
"handleToLogin"
>
{{
t
(
'common_module.login_now'
)
}}
</
NB
utton>
</
n-b
utton>
</div>
</div>
...
...
@@ -152,16 +156,16 @@ function handleMenuValueChange(key: string) {
</
template
>
<
style
lang=
"scss"
>
.v-binder-follower-container
{
.n-dropdown-menu
{
--n-border-radius
:
10px
!
important
;
--n-box-shadow
:
-3px
3px
4px
0px
#f2f2f2
!
important
;
padding
:
6px
0
!
important
;
}
.n-dropdown-menu
.n-dropdown-option-body
{
padding
:
0
10px
;
}
}
//
.v-binder-follower-container {
//
.n-dropdown-menu {
//
--n-border-radius: 10px !important;
//
--n-box-shadow: -3px 3px 4px 0px #f2f2f2 !important;
//
padding: 6px 0 !important;
//
}
//
.n-dropdown-menu .n-dropdown-option-body {
//
padding: 0 10px;
//
}
//
}
</
style
>
src/router/modules/personal-settings.ts
0 → 100644
View file @
0d05de2e
import
{
type
RouteRecordRaw
}
from
'vue-router'
import
Layout
from
'@/layout/index.vue'
export
default
[
{
path
:
'/personal/settings'
,
meta
:
{
rank
:
1001
,
title
:
''
,
},
component
:
Layout
,
children
:
[
{
path
:
''
,
name
:
'PersonalSettings'
,
meta
:
{
rank
:
1001
,
title
:
'router_title_module.personal_settings'
,
},
component
:
()
=>
import
(
'@/views/personal-settings/personal-settings.vue'
),
},
],
},
]
as
RouteRecordRaw
[]
src/store/modules/system-language.ts
View file @
0d05de2e
...
...
@@ -23,10 +23,10 @@ const defaultLanguageOptions = [
label
:
'中文繁體'
,
key
:
'zh-HK'
,
},
//
{
//
label: 'English',
//
key: 'en',
//
},
{
label
:
'English'
,
key
:
'en'
,
},
]
const
localeKey
=
ss
.
get
(
'i18nextLng'
)
||
defaultLocale
...
...
src/store/modules/user.ts
View file @
0d05de2e
...
...
@@ -9,6 +9,8 @@ function createDefaultUserInfoFactory(): UserInfo {
avatarUrl
:
''
,
nickName
:
''
,
mobilePhone
:
''
,
remark
:
''
,
email
:
''
,
}
}
...
...
src/store/types/user.ts
View file @
0d05de2e
...
...
@@ -3,6 +3,8 @@ export interface UserInfo {
mobilePhone
:
string
nickName
:
string
avatarUrl
:
string
remark
:
string
email
:
string
}
export
interface
UserState
{
...
...
src/views/login/login.vue
View file @
0d05de2e
...
...
@@ -93,7 +93,7 @@ const emailLoginFormRules = shallowReadonly<FormRules>({
const
phoneNumberAreaOptions
=
computed
(()
=>
{
return
[
{
label
:
`+86
${
t
(
'login_module.mainland_china'
)}
`
,
label
:
`+86
⠀
${
t
(
'login_module.mainland_china'
)}
`
,
value
:
'+86'
,
},
{
...
...
src/views/personal-settings/personal-settings.vue
0 → 100644
View file @
0d05de2e
<
script
setup
lang=
"ts"
>
import
{
useUserStore
}
from
'@/store/modules/user'
import
{
computed
,
reactive
,
ref
,
shallowReadonly
,
useTemplateRef
,
nextTick
}
from
'vue'
import
type
{
FormItemRule
,
FormInst
,
InputInst
}
from
'naive-ui'
import
isEmail
from
'validator/es/lib/isEmail'
import
{
useSystemLanguageStore
}
from
'@/store/modules/system-language'
import
{
useRouter
}
from
'vue-router'
const
userStore
=
useUserStore
()
const
systemLanguageStore
=
useSystemLanguageStore
()
const
router
=
useRouter
()
const
inputRefs
=
useTemplateRef
<
InputInst
|
InputInst
[]
>
(
'inputRefs'
)
const
avatarInputRef
=
useTemplateRef
(
'avatarInputRef'
)
const
emailInfoFormRef
=
useTemplateRef
<
FormInst
>
(
'emailInfoFormRef'
)
const
passwordInfoFormRef
=
useTemplateRef
<
FormInst
>
(
'passwordInfoFormRef'
)
const
userInfoForm
=
reactive
({
nickName
:
userStore
.
userInfo
.
nickName
,
remark
:
userStore
.
userInfo
.
remark
,
})
const
userInfoFormItemEdit
=
reactive
({
nickName
:
false
,
remark
:
false
,
})
/* 邮箱绑定 */
const
isShowMailboxBindingModal
=
ref
(
false
)
const
mailboxBindingSubmitBtnLoading
=
ref
(
false
)
const
emailInfoForm
=
ref
({
email
:
''
,
})
const
emailInfoFormRules
=
shallowReadonly
({
email
:
{
required
:
true
,
trigger
:
'blur'
,
validator
:
(
_rule
:
FormItemRule
,
value
:
string
)
=>
{
if
(
!
value
)
{
return
new
Error
(
'请填写邮箱地址'
)
}
else
if
(
!
isEmail
(
value
))
{
return
new
Error
(
'请填写正确邮箱地址'
)
}
return
},
},
})
/* 密码修改 */
const
isShowPasswordChangeModal
=
ref
(
false
)
const
passwordChangeSubmitBtnLoading
=
ref
(
false
)
const
passwordInfoForm
=
ref
({
password
:
''
,
confirmPassword
:
''
,
})
const
passwordFormRules
=
shallowReadonly
({
password
:
{
required
:
true
,
trigger
:
'blur'
,
validator
:
(
_rule
:
FormItemRule
,
value
:
string
)
=>
{
if
(
!
value
)
{
return
new
Error
(
'请输入新密码'
)
}
else
if
(
value
.
length
<=
6
)
{
return
new
Error
(
'密码长度不能小于6位'
)
}
return
},
},
confirmPassword
:
{
required
:
true
,
trigger
:
'blur'
,
validator
:
(
_rule
:
FormItemRule
,
value
:
string
)
=>
{
if
(
!
value
)
{
return
new
Error
(
'请输入确定密码'
)
}
else
if
(
value
!==
passwordInfoForm
.
value
.
password
)
{
return
new
Error
(
'确认密码与密码不一致'
)
}
return
},
},
})
const
currentLanguage
=
computed
({
get
()
{
return
systemLanguageStore
.
currentLanguageInfo
.
key
},
set
(
value
:
I18n
.
LangType
)
{
systemLanguageStore
.
updateCurrentLanguageInfo
(
value
)
router
.
go
(
0
)
},
})
const
userInfo
=
computed
(()
=>
userStore
.
userInfo
)
const
languageOptions
=
computed
(()
=>
{
return
systemLanguageStore
.
languageOptions
.
map
((
item
)
=>
({
value
:
item
.
key
,
label
:
item
.
label
}))
})
function
handleAvatarUpdate
()
{
avatarInputRef
.
value
&&
avatarInputRef
.
value
.
click
()
}
function
handleAvatarUpload
(
e
:
Event
)
{
const
target
=
e
.
target
as
HTMLInputElement
const
file
=
target
.
files
&&
target
.
files
[
0
]
if
(
file
)
{
const
reader
=
new
FileReader
()
reader
.
onload
=
(
e
)
=>
{
const
result
=
e
.
target
?.
result
console
.
log
(
'🟰🟰🟰🟰🟰🟰test🟰🟰🟰🟰🟰🟰'
)
console
.
log
(
result
)
}
}
}
function
handleUserInfoFormItemEditUpdate
(
key
:
keyof
typeof
userInfoFormItemEdit
,
status
:
boolean
)
{
userInfoFormItemEdit
[
key
]
=
status
nextTick
(()
=>
{
if
(
inputRefs
.
value
&&
!
Array
.
isArray
(
inputRefs
.
value
))
{
inputRefs
.
value
.
focus
()
}
})
}
function
onModalAfterLeave
()
{
emailInfoForm
.
value
.
email
=
''
}
function
handleMailboxBindingSubmit
()
{
emailInfoFormRef
.
value
?.
validate
((
errors
)
=>
{
if
(
errors
)
return
''
console
.
log
(
'🟰🟰🟰🟰🟰🟰提交🟰🟰🟰🟰🟰🟰'
)
})
}
function
handlePasswordChangeSubmit
()
{
passwordInfoFormRef
.
value
?.
validate
((
errors
)
=>
{
if
(
errors
)
return
''
console
.
log
(
'🟰🟰🟰🟰🟰🟰提交🟰🟰🟰🟰🟰🟰'
)
})
}
</
script
>
<
template
>
<div
class=
"h-full flex-col rounded-[20px] bg-white p-6 shadow-[0_2px_2px_#0000000a]"
>
<div
class=
"ml-[56px] pt-[6px]"
>
<ul>
<li
class=
"font-600 flex items-center text-[18px]"
>
<i
class=
"iconfont icon-personal-info text-theme-color"
></i>
<span
class=
"ml-[4px]"
>
基础信息设置
</span>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
头像:
</h4>
<div
class=
"group relative h-[60px] w-[60px] cursor-pointer overflow-hidden rounded-full bg-[#EAECEC]"
@
click=
"handleAvatarUpdate"
>
<img
class=
"h-full w-full object-cover"
:src=
"userInfo.avatarUrl"
alt=
"Avatar"
/>
<div
class=
"flex-center absolute bottom-0 h-[26px] w-full bg-[rgba(0,0,0,0.4)] text-[10px] text-white opacity-0 transition group-hover:opacity-100"
>
<span>
更换
</span>
</div>
<input
ref=
"avatarInputRef"
type=
"file"
class=
"hidden"
accept=
"image/*"
@
change=
"handleAvatarUpload"
/>
</div>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
账号昵称:
</h4>
<div
v-if=
"userInfoFormItemEdit.nickName"
class=
"flex"
>
<div
class=
"flex w-[220px] items-center"
>
<n-input
ref=
"inputRefs"
v-model:value=
"userInfoForm.nickName"
type=
"text"
size=
"small"
/>
</div>
<div
class=
"ml-[20px]"
>
<n-button
class=
"!mr-[6px]"
size=
"tiny"
@
click=
"handleUserInfoFormItemEditUpdate('nickName', false)"
>
<i
class=
"iconfont icon-close"
></i>
</n-button>
<n-button
type=
"primary"
size=
"tiny"
@
click=
"handleUserInfoFormItemEditUpdate('nickName', true)"
>
<i
class=
"iconfont icon-queren"
></i>
</n-button>
</div>
</div>
<div
v-else
class=
"flex items-center text-[14px] text-[#999]"
>
<span
class=
"mr-[5px]"
>
{{
userInfo
.
nickName
||
'-'
}}
</span>
<i
class=
"iconfont icon-edit1 cursor-pointer px-[5px]"
@
click=
"handleUserInfoFormItemEditUpdate('nickName', true)"
></i>
</div>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
个人简介:
</h4>
<div
v-if=
"userInfoFormItemEdit.remark"
class=
"flex"
>
<div
class=
"flex w-[220px] items-center"
>
<n-input
ref=
"inputRefs"
v-model:value=
"userInfoForm.remark"
type=
"textarea"
size=
"small"
:autosize=
"
{ minRows: 4, maxRows: 4 }"
/>
</div>
<div
class=
"ml-[20px] flex items-center"
>
<n-button
class=
"!mr-[6px]"
size=
"tiny"
@
click=
"handleUserInfoFormItemEditUpdate('remark', false)"
>
<i
class=
"iconfont icon-close"
></i>
</n-button>
<n-button
type=
"primary"
size=
"tiny"
@
click=
"handleUserInfoFormItemEditUpdate('remark', true)"
>
<i
class=
"iconfont icon-queren"
></i>
</n-button>
</div>
</div>
<div
v-else
class=
"flex items-center text-[14px] text-[#999]"
>
<span
class=
"mr-[5px]"
>
{{
userInfo
.
remark
||
'-'
}}
</span>
<i
class=
"iconfont icon-edit1 cursor-pointer px-[5px]"
@
click=
"handleUserInfoFormItemEditUpdate('remark', true)"
></i>
</div>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
手机号:
</h4>
<div
class=
"text-[14px] text-[#999]"
>
{{
userInfo
.
mobilePhone
||
'-'
}}
</div>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
邮箱:
</h4>
<div
class=
"text-[14px] text-[#999]"
>
<span
v-if=
"userInfo.email"
>
{{
userInfo
.
email
}}
</span>
<span
v-else
class=
"text-theme-color cursor-pointer select-none"
@
click=
"isShowMailboxBindingModal = true"
>
点击绑定邮箱
</span>
</div>
</li>
<li
class=
"mb-[30px] mt-[24px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
密码:
</h4>
<div
class=
"text-theme-color cursor-pointer select-none text-[14px]"
@
click=
"isShowPasswordChangeModal = true"
>
点击设置密码
</div>
</li>
</ul>
<ul
class=
"mt-[60px]"
>
<li
class=
"font-600 flex items-center text-[18px]"
>
<i
class=
"iconfont icon-language text-theme-color"
></i>
<span
class=
"ml-[4px]"
>
语言设置
</span>
</li>
<li
class=
"mb-[8px] ml-[121px]"
>
<h5
class=
"text-[12px] text-[#999]"
>
设定您的首选语言,用于界面和AI回应。
</h5>
</li>
<li
class=
"mb-[30px] flex items-center"
>
<h4
class=
"font-600 list-item-label mr-[19px] text-[14px]"
>
语言:
</h4>
<div
class=
"w-[200px]"
>
<n-select
v-model:value=
"currentLanguage"
:options=
"languageOptions"
/>
</div>
</li>
</ul>
</div>
<n-modal
v-model:show=
"isShowMailboxBindingModal"
:mask-closable=
"false"
:on-after-leave=
"onModalAfterLeave"
>
<n-card
class=
"!w-[600px]"
title=
"邮箱绑定"
:bordered=
"false"
size=
"medium"
closable
@
close=
"() => (isShowMailboxBindingModal = false)"
>
<n-form
ref=
"emailInfoFormRef"
label-placement=
"left"
label-width=
"auto"
:model=
"emailInfoForm"
:rules=
"emailInfoFormRules"
>
<n-form-item
label=
"邮箱地址"
path=
"email"
>
<n-input
v-model:value=
"emailInfoForm.email"
placeholder=
"请输入邮箱地址"
/>
</n-form-item>
</n-form>
<template
#
footer
>
<div
class=
"text-end"
>
<n-space
justify=
"end"
>
<n-button
@
click=
"() => (isShowMailboxBindingModal = false)"
>
取消
</n-button>
<n-button
type=
"primary"
:loading=
"mailboxBindingSubmitBtnLoading"
@
click=
"handleMailboxBindingSubmit"
>
确定
</n-button>
</n-space>
</div>
</
template
>
</n-card>
</n-modal>
<n-modal
v-model:show=
"isShowPasswordChangeModal"
:mask-closable=
"false"
:on-after-leave=
"onModalAfterLeave"
>
<n-card
class=
"!w-[600px]"
title=
"密码修改"
:bordered=
"false"
size=
"medium"
closable
@
close=
"() => (isShowPasswordChangeModal = false)"
>
<n-form
ref=
"passwordInfoFormRef"
label-placement=
"left"
label-width=
"auto"
:model=
"passwordInfoForm"
:rules=
"passwordFormRules"
>
<n-form-item
label=
"密码"
path=
"password"
>
<n-input
v-model:value=
"passwordInfoForm.password"
placeholder=
"请输入密码"
/>
</n-form-item>
<n-form-item
label=
"确认密码"
path=
"confirmPassword"
>
<n-input
v-model:value=
"passwordInfoForm.confirmPassword"
placeholder=
"请输入确定密码"
/>
</n-form-item>
</n-form>
<
template
#
footer
>
<div
class=
"text-end"
>
<n-space
justify=
"end"
>
<n-button
@
click=
"() => (isShowPasswordChangeModal = false)"
>
取消
</n-button>
<n-button
type=
"primary"
:loading=
"passwordChangeSubmitBtnLoading"
@
click=
"handlePasswordChangeSubmit"
>
确定
</n-button>
</n-space>
</div>
</
template
>
</n-card>
</n-modal>
</div>
</template>
<
style
lang=
"scss"
scoped
>
.list-item-label
{
width
:
100px
;
text-align
:
end
;
}
</
style
>
types/locales.d.ts
View file @
0d05de2e
...
...
@@ -167,6 +167,7 @@ declare namespace I18n {
multi_model_dialogue
:
string
explore
:
string
application_square
:
string
personal_settings
:
string
}
personal_space_module
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment