权限控制完善

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

View File

@@ -12,7 +12,7 @@
:rules="loginRules" :rules="loginRules"
class="login-form" class="login-form"
label-width="0" label-width="0"
@keyup.enter="handleLogin" @submit.prevent="handleLogin"
> >
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
@@ -21,6 +21,7 @@
size="large" size="large"
:prefix-icon="User" :prefix-icon="User"
clearable clearable
@keydown.enter.prevent="handleLogin"
/> />
</el-form-item> </el-form-item>
@@ -33,7 +34,7 @@
:prefix-icon="Lock" :prefix-icon="Lock"
show-password show-password
clearable clearable
@keyup.enter="handleLogin" @keydown.enter.prevent="handleLogin"
/> />
</el-form-item> </el-form-item>
@@ -191,54 +192,53 @@ const registerRules: FormRules = {
// 处理登录 // 处理登录
const handleLogin = async () => { const handleLogin = async () => {
if (!loginFormRef.value) return if (!loginFormRef.value || loginLoading.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 (token) { const valid = await loginFormRef.value.validate().catch(() => false)
authStore.setToken(token) if (!valid) return
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(response?.message || '登录成功') loginLoading.value = true
router.push('/home') try {
} else { const response: any = await userlogin(loginForm.username, loginForm.password)
ElMessage.error(response?.message || '登录失败,未获取到令牌') const loginData = response?.data ?? response
} const token = loginData?.token || response?.token || loginData
} catch (error: any) { const permissions = Array.isArray(loginData?.permissions)
// 错误提示已在全局响应拦截中处理,这里不重复弹出 ? loginData.permissions
console.warn('login error:', error) : Array.isArray(response?.permissions)
} finally { ? response.permissions
loginLoading.value = false : []
} 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>
<el-table-column prop="formType" label="表单控件" min-width="120" align="left"> <el-table-column prop="formType" label="表单控件" min-width="120" align="left">
<template #default="{ row }"> <template #default="{ row }">
<el-button <span v-if="row.formType === 'select'">
v-if="row.formType === 'select'" <el-button
type="success" v-if="canConfigSelectOptions"
size="small" type="success"
link size="small"
@click="handleConfigFormTypeOptions(row)" link
> @click="handleConfigFormTypeOptions(row)"
下拉框 >
</el-button> 下拉框
<el-button </el-button>
v-else-if="row.formType === 'input' || row.formType === 'number'" <span v-else>下拉框</span>
type="warning" </span>
size="small" <span v-else-if="row.formType === 'input' || row.formType === 'number'">
link <el-button
class="range-config-button" v-if="canConfigRange"
@click="handleConfigRange(row)" type="warning"
> size="small"
{{ getFormTypeLabel(row.formType) || '暂无' }} link
</el-button> 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> <span v-else>{{ getFormTypeLabel(row.formType) || '暂无' }}</span>
</template> </template>
</el-table-column> </el-table-column>
@@ -236,12 +242,13 @@
/> />
</el-select> </el-select>
<el-button <el-button
v-if="formData.formType === 'select'" v-if="formData.formType === 'select' && canConfigSelectOptions"
type="primary" type="primary"
@click="handleConfigFormTypeOptionsInDrawer" @click="handleConfigFormTypeOptionsInDrawer"
> >
配置 配置
</el-button> </el-button>
<span v-else-if="formData.formType === 'select'">配置</span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="参数分类" prop="dicTypeId"> <el-form-item label="参数分类" prop="dicTypeId">
@@ -259,11 +266,13 @@
/> />
</el-select> </el-select>
<el-button <el-button
v-if="canConfigSelectOptions"
type="primary" type="primary"
@click="handleConfigCategoryOptionsInDrawer" @click="handleConfigCategoryOptionsInDrawer"
> >
配置 配置
</el-button> </el-button>
<span v-else>配置</span>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="参数描述" prop="paramDesc"> <el-form-item label="参数描述" prop="paramDesc">
@@ -371,10 +380,11 @@
</template> </template>
<script setup lang="ts"> <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 type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Edit, Delete, Close } from '@element-plus/icons-vue' import { Plus, Edit, Delete, Close } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
import { import {
devparamadd, devparamadd,
devparamdel, devparamdel,
@@ -390,6 +400,10 @@ const loading = ref(false)
const submitting = ref(false) const submitting = ref(false)
const rangeSubmitting = 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 paramList = ref<any[]>([])
const total = ref(0) const total = ref(0)
@@ -1031,6 +1045,10 @@ const resetRangeForm = () => {
} }
const handleConfigRange = async (row: any) => { const handleConfigRange = async (row: any) => {
if (!canConfigRange.value) {
ElMessage.warning('您没有设置动作参数取值范围的权限')
return
}
if (!row?.paramName) { if (!row?.paramName) {
ElMessage.warning('缺少参数名称,无法配置取值范围') ElMessage.warning('缺少参数名称,无法配置取值范围')
return return
@@ -1158,11 +1176,19 @@ const isConfigFormType = ref(false) // 是否配置表单控件的下拉框选
// 打开表单控件下拉框选项配置对话框(从列表) // 打开表单控件下拉框选项配置对话框(从列表)
const handleConfigFormTypeOptions = async (row: any) => { const handleConfigFormTypeOptions = async (row: any) => {
if (!canConfigSelectOptions.value) {
ElMessage.warning('您没有设置动作参数下拉框选项列表的权限')
return
}
await openSelectOptionsDialog(row, true) await openSelectOptionsDialog(row, true)
} }
// 打开表单控件下拉框选项配置对话框(从抽屉) // 打开表单控件下拉框选项配置对话框(从抽屉)
const handleConfigFormTypeOptionsInDrawer = async () => { const handleConfigFormTypeOptionsInDrawer = async () => {
if (!canConfigSelectOptions.value) {
ElMessage.warning('您没有设置动作参数下拉框选项列表的权限')
return
}
if (!formData.paramName) { if (!formData.paramName) {
ElMessage.warning('请先输入参数名称') ElMessage.warning('请先输入参数名称')
return return

View File

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