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

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

View File

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

View File

@@ -213,6 +213,16 @@ export const accountRoutes = [
allAuthority: ['all'] allAuthority: ['all']
} }
}, },
{
path: 'purchased-machine-detail/:id',
name: 'purchasedMachineDetail',
component: () => import('../views/account/purchasedMachineDetail.vue'),
meta: {
title: '已购商品详情',
description: '查看已购买商品的详细信息',
allAuthority: ['all']
}
},
{ {
path: 'funds-flow', path: 'funds-flow',
name: 'accountFundsFlow', name: 'accountFundsFlow',
@@ -282,6 +292,16 @@ export const accountRoutes = [
description: '为商品添加出售机器', description: '为商品添加出售机器',
allAuthority: ['all'] 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) { if (code === 421) {
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接 localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
localStorage.removeItem('leasToken') localStorage.removeItem('leasToken')
// 触发登录状态变化事件,通知头部组件更新
window.dispatchEvent(new CustomEvent('login-status-changed'))
// 系统状态已过期请重新点击SUPPORT按钮进入 // 系统状态已过期请重新点击SUPPORT按钮进入
superReportError = localStorage.getItem('superReportError') superReportError = localStorage.getItem('superReportError')
if (!superReportError) { if (!superReportError) {
superReportError = 421 superReportError = 421
localStorage.setItem('superReportError', superReportError) 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'
}
).then(() => {
window.vm.$router.push(`/${window.vm.$i18n.locale}/login`)
localStorage.removeItem('token')
}).catch(() => {
window.vm.$router.push(`/${window.vm.$i18n.locale}/`)
localStorage.removeItem('leasToken')
});
// 获取 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(() => {
// 跳转到登录页
if (window.vm && window.vm.$router) {
window.vm.$router.push(getLoginPath())
} else {
window.location.href = getLoginPath()
}
localStorage.removeItem('token')
localStorage.removeItem('superReportError')
}).catch(() => {
// 跳转到首页
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: () => [] }, items: { type: Array, default: () => [] },
emptyText: { type: String, default: '暂无数据' }, emptyText: { type: String, default: '暂无数据' },
showCheckout: { type: Boolean, default: false }, showCheckout: { type: Boolean, default: false },
onCancel: { type: Function, default: null } onCancel: { type: Function, default: null },
isSeller: { type: Boolean, default: false } // 标识是否是卖家订单
}, },
data() { data() {
return { return {
@@ -310,7 +311,20 @@ export default {
}); });
return 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({ this.$message({
message: '无法跳转到详情页', message: '无法跳转到详情页',
type: 'error', type: 'error',

View File

@@ -3,10 +3,10 @@
<h2 class="title">已售出订单</h2> <h2 class="title">已售出订单</h2>
<el-tabs v-model="active" @tab-click="handleTabClick"> <el-tabs v-model="active" @tab-click="handleTabClick">
<el-tab-pane label="订单进行中" name="7"> <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>
<el-tab-pane label="订单已完成" name="8"> <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-tab-pane>
</el-tabs> </el-tabs>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -278,7 +278,7 @@
} }
} catch (error) { } catch (error) {
console.error('发送验证码失败:', error) console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试') this.$message.error(error.message || '发送验证码失败,请重试')
} finally { } finally {
this.sendingCode = false this.sendingCode = false
} }
@@ -353,7 +353,17 @@
} }
localStorage.setItem('userInfo', JSON.stringify(userInfo)) localStorage.setItem('userInfo', JSON.stringify(userInfo))
localStorage.setItem('leasEmail', this.loginForm.email) 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' const redirect = this.$route.query.redirect || '/productList'

View File

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

View File

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

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.d698a8ca.js"></script><script defer="defer" src="/js/app.58e678d6.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.262a57c7.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html> <!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.4369320b.js"></script><script defer="defer" src="/js/app.cc5f454d.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.395f1e08.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long