权限控制完善

This commit is contained in:
2026-05-07 11:37:02 +08:00
parent bdd5b08351
commit c9dc8e9b6c
4 changed files with 122 additions and 70 deletions

View File

@@ -15,6 +15,7 @@ const TokenManager = {
setToken(token: string) {
const authStore = useAuthStore()
authStore.setToken(token)
isAuthExpired = false
// 同时设置默认请求头
request.defaults.headers.common['Authorization'] = `Bearer ${token}`
},
@@ -45,15 +46,23 @@ TokenManager.initToken()
// 登录过期提示防重复标志
let isLoginExpiredShown = false
let isAuthExpired = false
const isAuthExpiredRequest = (url: string) => {
if (!isAuthExpired) return false
if (!url) return true
return !url.includes('/user/login') && !url.includes('/user/register')
}
// 处理登录过期(防重复提示)
const handleLoginExpired = () => {
if (!isLoginExpiredShown) {
isLoginExpiredShown = true
isAuthExpired = true
// 清除token
TokenManager.removeToken()
// 显示提示消息
ElMessage.error('登录已过期,请重新登录')
ElMessage.error('用户信息过期,请重新登录')
// 跳转到登录页
router.replace('/login').finally(() => {
// 延迟重置标志位,确保跳转完成后再允许下次提示
@@ -149,6 +158,11 @@ request.interceptors.response.use(
return Promise.reject(error)
}
// 如果是认证过期后的后续请求,静默处理业务错误,避免弹出部门加载失败等页面提示
if (isAuthExpiredRequest(url) && errorMsg) {
return Promise.reject(error)
}
// 网络错误或其他错误
ElMessage.error(errorMsg)
return Promise.reject(error)

View File

@@ -12,7 +12,7 @@
:rules="loginRules"
class="login-form"
label-width="0"
@keyup.enter="handleLogin"
@submit.prevent="handleLogin"
>
<el-form-item prop="username">
<el-input
@@ -21,6 +21,7 @@
size="large"
:prefix-icon="User"
clearable
@keydown.enter.prevent="handleLogin"
/>
</el-form-item>
@@ -33,7 +34,7 @@
:prefix-icon="Lock"
show-password
clearable
@keyup.enter="handleLogin"
@keydown.enter.prevent="handleLogin"
/>
</el-form-item>
@@ -191,54 +192,53 @@ const registerRules: FormRules = {
// 处理登录
const handleLogin = async () => {
if (!loginFormRef.value) return
await loginFormRef.value.validate(async (valid) => {
if (valid) {
loginLoading.value = true
try {
const response: any = await userlogin(loginForm.username, loginForm.password)
const loginData = response?.data ?? response
const token = loginData?.token || response?.token || loginData
const permissions = Array.isArray(loginData?.permissions)
? loginData.permissions
: Array.isArray(response?.permissions)
? response.permissions
: []
const roles = Array.isArray(loginData?.roles)
? loginData.roles
: Array.isArray(response?.roles)
? response.roles
: []
const userInfo = typeof loginData === 'object' ? loginData : { token }
if (!loginFormRef.value || loginLoading.value) return
if (token) {
authStore.setToken(token)
authStore.setUserInfo({
...userInfo,
username: userInfo.username || loginForm.username,
token,
roles,
permissions,
})
authStore.setPermissions(permissions)
localStorage.setItem('username', userInfo.username || loginForm.username)
localStorage.setItem('token', token)
localStorage.setItem('permissions', JSON.stringify(permissions))
const valid = await loginFormRef.value.validate().catch(() => false)
if (!valid) return
ElMessage.success(response?.message || '登录成功')
router.push('/home')
} else {
ElMessage.error(response?.message || '登录失败,未获取到令牌')
}
} catch (error: any) {
// 错误提示已在全局响应拦截中处理,这里不重复弹出
console.warn('login error:', error)
} finally {
loginLoading.value = false
}
loginLoading.value = true
try {
const response: any = await userlogin(loginForm.username, loginForm.password)
const loginData = response?.data ?? response
const token = loginData?.token || response?.token || loginData
const permissions = Array.isArray(loginData?.permissions)
? loginData.permissions
: Array.isArray(response?.permissions)
? response.permissions
: []
const roles = Array.isArray(loginData?.roles)
? loginData.roles
: Array.isArray(response?.roles)
? response.roles
: []
const userInfo = typeof loginData === 'object' ? loginData : { token }
if (token) {
authStore.setToken(token)
authStore.setUserInfo({
...userInfo,
username: userInfo.username || loginForm.username,
token,
roles,
permissions,
})
authStore.setPermissions(permissions)
localStorage.setItem('username', userInfo.username || loginForm.username)
localStorage.setItem('token', token)
localStorage.setItem('permissions', JSON.stringify(permissions))
ElMessage.success('登录成功')
router.push('/home')
} else {
ElMessage.error(response?.message || '登录失败,未获取到令牌')
}
})
} catch (error: any) {
// 错误提示已在全局响应拦截中处理,这里不重复弹出
console.warn('login error:', error)
} finally {
loginLoading.value = false
}
}
// 处理注册

View File

@@ -86,25 +86,31 @@
</el-table-column>
<el-table-column prop="formType" label="表单控件" min-width="120" align="left">
<template #default="{ row }">
<el-button
v-if="row.formType === 'select'"
type="success"
size="small"
link
@click="handleConfigFormTypeOptions(row)"
>
下拉框
</el-button>
<el-button
v-else-if="row.formType === 'input' || row.formType === 'number'"
type="warning"
size="small"
link
class="range-config-button"
@click="handleConfigRange(row)"
>
{{ getFormTypeLabel(row.formType) || '暂无' }}
</el-button>
<span v-if="row.formType === 'select'">
<el-button
v-if="canConfigSelectOptions"
type="success"
size="small"
link
@click="handleConfigFormTypeOptions(row)"
>
下拉框
</el-button>
<span v-else>下拉框</span>
</span>
<span v-else-if="row.formType === 'input' || row.formType === 'number'">
<el-button
v-if="canConfigRange"
type="warning"
size="small"
link
class="range-config-button"
@click="handleConfigRange(row)"
>
{{ getFormTypeLabel(row.formType) || '暂无' }}
</el-button>
<span v-else>{{ getFormTypeLabel(row.formType) || '暂无' }}</span>
</span>
<span v-else>{{ getFormTypeLabel(row.formType) || '暂无' }}</span>
</template>
</el-table-column>
@@ -236,12 +242,13 @@
/>
</el-select>
<el-button
v-if="formData.formType === 'select'"
v-if="formData.formType === 'select' && canConfigSelectOptions"
type="primary"
@click="handleConfigFormTypeOptionsInDrawer"
>
配置
</el-button>
<span v-else-if="formData.formType === 'select'">配置</span>
</div>
</el-form-item>
<el-form-item label="参数分类" prop="dicTypeId">
@@ -259,11 +266,13 @@
/>
</el-select>
<el-button
v-if="canConfigSelectOptions"
type="primary"
@click="handleConfigCategoryOptionsInDrawer"
>
配置
</el-button>
<span v-else>配置</span>
</div>
</el-form-item>
<el-form-item label="参数描述" prop="paramDesc">
@@ -371,10 +380,11 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { computed, ref, reactive, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Edit, Delete, Close } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
import {
devparamadd,
devparamdel,
@@ -390,6 +400,10 @@ const loading = ref(false)
const submitting = ref(false)
const rangeSubmitting = ref(false)
const authStore = useAuthStore()
const canConfigRange = computed(() => authStore.hasPermission('data:devparam:range'))
const canConfigSelectOptions = computed(() => authStore.hasPermission('data:devparam:select'))
// 参数列表
const paramList = ref<any[]>([])
const total = ref(0)
@@ -1031,6 +1045,10 @@ const resetRangeForm = () => {
}
const handleConfigRange = async (row: any) => {
if (!canConfigRange.value) {
ElMessage.warning('您没有设置动作参数取值范围的权限')
return
}
if (!row?.paramName) {
ElMessage.warning('缺少参数名称,无法配置取值范围')
return
@@ -1158,11 +1176,19 @@ const isConfigFormType = ref(false) // 是否配置表单控件的下拉框选
// 打开表单控件下拉框选项配置对话框(从列表)
const handleConfigFormTypeOptions = async (row: any) => {
if (!canConfigSelectOptions.value) {
ElMessage.warning('您没有设置动作参数下拉框选项列表的权限')
return
}
await openSelectOptionsDialog(row, true)
}
// 打开表单控件下拉框选项配置对话框(从抽屉)
const handleConfigFormTypeOptionsInDrawer = async () => {
if (!canConfigSelectOptions.value) {
ElMessage.warning('您没有设置动作参数下拉框选项列表的权限')
return
}
if (!formData.paramName) {
ElMessage.warning('请先输入参数名称')
return

View File

@@ -95,12 +95,16 @@
>
<template #default="{ row }">
<el-button
v-if="canViewStatusRecord"
:type="getStatusType(row.goodsStatus)"
link
@click="handleStatusClick(row)"
>
{{ getStatusText(row.goodsStatus) }}
</el-button>
<span v-else>
{{ getStatusText(row.goodsStatus) }}
</span>
</template>
</el-table-column>
@@ -327,7 +331,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { computed, ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { goodsInfolist } from '@/api/tb/goodsinfo'
import { flowInfobyid } from '@/api/tb/flowinfo'
@@ -338,6 +342,7 @@ import { islandInfobyid } from '@/api/tb/islandinfo'
import { devInfobyid } from '@/api/tb/devinfo'
import { stepInfolist } from '@/api/tb/stepinfo'
import { devparambyid } from '@/api/tb/devparam'
import { useAuthStore } from '@/stores/auth'
interface GoodsInfoItem {
id?: number | string
@@ -379,6 +384,9 @@ const detailData = reactive<GoodsInfoItem>({})
const detailStatusList = ref<any[]>([])
const detailStatusLoading = ref(false)
const authStore = useAuthStore()
const canViewStatusRecord = computed(() => authStore.hasPermission('tb:goodrecord:record'))
// 样品流程状态列表相关
const flowStatusDialogVisible = ref(false)
const flowStatusDialogTitle = ref('样品流程状态列表')
@@ -417,6 +425,10 @@ const getStatusText = (status: number | null | undefined) => {
// 样品状态点击事件处理
const handleStatusClick = (row: GoodsInfoItem) => {
if (!canViewStatusRecord.value) {
ElMessage.warning('您没有样品状态记录的权限')
return
}
const status = row.goodsStatus
if (status === 0) {
// 待处理 - 显示提示