ye-zhaojia 2 hete
szülő
commit
a484fc5c6e

+ 4 - 0
src/components/Sidebar/index.vue

@@ -85,6 +85,10 @@ const menuRoutes = computed(() => {
 })
 
 const filteredMenuRoutes = computed(() => {
+  // 角色用户只显示首页和审批中心
+  if (userStore.isRoleUser) {
+    return menuRoutes.value.filter(r => ['/', '/approval'].includes(r.path))
+  }
   const empType = userStore.userInfo?.employeeType || 'common_user'
   const allowedPaths = menuPermissions[empType] || menuPermissions.common_user
   return menuRoutes.value.filter(r => allowedPaths.includes(r.path))

+ 10 - 0
src/router/index.ts

@@ -101,6 +101,8 @@ const router = createRouter({
   ]
 })
 
+const roleAllowedPaths = ['/', '/dashboard', '/login', '/approval', '/approval/todo', '/approval/handled', '/approval/mine', '/approval/execute']
+
 router.beforeEach(async (to, from, next) => {
   const token = getToken()
   if (token) {
@@ -117,6 +119,14 @@ router.beforeEach(async (to, from, next) => {
           return
         }
       }
+      // 角色用户路由限制
+      if (userStore.isRoleUser) {
+        const allowed = roleAllowedPaths.some(p => to.path === p || to.path.startsWith(p + '/'))
+        if (!allowed) {
+          next('/approval/todo')
+          return
+        }
+      }
       next()
     }
   } else {

+ 5 - 0
src/stores/user-store.ts

@@ -8,8 +8,10 @@ export const useUserStore = defineStore('user', () => {
   const token = ref<string | undefined>(getToken())
   const userInfo = ref<User | null>(null)
   const roles = ref<string[]>([])
+  const userType = ref<string>('SYSTEM')
 
   const isLoggedIn = computed(() => !!token.value)
+  const isRoleUser = computed(() => userType.value === 'ROLE')
 
   async function loginAction(data: LoginData) {
     const res = await login(data)
@@ -21,6 +23,7 @@ export const useUserStore = defineStore('user', () => {
   async function fetchUserInfo() {
     const res = await getUserInfo()
     userInfo.value = res
+    userType.value = res.userType || 'SYSTEM'
     roles.value = res.roleIds?.map(String) || []
     return res
   }
@@ -41,7 +44,9 @@ export const useUserStore = defineStore('user', () => {
     token,
     userInfo,
     roles,
+    userType,
     isLoggedIn,
+    isRoleUser,
     loginAction,
     fetchUserInfo,
     logoutAction

+ 4 - 0
src/types/system.ts

@@ -9,6 +9,7 @@ export interface User {
   deptId?: number
   deptName?: string
   employeeType?: string
+  userType?: string // SYSTEM / ROLE
   roleIds?: number[]
   password?: string
   createTime?: string
@@ -18,6 +19,8 @@ export interface Role {
   id: number
   roleCode: string
   roleName: string
+  username?: string
+  password?: string
   roleScope?: string
   parentId?: number
   deptId?: number
@@ -58,6 +61,7 @@ export interface Dept {
 export interface LoginData {
   username: string
   password: string
+  loginType?: string // SYSTEM / ROLE
 }
 
 export interface LoginResult {

+ 25 - 4
src/views/login/index.vue

@@ -2,6 +2,10 @@
   <div class="login-container">
     <el-card class="login-box">
       <h2 class="login-title">通用型流程审批系统</h2>
+      <el-radio-group v-model="loginType" class="login-type-switch">
+        <el-radio-button label="SYSTEM">系统用户登录</el-radio-button>
+        <el-radio-button label="ROLE">角色用户登录</el-radio-button>
+      </el-radio-group>
       <el-form
         ref="loginFormRef"
         :model="loginForm"
@@ -11,7 +15,7 @@
         <el-form-item prop="username">
           <el-input
             v-model="loginForm.username"
-            placeholder="用户名"
+            :placeholder="loginType === 'ROLE' ? '角色账号' : '用户名'"
             :prefix-icon="User"
             size="large"
           />
@@ -46,7 +50,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue'
+import { ref, reactive, onMounted, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { User, Lock } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
@@ -59,6 +63,7 @@ const userStore = useUserStore()
 
 const loginFormRef = ref<FormInstance>()
 const loading = ref(false)
+const loginType = ref<'SYSTEM' | 'ROLE'>('SYSTEM')
 
 const loginForm = reactive({
   username: '',
@@ -72,22 +77,29 @@ const loginRules: FormRules = {
   password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
 }
 
+watch(loginType, (newType) => {
+  loginForm.username = ''
+  loginForm.password = ''
+})
+
 async function handleLogin() {
   const valid = await loginFormRef.value?.validate().catch(() => false)
   if (!valid) return
 
   loading.value = true
   try {
-    await userStore.loginAction({ ...loginForm })
+    await userStore.loginAction({ ...loginForm, loginType: loginType.value })
     await userStore.fetchUserInfo()
     if (remember.value) {
       localStorage.setItem('login_username', loginForm.username)
       localStorage.setItem('login_password', loginForm.password)
       localStorage.setItem('login_remember', 'true')
+      localStorage.setItem('login_type', loginType.value)
     } else {
       localStorage.removeItem('login_username')
       localStorage.removeItem('login_password')
       localStorage.removeItem('login_remember')
+      localStorage.removeItem('login_type')
     }
     ElMessage.success('登录成功')
     const redirect = route.query.redirect as string
@@ -103,10 +115,14 @@ onMounted(() => {
   const savedUsername = localStorage.getItem('login_username')
   const savedPassword = localStorage.getItem('login_password')
   const savedRemember = localStorage.getItem('login_remember')
+  const savedType = localStorage.getItem('login_type')
   if (savedRemember === 'true' && savedUsername) {
     loginForm.username = savedUsername
     loginForm.password = savedPassword || ''
     remember.value = true
+    if (savedType === 'ROLE' || savedType === 'SYSTEM') {
+      loginType.value = savedType as 'SYSTEM' | 'ROLE'
+    }
   }
 })
 </script>
@@ -125,7 +141,12 @@ onMounted(() => {
 }
 .login-title {
   text-align: center;
-  margin-bottom: 30px;
+  margin-bottom: 20px;
   color: #303133;
 }
+.login-type-switch {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+}
 </style>

+ 11 - 0
src/views/system/role/index.vue

@@ -49,6 +49,7 @@
             <el-table-column type="index" label="序号" width="60" />
             <el-table-column prop="roleCode" label="角色编码" />
             <el-table-column prop="roleName" label="角色名称" />
+            <el-table-column prop="username" label="登录账号" />
             <el-table-column prop="deptName" label="所属部门" />
             <el-table-column prop="roleScope" label="描述" />
             <el-table-column prop="status" label="状态" width="100">
@@ -90,6 +91,12 @@
         <el-form-item label="角色名称" prop="roleName">
           <el-input v-model="form.roleName" />
         </el-form-item>
+        <el-form-item label="登录账号" prop="username">
+          <el-input v-model="form.username" :disabled="isEdit" placeholder="角色用户的登录账号" />
+        </el-form-item>
+        <el-form-item label="密码" prop="password">
+          <el-input v-model="form.password" type="password" show-password :placeholder="isEdit ? '不填表示不修改密码' : '请输入密码'" />
+        </el-form-item>
         <el-form-item label="所属部门" prop="deptId">
           <el-tree-select
             v-model="form.deptId"
@@ -200,6 +207,8 @@ const form = reactive<Partial<Role>>({
   id: undefined,
   roleCode: '',
   roleName: '',
+  username: '',
+  password: '',
   roleScope: '',
   deptId: undefined,
   status: 1
@@ -286,6 +295,8 @@ function handleAdd() {
     id: undefined,
     roleCode: '',
     roleName: '',
+    username: '',
+    password: '',
     roleScope: '',
     deptId: selectedDeptId.value,
     status: 1