Commit 969889c6 by 李宁

1

1 parent dd3bdb7a
......@@ -5,37 +5,28 @@ import request from '../../request'
*/
export function queryAccountList(data) {
return request({
url: '/compass/api/system/account/page',
url: '/crm/getUserPageList',
data,
})
}
/**
* 查询角色列表
*/
export function queryRoleList(data) {
return request({
url: '/compass/api/common/enums/account-roles',
method: 'GET'
})
}
/**
* 删除账号
* 添加/更新账号
*/
export function deleteRole(data) {
export function addAndUpdateRole(data) {
return request({
url: '/compass/api/system/account/delete',
url: '/crm' + (data.id?'/updateUser':'/createUser'),
data,
})
}
/**
* 添加/更新账号
* 获取指定区域下的所有区域
*/
export function addAndUpdateRole(data) {
export function queryAreaData(data) {
return request({
url: '/compass/api/system/account' + (data.id?'/update':'/create'),
data,
url: '/crm/getArea?areaId='+data.areaId,
method: 'GET'
})
}
\ No newline at end of file
......@@ -15,7 +15,6 @@
<span class="text-[16px]">享零工云平台</span>
</h1>
</div>
<!-- 导航菜单 -->
<nav class="flex-1 py-6 px-3 overflow-hidden">
<div class="space-y-6">
......@@ -62,7 +61,6 @@
</button>
</template>
</div>
<!-- 系统管理菜单 -->
<div class="space-y-1" v-if="systemMenuItems.length>0">
<div v-if="!isSidebarCollapsed" class="px-4 mb-2">
......@@ -110,7 +108,6 @@
</div>
</div>
</nav>
<!-- 收缩按钮 -->
<div class="p-4 border-t border-neutral-800">
<button
......@@ -132,7 +129,6 @@
</button>
</div>
</div>
<!-- 右侧主内容区 -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- 顶部Header -->
......@@ -188,7 +184,6 @@
</div>
</template>
</div>
<!-- 用户下拉菜单 -->
<el-dropdown trigger="click" @command="handleCommand">
<el-button
......@@ -216,7 +211,6 @@
</template>
</el-dropdown>
</header>
<!-- 页面内容区 -->
<main class="flex-1 overflow-auto p-6 bg-[#F0F2F5]">
<!-- 订单监控页面 -->
......@@ -276,7 +270,6 @@
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import {
......@@ -290,10 +283,8 @@ import {
ChevronDown
} from 'lucide-vue-next'
import { ElMessage, ElMessageBox } from 'element-plus'
// 导入图片资源
import platformLogoImg from '@/assets/8a3322d5ba8c2ae3592af24d73566a63828a3a27.png'
// 导入子组件
import OrderMonitoring from './OrderMonitoring.vue'
import OrderDetail from './OrderDetail.vue'
......@@ -301,9 +292,7 @@ import BusinessRulesManagement from './BusinessRulesManagement.vue'
import RoleManagement, { type Permission } from './RoleManagement.vue'
import UserManagement, { type User as UserType } from './UserManagement.vue'
import RoleIcon from './icons/RoleIcon.vue'
const platformLogo = ref(platformLogoImg)
// 本地Role类型定义,确保permissions字段是必需的
interface Role {
id: string
......@@ -314,22 +303,17 @@ interface Role {
status: '启用' | '禁用'
createTime?: string
}
interface DesktopMainProps {
currentUser: { username: string; role: 'admin' | 'viewer' } | null;
}
const props = defineProps<DesktopMainProps>()
const emit = defineEmits(['logout'])
// 响应式数据
const isSidebarCollapsed = ref(false)
const activeMenu = ref<string>('orders') // 当前激活的菜单
const activeView = ref<string>('orders') // 当前显示的视图
const selectedOrderId = ref<string | null>(null) // 当前查看的订单ID
const userInfo = ref(localStorage.getItem('pcUserInfo')?JSON.parse(localStorage.getItem('pcUserInfo')):{})
// 菜单配置
// const businessMenuItems = [
// { id: 'orders', label: '登记订单管理', icon: LayoutDashboard },
......@@ -353,10 +337,8 @@ const businessMenuItems = computed(() => {
return barr
}
}
return []
})
// const systemMenuItems = [
// { id: 'roles', label: '角色管理', icon: RoleIcon },
// { id: 'users', label: '账号管理', icon: Users }
......@@ -379,24 +361,20 @@ const systemMenuItems = computed(() => {
return barr
}
}
return []
})
const menuMap: Record<string, string> = {
orders: '登记订单管理',
business: '业务酬金管理',
roles: '角色管理',
users: '账号管理',
}
const currentMenuTitle = computed(() => {
if (activeView.value === 'order-detail') {
return '订单详情'
}
return menuMap[activeView.value] || '首页'
})
// 业务规则数据
const businessRules = ref<BusinessRule[]>([
{
......@@ -432,14 +410,11 @@ const businessRules = ref<BusinessRule[]>([
createTime: '2025-10-20 10:15'
}
])
// 模拟订单数据
// 使用空数组,让 OrderMonitoring 组件自动生成模拟数据
const orders = ref([])
// 存储从 OrderMonitoring 组件获取的订单数据
const allOrders = ref([] as any[])
// 类型定义
interface BusinessRule {
id: string
......@@ -449,8 +424,6 @@ interface BusinessRule {
status: "生效中" | "已停用"
createTime: string
}
interface Organization {
id: string
name: string
......@@ -458,8 +431,6 @@ interface Organization {
parentId?: string
children?: Organization[]
}
// 角色管理数据
const roles = ref<Role[]>([
{
......@@ -509,21 +480,17 @@ const roles = ref<Role[]>([
createTime: '2025-10-20 10:00:00'
}
])
// 权限数据
const permissions = ref<Permission[]>(JSON.parse(localStorage.getItem('pcUserInfo') || '{}').functions || [])
// 根据订单ID获取订单详情
const getOrderById = (orderId: string) => {
return allOrders.value.find((order: any) => order.id === orderId)
}
// 方法
const handleMenuChange = (menuId: string) => {
activeMenu.value = menuId
activeView.value = menuId
}
const handleCommand = (command: string) => {
if (command === 'logout') {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
......@@ -540,17 +507,14 @@ const handleCommand = (command: string) => {
ElMessage.info(`点击了 ${command}`)
}
}
const handleViewOrderDetail = (order: any) => {
selectedOrderId.value = order.id
activeView.value = 'order-detail'
}
const handleBackToOrders = () => {
selectedOrderId.value = null
activeView.value = 'orders'
}
const handleUpdateOrder = (updates: any) => {
// 更新 allOrders 中的订单数据
const orderIndex = allOrders.value.findIndex(order => order.id === selectedOrderId.value)
......@@ -560,12 +524,10 @@ const handleUpdateOrder = (updates: any) => {
console.log('更新订单:', updates)
ElMessage.success('订单更新成功')
}
// 接收来自 OrderMonitoring 组件的订单数据
const handleOrdersUpdate = (ordersList: any[]) => {
allOrders.value = ordersList
}
// 面包屑导航点击处理
const handleBreadcrumbClick = (level: string) => {
if (level === 'level1') {
......@@ -579,7 +541,6 @@ const handleBreadcrumbClick = (level: string) => {
}
}
}
// 业务规则管理方法
const handleAddBusinessRule = (ruleData: Omit<BusinessRule, 'id' | 'createTime'>) => {
const newRule = {
......@@ -597,7 +558,6 @@ const handleAddBusinessRule = (ruleData: Omit<BusinessRule, 'id' | 'createTime'>
ElMessage.success('业务规则创建成功')
console.log('新增业务规则:', newRule)
}
const handleUpdateBusinessRule = (ruleId: string, updates: Partial<BusinessRule>) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
......@@ -610,7 +570,6 @@ const handleUpdateBusinessRule = (ruleId: string, updates: Partial<BusinessRule>
console.log('更新业务规则:', ruleId, updates)
}
}
const handleToggleBusinessRuleStatus = (ruleId: string) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
......@@ -624,7 +583,6 @@ const handleToggleBusinessRuleStatus = (ruleId: string) => {
}
}
}
const handleDeleteBusinessRule = (ruleId: string) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
......@@ -633,7 +591,6 @@ const handleDeleteBusinessRule = (ruleId: string) => {
console.log('删除业务规则:', ruleId)
}
}
// 角色管理方法
const handleAddRole = (roleData: any) => {
const newRole: Role = {
......@@ -652,7 +609,6 @@ const handleAddRole = (roleData: any) => {
roles.value.push(newRole)
console.log('新增角色:', newRole)
}
const handleUpdateRole = (roleId: string, updates: any) => {
const roleIndex = roles.value.findIndex(role => role.id === roleId)
if (roleIndex !== -1) {
......@@ -667,7 +623,6 @@ const handleUpdateRole = (roleId: string, updates: any) => {
}
}
}
// 组织架构数据
const organizations = ref<Organization[]>([
{
......@@ -720,10 +675,8 @@ const organizations = ref<Organization[]>([
]
}
])
// 当前登录用户ID
const currentUserId = ref('user-001')
// 用户数据
const users = ref<UserType[]>([
{
......@@ -775,7 +728,6 @@ const users = ref<UserType[]>([
creatorId: 'user-001'
}
])
// 用户管理方法
const handleAddUser = (userData: Omit<UserType, 'id' | 'createTime' | 'creatorId'>) => {
const newUser = {
......@@ -794,7 +746,6 @@ const handleAddUser = (userData: Omit<UserType, 'id' | 'createTime' | 'creatorId
users.value.push(newUser)
console.log('新增用户:', newUser)
}
const handleUpdateUser = (userId: string, updates: Partial<UserType>) => {
const userIndex = users.value.findIndex(user => user.id === userId)
if (userIndex !== -1) {
......@@ -806,7 +757,6 @@ const handleUpdateUser = (userId: string, updates: Partial<UserType>) => {
console.log('更新用户:', userId, updates)
}
}
const handleDeleteUser = (userId: string) => {
const userIndex = users.value.findIndex(user => user.id === userId)
if (userIndex !== -1) {
......@@ -815,18 +765,15 @@ const handleDeleteUser = (userId: string) => {
}
}
</script>
<style scoped>
/* 隐藏滚动条但保持滚动功能 */
.hide-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, and Opera */
}
/* 移除右上角用户信息按钮的悬浮背景效果 */
:deep(header .el-button.is-text:hover),
:deep(header .el-button.is-text:focus),
......
......@@ -48,7 +48,6 @@
class="inline-block w-2 h-2 rounded-sm bg-white"
/>
</div>
<!-- 组织图标 -->
<Building2
:class="[
......@@ -110,11 +109,9 @@
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { Building2, ChevronDown, ChevronRight } from 'lucide-vue-next'
interface Organization {
id: string
name: string
......@@ -122,13 +119,11 @@ interface Organization {
parentId?: string
children?: Organization[]
}
interface Role {
id: string
name: string
level: '地市级' | '区县级' | '一线人员'
}
interface Props {
organizations: Organization[]
selectedId?: string
......@@ -137,7 +132,6 @@ interface Props {
expandedIds?: Set<string>
accountType?: '地市级' | '区县级' | '一线人员'
}
const props = withDefaults(defineProps<Props>(), {
selectedId: '',
roleId: '',
......@@ -145,12 +139,10 @@ const props = withDefaults(defineProps<Props>(), {
expandedIds: () => new Set(),
accountType: undefined
})
const emit = defineEmits<{
select: [orgId: string]
'toggle-expand': [orgId: string]
}>()
// 如果没有传入 expandedIds,使用本地状态
const localExpandedIds = ref(new Set<string>())
const expandedIds = computed(() =>
......@@ -158,12 +150,10 @@ const expandedIds = computed(() =>
? props.expandedIds
: localExpandedIds.value
)
// 工具函数
const hasChildren = (org: Organization): boolean => {
return !!(org.children && org.children.length > 0)
}
const isSelectable = (org: Organization): boolean => {
console.log('检查组织可选性:', org.name, org.type, '账号类型:', props.accountType)
......@@ -196,7 +186,6 @@ const isSelectable = (org: Organization): boolean => {
// 如果没有账号类型,可以选择所有组织
return true
}
// 检查组织是否属于区县下
const isUnderCounty = (org: Organization): boolean => {
console.log('OrganizationTree.vue:202 检查组织是否属于区县下:', org.name, org.type, org.id)
......@@ -239,7 +228,6 @@ const isUnderCounty = (org: Organization): boolean => {
console.log('OrganizationTree.vue:241 未找到区县父组织')
return false
}
const getOrgTypeLabel = (type: Organization['type']) => {
switch (type) {
case '地市':
......@@ -252,21 +240,18 @@ const getOrgTypeLabel = (type: Organization['type']) => {
return type
}
}
const getRoleLevel = (roleId: string): string => {
if (!props.roles || props.roles.length === 0) return '区县级'
const role = props.roles.find(r => r.id === roleId)
return role ? role.level : '区县级'
}
// 事件处理
const handleSelect = (org: Organization) => {
if (isSelectable(org)) {
emit('select', org.id)
}
}
const toggleExpand = (orgId: string) => {
if (props.expandedIds && props.expandedIds.size > 0) {
// 使用父组件的展开状态
......@@ -280,11 +265,9 @@ const toggleExpand = (orgId: string) => {
}
}
}
const handleToggleExpand = (orgId: string) => {
emit('toggle-expand', orgId)
}
// 初始化展开顶级组织
if (props.expandedIds && props.expandedIds.size === 0) {
props.organizations.forEach(org => {
......@@ -294,128 +277,98 @@ if (props.expandedIds && props.expandedIds.size === 0) {
})
}
</script>
<style scoped>
.space-y-1 > * + * {
margin-top: 0.25rem;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.gap-2 {
gap: 0.5rem;
}
.p-1 {
padding: 0.25rem;
}
.p-2 {
padding: 0.5rem;
}
.rounded {
border-radius: 0.25rem;
}
.cursor-pointer {
cursor: pointer;
}
.cursor-not-allowed {
cursor: not-allowed;
}
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.opacity-40 {
opacity: 0.4;
}
.bg-blue-50 {
background-color: #eff6ff;
}
.border {
border-width: 1px;
}
.border-blue-500 {
border-color: #3b82f6;
}
.hover\:bg-neutral-50:hover {
background-color: #f9fafb;
}
.hover\:bg-neutral-200:hover {
background-color: #e5e7eb;
}
.h-4 {
height: 1rem;
}
.w-4 {
width: 1rem;
}
.w-6 {
width: 1.5rem;
}
.text-neutral-300 {
color: #d1d5db;
}
.text-neutral-400 {
color: #9ca3af;
}
.text-neutral-500 {
color: #6b7280;
}
.text-neutral-600 {
color: #4b5563;
}
.text-neutral-900 {
color: #111827;
}
.text-blue-600 {
color: #2563eb;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.flex-1 {
flex: 1 1 0%;
}
.ml-4 {
margin-left: 1rem;
}
.border-l-2 {
border-left-width: 2px;
}
.border-neutral-200 {
border-color: #e5e7eb;
}
</style>
</style>
\ No newline at end of file
......@@ -261,19 +261,30 @@ const topLevelPermissions = computed(() =>
const filteredRoles = ref([])
const getAllIds = (tree)=>{
const ids = [] // 结果池
const stack = [...tree] // 根节点入栈
while (stack.length) {
const node = stack.pop()
if (node.id !== undefined) ids.push(node.id) // 收集当前节点
if (node.children?.length) {
// 子节点全部入栈(顺序无所谓就 push,要顺序就 unshift)
stack.push(...node.children)
}
}
return ids
}
// 获取角色的权限ID列表,兼容不同的字段名
const getRolePermissionIds = async (role: Role)=> {
try {
const response = await $api.queryRolePermission({
id: role.id
})
return [1,2,3,4]
if (response.c === 0 && response.d) {
// 假设返回的数据结构是 { functionIds: ['id1', 'id2', ...] }
return response.d.functionIds || response.d.permissionIds || []
return getAllIds(response.d.list)
} else {
console.warn('获取角色权限失败:', response.msg)
return role.permissionIds || role.permissions || []
......@@ -340,17 +351,8 @@ const handleSave = async () => {
return
}
const roleData = {
name: roleName.value.trim(),
description: roleDescription.value.trim(),
permissionIds: selectedPermissions.value,
status: roleStatus.value
}
console.log(roleData)
const response = await $api.createOrUpdateRole({
roleId: '',
roleId: editingRole.value?editingRole.value.id:'',
roleName: roleName.value.trim(),
remark: roleDescription.value.trim(),
status: roleStatus.value,
......
......@@ -15,7 +15,6 @@
新增用户
</el-button>
</div>
<!-- 搜索和筛选 -->
<el-card>
<div class="grid grid-cols-5 gap-4 px-6 py-6">
......@@ -28,7 +27,6 @@
clearable
/>
</div>
<el-select
v-model="roleFilter"
placeholder="角色"
......@@ -43,7 +41,6 @@
:value="role.id"
/>
</el-select>
<el-select
v-model="accountTypeFilter"
placeholder="账号类型"
......@@ -55,7 +52,6 @@
<el-option label="区县级" value="区县级" />
<el-option label="一线人员" value="一线人员" />
</el-select>
<el-select
v-model="statusFilter"
placeholder="状态"
......@@ -66,7 +62,6 @@
<el-option label="正常" value="正常" />
<el-option label="禁用" value="禁用" />
</el-select>
<el-select
v-model="organizationFilter"
placeholder="所属组织"
......@@ -83,7 +78,6 @@
</el-select>
</div>
</el-card>
<!-- 用户列表 -->
<el-card class="p-5">
......@@ -182,7 +176,6 @@
</el-table>
</div>
</el-card>
<!-- 新增/编辑用户对话框 -->
<el-dialog
v-model="isDialogOpen"
......@@ -205,23 +198,18 @@
:disabled="!!editingUser"
/>
</el-form-item>
<el-form-item label="用户姓名" required>
<el-input
v-model="formData.realName"
placeholder="真实姓名"
/>
</el-form-item>
<el-form-item label="手机号" required>
<el-input
v-model="formData.phone"
placeholder="11位手机号"
/>
</el-form-item>
<el-form-item label="账号类型" required>
<el-select
v-model="formData.accountType"
......@@ -234,7 +222,6 @@
<el-option label="一线人员" value="一线人员" />
</el-select>
</el-form-item>
<el-form-item label="角色" required class="col-span-2">
<el-select
v-model="formData.roleId"
......@@ -254,7 +241,6 @@
</div>
</el-form-item>
</div>
<div class="flex items-center justify-between p-4 bg-neutral-50 rounded border border-neutral-200">
<div>
<div class="text-neutral-900 font-medium">用户状态</div>
......@@ -265,7 +251,6 @@
/>
</div>
</div>
<!-- 所属组织(当前登录用户不可修改) -->
<div v-if="!editingUser || !isCurrentUser(editingUser)" class="space-y-4">
<div class="flex items-center justify-between pb-2 border-b border-neutral-200">
......@@ -296,7 +281,6 @@
提示:点击组织名称选择,点击箭头展开/收起子组织。灰色不可选的组织表示不符合当前角色的层级要求。
</p>
</div>
<!-- 当前登录用户的组织信息(只读显示) -->
<div v-else-if="editingUser && isCurrentUser(editingUser)" class="space-y-4">
<h3 class="text-neutral-900 pb-2 border-b border-neutral-200">所属组织</h3>
......@@ -320,7 +304,6 @@
</div>
</div>
</div>
<template #footer>
<el-button @click="isDialogOpen = false">取消</el-button>
<el-button type="primary" @click="handleSave">确定</el-button>
......@@ -328,19 +311,16 @@
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Building2, Search } from 'lucide-vue-next'
import OrganizationTree from './OrganizationTree.vue'
// 类型定义
export interface User {
id: string
username: string
realName: string
organizationId: string
roleId: string
accountType?: '地市级' | '区县级' | '一线人员'
......@@ -350,7 +330,6 @@ export interface User {
createTime?: string
creatorId?: string
}
interface Role {
id: string
name: string
......@@ -359,7 +338,6 @@ interface Role {
status: '启用' | '禁用'
permissions: string[]
}
interface Organization {
id: string
name: string
......@@ -367,7 +345,6 @@ interface Organization {
parentId?: string
children?: Organization[]
}
// Props
interface UserManagementProps {
users: User[]
......@@ -375,13 +352,11 @@ interface UserManagementProps {
organizations: Organization[]
currentUserId: string
}
const props = defineProps<UserManagementProps>()
const emit = defineEmits<{
addUser: [user: Omit<User, 'id' | 'createTime' | 'creatorId'>]
updateUser: [id: string, updates: Partial<User>]
}>()
// 响应式数据
const isDialogOpen = ref(false)
const editingUser = ref<User | null>(null)
......@@ -391,30 +366,24 @@ const accountTypeFilter = ref<'all' | '地市级' | '区县级' | '一线人员'
const statusFilter = ref<'all' | '正常' | '禁用'>('all')
const organizationFilter = ref<string>('all')
const organizationExpandedIds = ref(new Set<string>())
// 表单数据
const formData = ref({
username: '',
realName: '',
phone: '',
accountType: '一线人员' as '地市级' | '区县级' | '一线人员',
organizationId: '',
roleId: '',
isActive: true
})
// 计算属性
const availableRoles = computed(() =>
props.roles.filter(r => r.status === '启用' && r.name !== '地市主管理员')
)
const topLevelOrganizations = computed(() =>
props.organizations.filter(o => !o.parentId)
)
const totalUsers = computed(() => props.users.length - 1) // 排除当前登录用户
const hasFilters = computed(() =>
generalSearch.value !== '' ||
roleFilter.value !== 'all' ||
......@@ -422,7 +391,6 @@ const hasFilters = computed(() =>
statusFilter.value !== 'all' ||
organizationFilter.value !== 'all'
)
const filteredUsers = computed(() => {
return props.users.filter(user => {
// 过滤掉当前登录用户
......@@ -441,11 +409,9 @@ const filteredUsers = computed(() => {
const matchAccountType = accountTypeFilter.value === 'all' || user.accountType === accountTypeFilter.value
const matchStatus = statusFilter.value === 'all' || user.status === statusFilter.value
const matchOrganization = organizationFilter.value === 'all' || user.organizationId === organizationFilter.value
return matchGeneral && matchRole && matchAccountType && matchStatus && matchOrganization
})
})
const roleHint = computed(() => {
if (!formData.value.roleId) return ''
......@@ -454,7 +420,6 @@ const roleHint = computed(() => {
return `${role.name}`
})
// 工具函数
const getOrganizationById = (id: string): Organization | undefined => {
const find = (orgs: Organization[]): Organization | undefined => {
......@@ -469,11 +434,9 @@ const getOrganizationById = (id: string): Organization | undefined => {
}
return find(props.organizations)
}
const getRoleById = (id: string): Role | undefined => {
return props.roles.find(r => r.id === id)
}
const getAccountTypeTagType = (accountType: string) => {
switch (accountType) {
case '地市级': return 'warning'
......@@ -482,25 +445,21 @@ const getAccountTypeTagType = (accountType: string) => {
default: return 'info'
}
}
const isCurrentUser = (user: User): boolean => {
return user.id === props.currentUserId
}
const getRowStyle = ({ row }: { row: User }) => {
if (isCurrentUser(row)) {
return { backgroundColor: '#f0f9ff', borderLeft: '4px solid #1677ff' }
}
return {}
}
// 事件处理
const handleOpenAddDialog = () => {
editingUser.value = null
formData.value = {
username: '',
realName: '',
phone: '',
accountType: '一线人员' as '地市级' | '区县级' | '一线人员',
organizationId: '',
......@@ -509,13 +468,11 @@ const handleOpenAddDialog = () => {
}
isDialogOpen.value = true
}
const handleOpenEditDialog = (user: User) => {
editingUser.value = user
formData.value = {
username: user.username,
realName: user.realName,
phone: user.phone || '',
accountType: user.accountType || '一线人员',
organizationId: user.organizationId,
......@@ -524,7 +481,6 @@ const handleOpenEditDialog = (user: User) => {
}
isDialogOpen.value = true
}
const handleAccountTypeChange = () => {
// 当账号类型变更时,清空组织选择
formData.value.organizationId = ''
......@@ -539,15 +495,12 @@ const handleAccountTypeChange = () => {
ElMessage.info('账号类型为一线人员,所属组织只能选择客户经理团队')
}
}
const handleRoleChange = () => {
// 角色变更时不进行层级检查
}
const handleOrganizationSelect = (orgId: string) => {
formData.value.organizationId = orgId
}
const handleToggleExpand = (orgId: string) => {
if (organizationExpandedIds.value.has(orgId)) {
organizationExpandedIds.value.delete(orgId)
......@@ -555,7 +508,6 @@ const handleToggleExpand = (orgId: string) => {
organizationExpandedIds.value.add(orgId)
}
}
const isOrganizationSelectable = (orgType: string, roleId: string): boolean => {
// 只根据账号类型进行组织层级限制,不根据角色层级限制
const accountType = formData.value.accountType
......@@ -575,33 +527,26 @@ const isOrganizationSelectable = (orgType: string, roleId: string): boolean => {
return true
}
}
const handleSave = () => {
// 表单验证
if (!formData.value.username.trim()) {
ElMessage.error('请输入工号')
return
}
if (!formData.value.realName.trim()) {
ElMessage.error('请输入用户姓名')
return
}
if (!formData.value.phone.trim()) {
ElMessage.error('请输入手机号')
return
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(formData.value.phone.trim())) {
ElMessage.error('请输入正确的11位手机号')
return
}
// 当前登录用户不需要验证组织(不可修改)
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
if (!formData.value.organizationId) {
......@@ -609,17 +554,14 @@ const handleSave = () => {
return
}
}
if (!formData.value.roleId) {
ElMessage.error('请选择角色')
return
}
if (!formData.value.accountType) {
ElMessage.error('请选择账号类型')
return
}
// 检查用户名是否重复
const existingUser = props.users.find(u =>
u.username === formData.value.username.trim() && u.id !== editingUser.value?.id
......@@ -628,7 +570,6 @@ const handleSave = () => {
ElMessage.error('该工号已存在')
return
}
const userData: any = {
username: formData.value.username.trim(),
realName: formData.value.realName.trim(),
......@@ -638,14 +579,10 @@ const handleSave = () => {
phone: formData.value.phone.trim() || undefined,
creatorId: props.currentUserId
}
// 当前登录用户不可修改自己的所属组织
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
userData.organizationId = formData.value.organizationId
}
if (editingUser.value) {
emit('updateUser', editingUser.value.id, userData)
ElMessage.success('用户更新成功')
......@@ -653,10 +590,8 @@ const handleSave = () => {
emit('addUser', userData)
ElMessage.success('用户创建成功')
}
isDialogOpen.value = false
}
// 组织下拉选项(扁平化带缩进)
const organizationOptions = computed(() => {
const result: { id: string; label: string }[] = []
......@@ -670,9 +605,7 @@ const organizationOptions = computed(() => {
traverse(props.organizations || [])
return result
})
</script>
<style scoped>
/* 让表单项标签和输入框呈上下结构 */
:deep(.el-form-item) {
......@@ -680,197 +613,150 @@ const organizationOptions = computed(() => {
flex-direction: column;
align-items: stretch;
}
:deep(.el-form-item__label) {
margin-bottom: 0;
text-align: left !important;
padding: 0 !important;
}
:deep(.el-form-item__content) {
margin-left: 0 !important;
}
.space-y-6 > * + * {
margin-top: 1.5rem;
}
.space-y-4 > * + * {
margin-top: 1rem;
}
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-cols-5 {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.gap-4 {
gap: 1rem;
}
.h-10 {
height: 2.5rem;
}
.w-full {
width: 100%;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-neutral-500 {
color: #6b7280;
}
.text-neutral-700 {
color: #374151;
}
.text-neutral-900 {
color: #111827;
}
.text-blue-600 {
color: #2563eb;
}
.text-red-500 {
color: #ef4444;
}
.border-neutral-200 {
border-color: #e5e7eb;
}
.border-neutral-300 {
border-color: #d1d5db;
}
.bg-neutral-50 {
background-color: #f9fafb;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
.border {
border-width: 1px;
}
.border-b {
border-bottom-width: 1px;
}
.p-4 {
padding: 1rem;
}
.p-5 {
padding: 1.25rem;
}
.p-6 {
padding: 1.5rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.pb-2 {
padding-bottom: 0.5rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.gap-2 {
gap: 0.5rem;
}
.gap-3 {
gap: 0.75rem;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-y-auto {
overflow-y: auto;
}
.max-h-\[400px\] {
max-height: 400px;
}
/* 用户管理对话框样式 */
:deep(.user-management-dialog) {
max-height: 90vh;
......@@ -879,30 +765,25 @@ const organizationOptions = computed(() => {
display: flex;
flex-direction: column;
}
:deep(.user-management-dialog .el-dialog__body) {
flex: 1;
overflow-y: auto;
max-height: calc(90vh - 120px); /* 减去头部和底部的空间 */
padding: 24px !important;
}
:deep(.user-management-dialog .el-dialog__header) {
flex-shrink: 0;
padding: 24px 24px 0 24px !important;
}
:deep(.user-management-dialog .el-dialog__footer) {
flex-shrink: 0;
padding: 0 24px 24px 24px !important;
}
/* 搜索输入框样式 - 与订单管理保持一致 */
:deep(.search-input .el-input__wrapper) {
padding-left: 2.5rem !important;
}
:deep(.search-input .el-input__inner) {
padding-left: 0 !important;
}
</style>
</style>
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!