feat(auth): 实现系统登录和权限控制功能
- 新增登录页面和相关逻辑 - 添加全局权限控制守卫 - 更新订单列表和详情页面,优化用户体验- 重构部分代码以支持新功能master
parent
781d598ae7
commit
0752efd3ff
|
|
@ -10,8 +10,11 @@ declare module 'vue' {
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
VanButton: typeof import('vant/es')['Button']
|
VanButton: typeof import('vant/es')['Button']
|
||||||
|
VanCellGroup: typeof import('vant/es')['CellGroup']
|
||||||
|
VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||||
VanEmpty: typeof import('vant/es')['Empty']
|
VanEmpty: typeof import('vant/es')['Empty']
|
||||||
VanField: typeof import('vant/es')['Field']
|
VanField: typeof import('vant/es')['Field']
|
||||||
|
VanForm: typeof import('vant/es')['Form']
|
||||||
VanIcon: typeof import('vant/es')['Icon']
|
VanIcon: typeof import('vant/es')['Icon']
|
||||||
VanList: typeof import('vant/es')['List']
|
VanList: typeof import('vant/es')['List']
|
||||||
VanLoading: typeof import('vant/es')['Loading']
|
VanLoading: typeof import('vant/es')['Loading']
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,17 @@ export const submitApproval = (params: any): Promise<AxiosResponse<ApiResponse<a
|
||||||
// 将所有参数添加到FormData中
|
// 将所有参数添加到FormData中
|
||||||
Object.keys(params).forEach(key => {
|
Object.keys(params).forEach(key => {
|
||||||
if (params[key] !== undefined && params[key] !== null) {
|
if (params[key] !== undefined && params[key] !== null) {
|
||||||
formData.append(key, params[key].toString())
|
// 特殊处理variables参数,它应该是一个对象
|
||||||
|
if (key === 'variables' && typeof params[key] === 'object' && params[key] !== null) {
|
||||||
|
// 将variables对象的每个属性单独添加到FormData中
|
||||||
|
Object.keys(params[key]).forEach(variableKey => {
|
||||||
|
if (params[key][variableKey] !== undefined && params[key][variableKey] !== null) {
|
||||||
|
formData.append(`variables[${variableKey}]`, params[key][variableKey].toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
formData.append(key, params[key].toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { createPinia } from 'pinia'
|
import { store, initStores } from './store'
|
||||||
|
|
||||||
// Vant样式
|
// Vant样式
|
||||||
import 'vant/lib/index.css'
|
import 'vant/lib/index.css'
|
||||||
|
|
@ -13,7 +13,10 @@ import '@/styles/index.scss'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(createPinia())
|
app.use(store)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
||||||
|
// 初始化 stores
|
||||||
|
initStores()
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
@ -1,17 +1,29 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
import { useAuthStore } from '@/store/auth'
|
||||||
|
import { store } from '@/store'
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: '/list'
|
redirect: '/login'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: () => import('@/views/Login/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '系统登录',
|
||||||
|
requiresAuth: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/list',
|
path: '/list',
|
||||||
name: 'OrderList',
|
name: 'OrderList',
|
||||||
component: () => import('@/views/List/index.vue'),
|
component: () => import('@/views/List/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '订单列表'
|
title: '订单列表',
|
||||||
|
requiresAuth: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +31,8 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: 'OrderDetail',
|
name: 'OrderDetail',
|
||||||
component: () => import('@/views/Detail/index.vue'),
|
component: () => import('@/views/Detail/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '订单详情'
|
title: '订单详情',
|
||||||
|
requiresAuth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -38,7 +51,26 @@ router.beforeEach((to, from, next) => {
|
||||||
if (to.meta?.title) {
|
if (to.meta?.title) {
|
||||||
document.title = to.meta.title as string
|
document.title = to.meta.title as string
|
||||||
}
|
}
|
||||||
next()
|
|
||||||
|
// 检查是否需要认证
|
||||||
|
const requiresAuth = to.matched.some(record => record.meta.requiresAuth !== false)
|
||||||
|
|
||||||
|
// 获取认证状态
|
||||||
|
const authStore = useAuthStore(store)
|
||||||
|
const isAuthenticated = authStore.isAuthenticated || localStorage.getItem('isAuthenticated') === 'true'
|
||||||
|
|
||||||
|
// 如果需要认证但未登录,重定向到登录页
|
||||||
|
if (requiresAuth && !isAuthenticated) {
|
||||||
|
next('/login')
|
||||||
|
}
|
||||||
|
// 如果已登录且访问登录页,重定向到列表页
|
||||||
|
else if (to.path === '/login' && isAuthenticated) {
|
||||||
|
next('/list')
|
||||||
|
}
|
||||||
|
// 其他情况正常跳转
|
||||||
|
else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
@ -6,6 +6,13 @@ export interface ApiResponse<T = any> {
|
||||||
total?: number
|
total?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登录参数类型
|
||||||
|
export interface LoginParams {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
rememberMe?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
// 订单状态类型
|
// 订单状态类型
|
||||||
export type OrderStatus = '0' | '1' | '2' // 待审批、已审批、已拒绝
|
export type OrderStatus = '0' | '1' | '2' // 待审批、已审批、已拒绝
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,9 @@ class HttpClient {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 401:
|
case 401:
|
||||||
message = '未授权,请重新登录'
|
message = '未授权,请重新登录'
|
||||||
// 这里可以处理登录跳转逻辑
|
// 清除认证信息并跳转到登录页
|
||||||
|
localStorage.removeItem('isAuthenticated')
|
||||||
|
window.location.href = '/login'
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
message = '拒绝访问'
|
message = '拒绝访问'
|
||||||
|
|
@ -62,6 +64,12 @@ class HttpClient {
|
||||||
case 404:
|
case 404:
|
||||||
message = '请求地址不存在'
|
message = '请求地址不存在'
|
||||||
break
|
break
|
||||||
|
case 302:
|
||||||
|
// 处理302重定向到登录页
|
||||||
|
message = '会话已过期,请重新登录'
|
||||||
|
localStorage.removeItem('isAuthenticated')
|
||||||
|
window.location.href = '/login'
|
||||||
|
break
|
||||||
case 500:
|
case 500:
|
||||||
message = '服务器内部错误'
|
message = '服务器内部错误'
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">BG</span>
|
<span class="label">BG</span>
|
||||||
<span class="value">{{ currentOrderInfo.bgProperty || '' }}</span>
|
<span class="value">{{ currentOrderInfo.bgPropertyDesc || '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">行业</span>
|
<span class="label">行业</span>
|
||||||
<span class="value">{{ currentOrderInfo.industryType || '' }}</span>
|
<span class="value">{{ currentOrderInfo.industryTypeDesc || '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">代表处</span>
|
<span class="label">代表处</span>
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">币种</span>
|
<span class="label">币种</span>
|
||||||
<span class="value">{{ currentOrderInfo.currencyType || '' }}</span>
|
<span class="value">{{ currentOrderInfo.currencyTypeDesc || '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
|
|
@ -100,11 +100,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">公司直发</span>
|
<span class="label">公司直发</span>
|
||||||
<span class="value">{{ currentOrderInfo.companyDelivery}}</span>
|
<span class="value">{{ currentOrderInfo.companyDeliveryDesc}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">下单通路</span>
|
<span class="label">下单通路</span>
|
||||||
<span class="value">{{ currentOrderInfo.orderChannel}}</span>
|
<span class="value">{{ currentOrderInfo.orderChannelDesc}}</span>
|
||||||
</div><div class="info-item">
|
</div><div class="info-item">
|
||||||
<span class="label">供货商</span>
|
<span class="label">供货商</span>
|
||||||
<span class="value">{{ currentOrderInfo.supplier}}</span>
|
<span class="value">{{ currentOrderInfo.supplier}}</span>
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
<span class="value">{{ currentOrderInfo.partnerName}}</span>
|
<span class="value">{{ currentOrderInfo.partnerName}}</span>
|
||||||
</div><div class="info-item">
|
</div><div class="info-item">
|
||||||
<span class="label">进货商类型</span>
|
<span class="label">进货商类型</span>
|
||||||
<span class="value">{{ currentOrderInfo.level}}</span>
|
<span class="value">{{ currentOrderInfo.levelDesc}}</span>
|
||||||
</div><div class="info-item">
|
</div><div class="info-item">
|
||||||
<span class="label">进货商联系人</span>
|
<span class="label">进货商联系人</span>
|
||||||
<span class="value">{{ currentOrderInfo.partnerUserName}}</span>
|
<span class="value">{{ currentOrderInfo.partnerUserName}}</span>
|
||||||
|
|
@ -579,8 +579,14 @@ const submitApproval = async () => {
|
||||||
showSuccessToast(currentApprovalStatus.value === 0 ? '驳回成功' : '审批通过')
|
showSuccessToast(currentApprovalStatus.value === 0 ? '驳回成功' : '审批通过')
|
||||||
approvalDialogVisible.value = false
|
approvalDialogVisible.value = false
|
||||||
|
|
||||||
// 重新加载详情
|
// 如果是审批通过,跳转到列表页面
|
||||||
await orderStore.fetchOrderDetail(route.params.id as string)
|
if (currentApprovalStatus.value !== 0) {
|
||||||
|
// 跳转到列表页面
|
||||||
|
router.push('/list')
|
||||||
|
} else {
|
||||||
|
// 驳回情况下重新加载详情
|
||||||
|
await orderStore.fetchOrderDetail(route.params.id as string)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交审批失败:', error)
|
console.error('提交审批失败:', error)
|
||||||
showToast('提交审批失败')
|
showToast('提交审批失败')
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@
|
||||||
<div v-for="order in orderList" :key="order.id" class="order-item" @click="goToDetail(order.id)">
|
<div v-for="order in orderList" :key="order.id" class="order-item" @click="goToDetail(order.id)">
|
||||||
<div class="order-header">
|
<div class="order-header">
|
||||||
<div class="order-code">{{ order.orderCode }}</div>
|
<div class="order-code">{{ order.orderCode }}</div>
|
||||||
<div class="status-tag" :class="getStatusClass(order.orderStatus)">
|
<div class="status-tag pending">
|
||||||
{{ formatOrderStatus(order.orderStatus) }}
|
待审批
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue