Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
boyueCEnd
Project
Project
Details
Activity
Releases
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
游洁
boyueCEnd
Commits
15d9ad15
Commit
15d9ad15
authored
Nov 26, 2025
by
youjie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
账户中心
parent
2e541790
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
672 additions
and
211 deletions
+672
-211
App.vue
src/App.vue
+7
-15
accDoc.png
src/assets/images/personal/accDoc.png
+0
-0
email.png
src/assets/images/personal/email.png
+0
-0
google.png
src/assets/images/personal/google.png
+0
-0
line.png
src/assets/images/personal/line.png
+0
-0
mm.png
src/assets/images/personal/mm.png
+0
-0
sj.png
src/assets/images/personal/sj.png
+0
-0
wx.png
src/assets/images/personal/wx.png
+0
-0
zh-CN.ts
src/i18n/locales/zh-CN.ts
+22
-1
UserService.ts
src/services/UserService.ts
+72
-0
user.ts
src/stores/user.ts
+57
-5
Login.vue
src/views/auth/Login.vue
+12
-6
forgePassword.vue
src/views/auth/forgePassword.vue
+11
-6
register.vue
src/views/auth/register.vue
+11
-6
accountCenter.vue
src/views/personalCenter/accountCenter.vue
+364
-59
LeftView.vue
src/views/personalCenter/components/LeftView.vue
+82
-80
editEmail.vue
src/views/personalCenter/components/myOrder/editEmail.vue
+5
-5
index.vue
src/views/personalCenter/index.vue
+1
-5
myOrder.vue
src/views/personalCenter/myOrder.vue
+28
-23
No files found.
src/App.vue
View file @
15d9ad15
...
@@ -13,16 +13,6 @@ const { t, locale } = useI18n()
...
@@ -13,16 +13,6 @@ const { t, locale } = useI18n()
const
useUser
=
useUserStore
()
const
useUser
=
useUserStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
systemConfigStore
=
useSystemConfigStore
()
// 判断是否显示语言切换器
const
showLanguageSwitcher
=
computed
(()
=>
{
// 在Layout页面中不显示APP.vue的语言切换器,因为Layout组件有自己的语言切换器
// const layoutRoutes = ['dashboard', 'products', 'orders', 'billing', 'account']
// const currentRouteName = route.name as string
// 如果当前路由使用Layout组件,则不显示APP.vue的语言切换器
return
useUser
.
userInfo
==
null
})
// 监听语言变化,更新 Arco Design 国际化
// 监听语言变化,更新 Arco Design 国际化
watch
(
locale
,
async
(
newLocale
)
=>
{
watch
(
locale
,
async
(
newLocale
)
=>
{
try
{
try
{
...
@@ -154,7 +144,9 @@ body {
...
@@ -154,7 +144,9 @@ body {
.customColor-text-7
{
color
:
var
(
--
customColor-text-7
);}
.customColor-text-7
{
color
:
var
(
--
customColor-text-7
);}
.customColor-text-6
{
color
:
var
(
--
customColor-text-6
);}
.customColor-text-6
{
color
:
var
(
--
customColor-text-6
);}
.customColor-text-5
{
color
:
var
(
--
customColor-text-5
);}
.customColor-text-5
{
color
:
var
(
--
customColor-text-5
);}
*
{
color
:
var
(
--
customColor-text-10
);
}
@font-face
{
@font-face
{
font-family
:
'Source Han Sans CN ExtraLight'
;
font-family
:
'Source Han Sans CN ExtraLight'
;
...
...
src/assets/images/personal/accDoc.png
0 → 100644
View file @
15d9ad15
376 Bytes
src/assets/images/personal/email.png
0 → 100644
View file @
15d9ad15
962 Bytes
src/assets/images/personal/google.png
0 → 100644
View file @
15d9ad15
4.65 KB
src/assets/images/personal/line.png
0 → 100644
View file @
15d9ad15
3.58 KB
src/assets/images/personal/mm.png
0 → 100644
View file @
15d9ad15
992 Bytes
src/assets/images/personal/sj.png
0 → 100644
View file @
15d9ad15
738 Bytes
src/assets/images/personal/wx.png
0 → 100644
View file @
15d9ad15
1.76 KB
src/i18n/locales/zh-CN.ts
View file @
15d9ad15
...
@@ -165,6 +165,27 @@ export default {
...
@@ -165,6 +165,27 @@ export default {
binEmail
:
'绑定邮箱'
,
binEmail
:
'绑定邮箱'
,
bindEmailFailed
:
'绑定邮箱失败'
,
bindEmailFailed
:
'绑定邮箱失败'
,
save
:
'保存'
,
save
:
'保存'
,
bindGoogleAccount
:
'绑定google'
,
bindWechatAccount
:
'绑定微信'
,
bindLineAccount
:
'绑定Lnline'
,
bindGoogleFailed
:
'绑定google失败'
,
bindWechatSuccess
:
'绑定微信成功'
,
bindGoogleSuccess
:
'绑定google成功'
,
changeBind
:
'换绑'
,
unBind
:
'解绑'
,
securityLevel
:
'安全等级'
,
low
:
'低'
,
medium
:
'中'
,
high
:
'高'
,
passwordSecurityTip
:
' 安全性高的密码可以使账号更安全。建议您定期更换密码,且设置一个包含数字和字母,并且长度超过8位以上的密码。'
,
bindPhoneTip
:
'绑定手机后,您即可享受手机号登录、动态码登录、找回密码等。为了帐号安全建议您在更换手机号后第一时间更换绑定手机。 '
,
changeBindData
:
'更换'
,
bindEmailTip
:
'邮箱用于验证码接收,账户登录。'
,
bindWechatTip
:
'通过微信账户快速登录,无需输入密码。'
,
bindGoogleTip
:
'通过谷歌账户快速登录,无需输入密码。'
,
bindLineTip
:
'绑定手机后,您即可享受手机号登录、动态码登录、找回密码等。为了帐号安全建议您在更换手机号后第一时间更换绑定手机。'
,
notYetBin
:
'暂未绑定'
,
goBind
:
'去绑定'
,
},
},
// HTTP 错误状态码
// HTTP 错误状态码
httpError
:
{
httpError
:
{
...
...
src/services/UserService.ts
View file @
15d9ad15
...
@@ -336,6 +336,32 @@ class UserService {
...
@@ -336,6 +336,32 @@ class UserService {
return
response
as
unknown
as
HttpResponse
return
response
as
unknown
as
HttpResponse
}
}
/**
* 谷歌绑定
* @param credential 谷歌token
* @returns
*/
static
async
GoogleBindAsync
(
tenantId
:
string
,
code
:
string
,
distributorId
:
number
,
parentId
?:
any
,
redirectUri
?:
string
):
Promise
<
HttpResponse
>
{
const
data
=
{
tenantId
,
code
,
distributorId
,
parentId
,
redirectUri
,
}
// OtaRequest 的响应拦截器会返回 response.data
const
response
=
await
OtaRequest
.
post
(
'/member-auth/google-bind-by-code'
,
data
,
{
headers
:
{
}
}
)
return
response
as
unknown
as
HttpResponse
}
/**
/**
* 微信登录
* 微信登录
* @param tenantId 租户ID
* @param tenantId 租户ID
...
@@ -361,6 +387,31 @@ class UserService {
...
@@ -361,6 +387,31 @@ class UserService {
)
)
return
response
as
unknown
as
HttpResponse
return
response
as
unknown
as
HttpResponse
}
}
/**
* 微信绑定
* @param tenantId 租户ID
* @returns
*/
static
async
wechatBindAsync
(
tenantId
:
string
,
code
:
string
,
distributorId
:
number
,
parentId
?:
any
,
redirectUri
?:
string
):
Promise
<
HttpResponse
>
{
const
data
=
{
tenantId
,
code
,
distributorId
,
parentId
,
redirectUri
,
}
// OtaRequest 的响应拦截器会返回 response.data
const
response
=
await
OtaRequest
.
post
(
'/member-auth/we-chat-bind-by-code'
,
data
,
{
headers
:
{
'__tenant'
:
tenantId
}
}
)
return
response
as
unknown
as
HttpResponse
}
/**
/**
* 获取微信AppID
* 获取微信AppID
* @param tenantId 租户ID
* @param tenantId 租户ID
...
@@ -555,6 +606,27 @@ class UserService {
...
@@ -555,6 +606,27 @@ class UserService {
)
)
return
response
as
unknown
as
HttpResponse
return
response
as
unknown
as
HttpResponse
}
}
/**
* 解绑外部登录账号
* @param tenantId 租户ID(可选)
* @param ProviderType 外部id
*/
static
async
externalunbindBind
(
tenantId
:
string
,
ProviderType
:
string
):
Promise
<
HttpResponse
>
{
const
data
=
{
ProviderType
}
const
response
=
await
OtaRequest
.
get
(
'/member-auth/member-center'
,
data
,
{
headers
:
tenantId
?
{
'__tenant'
:
tenantId
}
:
{}
}
)
return
response
as
unknown
as
HttpResponse
}
}
}
export
default
UserService
export
default
UserService
src/stores/user.ts
View file @
15d9ad15
...
@@ -42,8 +42,9 @@ export const useUserStore = defineStore('user', {
...
@@ -42,8 +42,9 @@ export const useUserStore = defineStore('user', {
state
:
()
=>
({
state
:
()
=>
({
token
:
''
as
string
,
token
:
''
as
string
,
userInfo
:
{}
as
any
,
// 登录用户信息
userInfo
:
{}
as
any
,
// 登录用户信息
loginType
:
0
as
number
,
// 0: 账号密码登录, 2: 微信登录, 3: Line登录, 1: Google登录 7: Facebook登录
memberData
:
{}
as
any
,
// 登录会员数据
memberData
:
{}
as
any
,
// 登录会员数据
personalInfo
:
{}
as
any
,
// 个人信息
personalInfo
r
:
{}
as
any
,
// 个人信息
}),
}),
getters
:
{
getters
:
{
getUserToken
:
(
state
)
=>
{
getUserToken
:
(
state
)
=>
{
...
@@ -60,7 +61,7 @@ export const useUserStore = defineStore('user', {
...
@@ -60,7 +61,7 @@ export const useUserStore = defineStore('user', {
* @param email email
* @param email email
*/
*/
setEmail
(
email
:
string
){
setEmail
(
email
:
string
){
this
.
personalInfo
.
email
=
email
this
.
personalInfo
r
.
email
=
email
},
},
/**
/**
* 设置用户头像
* 设置用户头像
...
@@ -69,8 +70,14 @@ export const useUserStore = defineStore('user', {
...
@@ -69,8 +70,14 @@ export const useUserStore = defineStore('user', {
setPhoto
(
photo
:
string
){
setPhoto
(
photo
:
string
){
this
.
memberData
.
photo
=
photo
this
.
memberData
.
photo
=
photo
},
},
setPersonalInfo
(
personalInfo
:
any
){
setPersonalInfor
(
personalInfor
:
any
){
this
.
personalInfo
=
personalInfo
this
.
personalInfor
=
{
...
this
.
personalInfor
,
...
personalInfor
,
}
},
setLoginType
(
loginType
:
number
){
this
.
loginType
=
loginType
},
},
/**
/**
* 账号密码登录 - 使用新的 UserService
* 账号密码登录 - 使用新的 UserService
...
@@ -143,6 +150,29 @@ export const useUserStore = defineStore('user', {
...
@@ -143,6 +150,29 @@ export const useUserStore = defineStore('user', {
}
}
},
},
/**
* 谷歌绑定
* @param credential 谷歌凭证
*/
async
setUserGoogleBindAsync
(
tenantId
:
string
,
code
:
string
,
distributorId
:
number
,
parentId
?:
any
,
redirectUri
?:
string
):
Promise
<
UserLoginResult
>
{
try
{
const
response
=
await
UserService
.
GoogleBindAsync
(
tenantId
,
code
,
distributorId
,
parentId
,
redirectUri
)
console
.
log
(
'Google login response:'
,
response
)
return
{
status
:
'SUCCESS'
,
verify
:
false
,
data
:
[
response
]
}
}
catch
(
error
:
any
)
{
console
.
error
(
'Google login error:'
,
error
)
ResultMessage
.
Error
(
error
.
message
||
i18n
.
global
.
t
(
'login.googleLoginFailed'
))
return
{
status
:
'ERROR'
,
verify
:
false
}
}
},
/**
/**
* 微信登录
* 微信登录
* @param tenantId 租户ID
* @param tenantId 租户ID
...
@@ -169,6 +199,28 @@ export const useUserStore = defineStore('user', {
...
@@ -169,6 +199,28 @@ export const useUserStore = defineStore('user', {
}
}
},
},
/**
* 微信绑定
* @param tenantId 租户ID
*/
async
setUserWechatBindAsync
(
tenantId
:
string
,
code
:
string
,
distributorId
:
number
,
parentId
?:
any
,
redirectUri
?:
string
):
Promise
<
UserLoginResult
>
{
try
{
const
response
=
await
UserService
.
wechatBindAsync
(
tenantId
,
code
,
distributorId
,
parentId
,
redirectUri
)
return
{
status
:
'SUCCESS'
,
verify
:
false
,
data
:
[
response
]
}
}
catch
(
error
:
any
)
{
console
.
error
(
'Google login error:'
,
error
)
ResultMessage
.
Error
(
error
.
message
||
i18n
.
global
.
t
(
'login.wechatLoginFailed'
))
return
{
status
:
'ERROR'
,
verify
:
false
}
}
},
/**
/**
* 获取微信appid
* 获取微信appid
* @param tenantId 租户ID
* @param tenantId 租户ID
...
@@ -247,7 +299,7 @@ export const useUserStore = defineStore('user', {
...
@@ -247,7 +299,7 @@ export const useUserStore = defineStore('user', {
async
setEmailAsync
(
data
:
setRegisterDto
):
Promise
<
UserLoginResult
>
{
async
setEmailAsync
(
data
:
setRegisterDto
):
Promise
<
UserLoginResult
>
{
try
{
try
{
const
response
=
await
UserService
.
resetEmail
(
data
.
tenantId
,
data
)
const
response
=
await
UserService
.
resetEmail
(
data
.
tenantId
,
data
)
this
.
userInfo
.
email
=
data
.
email
this
.
personalInfor
.
email
=
data
.
email
return
{
return
{
status
:
'SUCCESS'
,
status
:
'SUCCESS'
,
data
:
[
response
]
data
:
[
response
]
...
...
src/views/auth/Login.vue
View file @
15d9ad15
<
template
>
<
template
>
<
div
class=
"login h-screen overflow-hidden"
>
<
a-spin
:loading=
"loading"
class=
"login h-screen overflow-hidden"
>
<div
ref=
"loginPage"
<div
ref=
"loginPage"
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto light-login-bg"
>
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto light-login-bg"
>
<loginHeader
/>
<loginHeader
/>
...
@@ -123,7 +123,7 @@
...
@@ -123,7 +123,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
</
div
>
</
a-spin
>
</template>
</template>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
reactive
,
computed
,
onMounted
}
from
"vue"
;
import
{
ref
,
reactive
,
computed
,
onMounted
}
from
"vue"
;
...
@@ -147,7 +147,7 @@ const { t } = useI18n();
...
@@ -147,7 +147,7 @@ const { t } = useI18n();
const
userStore
=
useUserStore
()
const
userStore
=
useUserStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
loading
=
ref
(
fals
e
)
const
loading
=
ref
(
tru
e
)
const
router
=
useRouter
()
const
router
=
useRouter
()
const
googleButtonContainer
=
ref
(
null
);
const
googleButtonContainer
=
ref
(
null
);
...
@@ -156,8 +156,8 @@ const loginMsg = reactive({
...
@@ -156,8 +156,8 @@ const loginMsg = reactive({
tenantId
:
systemConfigStore
.
tenantId
||
null
,
tenantId
:
systemConfigStore
.
tenantId
||
null
,
reType
:
0
,
//登录方式 0账号密码 1谷歌授权 3LINE授权 7FaceBook授权
reType
:
0
,
//登录方式 0账号密码 1谷歌授权 3LINE授权 7FaceBook授权
openId
:
""
,
openId
:
""
,
email
:
"
2310721242@qq.com"
,
//
email
:
"
"
,
//2310721242@qq.com
password
:
'
yj123456'
,
//
123456
password
:
'
'
,
//yj
123456
distributorId
:
systemConfigStore
.
distributorId
as
any
,
distributorId
:
systemConfigStore
.
distributorId
as
any
,
parentId
:
null
,
parentId
:
null
,
redirectUri
:
''
,
redirectUri
:
''
,
...
@@ -242,6 +242,7 @@ const useWechatLogin = async(code:string) => {
...
@@ -242,6 +242,7 @@ const useWechatLogin = async(code:string) => {
try
{
try
{
const
response
=
await
userStore
.
setUserWechatLoginAsync
(
loginMsg
.
tenantId
?.
toString
()
||
''
,
code
,
loginMsg
.
distributorId
,
loginMsg
.
parentId
,
loginMsg
.
redirectUri
)
const
response
=
await
userStore
.
setUserWechatLoginAsync
(
loginMsg
.
tenantId
?.
toString
()
||
''
,
code
,
loginMsg
.
distributorId
,
loginMsg
.
parentId
,
loginMsg
.
redirectUri
)
if
(
response
.
status
==
'SUCCESS'
)
{
if
(
response
.
status
==
'SUCCESS'
)
{
userStore
.
setLoginType
(
loginMsg
.
reType
||
0
)
Message
.
success
(
t
(
'login.loginSuccess'
))
Message
.
success
(
t
(
'login.loginSuccess'
))
const
forward
=
localStorage
.
getItem
(
'forward'
)
const
forward
=
localStorage
.
getItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
...
@@ -317,6 +318,7 @@ const handleSignInSuccess = async (googleUser:any) => {
...
@@ -317,6 +318,7 @@ const handleSignInSuccess = async (googleUser:any) => {
// 获取授权码
// 获取授权码
const
response
=
await
userStore
.
setUserGoogleLoginAsync
(
loginMsg
.
tenantId
?.
toString
()
||
''
,
googleUser
.
credential
,
loginMsg
.
distributorId
,
loginMsg
.
parentId
,
loginMsg
.
redirectUri
)
const
response
=
await
userStore
.
setUserGoogleLoginAsync
(
loginMsg
.
tenantId
?.
toString
()
||
''
,
googleUser
.
credential
,
loginMsg
.
distributorId
,
loginMsg
.
parentId
,
loginMsg
.
redirectUri
)
if
(
response
.
status
==
'SUCCESS'
)
{
if
(
response
.
status
==
'SUCCESS'
)
{
userStore
.
setLoginType
(
loginMsg
.
reType
||
0
)
Message
.
success
(
t
(
'login.loginSuccess'
))
Message
.
success
(
t
(
'login.loginSuccess'
))
const
forward
=
localStorage
.
getItem
(
'forward'
)
const
forward
=
localStorage
.
getItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
...
@@ -363,6 +365,7 @@ const handleLogin = async () => {
...
@@ -363,6 +365,7 @@ const handleLogin = async () => {
)
)
loading
.
value
=
false
loading
.
value
=
false
if
(
result
.
status
==
'SUCCESS'
)
{
if
(
result
.
status
==
'SUCCESS'
)
{
userStore
.
setLoginType
(
loginMsg
.
reType
||
0
)
Message
.
success
(
t
(
'login.loginSuccess'
))
Message
.
success
(
t
(
'login.loginSuccess'
))
const
forward
=
localStorage
.
getItem
(
'forward'
)
const
forward
=
localStorage
.
getItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
localStorage
.
removeItem
(
'forward'
)
...
@@ -391,6 +394,7 @@ onMounted(async () => {
...
@@ -391,6 +394,7 @@ onMounted(async () => {
const
queryParams
=
query
()
const
queryParams
=
query
()
const
code
=
queryParams
.
code
const
code
=
queryParams
.
code
if
(
code
)
{
if
(
code
)
{
loginMsg
.
reType
=
2
useWechatLogin
(
code
)
useWechatLogin
(
code
)
}
}
try
{
try
{
...
@@ -400,7 +404,9 @@ onMounted(async () => {
...
@@ -400,7 +404,9 @@ onMounted(async () => {
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
));
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
));
renderGoogleButton
()
renderGoogleButton
()
setTimeout
(()
=>
{
loading
.
value
=
false
},
500
)
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'SDK 初始化失败:'
,
error
);
console
.
error
(
'SDK 初始化失败:'
,
error
);
}
}
...
...
src/views/auth/forgePassword.vue
View file @
15d9ad15
<
template
>
<
template
>
<
div
class=
"h-screen overflow-hidden"
>
<
a-spin
:loading=
"loading"
class=
"h-screen overflow-hidden"
>
<div
ref=
"loginPage"
<div
ref=
"loginPage"
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto light-login-bgActive"
>
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto light-login-bgActive"
>
<loginHeader
/>
<loginHeader
/>
...
@@ -55,7 +55,7 @@
...
@@ -55,7 +55,7 @@
<
a
-
input
-
password
class
=
"formData-input"
<
a
-
input
-
password
class
=
"formData-input"
v
-
model
=
"formData.password"
v
-
model
=
"formData.password"
:
placeholder
=
"t('login.passwordRequiredReset')"
:
placeholder
=
"t('login.passwordRequiredReset')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
size
=
"large"
>
size
=
"large"
>
...
@@ -66,7 +66,7 @@
...
@@ -66,7 +66,7 @@
v
-
model
=
"formData.newPassword"
v
-
model
=
"formData.newPassword"
size
=
"large"
size
=
"large"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
>
>
...
@@ -109,10 +109,10 @@
...
@@ -109,10 +109,10 @@
<
/div
>
<
/div
>
<
/div> --
>
<
/div> --
>
<
/div
>
<
/div
>
<
/
div
>
<
/
a-spin
>
<
/template
>
<
/template
>
<
script
setup
lang
=
"ts"
>
<
script
setup
lang
=
"ts"
>
import
{
reactive
,
ref
,
watch
,
computed
}
from
"vue"
;
import
{
reactive
,
ref
,
watch
,
computed
,
onMounted
}
from
"vue"
;
import
{
useI18n
}
from
"vue-i18n"
;
import
{
useI18n
}
from
"vue-i18n"
;
import
{
useSystemConfigStore
}
from
'@/stores/index'
import
{
useSystemConfigStore
}
from
'@/stores/index'
import
loginHeader
from
"./components/header.vue"
;
import
loginHeader
from
"./components/header.vue"
;
...
@@ -177,7 +177,7 @@ const rules = computed(() => ({
...
@@ -177,7 +177,7 @@ const rules = computed(() => ({
}
))
}
))
const
loginPage
=
ref
(
null
as
unknown
as
HTMLElement
)
const
loginPage
=
ref
(
null
as
unknown
as
HTMLElement
)
const
loading
=
ref
(
fals
e
)
const
loading
=
ref
(
tru
e
)
const
currentStep
=
ref
(
1
)
const
currentStep
=
ref
(
1
)
// 判断用户是否已登录
// 判断用户是否已登录
...
@@ -449,6 +449,11 @@ const init = async () => {
...
@@ -449,6 +449,11 @@ const init = async () => {
init
()
init
()
onMounted
(()
=>
{
setTimeout
(()
=>
{
loading
.
value
=
false
}
,
500
)
}
)
<
/script
>
<
/script
>
<
style
scoped
lang
=
"scss"
>
<
style
scoped
lang
=
"scss"
>
.
light
-
login
-
bg
{
.
light
-
login
-
bg
{
...
...
src/views/auth/register.vue
View file @
15d9ad15
<
template
>
<
template
>
<
div
class=
"h-screen overflow-hidden"
>
<
a-spin
:loading=
"loading"
class=
"h-screen overflow-hidden"
>
<div
ref=
"loginPage"
<div
ref=
"loginPage"
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto"
class=
"light-login-bg pl-[85px] pr-[98px] pt-[33px] h-full !overflow-y-auto"
:class=
"[currentStep
<3
?'
light-login-bg
'
:
'
light-login-bgActive
']"
>
:class=
"[currentStep
<3
?'
light-login-bg
'
:
'
light-login-bgActive
']"
>
...
@@ -63,7 +63,7 @@
...
@@ -63,7 +63,7 @@
v
-
model
=
"formData.password"
v
-
model
=
"formData.password"
size
=
"large"
size
=
"large"
:
placeholder
=
"t('login.setPasswordRequired')"
:
placeholder
=
"t('login.setPasswordRequired')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
>
>
...
@@ -77,7 +77,7 @@
...
@@ -77,7 +77,7 @@
v
-
model
=
"formData.confirmPassword"
v
-
model
=
"formData.confirmPassword"
size
=
"large"
size
=
"large"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
>
>
...
@@ -221,10 +221,10 @@
...
@@ -221,10 +221,10 @@
<
/div
>
<
/div
>
<
registerSuccess
v
-
if
=
"currentStep==3"
/>
<
registerSuccess
v
-
if
=
"currentStep==3"
/>
<
/div
>
<
/div
>
<
/
div
>
<
/
a-spin
>
<
/template
>
<
/template
>
<
script
setup
lang
=
"ts"
>
<
script
setup
lang
=
"ts"
>
import
{
reactive
,
ref
,
watch
,
computed
,
provide
}
from
"vue"
;
import
{
reactive
,
ref
,
watch
,
computed
,
provide
,
onMounted
}
from
"vue"
;
import
{
useI18n
}
from
"vue-i18n"
;
import
{
useI18n
}
from
"vue-i18n"
;
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
{
Message
}
from
'@arco-design/web-vue'
import
{
Message
}
from
'@arco-design/web-vue'
...
@@ -331,7 +331,7 @@ const rules = computed(() => ({
...
@@ -331,7 +331,7 @@ const rules = computed(() => ({
const
loginPage
=
ref
(
null
)
const
loginPage
=
ref
(
null
)
const
currentStep
=
ref
(
1
)
const
currentStep
=
ref
(
1
)
const
loading
=
ref
(
fals
e
)
const
loading
=
ref
(
tru
e
)
const
isFromOta
=
ref
(
false
)
// 是否从 OTA 授权进入
const
isFromOta
=
ref
(
false
)
// 是否从 OTA 授权进入
provide
(
'currentStep'
,
currentStep
.
value
)
provide
(
'currentStep'
,
currentStep
.
value
)
...
@@ -673,6 +673,11 @@ init()
...
@@ -673,6 +673,11 @@ init()
getSimples
()
getSimples
()
onMounted
(()
=>
{
setTimeout
(()
=>
{
loading
.
value
=
false
}
,
500
)
}
)
<
/script
>
<
/script
>
<
style
>
<
style
>
.
light
-
login
-
bg
{
.
light
-
login
-
bg
{
...
...
src/views/personalCenter/accountCenter.vue
View file @
15d9ad15
<
template
>
<
template
>
<div
class=
"w-[977px] h-full flex flex-col flex-shrink-0 overflow-hidden"
>
<a-spin
:loading=
"loading"
class=
"w-[977px] h-full flex flex-col flex-shrink-0 overflow-hidden"
>
账户中心
<a-scrollbar
class=
"max-h-[815px] overflow-auto"
ref=
"scrollContainer"
>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"w-[102px] mr-[43px]"
>
<div
class=
"customPrimary-bg-7 rounded-full w-[50px] h-[50px] flex justify-center items-center"
>
<img
class=
"w-[29px] h-[36px]"
src=
"../../assets/images/personal/mm.png"
alt=
""
/>
</div>
</div>
</div>
<div
class=
"text-sm w-[200px] SourceHanSansCN"
>
<span>
{{
t
(
'personal.securityLevel'
)
}}
:
</span>
<span
class=
"customPrimary-9"
>
{{
t
(
'personal.low'
)
}}
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"w-[325px] customColor-text-7"
>
{{
t
(
'personal.passwordSecurityTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end"
>
<a-button
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
t
(
'login.resetPassword'
)
}}
</span>
</a-button>
</div>
</div>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"w-[102px] mr-[43px]"
>
<div
class=
"customPrimary-bg-7 rounded-full w-[50px] h-[50px] flex justify-center items-center"
>
<img
class=
"w-[24px] h-[35px]"
src=
"../../assets/images/personal/sj.png"
alt=
""
/>
</div>
</div>
<div
class=
"text-sm w-[200px] font-medium"
>
<span>
180****1613
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"customColor-text-7"
>
{{
t
(
'personal.bindPhoneTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end"
>
<a-button
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
t
(
'personal.changeBindData'
)
}}
</span>
</a-button>
<a-button
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
t
(
'personal.unBind'
)
}}
</span>
</a-button>
</div>
</div>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"w-[102px] mr-[43px]"
>
<div
class=
"customPrimary-bg-7 rounded-full w-[50px] h-[50px] flex justify-center items-center"
>
<img
class=
"w-[35px] h-[25px]"
src=
"../../assets/images/personal/email.png"
alt=
""
/>
</div>
</div>
<div
class=
"text-sm w-[200px] font-medium"
>
<span>
{{
userInfor
&&
userInfor
.
email
?
userInfor
.
email
:
t
(
'personal.notYetBin'
)
}}
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"customColor-text-7"
>
{{
t
(
'personal.bindEmailTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end"
>
<a-button
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
t
(
'personal.changeBindData'
)
}}
</span>
</a-button>
<a-button
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
t
(
'personal.unBind'
)
}}
</span>
</a-button>
</div>
</div>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"w-[102px] mr-[43px]"
>
<div
class=
"rounded-full w-[52px] h-[52px] flex justify-center items-center"
>
<img
class=
"w-[42px] h-[35px]"
src=
"../../assets/images/personal/wx.png"
alt=
""
/>
</div>
</div>
<div
class=
"text-sm w-[200px] font-medium"
>
<span>
{{
WeChatInfor
?
WeChatInfor
.
providerDisplayName
:
t
(
'personal.notYetBin'
)
}}
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"customColor-text-7"
>
{{
t
(
'personal.bindWechatTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end"
>
<a-button
@
click=
"WeChatInfor?unBindWeChatAccount(2):getAppIdRedirectUri()"
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
WeChatInfor
?
t
(
'personal.unBind'
):
t
(
'personal.goBind'
)
}}
</span>
</a-button>
</div>
</div>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"rounded-full w-[102px] h-[50px] flex justify-center items-center mr-[43px]"
>
<img
class=
"w-[102px]"
src=
"../../assets/images/personal/google.png"
alt=
""
/>
</div>
<div
class=
"text-sm w-[200px] font-medium"
>
<span>
{{
GoogleInfor
?
GoogleInfor
.
providerDisplayName
:
t
(
'personal.notYetBin'
)
}}
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"customColor-text-7"
>
{{
t
(
'personal.bindGoogleTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end relative"
>
<a-button
@
click=
"GoogleInfor?unBindWeChatAccount(1):''"
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
WeChatInfor
?
t
(
'personal.unBind'
):
t
(
'personal.unBind'
)
}}
</span>
<div
v-if=
"!GoogleInfor"
ref=
"googleButtonContainer"
class=
"g-signin2 rounded-full !rounded-[13px] overflow-hidden
w-full h-full z-[6] flex items-center justify-center cursor-pointer
absolute top-0 left-0 bottom-0 right-0"
data-onsuccess=
"onSignIn"
data-theme=
"dark"
@
click
.
stop=
""
>
</div>
</a-button>
</div>
</div>
<div
class=
"accountCenter rounded-[14px] border-[1px] mb-[28px] flex items-center py-[30px] pl-[43px] pr-[35px]"
>
<div
class=
"rounded-full w-[102px] h-[52px] flex justify-center items-center mr-[43px]"
>
<img
class=
"w-[43px] h-[41px]"
src=
"../../assets/images/personal/line.png"
alt=
""
/>
</div>
<div
class=
"text-sm w-[200px] font-medium"
>
<span>
{{
LnlineInfor
?
LnlineInfor
.
providerDisplayName
:
t
(
'personal.notYetBin'
)
}}
</span>
</div>
<div
class=
"w-[415px] leading-[20px] SourceHanSansCN"
>
<div
class=
"customColor-text-7"
>
{{
t
(
'personal.bindLineTip'
)
}}
</div>
</div>
<div
class=
"flex-1 flex justify-end items-end"
>
<a-button
@
click=
"LnlineInfor?unBindWeChatAccount(3):''"
type=
"primary"
size=
"large"
class=
"acc-button !h-[34px] !rounded-[8px] !text-sm !px-[13px]"
>
<span
class=
"customPrimary-6 font-medium "
>
{{
LnlineInfor
?
t
(
'personal.unBind'
):
t
(
'personal.goBind'
)
}}
</span>
</a-button>
</div>
</div>
</a-scrollbar>
</a-spin>
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
ref
,
reactive
,
onMounted
,
watch
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
useUserStore
}
from
'@/stores/user'
import
{
useUserStore
}
from
'@/stores/user'
import
{
useSystemConfigStore
}
from
'@/stores/index'
import
{
useSystemConfigStore
}
from
'@/stores/index'
// 引入订单状态枚举
import
{
Message
}
from
'@arco-design/web-vue'
import
OrderStatusEnum
from
'@/utils/orderStautsEnum'
import
{
query
}
from
'@/utils/common'
import
{
useRouter
}
from
'vue-router'
import
UserService
from
'@/services/UserService'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
();
const
router
=
useRouter
()
const
userStore
=
useUserStore
()
const
userStore
=
useUserStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
systemConfig
=
reactive
({
tenantId
:
systemConfigStore
.
tenantId
||
null
,
distributorId
:
systemConfigStore
.
distributorId
as
any
,
})
const
loginType
=
ref
(
userStore
.
loginType
)
const
userInfor
=
ref
(
null
as
any
)
const
orderList
=
ref
<
any
>
([])
const
orderList
=
ref
<
any
>
([])
const
currentStatus
=
ref
(
0
)
const
currentStatus
=
ref
(
0
)
orderList
.
value
.
push
({
value
:
0
,
lable
:
t
(
'personal.orderStatus.ALL'
),
})
orderList
.
value
.
push
({
value
:
OrderStatusEnum
.
UN_PAY
.
value
,
lable
:
OrderStatusEnum
.
UN_PAY
.
desc
,
})
orderList
.
value
.
push
({
value
:
OrderStatusEnum
.
PAYED
.
value
,
lable
:
OrderStatusEnum
.
PAYED
.
desc
,
})
orderList
.
value
.
push
({
value
:
OrderStatusEnum
.
FINISH
.
value
,
lable
:
OrderStatusEnum
.
FINISH
.
desc
,
})
orderList
.
value
.
push
({
value
:
OrderStatusEnum
.
CANCEL
.
value
,
lable
:
OrderStatusEnum
.
CANCEL
.
desc
,
})
const
queryParams
=
ref
<
any
>
({
const
queryParams
=
ref
<
any
>
({
pageIndex
:
1
,
pageIndex
:
1
,
pageSize
:
10
,
pageSize
:
10
,
orderStatus
:
currentStatus
.
value
,
orderStatus
:
currentStatus
.
value
,
})
})
const
dataList
=
ref
<
any
>
([])
const
loding
=
ref
(
false
)
const
loading
=
ref
(
true
)
const
noMoreData
=
ref
(
false
)
// 绑定Google账号
const
scrollContainer
=
ref
<
any
>
(
null
)
const
googleButtonContainer
=
ref
(
null
);
const
scrollThreshold
=
0
// 阈值,距离底部多少距离时触发加载
// 绑定账号信息
const
changeStatus
=
(
key
:
number
)
=>
{
const
WeChatInfor
=
ref
(
null
as
any
)
currentStatus
.
value
=
key
const
GoogleInfor
=
ref
(
null
as
any
)
const
LnlineInfor
=
ref
(
null
as
any
)
// 打开微信绑定账号弹窗信息
const
openInfo
=
ref
({}
as
any
)
watch
(()
=>
userStore
.
personalInfor
,
(
newVal
,
oldVal
)
=>
{
userInfor
.
value
=
newVal
userInfor
.
value
?.
externalLoginList
.
forEach
((
x
:
any
)
=>
{
if
(
x
.
provider
==
'WeChat'
){
WeChatInfor
.
value
=
x
}
if
(
x
.
provider
==
'Google'
){
GoogleInfor
.
value
=
x
}
if
(
x
.
provider
==
'Lnline'
){
LnlineInfor
.
value
=
x
}
});
})
const
unBindWeChatAccount
=
async
(
type
:
number
)
=>
{
loading
.
value
=
true
let
ProviderType
=
null
as
string
if
(
type
==
2
){
ProviderType
=
WeChatInfor
.
value
.
id
}
else
if
(
type
==
1
){
ProviderType
=
LnlineInfor
.
value
.
id
}
else
if
(
type
==
3
){
ProviderType
=
GoogleInfor
.
value
.
id
}
try
{
const
response
=
await
UserService
.
externalunbindBind
(
systemConfig
.
tenantId
,
ProviderType
)
if
(
response
.
status
==
'SUCCESS'
){
getPersonalInfor
()
}
}
catch
(
error
:
any
)
{
Message
.
error
(
error
.
message
||
t
(
'login.bindGoogleFailed'
))
}
finally
{
loading
.
value
=
false
}
}
const
getPersonalInfor
=
async
()
=>
{
const
response
=
await
UserService
.
memberCenterAsync
(
systemConfig
.
tenantId
)
userStore
.
setPersonalInfor
(
response
)
}
}
const
loadData
=
async
()
=>
{
// 获取微信绑定AppID 域名 重定向页面
const
getAppIdRedirectUri
=
async
()
=>
{
// const { AppID, State, OpenRedirectUri } = openInfo.value;
// let redirect_uri = OpenRedirectUri;
// const url = `https://open.weixin.qq.com/connect/qrconnect?appid=${AppID}&redirect_uri=${encodeURIComponent('https://www.oytour.com/#/login')}&response_type=code&scope=snsapi_login&state=${State}&wechat_redirect=${redirect_uri}`;
// window.location.href = url;
// return
loading
.
value
=
true
try
{
// 获取微信绑定AppID 域名
const
response
=
await
userStore
.
getUserWechatAppIdAsync
(
systemConfig
.
tenantId
?.
toString
()
||
''
,
2
)
if
(
response
.
status
==
'SUCCESS'
)
{
openInfo
.
value
=
response
.
data
[
0
]
const
{
appId
,
redirectUri
}
=
openInfo
.
value
;
const
redirect_url
=
redirectUri
?
redirectUri
:
'https://www.oytour.com/#/login'
const
url
=
`https://open.weixin.qq.com/connect/qrconnect?appid=
${
appId
}
&redirect_uri=
${
encodeURIComponent
(
redirect_url
)}
&response_type=code&scope=snsapi_login&state=
${
1
}
&wechat_redirect=
${
redirect_url
}
`
;
window
.
location
.
href
=
url
;
}
}
catch
(
error
:
any
)
{
Message
.
error
(
error
.
message
)
}
finally
{
loading
.
value
=
false
}
}
}
// 处理 div 滚动事件(核心修改)
// 微信绑定
const
handleDivScroll
=
(
e
:
any
)
=>
{
const
useWechatBind
=
async
(
code
:
string
)
=>
{
if
(
loding
.
value
||
noMoreData
.
value
)
return
;
loading
.
value
=
true
const
container
=
e
.
target
;
try
{
if
(
!
container
)
return
;
const
response
=
await
userStore
.
setUserWechatBindAsync
(
systemConfig
.
tenantId
?.
toString
()
||
''
,
code
,
systemConfig
.
distributorId
,
null
,
''
)
// 计算滚动位置(兼容不同浏览器)
if
(
response
.
status
==
'SUCCESS'
)
{
const
scrollTop
=
container
.
scrollTop
;
Message
.
success
(
t
(
'personal.bindWechatSuccess'
))
const
clientHeight
=
container
.
clientHeight
;
getPersonalInfor
()
const
scrollHeight
=
container
.
scrollHeight
;
}
// 触底条件:滚动距离 + 可见高度 ≥ 总高度 - 阈值
router
.
push
(
'/accountCenter'
)
if
(
scrollTop
+
clientHeight
>=
scrollHeight
-
scrollThreshold
)
{
}
catch
(
error
:
any
)
{
noMoreData
.
value
=
false
// Message.error(error.message)
queryParams
.
value
.
pageIndex
++
router
.
push
(
'/accountCenter'
)
loadData
();
// 加载下一页
}
finally
{
loading
.
value
=
false
}
}
}
}
// 渲染 Google 登录按钮
const
renderGoogleButton
=
()
=>
{
try
{
// 渲染 Google 登录按钮
window
.
google
.
accounts
.
id
.
initialize
({
client_id
:
'532164762940-vk65sge5jab1eq8mgbv1srh672ehnkff.apps.googleusercontent.com'
,
callback
:
handleSignInSuccess
,
error_callback
:
handleSignInError
,
ux_mode
:
'popup'
// 可选:popup/redirect
});
window
.
google
.
accounts
.
id
.
renderButton
(
googleButtonContainer
.
value
,
{
theme
:
'outline'
,
size
:
'large'
}
);
// window.google.accounts.id.configure({
// language: locale.value // 支持的语言代码
// });
}
catch
(
error
)
{
console
.
error
(
'Error rendering Google Sign-In button:'
,
error
);
}
};
// Google 绑定回调
const
handleSignInSuccess
=
async
(
googleUser
:
any
)
=>
{
loading
.
value
=
true
try
{
// 获取授权码
const
response
=
await
userStore
.
setUserGoogleBindAsync
(
systemConfig
.
tenantId
?.
toString
()
||
''
,
googleUser
.
credential
,
systemConfig
.
distributorId
,
null
,
''
)
if
(
response
.
status
==
'SUCCESS'
)
{
Message
.
success
(
t
(
'personal.bindGoogleSuccess'
))
getPersonalInfor
()
}
}
catch
(
error
:
any
)
{
Message
.
error
(
error
.
message
||
t
(
'login.bindGoogleFailed'
))
}
finally
{
loading
.
value
=
false
}
};
// 处理登录错误
const
handleSignInError
=
(
error
:
any
)
=>
{
console
.
error
(
'绑定错误:'
,
error
);
Message
.
error
(
t
(
'login.googleLoginFailed'
))
};
// 初始化谷歌绑定
const
initGoogleSDK
=
async
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
// 动态加载 Google SDK
const
script
=
document
.
createElement
(
'script'
);
script
.
src
=
'https://accounts.google.com/gsi/client'
;
script
.
async
=
true
;
script
.
defer
=
true
;
script
.
onload
=
resolve
;
script
.
onerror
=
reject
;
document
.
head
.
appendChild
(
script
);
});
};
onMounted
(
async
()
=>
{
const
queryParams
=
query
()
const
code
=
queryParams
.
code
if
(
code
)
{
useWechatBind
(
code
)
}
try
{
if
(
loginType
.
value
!=
1
)
{
await
initGoogleSDK
();
// 确保 SDK 加载完成后渲染按钮
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
));
renderGoogleButton
()
}
setTimeout
(()
=>
{
loading
.
value
=
false
});
}
catch
(
error
)
{
console
.
error
(
'SDK 初始化失败:'
,
error
);
}
});
</
script
>
</
script
>
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
.
myOrderData
{
.
accountCenter
{
background
:
#FBFBFA
;
border-color
:
var
(
--
customPrimary-4
)
;
}
}
.
myOrder-text-l
{
.
acc-button
{
color
:
rgb
(
var
(
--
gray-7
)
);
background-color
:
rgba
(
225
,
225
,
225
,
0
);
}
}
:deep
(
.arco-scrollbar-track-direction-vertical
)
{
.acc-button
:hover
{
background-color
:
var
(
--
customPrimary-7
);
}
:deep
(
.arco-divider-horizontal
)
{
width
:
100%
;
min-width
:
100%
;
border-bottom
:
1px
solid
#EEF0E8
;
}
:deep
(
.arco-input
)
{
text-indent
:
4px
;
}
:deep
(
.arco-form-item-label-required-symbol
)
{
display
:
none
;
display
:
none
;
}
}
.myOrder-time
{
:deep
(
.arco-form-item-message
)
{
color
:
rgb
(
var
(
--
gray-6
));
color
:
rgba
(
255
,
0
,
0
,
0
);
}
:deep
(
.S9gUrf-YoZ4jf
)
{
opacity
:
0
;
}
:deep
(
.arco-scrollbar-track-direction-vertical
)
{
display
:
none
;
}
}
</
style
>
</
style
>
\ No newline at end of file
src/views/personalCenter/components/LeftView.vue
View file @
15d9ad15
<
template
>
<
template
>
<div
class=
"flex flex flex-col w-[198px]"
>
<div
class=
"flex flex flex-col w-[198px]"
>
<div
class=
"h-full bg-[#F9F9F7] rounded-[14px]"
>
<div
class=
"h-full bg-[#F9F9F7] rounded-[14px]"
>
<template
v-if=
"userInfo"
>
<div
class=
"mt-[37px] flex justify-center items-center"
>
<div
class=
"mt-[37px] flex justify-center items-center"
>
<a-avatar
class=
"LeftViewImg cursor-pointer flex-shrink-0 !w-[80px] !h-[80px]"
>
<a-avatar
class=
"LeftViewImg cursor-pointer flex-shrink-0 !w-[80px] !h-[80px]"
>
<img
class=
"w-full h-full cursor-pointer"
<img
class=
"w-full h-full cursor-pointer"
...
@@ -37,6 +38,7 @@
...
@@ -37,6 +38,7 @@
</span>
</span>
</div>
</div>
</div>
</div>
</
template
>
<a-divider
class=
"border-[#ECECE7]"
/>
<a-divider
class=
"border-[#ECECE7]"
/>
<div
class=
"flex items-center cursor-pointer py-[7px] rounded-[8px] pl-[27px] mb-[5px]"
>
<div
class=
"flex items-center cursor-pointer py-[7px] rounded-[8px] pl-[27px] mb-[5px]"
>
...
@@ -118,24 +120,24 @@ const userStore = useUserStore()
...
@@ -118,24 +120,24 @@ const userStore = useUserStore()
const
systemConfigStore
=
useSystemConfigStore
()
const
systemConfigStore
=
useSystemConfigStore
()
const
tenantId
=
computed
(()
=>
systemConfigStore
.
tenantId
)
const
tenantId
=
computed
(()
=>
systemConfigStore
.
tenantId
)
const
userData
=
ref
({}
as
any
)
const
userInfo
=
ref
(
null
as
any
)
userData
.
value
=
inject
(
'userData'
)
const
userInfo
=
computed
(()
=>
userData
.
value
.
userInfo
)
const
activeMenu
=
ref
(
props
.
activeMenu
)
const
activeMenu
=
ref
(
props
.
activeMenu
)
watch
(()
=>
props
.
activeMenu
,
(
newVal
,
oldVal
)
=>
{
watch
(()
=>
props
.
activeMenu
,
(
newVal
,
oldVal
)
=>
{
activeMenu
.
value
=
newVal
activeMenu
.
value
=
newVal
})
})
watch
(()
=>
userStore
.
personalInfor
,
(
newVal
,
oldVal
)
=>
{
userInfo
.
value
=
newVal
})
const
goPage
=
(
path
:
string
)
=>
{
const
goPage
=
(
path
:
string
)
=>
{
router
.
push
(
path
)
router
.
push
(
path
)
}
}
const
getPersonalInfor
=
async
()
=>
{
const
getPersonalInfor
=
async
()
=>
{
const
response
=
await
UserService
.
memberCenterAsync
(
tenantId
.
value
)
const
response
=
await
UserService
.
memberCenterAsync
(
tenantId
.
value
)
userData
.
value
.
userInfo
=
response
userStore
.
setPersonalInfor
(
response
)
userStore
.
setPersonalInfo
(
response
)
}
}
getPersonalInfor
()
getPersonalInfor
()
</
script
>
</
script
>
...
...
src/views/personalCenter/components/myOrder/editEmail.vue
View file @
15d9ad15
...
@@ -40,7 +40,7 @@
...
@@ -40,7 +40,7 @@
v
-
model
=
"formData.password"
v
-
model
=
"formData.password"
size
=
"large"
size
=
"large"
:
placeholder
=
"t('login.setPasswordRequired')"
:
placeholder
=
"t('login.setPasswordRequired')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
>
>
...
@@ -54,7 +54,7 @@
...
@@ -54,7 +54,7 @@
v
-
model
=
"formData.newPassword"
v
-
model
=
"formData.newPassword"
size
=
"large"
size
=
"large"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
placeholder
=
"t('login.confirmPasswordRequired')"
:
maxLength
=
"
8
"
:
maxLength
=
"
100
"
:
defaultVisibility
=
"true"
:
defaultVisibility
=
"true"
:
invisible
-
button
=
"false"
:
invisible
-
button
=
"false"
>
>
...
@@ -64,7 +64,7 @@
...
@@ -64,7 +64,7 @@
<
div
class
=
"mt-[27px] w-full flex flex-col items-center items-center-button mb-[20px]"
>
<
div
class
=
"mt-[27px] w-full flex flex-col items-center items-center-button mb-[20px]"
>
<
div
class
=
"w-full "
<
div
class
=
"w-full "
:
class
=
"[(formData.email&&formData.verificationCode&&formData.verificationCode.length==6)
:
class
=
"[(formData.email&&formData.verificationCode&&formData.verificationCode.length==6)
||(data.email&&formData.password&&formData.password.length
=
=8
||(data.email&&formData.password&&formData.password.length
>
=8
&&formData.password==formData.newPassword)?'isClick':'']"
>
&&formData.password==formData.newPassword)?'isClick':'']"
>
<
a
-
button
<
a
-
button
type
=
"primary"
type
=
"primary"
...
@@ -103,7 +103,7 @@ const props = defineProps({
...
@@ -103,7 +103,7 @@ const props = defineProps({
}
)
}
)
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
(
e
:
'save-success'
,
email
:
string
):
void
(
e
:
'save-success'
):
void
}
>
()
}
>
()
const
{
t
}
=
useI18n
();
const
{
t
}
=
useI18n
();
...
@@ -410,7 +410,7 @@ const handleSubmit = async () => {
...
@@ -410,7 +410,7 @@ const handleSubmit = async () => {
const
response
=
await
userStore
.
setEmailAsync
(
registerData
)
const
response
=
await
userStore
.
setEmailAsync
(
registerData
)
if
(
response
.
status
==
'SUCCESS'
)
{
if
(
response
.
status
==
'SUCCESS'
)
{
emit
(
'save-success'
,
formData
.
email
)
emit
(
'save-success'
)
}
}
}
catch
(
error
:
any
)
{
}
catch
(
error
:
any
)
{
console
.
error
(
'提交失败:'
,
error
)
console
.
error
(
'提交失败:'
,
error
)
...
...
src/views/personalCenter/index.vue
View file @
15d9ad15
...
@@ -13,7 +13,7 @@
...
@@ -13,7 +13,7 @@
</div>
</div>
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
reactive
,
provide
}
from
'vue'
import
{
ref
,
computed
,
reactive
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
import
{
useRoute
}
from
'vue-router'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
LeftView
from
'./components/LeftView.vue'
import
LeftView
from
'./components/LeftView.vue'
...
@@ -69,10 +69,6 @@ const menuList = ref([
...
@@ -69,10 +69,6 @@ const menuList = ref([
key
:
'distributionCenter'
,
key
:
'distributionCenter'
,
},
},
])
])
const
userData
=
reactive
({
userInfo
:
{},
})
provide
(
'userData'
,
userData
)
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
...
...
src/views/personalCenter/myOrder.vue
View file @
15d9ad15
<
template
>
<
template
>
<
div
class=
"w-[977px] h-full flex flex-col flex-shrink-0"
>
<
a-spin
:loading=
"loading"
class=
"w-[977px] h-full flex flex-col flex-shrink-0"
>
<div
class=
"myOrderData rounded-[14px] flex justify-between pt-[22px] pb-[19px] pl-[16px] relative"
>
<div
class=
"myOrderData rounded-[14px] flex justify-between pt-[22px] pb-[19px] pl-[16px] relative"
>
<div
class=
"flex items-center p-[20px]"
>
<div
class=
"flex items-center p-[20px]"
>
<div
class=
"bg-[#F3F3F2] rounded-full w-[52px] h-[52px] flex justify-center items-center mr-[16px]"
>
<div
class=
"bg-[#F3F3F2] rounded-full w-[52px] h-[52px] flex justify-center items-center mr-[16px]"
>
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
{{
t
(
'personal.bindingEmail'
)
}}
{{
t
(
'personal.bindingEmail'
)
}}
</div>
</div>
<div
class=
"mt-[10px] flex items-center"
>
<div
class=
"mt-[10px] flex items-center"
>
<span
class=
"text-base font-normal"
>
{{
userInfo
?.
email
||
t
(
'personal.unbound'
)
}}
</span>
<span
class=
"text-base font-normal"
>
{{
userInfo
r
?.
email
||
t
(
'personal.unbound'
)
}}
</span>
<img
class=
"w-[15px] h-[15px] ml-[10px] cursor-pointer"
<img
class=
"w-[15px] h-[15px] ml-[10px] cursor-pointer"
src=
"../../assets/images/personal/pen.png"
src=
"../../assets/images/personal/pen.png"
alt=
""
@
click=
"editEmailClick"
/>
alt=
""
@
click=
"editEmailClick"
/>
...
@@ -50,7 +50,7 @@
...
@@ -50,7 +50,7 @@
</div>
</div>
<div
class=
"flex justify-between items-center text-base relative"
>
<div
class=
"flex justify-between items-center text-base relative"
>
<div
class=
"flex"
>
<div
class=
"flex"
>
<div
v-for=
"(item,index) in order
List
"
<div
v-for=
"(item,index) in order
Types
"
class=
"myOrder-status px-[13px] py-[22px] cursor-pointer relative"
class=
"myOrder-status px-[13px] py-[22px] cursor-pointer relative"
:class=
"[currentStatus==item.value?'active font-medium':'font-light']"
:class=
"[currentStatus==item.value?'active font-medium':'font-light']"
@
click=
"changeStatus(item.value)"
>
{{
item
.
label
}}
@
click=
"changeStatus(item.value)"
>
{{
item
.
label
}}
...
@@ -87,7 +87,7 @@
...
@@ -87,7 +87,7 @@
</div>
</div>
<a-divider
class=
"!m-[0]"
/>
<a-divider
class=
"!m-[0]"
/>
<div
class=
"flex-1 flex flex-col flex-shrink-0"
<div
class=
"flex-1 flex flex-col flex-shrink-0"
:class=
"[!(dataList.length==0&&!loding)?'items-center justify-center':'']"
>
:class=
"[!(dataList.length==0&&!lo
a
ding)?'items-center justify-center':'']"
>
<a-scrollbar
class=
"max-h-[615px] overflow-auto"
<a-scrollbar
class=
"max-h-[615px] overflow-auto"
@
scroll=
"handleDivScroll"
@
scroll=
"handleDivScroll"
ref=
"scrollContainer"
>
ref=
"scrollContainer"
>
...
@@ -127,26 +127,26 @@
...
@@ -127,26 +127,26 @@
</div>
</div>
</div>
</div>
</div>
</div>
<div
v-else-if=
"dataList.length==0&&!loding"
class=
"flex flex-col items-center justify-center"
>
<div
v-else-if=
"dataList.length==0&&!lo
a
ding"
class=
"flex flex-col items-center justify-center"
>
<img
class=
"w-[250px] h-[213px]"
src=
"../../assets/images/personal/wsj.png"
alt=
""
/>
<img
class=
"w-[250px] h-[213px]"
src=
"../../assets/images/personal/wsj.png"
alt=
""
/>
<span
class=
"text-base SourceHanSansCN mt-[15px]"
>
{{ t('personal.noOrder') }}
</span>
<span
class=
"text-base SourceHanSansCN mt-[15px]"
>
{{ t('personal.noOrder') }}
</span>
</div>
</div>
</a-scrollbar>
</a-scrollbar>
</div>
</div>
</
div
>
</
a-spin
>
<Modal
class=
"ModalRef"
<Modal
class=
"ModalRef"
ref=
"ModalRef"
ref=
"ModalRef"
:modal-config=
"modalConfig"
:modal-config=
"modalConfig"
:modal-mask=
"true"
>
:modal-mask=
"true"
>
<
template
#
modal=
"{ visible }"
>
<
template
#
modal=
"{ visible }"
>
<editEmail
v-if=
"visible&&showType==1"
:data=
"userInfo"
<editEmail
v-if=
"visible&&showType==1"
:data=
"userInfo
r
"
@
save-success=
"handleSaveSuccess"
@
save-success=
"handleSaveSuccess"
/>
/>
</
template
>
</
template
>
</Modal>
</Modal>
</template>
</template>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
inject
,
computed
,
watch
}
from
'vue'
import
{
ref
,
inject
,
computed
,
watch
,
onMounted
}
from
'vue'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
useUserStore
}
from
'@/stores/user'
import
{
useUserStore
}
from
'@/stores/user'
import
{
useSystemConfigStore
}
from
'@/stores/index'
import
{
useSystemConfigStore
}
from
'@/stores/index'
...
@@ -162,7 +162,7 @@ const systemConfigStore = useSystemConfigStore()
...
@@ -162,7 +162,7 @@ const systemConfigStore = useSystemConfigStore()
const
userData
=
ref
({}
as
any
)
const
userData
=
ref
({}
as
any
)
userData
.
value
=
inject
(
'userData'
)
userData
.
value
=
inject
(
'userData'
)
const
userInfo
=
ref
(
userStore
.
personalInfo
)
const
userInfo
r
=
ref
(
null
as
any
)
const
showType
=
ref
(
null
as
number
|
null
)
const
showType
=
ref
(
null
as
number
|
null
)
const
modalConfig
=
ref
({
const
modalConfig
=
ref
({
...
@@ -183,6 +183,7 @@ const modalConfig = ref({
...
@@ -183,6 +183,7 @@ const modalConfig = ref({
})
})
const
ModalRef
=
ref
<
any
>
(
null
)
const
ModalRef
=
ref
<
any
>
(
null
)
const
orderTypes
=
ref
<
any
>
([])
const
orderList
=
ref
<
any
>
([])
const
orderList
=
ref
<
any
>
([])
const
currentStatus
=
ref
(
0
)
const
currentStatus
=
ref
(
0
)
const
productTypeList
=
ref
<
any
>
([])
const
productTypeList
=
ref
<
any
>
([])
...
@@ -197,23 +198,23 @@ productTypeList.value.push({
...
@@ -197,23 +198,23 @@ productTypeList.value.push({
})
})
// console.log(productTypeList.value,'--------')
// console.log(productTypeList.value,'--------')
order
List
.
value
.
push
({
order
Types
.
value
.
push
({
value
:
0
,
value
:
0
,
label
:
t
(
'personal.orderStatus.ALL'
),
label
:
t
(
'personal.orderStatus.ALL'
),
})
})
order
List
.
value
.
push
({
order
Types
.
value
.
push
({
value
:
OrderStatusEnum
.
UN_PAY
.
value
,
value
:
OrderStatusEnum
.
UN_PAY
.
value
,
label
:
OrderStatusEnum
.
UN_PAY
.
desc
,
label
:
OrderStatusEnum
.
UN_PAY
.
desc
,
})
})
order
List
.
value
.
push
({
order
Types
.
value
.
push
({
value
:
OrderStatusEnum
.
PAYED
.
value
,
value
:
OrderStatusEnum
.
PAYED
.
value
,
label
:
OrderStatusEnum
.
PAYED
.
desc
,
label
:
OrderStatusEnum
.
PAYED
.
desc
,
})
})
order
List
.
value
.
push
({
order
Types
.
value
.
push
({
value
:
OrderStatusEnum
.
FINISH
.
value
,
value
:
OrderStatusEnum
.
FINISH
.
value
,
label
:
OrderStatusEnum
.
FINISH
.
desc
,
label
:
OrderStatusEnum
.
FINISH
.
desc
,
})
})
order
List
.
value
.
push
({
order
Types
.
value
.
push
({
value
:
OrderStatusEnum
.
CANCEL
.
value
,
value
:
OrderStatusEnum
.
CANCEL
.
value
,
label
:
OrderStatusEnum
.
CANCEL
.
desc
,
label
:
OrderStatusEnum
.
CANCEL
.
desc
,
})
})
...
@@ -224,27 +225,26 @@ const queryParams = ref<any>({
...
@@ -224,27 +225,26 @@ const queryParams = ref<any>({
orderStatus
:
currentStatus
.
value
,
orderStatus
:
currentStatus
.
value
,
})
})
const
dataList
=
ref
<
any
>
([])
const
dataList
=
ref
<
any
>
([])
const
lo
ding
=
ref
(
fals
e
)
const
lo
ading
=
ref
(
tru
e
)
const
noMoreData
=
ref
(
fals
e
)
const
noMoreData
=
ref
(
tru
e
)
const
scrollContainer
=
ref
<
any
>
(
null
)
const
scrollContainer
=
ref
<
any
>
(
null
)
const
scrollThreshold
=
0
// 阈值,距离底部多少距离时触发加载
const
scrollThreshold
=
0
// 阈值,距离底部多少距离时触发加载
watch
(()
=>
userStore
.
personalInfo
,
(
newVal
,
oldVal
)
=>
{
watch
(()
=>
userStore
.
personalInfo
r
,
(
newVal
,
oldVal
)
=>
{
if
(
newVal
!=
oldVal
){
if
(
newVal
!=
oldVal
){
userInfo
.
value
=
newVal
userInfo
r
.
value
=
newVal
}
}
})
})
const
handleSaveSuccess
=
(
email
:
string
)
=>
{
const
handleSaveSuccess
=
()
=>
{
ModalRef
.
value
.
handleCancel
()
ModalRef
.
value
.
handleCancel
()
userStore
.
setEmail
(
email
)
}
}
// 编辑邮箱 绑定邮箱
// 编辑邮箱 绑定邮箱
const
editEmailClick
=
async
()
=>
{
const
editEmailClick
=
async
()
=>
{
showType
.
value
=
1
showType
.
value
=
1
modalConfig
.
value
.
title
=
userInfo
.
value
.
email
?
t
(
'personal.editEmail'
):
t
(
'personal.binEmail'
)
modalConfig
.
value
.
title
=
userInfo
r
.
value
.
email
?
t
(
'personal.editEmail'
):
t
(
'personal.binEmail'
)
if
(
ModalRef
.
value
)
await
ModalRef
.
value
.
openModal
(
userInfo
.
value
)
if
(
ModalRef
.
value
)
await
ModalRef
.
value
.
openModal
(
userInfo
r
.
value
)
}
}
const
changeStatus
=
(
key
:
number
)
=>
{
const
changeStatus
=
(
key
:
number
)
=>
{
...
@@ -256,7 +256,7 @@ const loadData = async () => {
...
@@ -256,7 +256,7 @@ const loadData = async () => {
// 处理 div 滚动事件(核心修改)
// 处理 div 滚动事件(核心修改)
const
handleDivScroll
=
(
e
:
any
)
=>
{
const
handleDivScroll
=
(
e
:
any
)
=>
{
if
(
loding
.
value
||
noMoreData
.
value
)
return
;
if
(
lo
a
ding
.
value
||
noMoreData
.
value
)
return
;
const
container
=
e
.
target
;
const
container
=
e
.
target
;
if
(
!
container
)
return
;
if
(
!
container
)
return
;
// 计算滚动位置(兼容不同浏览器)
// 计算滚动位置(兼容不同浏览器)
...
@@ -271,6 +271,11 @@ const handleDivScroll = (e: any) => {
...
@@ -271,6 +271,11 @@ const handleDivScroll = (e: any) => {
}
}
}
}
onMounted
(()
=>
{
setTimeout
(()
=>
{
loading
.
value
=
false
})
})
</
script
>
</
script
>
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
.myOrderData
{
.myOrderData
{
...
...
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