添加登录、注册、重置密码、谷歌验证码处理中

This commit is contained in:
2025-12-26 16:26:26 +08:00
parent e9bca8163d
commit a325efb57f
23 changed files with 1464 additions and 40 deletions

View File

@@ -0,0 +1,69 @@
import request from '../utils/request'
//获取谷歌验证器二维码和密钥
export function getBindInfo(data) {
return request({
url: `/lease/auth/getBindInfo`,
method: 'post',
data
})
}
//绑定谷歌验证码
export function bindGoogle(data) {
return request({
url: `/lease/auth/bindGoogle`,
method: 'post',
data
})
}
//开启谷歌验证器 发送邮箱验证码
export function sendOpenGoogleCode(data) {
return request({
url: `/lease/auth/sendOpenGoogleCode`,
method: 'post',
data
})
}
//关闭双重验证
export function closeStepTwo(data) {
return request({
url: `/lease/auth/closeStepTwo`,
method: 'post',
data
})
}
//关闭谷歌验证器 发送邮箱验证码
export function sendCloseGoogleCode(data) {
return request({
url: `/lease/auth/sendCloseGoogleCode`,
method: 'post',
data
})
}
//谷歌验证开启状态
export function getGoogleStatus(data) {
return request({
url: `/lease/auth/getGoogleStatus`,
method: 'post',
data
})
}
//开启谷歌验证
export function openStepTwo(data) {
return request({
url: `/lease/auth/openStepTwo`,
method: 'post',
data
})
}

View File

@@ -75,7 +75,9 @@ export default {
cartServerCount: 0,
navigation: mainNavigation,
// 用户邮箱
userEmail: ''
userEmail: '',
// 登录状态(改为 data 属性,支持响应式更新)
isLoggedIn: false
}
},
computed: {
@@ -87,15 +89,12 @@ export default {
// 计算面包屑导航
breadcrumbs() {
return getBreadcrumb(this.$route.path)
},
// 判断是否已登录检查localStorage中是否有token
isLoggedIn() {
const token = JSON.parse(localStorage.getItem('leasToken'))
return !!token // 有token就是已登录没有就是未登录
}
},
watch: {},
mounted() {
// 初始化登录状态
this.updateLoginStatus()
this.loadCart()
// 监听购物车变化
window.addEventListener('storage', this.handleStorageChange)
@@ -103,12 +102,15 @@ export default {
this.loadServerCartCount()
// 监听应用内购物车更新事件
window.addEventListener('cart-updated', this.handleCartUpdated)
// 监听登录状态变化事件(当 token 被清除时触发)
window.addEventListener('login-status-changed', this.handleLoginStatusChanged)
// 加载用户信息(邮箱)
this.loadUserEmail()
},
beforeDestroy() {
window.removeEventListener('storage', this.handleStorageChange)
window.removeEventListener('cart-updated', this.handleCartUpdated)
window.removeEventListener('login-status-changed', this.handleLoginStatusChanged)
},
methods: {
loadCart() {
@@ -151,6 +153,38 @@ export default {
if (event.key === 'power_leasing_cart_v1') {
this.loadCart()
this.loadServerCartCount()
} else if (event.key === 'leasToken') {
// 当 token 变化时,更新登录状态
this.updateLoginStatus()
// 如果 token 被清除,同时清除用户信息
if (!event.newValue) {
this.userEmail = ''
} else {
this.loadUserEmail()
}
}
},
/**
* 处理登录状态变化事件
*/
handleLoginStatusChanged() {
this.updateLoginStatus()
// 如果未登录,清除用户信息
if (!this.isLoggedIn) {
this.userEmail = ''
} else {
this.loadUserEmail()
}
},
/**
* 更新登录状态
*/
updateLoginStatus() {
try {
const token = localStorage.getItem('leasToken')
this.isLoggedIn = !!token && token !== 'null' && token !== 'undefined'
} catch (e) {
this.isLoggedIn = false
}
},
handleCartUpdated(event) {
@@ -207,11 +241,17 @@ export default {
localStorage.removeItem('userId')
localStorage.removeItem('username')
// 更新登录状态
this.updateLoginStatus()
// 清空购物车
this.user = null
this.cart = []
this.userEmail = ''
// 触发登录状态变化事件
window.dispatchEvent(new CustomEvent('login-status-changed'))
// 提示用户
this.$message.success('退出登录成功')

View File

@@ -16,8 +16,11 @@ Vue.config.productionTip = false
Vue.use(ElementUI);
// 初始化全局防表情拦截器
initNoEmojiGuard();
new Vue({
const vm = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
// 将 Vue 实例挂载到 window 上,供 request.js 等工具使用
window.vm = vm

View File

@@ -213,6 +213,16 @@ export const accountRoutes = [
allAuthority: ['all']
}
},
{
path: 'purchased-machine-detail/:id',
name: 'purchasedMachineDetail',
component: () => import('../views/account/purchasedMachineDetail.vue'),
meta: {
title: '已购商品详情',
description: '查看已购买商品的详细信息',
allAuthority: ['all']
}
},
{
path: 'funds-flow',
name: 'accountFundsFlow',
@@ -282,6 +292,16 @@ export const accountRoutes = [
description: '为商品添加出售机器',
allAuthority: ['all']
}
},
{
path: 'security-settings',
name: 'accountSecuritySettings',
component: () => import('../views/account/securitySettings.vue'),
meta: {
title: '安全设置',
description: '管理账户安全选项',
allAuthority: ['all']
}
}
]
}

View File

@@ -276,28 +276,67 @@ service.interceptors.response.use(res => {
if (code === 421) {
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
localStorage.removeItem('leasToken')
// 触发登录状态变化事件,通知头部组件更新
window.dispatchEvent(new CustomEvent('login-status-changed'))
// 系统状态已过期请重新点击SUPPORT按钮进入
superReportError = localStorage.getItem('superReportError')
if (!superReportError) {
superReportError = 421
localStorage.setItem('superReportError', superReportError)
MessageBox.confirm(window.vm.$i18n.t(`user.loginExpired`), window.vm.$i18n.t(`user.overduePrompt`), {
distinguishCancelAndClose: true,
confirmButtonText: window.vm.$i18n.t(`user.login`),
cancelButtonText: window.vm.$i18n.t(`user.Home`),
// showCancelButton: false, // 隐藏取消按钮
closeOnClickModal: false, // 点击空白处不关闭对话框
showClose: false, // 隐藏关闭按钮
type: 'warning'
// 获取 i18n 文本,如果 window.vm 不存在则使用默认中文
const getText = (key, defaultValue) => {
if (window.vm && window.vm.$i18n) {
return window.vm.$i18n.t(key) || defaultValue
}
return defaultValue
}
// 获取路由跳转路径
const getLoginPath = () => {
if (window.vm && window.vm.$i18n && window.vm.$i18n.locale) {
return `/${window.vm.$i18n.locale}/login`
}
return '/login'
}
const getHomePath = () => {
if (window.vm && window.vm.$i18n && window.vm.$i18n.locale) {
return `/${window.vm.$i18n.locale}/`
}
return '/'
}
MessageBox.confirm(
getText('user.loginExpired', '登录状态已过期'),
getText('user.overduePrompt', '您的登录状态已过期,请重新登录'),
{
distinguishCancelAndClose: true,
confirmButtonText: getText('user.login', '登录'),
cancelButtonText: getText('user.Home', '返回首页'),
closeOnClickModal: false, // 点击空白处不关闭对话框
showClose: false, // 隐藏关闭按钮
type: 'warning'
}
).then(() => {
window.vm.$router.push(`/${window.vm.$i18n.locale}/login`)
// 跳转到登录页
if (window.vm && window.vm.$router) {
window.vm.$router.push(getLoginPath())
} else {
window.location.href = getLoginPath()
}
localStorage.removeItem('token')
localStorage.removeItem('superReportError')
}).catch(() => {
window.vm.$router.push(`/${window.vm.$i18n.locale}/`)
// 跳转到首页
if (window.vm && window.vm.$router) {
window.vm.$router.push(getHomePath())
} else {
window.location.href = getHomePath()
}
localStorage.removeItem('leasToken')
localStorage.removeItem('superReportError')
});
}

View File

@@ -208,7 +208,8 @@ export default {
items: { type: Array, default: () => [] },
emptyText: { type: String, default: '暂无数据' },
showCheckout: { type: Boolean, default: false },
onCancel: { type: Function, default: null }
onCancel: { type: Function, default: null },
isSeller: { type: Boolean, default: false } // 标识是否是卖家订单
},
data() {
return {
@@ -310,7 +311,20 @@ export default {
});
return
}
try { this.$router.push(`/account/order-detail/${id}`) } catch (e) {
try {
// 判断是买家还是卖家订单,传递 from 参数
const from = this.isSeller ? 'seller' : 'buyer'
// 保存到 sessionStorage以便详情页可以读取
try {
sessionStorage.setItem('orderDetailFrom', from)
} catch (e) {
console.warn('保存订单来源失败', e)
}
this.$router.push({
path: `/account/order-detail/${id}`,
query: { from: from }
})
} catch (e) {
this.$message({
message: '无法跳转到详情页',
type: 'error',

View File

@@ -3,10 +3,10 @@
<h2 class="title">已售出订单</h2>
<el-tabs v-model="active" @tab-click="handleTabClick">
<el-tab-pane label="订单进行中" name="7">
<order-list :items="orders[7]" :show-checkout="false" empty-text="暂无进行中的订单" />
<order-list :items="orders[7]" :show-checkout="false" :is-seller="true" empty-text="暂无进行中的订单" />
</el-tab-pane>
<el-tab-pane label="订单已完成" name="8">
<order-list :items="orders[8]" :show-checkout="false" empty-text="暂无已完成的订单" />
<order-list :items="orders[8]" :show-checkout="false" :is-seller="true" empty-text="暂无已完成的订单" />
</el-tab-pane>
</el-tabs>
</div>

View File

@@ -68,6 +68,7 @@ export default {
// { label: '充值记录', to: '/account/rechargeRecord' },
// { label: '提现记录', to: '/account/withdrawalHistory' },
{ label: '资金流水', to: '/account/funds-flow' },
{ label: '安全设置', to: '/account/security-settings' },
],
// 卖家侧导航
sellerLinks: [
@@ -76,7 +77,7 @@ export default {
{ label: '商品列表', to: '/account/products' },
{ label: '已售出订单', to: '/account/seller-orders' },
{ label: '资金流水', to: '/account/seller-funds-flow' },
{ label: '安全设置', to: '/account/security-settings' },
],
}
},
@@ -140,13 +141,15 @@ export default {
setActiveRoleByRoute() {
const path = (this.$route && this.$route.path) || ''
// 详情页:根据来源 from=buyer/seller 判定(优先 query其次 sessionStorage
// 如果未指定 from默认使用买家分组因为订单详情页默认高亮订单列表
if (path.indexOf('/account/order-detail') === 0) {
const qFrom = (this.$route && this.$route.query && this.$route.query.from) || ''
let from = qFrom
if (!from) {
try { from = sessionStorage.getItem('orderDetailFrom') || '' } catch (e) { from = '' }
}
const role = from === 'buyer' ? 'buyer' : (from === 'seller' ? 'seller' : this.activeRole)
// 如果明确指定了 from=seller使用卖家分组否则默认使用买家分组
const role = from === 'seller' ? 'seller' : 'buyer'
if (this.activeRole !== role) {
this.activeRole = role
try { localStorage.setItem('accountActiveRole', JSON.stringify(role)) } catch (e) {}
@@ -175,6 +178,10 @@ export default {
'/account/withdraw-record',
'/account/shop-config'
]
// 安全设置页面买家和卖家都可见,不参与分组判断
if (path === '/account/security-settings') {
return
}
const shouldBuyer = buyerPrefixes.some(p => path.indexOf(p) === 0)
const shouldSeller = sellerPrefixes.some(p => path.indexOf(p) === 0)
const role = shouldBuyer ? 'buyer' : (shouldSeller ? 'seller' : this.activeRole)
@@ -186,7 +193,7 @@ export default {
/**
* 判断左侧导航项是否高亮
* - 普通路径完全匹配
* - 已售出订单需同时匹配详情页 /account/order-detail/:id
* - "已售出订单"需同时匹配详情页 /account/order-detail/:id
*/
isActiveLink(pathLike) {
const current = (this.$route && this.$route.path) || ''
@@ -198,9 +205,10 @@ export default {
if (!from) {
try { from = sessionStorage.getItem('orderDetailFrom') || '' } catch (e) { from = '' }
}
if (from === 'buyer' && pathLike === '/account/orders') return true
// 如果明确指定了 from=seller高亮卖家订单列表
if (from === 'seller' && pathLike === '/account/seller-orders') return true
// 兜底:不匹配
// 默认高亮买家订单列表(包括 from=buyer 或未指定 from 的情况)
if (pathLike === '/account/orders') return true
return false
}
// 列表-详情联动高亮映射

View File

@@ -772,7 +772,7 @@ export default {
}
} catch (e) {
console.error('修改配置失败', e)
this.$message.error('修改配置失败,请重试')
}
},
removeSelectedCoin(labelUpper) {

View File

@@ -675,7 +675,7 @@ export default {
}
} catch (e) {
console.error("加载币种列表失败", e);
this.$message.error("加载币种列表失败,请稍后重试");
} finally {
this.loadingCoins = false;
}
@@ -734,7 +734,7 @@ export default {
}
} catch (e) {
console.error(`加载币种 ${coin} 的算法列表失败`, e);
this.$message.error(`加载算法列表失败,请稍后重试`);
// 设置空数组,避免重复请求
this.$set(this.algoOptionsMap, coin, []);
} finally {

View File

@@ -1319,7 +1319,7 @@ export default {
}
} catch (error) {
console.error('更新失败', error)
this.$message.error('修改失败')
} finally {
this.editDialog.saving = false
}

View File

@@ -249,14 +249,22 @@ export default {
* @param {Object} row - 行数据
*/
handleViewDetail(row) {
console.log('查看详情,行数据:', row) // 调试用
// 跳转到详情页面传递行数据的ID
const id = row.id || row.productMachineId || row.machineId
console.log('提取的ID:', id) // 调试用
if (id) {
this.$router.push({
name: 'purchasedMachineDetail',
params: { id: id }
})
try {
this.$router.push({
name: 'purchasedMachineDetail',
params: { id: id }
})
} catch (e) {
console.error('路由跳转失败:', e)
this.$message.error('跳转失败,请稍后重试')
}
} else {
console.warn('行数据中缺少ID字段:', row) // 调试用
this.$message.warning('无法获取详情缺少ID信息')
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -278,7 +278,7 @@
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
this.$message.error(error.message || '发送验证码失败,请重试')
} finally {
this.sendingCode = false
}
@@ -353,7 +353,17 @@
}
localStorage.setItem('userInfo', JSON.stringify(userInfo))
localStorage.setItem('leasEmail', this.loginForm.email)
this.$message.success('登录成功')
// 触发登录状态变化事件,通知头部组件更新
window.dispatchEvent(new CustomEvent('login-status-changed'))
this.$message({
message: '登录成功',
type: 'success',
duration: 3000,
showClose: true
})
// 跳转到首页或者来源页面
const redirect = this.$route.query.redirect || '/productList'

View File

@@ -371,7 +371,7 @@ export default {
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
this.$message.error(error.message || '发送验证码失败,请重试')
} finally {
this.sendingCode = false
}

View File

@@ -349,7 +349,7 @@ export default {
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
this.$message.error(error.message || '发送验证码失败,请重试')
} finally {
this.sendingCode = false
}