|
|
@@ -0,0 +1,446 @@
|
|
|
+"use client";
|
|
|
+import { useMemo, useState, useEffect } from "react";
|
|
|
+import { AgGridReact } from "ag-grid-react";
|
|
|
+import {
|
|
|
+ ClientSideRowModelModule,
|
|
|
+ ColDef,
|
|
|
+ ModuleRegistry,
|
|
|
+ ICellRendererParams,
|
|
|
+ SelectEditorModule,
|
|
|
+ CellStyleModule
|
|
|
+} from "ag-grid-community";
|
|
|
+import "ag-grid-community/styles/ag-theme-alpine.css";
|
|
|
+import { useDataProvider } from 'react-admin';
|
|
|
+
|
|
|
+// 注册必要的模块
|
|
|
+ModuleRegistry.registerModules([
|
|
|
+ ClientSideRowModelModule,
|
|
|
+ SelectEditorModule,
|
|
|
+ CellStyleModule
|
|
|
+]);
|
|
|
+
|
|
|
+export const usermanagementsList = () => {
|
|
|
+ const containerStyle = useMemo(() => ({
|
|
|
+ display: "flex",
|
|
|
+ alignItems: "flex-start",
|
|
|
+ gap: "30px",
|
|
|
+ width: "100%",
|
|
|
+ padding: "8px"
|
|
|
+ }), []);
|
|
|
+
|
|
|
+ const gridStyle = useMemo(() => ({ width: "50%" }), []);
|
|
|
+ const [rowData, setRowData] = useState<any[]>([]);
|
|
|
+ const [, setLoading] = useState<boolean>(true);
|
|
|
+ const [isEditing, setIsEditing] = useState(false);
|
|
|
+ const [isAdding, setIsAdding] = useState(false);
|
|
|
+ const [editingData, setEditingData] = useState<any>({});
|
|
|
+ const [isDeleting, setIsDeleting] = useState<boolean>(false);
|
|
|
+
|
|
|
+ const dataProvider = useDataProvider();
|
|
|
+
|
|
|
+ // 初始化 CSS 样式
|
|
|
+ useEffect(() => {
|
|
|
+ const style = document.createElement('style');
|
|
|
+ style.textContent = `
|
|
|
+ .ag-theme-alpine .header-center .ag-header-cell-label {
|
|
|
+ justify-content: left !important;
|
|
|
+ text-align: center !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ag-theme-alpine .header-center.ag-header-group-cell {
|
|
|
+ text-align: center !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ag-theme-alpine .header-center.ag-header-group-cell .ag-header-group-cell-label {
|
|
|
+ justify-content: center !important;
|
|
|
+ display: flex !important;
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ag-header-cell-resize:after {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ag-theme-alpine .ag-cell {
|
|
|
+ border-right: 1px solid #dcdcdc;
|
|
|
+ text-align: center;
|
|
|
+ justify-content: left !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ag-theme-alpine .ag-header-cell {
|
|
|
+ border-right: 1px solid #dcdcdc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .line-group-header {
|
|
|
+ border-right: 1px solid #dcdcdc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .date-group-header {
|
|
|
+ border-right: 1px solid #dcdcdc !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 操作链接样式 */
|
|
|
+ .action-link {
|
|
|
+ color: #5b9bd5;
|
|
|
+ text-decoration: underline;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-link:hover {
|
|
|
+ color: #004080;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 新规作成按钮样式 */
|
|
|
+ .new-button {
|
|
|
+ background-color: #000;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 5px 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ align-self: flex-start;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 标题样式 */
|
|
|
+ .page-title {
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-edit-section {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ .user-edit-title {
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: block;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ .user-edit-section label {
|
|
|
+ display: inline-block;
|
|
|
+ width: 120px;
|
|
|
+ background-color: #dcdcdc;
|
|
|
+ text-align: left;
|
|
|
+ border: 1px solid #999;
|
|
|
+ }
|
|
|
+ .user-edit-section input {
|
|
|
+ width: 300px;
|
|
|
+ padding: 4px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 1px solid #999;
|
|
|
+ }
|
|
|
+ .user-edit-buttons {
|
|
|
+ margin-top: 25px;
|
|
|
+ }
|
|
|
+ .user-edit-buttons button {
|
|
|
+ margin-right: 30px;
|
|
|
+ padding: 5px 20px;
|
|
|
+ border: none;
|
|
|
+ background-color: #000;
|
|
|
+ color: white;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ .issue-button {
|
|
|
+ background-color: #000;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 5px 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ .loading-indicator {
|
|
|
+ text-align: center;
|
|
|
+ padding: 20px;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ `;
|
|
|
+ document.head.appendChild(style);
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ document.head.removeChild(style);
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ // 获取用户列表数据
|
|
|
+ const fetchUserList = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const response = await dataProvider.getList('users', {
|
|
|
+ filter: {}
|
|
|
+ });
|
|
|
+
|
|
|
+ const formattedData = response.data.map((user: any) => ({
|
|
|
+ userId: user.userId || '',
|
|
|
+ password: user.password || '',
|
|
|
+ userName: user.userName || '',
|
|
|
+ id: user.id || user.userId
|
|
|
+ }));
|
|
|
+
|
|
|
+ setRowData(formattedData);
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取用户列表失败:", error);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 组件挂载时获取数据
|
|
|
+ useEffect(() => {
|
|
|
+ fetchUserList();
|
|
|
+ }, [dataProvider]);
|
|
|
+
|
|
|
+ // 编辑操作处理
|
|
|
+ const handleEdit = (data: any) => {
|
|
|
+ setIsEditing(true);
|
|
|
+ setIsAdding(false);
|
|
|
+ setEditingData({...data});
|
|
|
+ };
|
|
|
+
|
|
|
+ // 新增操作处理
|
|
|
+ const handleAdd = () => {
|
|
|
+ setIsAdding(true);
|
|
|
+ setIsEditing(false);
|
|
|
+ setEditingData({
|
|
|
+ userId: '',
|
|
|
+ userName: '',
|
|
|
+ password: ''
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除操作处理
|
|
|
+ const handleDelete = async (data: any) => {
|
|
|
+ if (isDeleting) return;
|
|
|
+
|
|
|
+ if (window.confirm(`确定要删除用户 ${data.userName} (${data.userId}) 吗?`)) {
|
|
|
+ setIsDeleting(true);
|
|
|
+ try {
|
|
|
+ await dataProvider.delete('users', {
|
|
|
+ id: data.id || data.userId,
|
|
|
+ previousData: data
|
|
|
+ });
|
|
|
+ setRowData(prev => prev.filter(row => row.userId !== data.userId));
|
|
|
+ alert("用户删除成功");
|
|
|
+ } catch (error) {
|
|
|
+ console.error("删除用户失败:", error);
|
|
|
+ alert("删除失败,请重试");
|
|
|
+ } finally {
|
|
|
+ setIsDeleting(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 取消按钮处理
|
|
|
+ const handleCancel = () => {
|
|
|
+ setIsEditing(false);
|
|
|
+ setIsAdding(false);
|
|
|
+ setEditingData({});
|
|
|
+ };
|
|
|
+
|
|
|
+ // 保存按钮处理
|
|
|
+ const handleSave = async () => {
|
|
|
+ try {
|
|
|
+ const originalUser = rowData.find(row => row.userId === editingData.userId);
|
|
|
+
|
|
|
+ if (!originalUser) {
|
|
|
+ alert("未找到用户信息");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const updateParams = {
|
|
|
+ ...originalUser,
|
|
|
+ ...editingData,
|
|
|
+ id: originalUser.id || originalUser.userId
|
|
|
+ };
|
|
|
+
|
|
|
+ await dataProvider.update('usermanagements', {
|
|
|
+ id: updateParams.id,
|
|
|
+ data: updateParams,
|
|
|
+ previousData: originalUser
|
|
|
+ });
|
|
|
+
|
|
|
+ alert("用户信息更新成功");
|
|
|
+ setIsEditing(false);
|
|
|
+ fetchUserList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error("更新用户失败:", error);
|
|
|
+ alert("更新失败,请重试");
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 保存新增用户
|
|
|
+ const handleAddSave = async () => {
|
|
|
+ try {
|
|
|
+ if (!editingData.userId || !editingData.userName || !editingData.password) {
|
|
|
+ alert("ユーザID、ユーザ名及びパスワードは必須です");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const newUser = {
|
|
|
+ ...editingData,
|
|
|
+ nickName: editingData.userName,
|
|
|
+ userType: '0',
|
|
|
+ email: '',
|
|
|
+ phonenumber: '',
|
|
|
+ sex: '0',
|
|
|
+ avatar: '',
|
|
|
+ status: '0',
|
|
|
+ delFlag: '0',
|
|
|
+ loginIp: '',
|
|
|
+ createBy: '',
|
|
|
+ updateBy: '',
|
|
|
+ remark: '',
|
|
|
+ id: editingData.userId
|
|
|
+ };
|
|
|
+
|
|
|
+ await dataProvider.create('usermanagements', {
|
|
|
+ data: newUser
|
|
|
+ });
|
|
|
+
|
|
|
+ alert("用户新增成功");
|
|
|
+ setIsAdding(false);
|
|
|
+ fetchUserList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error("新增用户失败:", error);
|
|
|
+ alert("新增失败,请重试");
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 表格列定义
|
|
|
+ const columnDefs: ColDef[] = [
|
|
|
+ {
|
|
|
+ field: "userId",
|
|
|
+ headerClass: "header-center",
|
|
|
+ headerName: "ユーザID",
|
|
|
+ cellRenderer: (params: ICellRendererParams) => (
|
|
|
+ <div style={{ textAlign: 'left' }}>{params.value || ''}</div>
|
|
|
+ ),
|
|
|
+ flex: 1,
|
|
|
+ minWidth: 100,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: "userName",
|
|
|
+ headerClass: "header-center",
|
|
|
+ headerName: "ユーザ名",
|
|
|
+ cellRenderer: (params: ICellRendererParams) => (
|
|
|
+ <div style={{ textAlign: 'left' }}>{params.value || ''}</div>
|
|
|
+ ),
|
|
|
+ flex: 1.5,
|
|
|
+ minWidth: 120,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: "編集",
|
|
|
+ headerClass: "header-center",
|
|
|
+ headerName: "編集",
|
|
|
+ cellRenderer: (params: ICellRendererParams) => (
|
|
|
+ <div style={{ textAlign: 'left' }}>
|
|
|
+ <span className="action-link" onClick={() => handleEdit(params.data)}>編集</span>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ flex: 0.8,
|
|
|
+ minWidth: 80,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: "削除",
|
|
|
+ headerClass: "header-center",
|
|
|
+ headerName: "削除",
|
|
|
+ cellRenderer: (params: ICellRendererParams) => (
|
|
|
+ <div style={{ textAlign: 'left' }}>
|
|
|
+ <span className="action-link" onClick={() => handleDelete(params.data)}>削除</span>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ flex: 0.8,
|
|
|
+ minWidth: 80,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 渲染部分
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {!isEditing && !isAdding && (
|
|
|
+ <>
|
|
|
+ <span className="page-title">ユーザ一覧</span>
|
|
|
+ <div style={containerStyle}>
|
|
|
+ <div className="ag-theme-alpine" style={gridStyle}>
|
|
|
+ <AgGridReact
|
|
|
+ rowData={rowData}
|
|
|
+ columnDefs={columnDefs}
|
|
|
+ domLayout="autoHeight"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <button className="new-button" onClick={handleAdd}>
|
|
|
+ 新規作成
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 编辑表单*/}
|
|
|
+ {isEditing && (
|
|
|
+ <div className="user-edit-section">
|
|
|
+ <span className="user-edit-title">ユーザ編集</span>
|
|
|
+ <label>ユーザID</label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={editingData.userId || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, userId: e.target.value})}
|
|
|
+ disabled={!!editingData.userId}
|
|
|
+ />
|
|
|
+ <br/>
|
|
|
+ <label>ユーザ名</label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={editingData.userName || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, userName: e.target.value})}
|
|
|
+ />
|
|
|
+ <br/>
|
|
|
+ <label>パスワード</label>
|
|
|
+ <input
|
|
|
+ type="password"
|
|
|
+ value={editingData.password || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, password: e.target.value})}
|
|
|
+ />
|
|
|
+ <button className="issue-button" style={{ marginLeft: '30px' }}>発行</button>
|
|
|
+ <div className="user-edit-buttons">
|
|
|
+ <button onClick={handleCancel}>キャンセル</button>
|
|
|
+ <button onClick={handleSave}>保存</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 新增表单*/}
|
|
|
+ {isAdding && (
|
|
|
+ <div className="user-edit-section">
|
|
|
+ <span className="user-edit-title">ユーザ編集</span>
|
|
|
+ <label>ユーザID</label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={editingData.userId || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, userId: e.target.value})}
|
|
|
+ placeholder=""
|
|
|
+ />
|
|
|
+ <br/>
|
|
|
+ <label>ユーザ名</label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={editingData.userName || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, userName: e.target.value})}
|
|
|
+ placeholder=""
|
|
|
+ />
|
|
|
+ <br/>
|
|
|
+ <label>パスワード</label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={editingData.password || ''}
|
|
|
+ onChange={(e) => setEditingData({...editingData, password: e.target.value})}
|
|
|
+ placeholder=""
|
|
|
+ />
|
|
|
+ <button className="issue-button" style={{ marginLeft: '30px' }}>発行</button>
|
|
|
+ <div className="user-edit-buttons">
|
|
|
+ <button onClick={handleCancel}>キャンセル</button>
|
|
|
+ <button onClick={handleAddSave}>保存</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default usermanagementsList;
|