-
diff --git a/src/components/panel.vue b/src/components/panel.vue
index 822dd6e..01ef49d 100644
--- a/src/components/panel.vue
+++ b/src/components/panel.vue
@@ -2,18 +2,20 @@
import {ref, computed} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import { ElMessageBox } from 'element-plus'
-import { getUserInfo, logout, hasPermission } from '../store/user'
+import { getUserInfo, logout, hasPermission, getRoleLevel, isManager } from '../store/user'
const route = useRoute()
const router = useRouter()
-const isCollapse = ref(false)
-const switchFold = () => {
- isCollapse.value = !isCollapse.value
-}
-
// 获取用户信息
const userInfo = computed(() => getUserInfo())
+const roleLevel = computed(() => getRoleLevel())
+const isManagerLevel = computed(() => isManager())
+
+// 主题色:副经理以上(level<=3)用红色,一般专员(level=4)用浅色
+const themeColor = computed(() => roleLevel.value <= 3 ? '#E60012' : '#FF8C69')
+const themeBg = computed(() => roleLevel.value <= 3 ? '#FEF0F0' : '#FFF5F0')
+const themeBorder = computed(() => roleLevel.value <= 3 ? '#FECDD0' : '#FFDAB9')
// 退出登录
const handleLogout = () => {
@@ -31,102 +33,55 @@ const handleLogout = () => {
const checkPermission = (resource, action) => {
return hasPermission(resource, action)
}
+
+// 角色级别名称
+const roleName = computed(() => {
+ const level = roleLevel.value
+ if (level === 1) return '总经理'
+ if (level === 2) return '部门经理'
+ if (level === 3) return '部门副经理'
+ return '一般专员'
+})
-
+
-
@@ -137,29 +92,22 @@ const checkPermission = (resource, action) => {
align-items: center;
background: #fff;
border-bottom: 2px solid #E60012;
- padding: 0;
+ padding: 0 20px;
height: 60px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
-.header-menu {
- flex: 1;
- border-bottom: none !important;
+.header-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
}
-.logo-item {
+.logo-section {
display: flex;
align-items: center;
gap: 10px;
- border-bottom: none !important;
-}
-
-.logo-item.is-active {
- border-bottom: none !important;
-}
-
-.logo-item.is-active::after {
- display: none !important;
}
.logo-img {
@@ -176,101 +124,56 @@ const checkPermission = (resource, action) => {
white-space: nowrap;
}
+.back-btn {
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ margin-left: 20px;
+ padding: 6px 12px;
+ border-radius: 6px;
+ transition: background 0.2s;
+}
+
+.back-btn:hover {
+ background: rgba(0, 0, 0, 0.05);
+}
+
.header-right {
display: flex;
align-items: center;
- margin-left: auto;
+ gap: 20px;
}
.user-info {
display: flex;
align-items: center;
gap: 6px;
- margin-right: 20px;
color: #606266;
font-size: 14px;
}
-.logout-item {
- color: #909399;
-}
-
-.logout-item:hover {
- color: #E60012 !important;
-}
-
-/* ========== 侧边栏 ========== */
-.app-aside {
- transition: width 0.3s ease;
- background: #fff;
- border-right: 1px solid #eee;
- overflow: hidden;
-}
-
-.aside-menu {
- height: 100%;
+.logout-btn {
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ font-size: 14px;
display: flex;
- flex-direction: column;
- border-right: none;
- overflow: hidden;
+ align-items: center;
+ gap: 4px;
}
-.aside-menu:not(.el-menu--collapse) {
- width: 200px;
-}
-
-/* 菜单项样式 */
-.aside-menu .el-menu-item {
- height: 50px;
- line-height: 50px;
- margin: 4px 8px;
- border-radius: 8px;
- color: #606266;
- transition: all 0.3s;
-}
-
-.aside-menu .el-menu-item:hover {
- background: #FEF0F0;
- color: #E60012;
-}
-
-.aside-menu .el-menu-item.is-active {
- background: #E60012;
- color: #fff;
-}
-
-.aside-menu .el-menu-item.is-active .el-icon {
- color: #fff;
-}
-
-/* 底部收缩按钮 */
-.collapse-btn {
- margin-top: auto !important;
+.logout-btn:hover {
+ opacity: 0.8;
}
/* ========== 主内容区 ========== */
.app-main {
padding: 20px;
- background: #f5f7fa;
overflow-y: auto;
-}
-
-/* ========== 收缩动画 ========== */
-.rotate-180-animation {
- animation: rotate-180 0.3s ease-in-out forwards;
-}
-
-.rotate-180-animation-reverse {
- animation: rotate-180-reverse 0.3s ease-in-out forwards;
-}
-
-@keyframes rotate-180 {
- from { transform: rotate(0deg); }
- to { transform: rotate(180deg); }
-}
-
-@keyframes rotate-180-reverse {
- from { transform: rotate(180deg); }
- to { transform: rotate(0deg); }
+ flex: 1;
}
diff --git a/src/components/service.vue b/src/components/service.vue
index 768238f..fb26d06 100644
--- a/src/components/service.vue
+++ b/src/components/service.vue
@@ -11,7 +11,7 @@ import {
} from '../api/service'
import { getSimpleCustomerList } from '../api/customer'
import { getSimpleEmployeeList } from '../api/employee'
-import { hasPermission, getUserInfo } from '../store/user'
+import { hasPermission, getUserInfo, getRoleLevel } from '../store/user'
import { formatDate } from '../utils/date'
const form = reactive({
@@ -300,6 +300,12 @@ const isOperationsManager = computed(() => getUserInfo()?.departmentName === 'op
//是否是管理员
const isAdmin = computed(() => getUserInfo()?.departmentName === 'admin')
+// 是否是一般专员(role_level === 4)
+const isGeneralStaff = computed(() => getRoleLevel() === 4)
+
+// 运营部专员:锁定处理人员为自己
+const isOperationsStaff = computed(() => isOperationsManager.value && isGeneralStaff.value)
+
const currentUserId = computed(() => getUserInfo()?.id)
@@ -467,9 +473,10 @@ const canDeleteRow = (row) => {
-
+
+ 专员无法修改处理人员,已锁定为自己
@@ -560,4 +567,11 @@ const canDeleteRow = (row) => {
.detail-content {
padding: 10px 0;
}
+
+.field-hint {
+ font-size: 12px;
+ color: #909399;
+ margin-top: 4px;
+ line-height: 1.4;
+}
diff --git a/src/components/user.vue b/src/components/user.vue
index a323b5c..c3c7c36 100644
--- a/src/components/user.vue
+++ b/src/components/user.vue
@@ -10,7 +10,7 @@ import {
deleteUserAPI
} from '../api/user'
import { getSimpleEmployeeList, getDepartmentList } from '../api/employee'
-import { hasPermission, getUserInfo } from '../store/user'
+import { hasPermission, getUserInfo, getRoleLevel } from '../store/user'
import { formatDateTime } from '../utils/date'
import { DEPARTMENT_LIST } from '../constants/departments'
@@ -19,7 +19,8 @@ const form = reactive({
password: '',
department_id: 2,
employee_id: null,
- is_active: 1
+ is_active: 1,
+ role_level: 4
})
const search = ref('')
@@ -44,6 +45,27 @@ const totalPages = ref(0)
// 部门列表(动态加载,回退到常量)
const deptList = ref([...DEPARTMENT_LIST])
+// 角色级别选项(根据部门动态变化)
+const roleLevelOptions = computed(() => {
+ const dept = deptList.value.find(d => d.id === form.department_id)
+ if (dept?.name === 'general_manager') {
+ // 总经理办公室只能选择副总经理
+ return [{ value: 2, label: '副总经理' }]
+ }
+ // 一般部门没有总经理,只有部门经理以下三级
+ return [
+ { value: 2, label: '部门经理' },
+ { value: 3, label: '部门副经理' },
+ { value: 4, label: '一般专员' }
+ ]
+})
+
+// 是否是总经理办公室部门
+const isGeneralManagerDept = computed(() => {
+ const dept = deptList.value.find(d => d.id === form.department_id)
+ return dept?.name === 'general_manager'
+})
+
// 员工列表
const employeeList = ref([])
@@ -67,6 +89,16 @@ watch([filterType, filterStatus], () => {
handleSearch()
})
+// 监听部门变化:总经理办公室自动锁定角色级别为副总经理(2),一般部门重置为一般专员(4)
+watch(() => form.department_id, (val) => {
+ const dept = deptList.value.find(d => d.id === val)
+ if (dept?.name === 'general_manager') {
+ form.role_level = 2 // 副总经理
+ } else {
+ form.role_level = 4 // 默认一般专员
+ }
+})
+
// 监听搜索结果变化(服务端分页:不重置页码,避免翻页后被重置回第 1 页)
// 分页后的数据(服务端已分页)
@@ -74,12 +106,34 @@ const paginatedData = computed(() => searchResults.value)
// 获取员工列表(用于下拉选择,改用 simple 接口:无数据范围限制,仅返还在职员工基本信息)
const fetchEmployees = async () => {
- const res = await getSimpleEmployeeList()
+ const params = {}
+ // 一般专员只能选择同级别或更低级别的员工
+ if (currentRoleLevel.value === 4) {
+ params.max_role_level = 4
+ }
+ const res = await getSimpleEmployeeList(params)
if (res.code === 0 || res.code === 200) {
employeeList.value = res.data
}
}
+// 过滤后的部门列表(一般专员不能选择总经理办公室)
+const filteredDeptList = computed(() => {
+ const level = currentRoleLevel.value
+ if (level === 4) {
+ // 一般专员不能选择总经理办公室
+ return deptList.value.filter(d => d.name !== 'general_manager')
+ }
+ return deptList.value
+})
+
+// 过滤后的员工列表(不能选择比自己职位高的员工)
+const filteredEmployeeList = computed(() => {
+ const level = currentRoleLevel.value
+ // 后端已经通过max_role_level参数过滤了,直接返回
+ return employeeList.value
+})
+
// 获取用户列表
const fetchData = async (page) => {
loading.value = true
@@ -149,6 +203,7 @@ const resetForm = () => {
form.department_id = 2
form.employee_id = null
form.is_active = 1
+ form.role_level = 4
isEditMode.value = false
currentUserId.value = null
}
@@ -169,6 +224,7 @@ const editUser = (row) => {
form.department_id = row.department_id
form.employee_id = row.employee_id
form.is_active = row.is_active
+ form.role_level = row.role_level || 4
showAddForm.value = true
showSearchResults.value = false
}
@@ -188,7 +244,8 @@ const saveUser = async () => {
username: form.username,
department_id: form.department_id,
employee_id: form.employee_id,
- is_active: form.is_active
+ is_active: form.is_active,
+ role_level: form.role_level
}
if (!isEditMode.value) {
formData.password = form.password
@@ -267,7 +324,24 @@ const handleCurrentChange = (val) => {
// 检查权限
const canManage = computed(() => hasPermission('user', 'manage'))
-const isAdmin = computed(() => getUserInfo()?.departmentName === 'admin')
+const currentRoleLevel = computed(() => getRoleLevel())
+const isGeneralManager = computed(() => currentRoleLevel.value === 1)
+
+// 角色级别显示
+const getRoleLevelName = (level) => {
+ if (level === 1) return '总经理'
+ if (level === 2) return '部门经理'
+ if (level === 3) return '部门副经理'
+ return '一般专员'
+}
+
+const getRoleLevelTagType = (level) => {
+ const l = Number(level) || 4
+ if (l === 1) return 'danger'
+ if (l === 2) return 'warning'
+ if (l === 3) return 'success'
+ return 'info'
+}
@@ -291,7 +365,7 @@ const isAdmin = computed(() => getUserInfo()?.departmentName === 'admin')
- 新增用户
+ 新增用户
重置
@@ -336,6 +410,13 @@ const isAdmin = computed(() => getUserInfo()?.departmentName === 'admin')
{{ row.dept_desc || '-' }}
+