权限控制完善
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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) => {
|
const valid = await loginFormRef.value.validate().catch(() => false)
|
||||||
if (valid) {
|
if (!valid) return
|
||||||
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) {
|
loginLoading.value = true
|
||||||
authStore.setToken(token)
|
try {
|
||||||
authStore.setUserInfo({
|
const response: any = await userlogin(loginForm.username, loginForm.password)
|
||||||
...userInfo,
|
const loginData = response?.data ?? response
|
||||||
username: userInfo.username || loginForm.username,
|
const token = loginData?.token || response?.token || loginData
|
||||||
token,
|
const permissions = Array.isArray(loginData?.permissions)
|
||||||
roles,
|
? loginData.permissions
|
||||||
permissions,
|
: Array.isArray(response?.permissions)
|
||||||
})
|
? response.permissions
|
||||||
authStore.setPermissions(permissions)
|
: []
|
||||||
localStorage.setItem('username', userInfo.username || loginForm.username)
|
const roles = Array.isArray(loginData?.roles)
|
||||||
localStorage.setItem('token', token)
|
? loginData.roles
|
||||||
localStorage.setItem('permissions', JSON.stringify(permissions))
|
: Array.isArray(response?.roles)
|
||||||
|
? response.roles
|
||||||
|
: []
|
||||||
|
const userInfo = typeof loginData === 'object' ? loginData : { token }
|
||||||
|
|
||||||
ElMessage.success(response?.message || '登录成功')
|
if (token) {
|
||||||
router.push('/home')
|
authStore.setToken(token)
|
||||||
} else {
|
authStore.setUserInfo({
|
||||||
ElMessage.error(response?.message || '登录失败,未获取到令牌')
|
...userInfo,
|
||||||
}
|
username: userInfo.username || loginForm.username,
|
||||||
} catch (error: any) {
|
token,
|
||||||
// 错误提示已在全局响应拦截中处理,这里不重复弹出
|
roles,
|
||||||
console.warn('login error:', error)
|
permissions,
|
||||||
} finally {
|
})
|
||||||
loginLoading.value = false
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理注册
|
// 处理注册
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
// 待处理 - 显示提示
|
// 待处理 - 显示提示
|
||||||
|
|||||||
Reference in New Issue
Block a user