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
2e541790
Commit
2e541790
authored
Nov 26, 2025
by
youjie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
绑定邮箱
parent
6332dc3f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
723 additions
and
26 deletions
+723
-26
App.vue
src/App.vue
+1
-0
modal.vue
src/components/common/modal.vue
+58
-0
zh-CN.ts
src/i18n/locales/zh-CN.ts
+4
-0
UserService.ts
src/services/UserService.ts
+33
-1
user.ts
src/stores/user.ts
+44
-3
Login.vue
src/views/auth/Login.vue
+2
-2
register.vue
src/views/auth/register.vue
+0
-12
LeftView.vue
src/views/personalCenter/components/LeftView.vue
+1
-0
editEmail.vue
src/views/personalCenter/components/myOrder/editEmail.vue
+528
-0
myOrder.vue
src/views/personalCenter/myOrder.vue
+52
-8
No files found.
src/App.vue
View file @
2e541790
...
...
@@ -66,6 +66,7 @@ onMounted(async () => {
</
template
>
<
style
lang=
"scss"
>
@import
'./assets/styles.css'
;
body
{
--arcoblue-10
:
245
,
246
,
240
;
//#F5F6F0
--arcoblue-9
:
255
,
151
,
7
;
//#FF9707
...
...
src/components/common/modal.vue
0 → 100644
View file @
2e541790
<
template
>
<a-modal
v-model:visible=
"modalVisible"
v-bind=
"modalConfig"
@
ok=
"handleOk"
@
cancel=
"handleCancel"
draggable
:mask=
"modalMask"
>
<slot
name=
"modal"
:visible=
"modalVisible"
:data=
"modalrData"
:close=
"handleCancel"
>
</slot>
</a-modal>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
;
const
props
=
defineProps
({
modalMask
:
{
type
:
Boolean
,
default
:
false
,
},
modalConfig
:
{
type
:
Object
,
default
:
null
,
},
});
const
emit
=
defineEmits
<
{
(
e
:
'modal-cancel'
):
void
(
e
:
'modal-ok'
,
key
:
string
):
void
}
>
()
const
modalrData
=
ref
(
null
);
const
modalVisible
=
ref
(
false
);
// 弹窗是否显示
const
handleOk
=
()
=>
{
modalrData
.
value
=
null
;
modalVisible
.
value
=
false
;
emit
(
'modal-ok'
,
'key'
);
};
const
handleCancel
=
()
=>
{
modalrData
.
value
=
null
;
modalVisible
.
value
=
false
;
emit
(
'modal-cancel'
);
};
const
openModal
=
(
data
:
null
|
any
)
=>
{
modalrData
.
value
=
data
;
modalVisible
.
value
=
true
;
}
defineExpose
({
openModal
,
handleCancel
,
})
</
script
>
\ No newline at end of file
src/i18n/locales/zh-CN.ts
View file @
2e541790
...
...
@@ -161,6 +161,10 @@ export default {
systemMessage
:
'系统消息'
,
orderType
:
'所有类型'
,
management
:
'管理'
,
editEmail
:
'编辑邮箱'
,
binEmail
:
'绑定邮箱'
,
bindEmailFailed
:
'绑定邮箱失败'
,
save
:
'保存'
,
},
// HTTP 错误状态码
httpError
:
{
...
...
src/services/UserService.ts
View file @
2e541790
...
...
@@ -92,6 +92,20 @@ export interface DistributorSelfRegisterDto {
wechatId
?:
null
|
string
;
}
/**
* 修改邮箱请求参数
*/
export
interface
setRegisterDto
{
/*** 电子邮箱(需要判断唯一,作为登录账号)*/
email
:
string
;
/*** 确认密码*/
newPassword
:
string
;
/*** 临时令牌token*/
temporaryToken
?:
null
|
string
;
/*** 租户ID(手动传入,用于多租户隔离)*/
tenantId
:
string
;
}
// ==================== 响应 DTO ====================
/**
...
...
@@ -357,7 +371,7 @@ class UserService {
providerType
}
// OtaRequest 的响应拦截器会返回 response.data
const
response
=
await
OtaRequest
.
pos
t
(
const
response
=
await
OtaRequest
.
ge
t
(
'/member-auth/third-config'
,
data
,
{
...
...
@@ -523,6 +537,24 @@ class UserService {
)
return
response
as
unknown
as
HttpResponse
}
/**
* 设置邮箱
* @param tenantId 租户ID(可选)
* @param email 邮箱地址
* @returns 验证结果
*/
static
async
resetEmail
(
tenantId
:
string
,
data
:
setRegisterDto
,):
Promise
<
HttpResponse
>
{
const
response
=
await
OtaRequest
.
post
(
'/member-auth/reset-email'
,
data
,
{
headers
:
tenantId
?
{
'__tenant'
:
tenantId
}
:
{}
}
)
return
response
as
unknown
as
HttpResponse
}
}
export
default
UserService
src/stores/user.ts
View file @
2e541790
...
...
@@ -4,10 +4,11 @@ import SecureLS from 'secure-ls'
import
UserService
,
{
type
DistributorSelfRegisterDto
,
ApplicationStatus
,
type
setRegisterDto
,
}
from
'@/services/UserService'
import
{
ResultMessage
}
from
'@/utils/message'
import
i18n
from
'@/i18n'
import
{
string
}
from
'three/tsl'
const
ls
=
new
SecureLS
({
isCompression
:
false
,
encryptionSecret
:
'38c31684-d00d-30dc-82e0-fad9eec46d1d'
,
...
...
@@ -40,8 +41,9 @@ export interface RegisterResult {
export
const
useUserStore
=
defineStore
(
'user'
,
{
state
:
()
=>
({
token
:
''
as
string
,
userInfo
:
{}
as
any
,
memberData
:
{}
as
any
,
userInfo
:
{}
as
any
,
// 登录用户信息
memberData
:
{}
as
any
,
// 登录会员数据
personalInfo
:
{}
as
any
,
// 个人信息
}),
getters
:
{
getUserToken
:
(
state
)
=>
{
...
...
@@ -53,6 +55,13 @@ export const useUserStore = defineStore('user', {
},
actions
:
{
/**
* 设置email
* @param email email
*/
setEmail
(
email
:
string
){
this
.
personalInfo
.
email
=
email
},
/**
* 设置用户头像
* @param photo 头像URL
...
...
@@ -60,6 +69,9 @@ export const useUserStore = defineStore('user', {
setPhoto
(
photo
:
string
){
this
.
memberData
.
photo
=
photo
},
setPersonalInfo
(
personalInfo
:
any
){
this
.
personalInfo
=
personalInfo
},
/**
* 账号密码登录 - 使用新的 UserService
* @param email 账号(邮箱)
...
...
@@ -227,6 +239,35 @@ export const useUserStore = defineStore('user', {
}
}
},
/**
* 设置邮箱
* @param data 邮箱信息
*/
async
setEmailAsync
(
data
:
setRegisterDto
):
Promise
<
UserLoginResult
>
{
try
{
const
response
=
await
UserService
.
resetEmail
(
data
.
tenantId
,
data
)
this
.
userInfo
.
email
=
data
.
email
return
{
status
:
'SUCCESS'
,
data
:
[
response
]
}
}
catch
(
error
:
any
)
{
console
.
error
(
'Bind email error:'
,
error
)
// 处理 ABP 错误响应
if
(
error
.
error
)
{
const
errorInfo
=
error
.
error
const
errorMessage
=
errorInfo
.
message
||
i18n
.
global
.
t
(
'login.bindEmailFailed'
)
ResultMessage
.
Error
(
errorMessage
)
}
else
{
ResultMessage
.
Error
(
error
.
message
||
i18n
.
global
.
t
(
'httpError.networkError'
))
}
return
{
status
:
'ERROR'
,
}
}
},
},
persist
:
{
storage
:
st
,
...
...
src/views/auth/Login.vue
View file @
2e541790
...
...
@@ -222,11 +222,11 @@ const getAppIdRedirectUri = async () => {
try
{
// 获取微信登录AppID 域名
const
response
=
await
userStore
.
getUserWechatAppIdAsync
(
loginMsg
.
tenantId
?.
toString
()
||
''
,
loginMsg
.
reType
||
2
)
console
.
log
(
'微信appid-----'
,
response
)
if
(
response
.
status
==
'SUCCESS'
)
{
openInfo
.
value
=
response
.
data
[
0
]
const
{
appId
,
redirectUri
}
=
openInfo
.
value
;
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&wechat_redirect=
${
redirectUri
}
`
;
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
)
{
...
...
src/views/auth/register.vue
View file @
2e541790
...
...
@@ -289,18 +289,6 @@ const rules = computed(() => ({
{
required
:
true
,
message
:
t
(
'login.emailRequired'
)
}
,
{
type
:
'email'
,
message
:
t
(
'login.emailInvalid'
)
}
],
emailNum
:
[
{
required
:
true
,
message
:
t
(
'login.emailFormat'
),
}
,
],
emailSuffix
:
[
{
required
:
true
,
message
:
t
(
'login.emailFormat'
),
}
,
],
verificationCode
:
[
{
required
:
true
,
...
...
src/views/personalCenter/components/LeftView.vue
View file @
2e541790
...
...
@@ -135,6 +135,7 @@ const goPage = (path:string) => {
const
getPersonalInfor
=
async
()
=>
{
const
response
=
await
UserService
.
memberCenterAsync
(
tenantId
.
value
)
userData
.
value
.
userInfo
=
response
userStore
.
setPersonalInfo
(
response
)
}
getPersonalInfor
()
</
script
>
...
...
src/views/personalCenter/components/myOrder/editEmail.vue
0 → 100644
View file @
2e541790
This diff is collapsed.
Click to expand it.
src/views/personalCenter/myOrder.vue
View file @
2e541790
<
template
>
<div
class=
"w-[977px] h-full flex flex-col flex-shrink-0
overflow-hidden
"
>
<div
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=
"flex items-center p-[20px]"
>
<div
class=
"bg-[#F3F3F2] rounded-full w-[52px] h-[52px] flex justify-center items-center mr-[16px]"
>
...
...
@@ -11,7 +11,9 @@
</div>
<div
class=
"mt-[10px] flex items-center"
>
<span
class=
"text-base font-normal"
>
{{
userInfo
?.
email
||
t
(
'personal.unbound'
)
}}
</span>
<img
class=
"w-[15px] h-[15px] ml-[10px] cursor-pointer"
src=
"../../assets/images/personal/pen.png"
alt=
""
/>
<img
class=
"w-[15px] h-[15px] ml-[10px] cursor-pointer"
src=
"../../assets/images/personal/pen.png"
alt=
""
@
click=
"editEmailClick"
/>
</div>
</div>
</div>
...
...
@@ -97,7 +99,7 @@
<span
class=
"myOrder-number"
>
1128143664585527
</span>
<span
class=
"ml-[45px] SourceHanSansCN"
>
{{ t('personal.reserveDate') }}:2025-11-10
</span>
</div>
<i
mg
class=
"w-[14px] h-[16px]"
src=
"../../assets/images/personal/right_n.png"
alt=
""
/>
<i
con-delete
size=
"16"
class=
"customPrimary-5 cursor-pointer"
/>
</div>
<div
class=
"pl-[25px] pr-[24px] pt-[13px] pb-[19px]"
>
<div
class=
"flex justify-between"
>
...
...
@@ -106,7 +108,7 @@
</div>
<div
class=
"flex justify-between items-center mt-[0px]"
>
<div
class=
"flex-1 flex-shrink-0 flex items-center"
>
<i
mg
class=
"w-[12px] h-[14px] mr-[6px]"
src=
"../../assets/images/personal/right_n.png"
alt=
""
/>
<i
con-location
size=
"14"
class=
"customPrimary-5 mr-[6px]"
/>
<span
class=
"text-sm SourceHanSansCN"
>
红宝石路188号B幢
</span>
</div>
<span
class=
"text-lg font-medium flex-shrink-0"
>
¥1756.28
</span>
...
...
@@ -132,6 +134,16 @@
</a-scrollbar>
</div>
</div>
<Modal
class=
"ModalRef"
ref=
"ModalRef"
:modal-config=
"modalConfig"
:modal-mask=
"true"
>
<
template
#
modal=
"{ visible }"
>
<editEmail
v-if=
"visible&&showType==1"
:data=
"userInfo"
@
save-success=
"handleSaveSuccess"
/>
</
template
>
</Modal>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
inject
,
computed
,
watch
}
from
'vue'
...
...
@@ -141,7 +153,8 @@ import { useSystemConfigStore } from '@/stores/index'
// 引入订单状态枚举
import
OrderStatusEnum
from
'@/utils/orderStautsEnum'
import
ListProductTypeEnum
from
'@/utils/listProductType'
import
Modal
from
'@/components/common/modal.vue'
import
editEmail
from
'./components/myOrder/editEmail.vue'
const
{
t
}
=
useI18n
()
const
userStore
=
useUserStore
()
...
...
@@ -149,7 +162,26 @@ const systemConfigStore = useSystemConfigStore()
const
userData
=
ref
({}
as
any
)
userData
.
value
=
inject
(
'userData'
)
const
userInfo
=
ref
({}
as
any
)
const
userInfo
=
ref
(
userStore
.
personalInfo
)
const
showType
=
ref
(
null
as
number
|
null
)
const
modalConfig
=
ref
({
title
:
t
(
'personal.editEmail'
),
width
:
'500px'
,
maskClosable
:
false
,
footer
:
false
,
modalStyle
:{
borderRadius
:
'8px'
,
},
// okButtonProps:{
// size:'large',
// },
// okText: t('personal.save'),
// cancelButtonProps:{
// size:'large',
// },
})
const
ModalRef
=
ref
<
any
>
(
null
)
const
orderList
=
ref
<
any
>
([])
const
currentStatus
=
ref
(
0
)
...
...
@@ -197,12 +229,24 @@ const noMoreData = ref(false)
const
scrollContainer
=
ref
<
any
>
(
null
)
const
scrollThreshold
=
0
// 阈值,距离底部多少距离时触发加载
watch
(()
=>
user
Data
.
value
.
user
Info
,
(
newVal
,
oldVal
)
=>
{
watch
(()
=>
user
Store
.
personal
Info
,
(
newVal
,
oldVal
)
=>
{
if
(
newVal
!=
oldVal
){
userInfo
.
value
=
userData
.
value
.
userInfo
userInfo
.
value
=
newVal
}
})
const
handleSaveSuccess
=
(
email
:
string
)
=>
{
ModalRef
.
value
.
handleCancel
()
userStore
.
setEmail
(
email
)
}
// 编辑邮箱 绑定邮箱
const
editEmailClick
=
async
()
=>
{
showType
.
value
=
1
modalConfig
.
value
.
title
=
userInfo
.
value
.
email
?
t
(
'personal.editEmail'
):
t
(
'personal.binEmail'
)
if
(
ModalRef
.
value
)
await
ModalRef
.
value
.
openModal
(
userInfo
.
value
)
}
const
changeStatus
=
(
key
:
number
)
=>
{
currentStatus
.
value
=
key
}
...
...
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