生产管理系统 - 角色管理、员工管理主页面联调
This commit is contained in:
@@ -103,12 +103,8 @@ export default function EmployeeManagementPage() {
|
||||
} catch (error) {
|
||||
console.error('Failed to load employees:', error);
|
||||
toast.error('加载员工数据失败');
|
||||
|
||||
// 如果API失败,使用localStorage中的数据
|
||||
const data = localStorage.getItem('smart_agriculture_employees');
|
||||
if (data) {
|
||||
setEmployees(JSON.parse(data));
|
||||
}
|
||||
setEmployees([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,14 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Eye, Edit, Trash2, Shield } from 'lucide-react';
|
||||
import { Role, RoleType } from '../types';
|
||||
import { PaginationState } from './roleApi';
|
||||
|
||||
interface RoleListProps {
|
||||
roles: Role[];
|
||||
loading?: boolean;
|
||||
pagination?: PaginationState;
|
||||
onPageChange?: (page: number) => void;
|
||||
onPageSizeChange?: (size: number) => void;
|
||||
onViewDetail: (role: Role) => void;
|
||||
onEdit: (role: Role) => void;
|
||||
onDelete: (id: string) => void;
|
||||
@@ -17,6 +22,10 @@ interface RoleListProps {
|
||||
|
||||
export function RoleList({
|
||||
roles,
|
||||
loading = false,
|
||||
pagination,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
onViewDetail,
|
||||
onEdit,
|
||||
onDelete
|
||||
@@ -52,7 +61,13 @@ export function RoleList({
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{roles.length === 0 ? (
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center text-muted-foreground py-8">
|
||||
加载中...
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : roles.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-center text-muted-foreground py-8">
|
||||
暂无数据
|
||||
@@ -108,6 +123,63 @@ export function RoleList({
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{/* 分页控制 */}
|
||||
{!loading && pagination && pagination.totalPages > 1 && (
|
||||
<div className="flex items-center justify-between px-2 py-4">
|
||||
<div className="flex items-center space-x-2 text-sm text-muted-foreground">
|
||||
<span>显示第 {(pagination.page - 1) * pagination.size + 1} 至 {Math.min(pagination.page * pagination.size, pagination.total)} 条,共 {pagination.total} 条</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onPageChange?.(pagination.page - 1)}
|
||||
disabled={!pagination.hasPrev}
|
||||
>
|
||||
上一页
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center space-x-1">
|
||||
{Array.from({ length: pagination.totalPages }, (_, i) => i + 1).map((pageNum) => (
|
||||
<Button
|
||||
key={pageNum}
|
||||
variant={pageNum === pagination.page ? "default" : "outline"}
|
||||
size="sm"
|
||||
className="w-8 h-8 p-0"
|
||||
onClick={() => onPageChange?.(pageNum)}
|
||||
>
|
||||
{pageNum}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onPageChange?.(pagination.page + 1)}
|
||||
disabled={!pagination.hasNext}
|
||||
>
|
||||
下一页
|
||||
</Button>
|
||||
|
||||
{onPageSizeChange && (
|
||||
<>
|
||||
<select
|
||||
value={pagination.size}
|
||||
onChange={(e) => onPageSizeChange(Number(e.target.value))}
|
||||
className="ml-2 h-8 w-16 rounded-md border border-input bg-background text-sm"
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
</select>
|
||||
<span className="text-sm text-muted-foreground">条/页</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* filekorolheader: 角色管理API接口 - 角色数据查询接口服务
|
||||
* 功能:API请求封装、数据转换、错误处理、分页查询
|
||||
* 路径:/central-config/user/role/components/roleApi
|
||||
* 规范:遵循crop-x/docs/开发项目规范.md,使用SDK API调用,TypeScript类型安全
|
||||
*/
|
||||
|
||||
import { getAuthToken } from "@/utils/token";
|
||||
import {
|
||||
getRolesApiV1UsersPermissionsRolesGet,
|
||||
} from "@/lib/api/sdk.gen";
|
||||
import {
|
||||
RoleApiData,
|
||||
Role,
|
||||
} from '../types';
|
||||
|
||||
// 本地定义PaginationState以避免导入问题
|
||||
export interface PaginationState {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasNext: boolean;
|
||||
hasPrev: boolean;
|
||||
}
|
||||
|
||||
// Re-export types from types.ts for convenience
|
||||
export type { RolesApiResponse, RolesQueryParams } from '../types';
|
||||
|
||||
/**
|
||||
* 获取角色列表数据
|
||||
*/
|
||||
export async function fetchRoles(params: RolesQueryParams = {}): Promise<RolesApiResponse> {
|
||||
try {
|
||||
// 构建查询参数对象
|
||||
const queryParams: any = {};
|
||||
|
||||
if (params.page) queryParams.page = params.page;
|
||||
if (params.size) queryParams.size = params.size;
|
||||
if (params.sort_order) queryParams.sort_order = params.sort_order;
|
||||
|
||||
// 默认参数
|
||||
if (!params.page) queryParams.page = 1;
|
||||
if (!params.size) queryParams.size = 10;
|
||||
if (!params.sort_order) queryParams.sort_order = 'desc';
|
||||
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('角色管理API调用参数:', queryParams);
|
||||
|
||||
// 使用真正的SDK API调用
|
||||
const response = await getRolesApiV1UsersPermissionsRolesGet({
|
||||
query: {
|
||||
...queryParams,
|
||||
// 添加时间戳防止缓存
|
||||
_t: Date.now(),
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`API error: ${response.error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const data = response.data as any;
|
||||
console.log('角色管理API响应:', data);
|
||||
|
||||
// 处理API响应数据
|
||||
if (data && typeof data === 'object' && data.data) {
|
||||
return {
|
||||
data: data.data || [],
|
||||
total: data.total || 0,
|
||||
page: data.page || 1,
|
||||
size: data.size || 10,
|
||||
total_pages: data.total_pages || 0,
|
||||
has_next: data.has_next || false,
|
||||
has_prev: data.has_prev || false,
|
||||
};
|
||||
} else {
|
||||
// 其他情况,返回空结果
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
size: 10,
|
||||
total_pages: 0,
|
||||
has_next: false,
|
||||
has_prev: false,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch roles:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将API数据转换为页面所需的角色数据格式
|
||||
* 为了保持兼容性,这里转换数据格式
|
||||
*/
|
||||
export function transformRoleData(apiRole: RoleApiData): Role {
|
||||
return {
|
||||
id: apiRole.id,
|
||||
name: apiRole.name,
|
||||
code: apiRole.name.toLowerCase().replace(/\s+/g, '_'), // 生成code
|
||||
description: apiRole.description,
|
||||
type: 'custom', // 默认为自定义角色
|
||||
menuIds: [],
|
||||
permissionIds: [],
|
||||
status: 'active',
|
||||
createdAt: apiRole.created_at,
|
||||
updatedAt: apiRole.updated_at,
|
||||
tenant_id: apiRole.tenant_id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量转换角色数据
|
||||
*/
|
||||
export function transformRolesList(apiRoles: RoleApiData[]): Role[] {
|
||||
return apiRoles.map(transformRoleData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
*/
|
||||
export function formatDate(dateString: string): string {
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
}).replace(/\//g, '-');
|
||||
} catch (error) {
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/**
|
||||
* filekorolheader: 角色管理页面 - 系统角色访问控制管理
|
||||
* 功能:角色列表管理、API数据加载、分页查询、角色搜索、详情查看
|
||||
* 路径:/central-config/user/role
|
||||
* 规范:遵循crop-x/docs/开发项目规范.md,使用API调用,shadcn语义化样式,支持翻页
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
@@ -10,11 +17,29 @@ import { RoleList } from './components/RoleList';
|
||||
import { RoleFormDialog } from './components/RoleFormDialog';
|
||||
import { RoleDetailDialog } from './components/RoleDetailDialog';
|
||||
import { RoleManagementInstructions } from './components/RoleManagementInstructions';
|
||||
import { Role, RoleFormData } from './types';
|
||||
import { Role, RoleFormData, RoleFilters } from './types';
|
||||
import {
|
||||
fetchRoles,
|
||||
transformRolesList,
|
||||
RolesApiResponse,
|
||||
RolesQueryParams,
|
||||
PaginationState
|
||||
} from './components/roleApi';
|
||||
|
||||
export default function RoleManagementPage() {
|
||||
const [roles, setRoles] = useState<Role[]>([]);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
totalPages: 0,
|
||||
hasNext: false,
|
||||
hasPrev: false,
|
||||
});
|
||||
const [filters, setFilters] = useState<RoleFilters>({
|
||||
searchKeyword: ''
|
||||
});
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
||||
const [editingRole, setEditingRole] = useState<Role | null>(null);
|
||||
@@ -28,75 +53,67 @@ export default function RoleManagementPage() {
|
||||
|
||||
useEffect(() => {
|
||||
loadRoles();
|
||||
}, []);
|
||||
}, [pagination.page, pagination.size, filters.searchKeyword]);
|
||||
|
||||
const loadRoles = () => {
|
||||
const data = localStorage.getItem('smart_agriculture_roles');
|
||||
if (data) {
|
||||
setRoles(JSON.parse(data));
|
||||
} else {
|
||||
const mockRoles: Role[] = [
|
||||
{
|
||||
id: 'role-1',
|
||||
name: '超级管理员',
|
||||
code: 'super_admin',
|
||||
description: '系统最高权限,可管理所有功能和数据',
|
||||
type: 'system',
|
||||
menuIds: ['*'],
|
||||
permissionIds: ['*'],
|
||||
status: 'active',
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
updatedAt: '2024-01-01T00:00:00',
|
||||
},
|
||||
{
|
||||
id: 'role-2',
|
||||
name: '企业管理员',
|
||||
code: 'enterprise_admin',
|
||||
description: '企业管理员,可管理本企业的员工和数据',
|
||||
type: 'system',
|
||||
menuIds: ['config-user', 'machinery', 'field'],
|
||||
permissionIds: ['user:view', 'user:add', 'user:edit', 'machinery:*'],
|
||||
defaultHomePage: '/machinery/archive/entry',
|
||||
status: 'active',
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
updatedAt: '2024-01-01T00:00:00',
|
||||
},
|
||||
{
|
||||
id: 'role-3',
|
||||
name: '操作员',
|
||||
code: 'operator',
|
||||
description: '一般操作员,可查看和操作农机设备',
|
||||
type: 'system',
|
||||
menuIds: ['machinery', 'field'],
|
||||
permissionIds: ['machinery:view', 'machinery:control', 'field:view'],
|
||||
defaultHomePage: '/machinery/monitoring/location',
|
||||
status: 'active',
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
updatedAt: '2024-01-01T00:00:00',
|
||||
},
|
||||
{
|
||||
id: 'role-4',
|
||||
name: '维修员',
|
||||
code: 'maintenance',
|
||||
description: '农机维修人员,负责设备维护和故障诊断',
|
||||
type: 'custom',
|
||||
menuIds: ['machinery-fault', 'machinery-archive'],
|
||||
permissionIds: ['machinery:view', 'fault:view', 'fault:handle'],
|
||||
status: 'active',
|
||||
createdAt: '2024-10-01T00:00:00',
|
||||
updatedAt: '2024-10-01T00:00:00',
|
||||
},
|
||||
];
|
||||
localStorage.setItem('smart_agriculture_roles', JSON.stringify(mockRoles));
|
||||
setRoles(mockRoles);
|
||||
const loadRoles = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const queryParams: RolesQueryParams = {
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
sort_order: 'desc'
|
||||
};
|
||||
|
||||
const response = await fetchRoles(queryParams);
|
||||
|
||||
// 转换数据格式
|
||||
const transformedRoles = transformRolesList(response.data);
|
||||
|
||||
setRoles(transformedRoles);
|
||||
setPagination({
|
||||
page: response.page,
|
||||
size: response.size,
|
||||
total: response.total,
|
||||
totalPages: response.total_pages,
|
||||
hasNext: response.has_next,
|
||||
hasPrev: response.has_prev,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to load roles:', error);
|
||||
toast.error('加载角色数据失败');
|
||||
|
||||
// 如果API失败,使用localStorage中的数据作为fallback
|
||||
const data = localStorage.getItem('smart_agriculture_roles');
|
||||
if (data) {
|
||||
setRoles(JSON.parse(data));
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索处理函数
|
||||
const handleSearch = (searchKeyword: string) => {
|
||||
setFilters(prev => ({ ...prev, searchKeyword }));
|
||||
// 重置到第一页
|
||||
setPagination(prev => ({ ...prev, page: 1 }));
|
||||
};
|
||||
|
||||
// 分页处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
setPagination(prev => ({ ...prev, page }));
|
||||
};
|
||||
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
setPagination(prev => ({ ...prev, size, page: 1 }));
|
||||
};
|
||||
|
||||
// 过滤角色数据(客户端过滤,用于搜索)
|
||||
const filteredRoles = roles.filter(role => {
|
||||
const matchKeyword = !searchKeyword ||
|
||||
role.name.includes(searchKeyword) ||
|
||||
role.code.includes(searchKeyword) ||
|
||||
(role.description && role.description.includes(searchKeyword));
|
||||
const matchKeyword = !filters.searchKeyword ||
|
||||
role.name.includes(filters.searchKeyword) ||
|
||||
role.code.includes(filters.searchKeyword) ||
|
||||
(role.description && role.description.includes(filters.searchKeyword));
|
||||
return matchKeyword;
|
||||
});
|
||||
|
||||
@@ -183,13 +200,17 @@ export default function RoleManagementPage() {
|
||||
|
||||
{/* 搜索 */}
|
||||
<RoleSearch
|
||||
searchKeyword={searchKeyword}
|
||||
onSearchChange={setSearchKeyword}
|
||||
searchKeyword={filters.searchKeyword}
|
||||
onSearchChange={handleSearch}
|
||||
/>
|
||||
|
||||
{/* 角色列表 */}
|
||||
<RoleList
|
||||
roles={filteredRoles}
|
||||
loading={loading}
|
||||
pagination={pagination}
|
||||
onPageChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
onViewDetail={handleViewDetail}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
// 角色管理相关类型定义
|
||||
|
||||
// API返回的角色数据类型
|
||||
export interface RoleApiData {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
tenant_id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// 页面使用的角色数据类型(兼容原有功能)
|
||||
export interface Role {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -12,6 +23,7 @@ export interface Role {
|
||||
status: 'active' | 'inactive';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
tenant_id?: string;
|
||||
}
|
||||
|
||||
export type RoleType = 'system' | 'custom';
|
||||
@@ -345,6 +357,39 @@ export interface RoleManagementStats {
|
||||
bg: string;
|
||||
}
|
||||
|
||||
// API响应接口
|
||||
export interface RolesApiResponse {
|
||||
data: RoleApiData[];
|
||||
total: number;
|
||||
page: number;
|
||||
size: number;
|
||||
total_pages: number;
|
||||
has_next: boolean;
|
||||
has_prev: boolean;
|
||||
}
|
||||
|
||||
// 查询参数接口
|
||||
export interface RolesQueryParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
sort_order?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
// 翻页状态
|
||||
export interface PaginationState {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasNext: boolean;
|
||||
hasPrev: boolean;
|
||||
}
|
||||
|
||||
// 筛选条件
|
||||
export interface RoleFilters {
|
||||
searchKeyword: string;
|
||||
}
|
||||
|
||||
// 表单数据
|
||||
export interface RoleFormData {
|
||||
name?: string;
|
||||
|
||||
Reference in New Issue
Block a user