Procházet zdrojové kódy

Merge branch 'develop' of http://192.168.0.253:3000/bozzysadmb/ISCS-Client-V1.0 into develop

# Conflicts:
#	src/Dashboard.tsx
pm před 5 měsíci
rodič
revize
3fd4e77793

+ 441 - 0
src/components/NotificationManagement.tsx

@@ -0,0 +1,441 @@
+import React, { useState } from 'react';
+import { Plus, Search, Edit2, Trash2, MoreVertical, Mail, Phone, MessageSquare, Smartphone } from 'lucide-react';
+
+interface TableRow {
+  id: number;
+  [key: string]: any;
+}
+
+export default function NotificationManagement() {
+  const [searchTerm, setSearchTerm] = useState('');
+  const [showAddModal, setShowAddModal] = useState(false);
+  const [editingItem, setEditingItem] = useState<TableRow | null>(null);
+  const [notificationType, setNotificationType] = useState<string>('email'); // 通知类型:email, sms, message, app
+
+  // 通知管理数据
+  const notificationData: TableRow[] = [
+    {
+      id: 1,
+      title: '系统升级通知',
+      content: '系统将于本周六凌晨2:00-4:00进行升级维护,期间系统将暂停服务',
+      type: '系统通知',
+      level: '重要',
+      sender: '系统管理员',
+      target: '全体用户',
+      status: '已发送',
+      readCount: 156,
+      totalCount: 200,
+      sendTime: '2025-12-01 10:00',
+      createTime: '2025-12-01 09:30'
+    },
+    {
+      id: 2,
+      title: '设备异常告警',
+      content: '1号变压器出现异常,请相关人员立即检处理',
+      type: '告警通知',
+      level: '紧急',
+      sender: '监控系统',
+      target: '技术部、运维部',
+      status: '已发送',
+      readCount: 45,
+      totalCount: 50,
+      sendTime: '2025-12-04 08:30',
+      createTime: '2025-12-04 08:25'
+    },
+    {
+      id: 3,
+      title: '月度安全培训通知',
+      content: '本月安全培训将于12月10日14:00在会议室举行,请全体员工准时参加',
+      type: '培训通知',
+      level: '普通',
+      sender: '安全部',
+      target: '全体用户',
+      status: '定时发送',
+      readCount: 0,
+      totalCount: 200,
+      sendTime: '2025-12-10 09:00',
+      createTime: '2025-12-03 16:00'
+    },
+    {
+      id: 4,
+      title: '隔离作业审批通过',
+      content: '您提交的2号配电柜维护隔离作业已通过审批,可以开始执行',
+      type: '审批通知',
+      level: '普通',
+      sender: '审批系统',
+      target: '张三',
+      status: '已发送',
+      readCount: 1,
+      totalCount: 1,
+      sendTime: '2025-12-03 15:30',
+      createTime: '2025-12-03 15:28'
+    },
+    {
+      id: 5,
+      title: '密码过期提醒',
+      content: '您的密码将于7天后过期,请及时修改密码',
+      type: '安全提醒',
+      level: '普通',
+      sender: '系统管理员',
+      target: '李四、王五',
+      status: '已发送',
+      readCount: 2,
+      totalCount: 2,
+      sendTime: '2025-12-04 09:00',
+      createTime: '2025-12-04 08:55'
+    },
+  ];
+
+  // 表格列配置
+  const columns = [
+    { key: 'title', label: '通知标题', width: '20%' },
+    { key: 'type', label: '类型', width: '10%' },
+    { key: 'level', label: '级别', width: '8%' },
+    { key: 'sender', label: '发送人', width: '10%' },
+    { key: 'target', label: '接收对象', width: '12%' },
+    { key: 'status', label: '状态', width: '10%' },
+    { key: 'readCount', label: '已读/总数', width: '10%' },
+    { key: 'sendTime', label: '发送时间', width: '15%' },
+  ];
+
+  // 过滤数据
+  const filteredData = notificationData.filter((item) =>
+    Object.values(item).some((value) =>
+      String(value).toLowerCase().includes(searchTerm.toLowerCase())
+    )
+  );
+
+  const handleDelete = (id: number) => {
+    if (confirm('确定要删除这条数据吗?')) {
+      console.log('删除:', id);
+    }
+  };
+
+  const handleEdit = (item: TableRow) => {
+    setEditingItem(item);
+    setShowAddModal(true);
+  };
+
+  // 渲染表格内容
+  const renderTable = () => (
+    <>
+      <div className="overflow-x-auto">
+        <table className="w-full">
+          <thead>
+            <tr className="bg-gradient-to-r from-gray-50 to-gray-100/50 border-b border-gray-200">
+              <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '5%' }}>
+                序号
+              </th>
+              {columns.map((column) => (
+                <th
+                  key={column.key}
+                  className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider"
+                  style={{ width: column.width }}
+                >
+                  {column.label}
+                </th>
+              ))}
+              <th className="px-6 py-4 text-center text-xs text-gray-600 uppercase tracking-wider" style={{ width: '10%' }}>
+                操作
+              </th>
+            </tr>
+          </thead>
+          <tbody className="divide-y divide-gray-100">
+            {filteredData.map((row, index) => (
+              <tr
+                key={row.id}
+                className="hover:bg-blue-50/30 transition-colors"
+              >
+                <td className="px-6 py-4 text-sm text-gray-900">
+                  {index + 1}
+                </td>
+                {columns.map((column) => (
+                  <td key={column.key} className="px-6 py-4 text-sm text-gray-900">
+                    {column.key === 'status' ? (
+                      <span
+                        className={`inline-flex px-3 py-1 rounded-lg text-xs ${
+                          row[column.key] === '已发送'
+                            ? 'bg-green-100 text-green-700'
+                            : row[column.key] === '定时发送'
+                            ? 'bg-blue-100 text-blue-700'
+                            : 'bg-gray-100 text-gray-700'
+                        }`}
+                      >
+                        {row[column.key]}
+                      </span>
+                    ) : column.key === 'level' ? (
+                      <span
+                        className={`inline-flex px-3 py-1 rounded-lg text-xs ${
+                          row[column.key] === '紧急'
+                            ? 'bg-red-100 text-red-700'
+                            : row[column.key] === '重要'
+                            ? 'bg-orange-100 text-orange-700'
+                            : 'bg-blue-100 text-blue-700'
+                        }`}
+                      >
+                        {row[column.key]}
+                      </span>
+                    ) : column.key === 'readCount' ? (
+                      <span className="text-gray-900">
+                        {row.readCount}/{row.totalCount}
+                      </span>
+                    ) : (
+                      row[column.key]
+                    )}
+                  </td>
+                ))}
+                <td className="px-6 py-4">
+                  <div className="flex items-center justify-center gap-2">
+                    <button
+                      onClick={() => handleEdit(row)}
+                      className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
+                      title="编辑"
+                    >
+                      <Edit2 className="w-4 h-4" />
+                    </button>
+                    <button
+                      onClick={() => handleDelete(row.id)}
+                      className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
+                      title="删除"
+                    >
+                      <Trash2 className="w-4 h-4" />
+                    </button>
+                    <button
+                      className="p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
+                      title="更多"
+                    >
+                      <MoreVertical className="w-4 h-4" />
+                    </button>
+                  </div>
+                </td>
+              </tr>
+            ))}
+          </tbody>
+        </table>
+      </div>
+
+      {/* 分页 */}
+      <div className="px-6 py-4 bg-gray-50/50 border-t border-gray-200">
+        <div className="flex items-center justify-between">
+          <div className="text-sm text-gray-600">
+            共 <span className="text-blue-600">{filteredData.length}</span> 条记录
+          </div>
+          <div className="flex gap-2">
+            <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
+              上一页
+            </button>
+            <button className="px-4 py-2 text-sm text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition-colors">
+              1
+            </button>
+            <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
+              2
+            </button>
+            <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
+              3
+            </button>
+            <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
+              下一页
+            </button>
+          </div>
+        </div>
+      </div>
+    </>
+  );
+
+  return (
+    <div className="space-y-6">
+      {/* Tab按钮栏 */}
+      <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-2">
+        <div className="flex items-center gap-2">
+          <button
+            onClick={() => setNotificationType('email')}
+            className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm transition-all duration-200 whitespace-nowrap ${
+              notificationType === 'email'
+                ? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-lg shadow-blue-400/40'
+                : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600'
+            }`}
+          >
+            <Mail className="w-4 h-4" />
+            <span>邮件</span>
+          </button>
+          <button
+            onClick={() => setNotificationType('sms')}
+            className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm transition-all duration-200 whitespace-nowrap ${
+              notificationType === 'sms'
+                ? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-lg shadow-blue-400/40'
+                : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600'
+            }`}
+          >
+            <Phone className="w-4 h-4" />
+            <span>短信</span>
+          </button>
+          <button
+            onClick={() => setNotificationType('message')}
+            className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm transition-all duration-200 whitespace-nowrap ${
+              notificationType === 'message'
+                ? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-lg shadow-blue-400/40'
+                : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600'
+            }`}
+          >
+            <MessageSquare className="w-4 h-4" />
+            <span>站内信</span>
+          </button>
+          <button
+            onClick={() => setNotificationType('app')}
+            className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm transition-all duration-200 whitespace-nowrap ${
+              notificationType === 'app'
+                ? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-lg shadow-blue-400/40'
+                : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600'
+            }`}
+          >
+            <Smartphone className="w-4 h-4" />
+            <span>APP通知</span>
+          </button>
+        </div>
+      </div>
+
+      {/* 表格容器 */}
+      <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
+        {/* 工具栏 */}
+        <div className="p-4 border-b border-gray-200/50">
+          <div className="flex items-center justify-between">
+            <div className="relative w-80">
+              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
+              <input
+                type="text"
+                placeholder="搜索通知..."
+                value={searchTerm}
+                onChange={(e) => setSearchTerm(e.target.value)}
+                className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+              />
+            </div>
+
+            <button
+              onClick={() => {
+                setEditingItem(null);
+                setShowAddModal(true);
+              }}
+              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
+            >
+              <Plus className="w-4 h-4" strokeWidth={2.5} />
+              <span className="text-sm">新增通知</span>
+            </button>
+          </div>
+        </div>
+
+        {/* 通知内容区域 */}
+        <div>
+          {notificationType === 'email' && renderTable()}
+          {notificationType === 'sms' && renderTable()}
+          {notificationType === 'message' && renderTable()}
+          {notificationType === 'app' && renderTable()}
+        </div>
+      </div>
+
+      {/* 新增/编辑弹窗 */}
+      {showAddModal && (
+        <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 animate-in fade-in duration-200">
+          <div className="bg-white rounded-2xl shadow-2xl w-full max-w-3xl mx-4 max-h-[90vh] overflow-y-auto animate-in zoom-in duration-200">
+            {/* 弹窗标题 */}
+            <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between sticky top-0 bg-white z-10">
+              <h3 className="text-lg text-gray-900">
+                {editingItem ? '编辑通知' : '新增通知'}
+              </h3>
+              <button
+                onClick={() => setShowAddModal(false)}
+                className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
+              >
+                <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
+                </svg>
+              </button>
+            </div>
+
+            {/* 弹窗内容 */}
+            <div className="px-6 py-6">
+              <div className="space-y-4">
+                <div>
+                  <label className="block text-sm text-gray-700 mb-2">通知标题 *</label>
+                  <input
+                    type="text"
+                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+                    placeholder="请输入通知标题"
+                    defaultValue={editingItem?.title || ''}
+                  />
+                </div>
+                <div className="grid grid-cols-3 gap-4">
+                  <div>
+                    <label className="block text-sm text-gray-700 mb-2">通知类型 *</label>
+                    <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
+                      <option value="">请选择</option>
+                      <option value="系统通知">系统通知</option>
+                      <option value="告警通知">告警通知</option>
+                      <option value="培训通知">培训通知</option>
+                      <option value="审批通知">审批通知</option>
+                      <option value="安全提醒">安全提醒</option>
+                    </select>
+                  </div>
+                  <div>
+                    <label className="block text-sm text-gray-700 mb-2">优先级 *</label>
+                    <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
+                      <option value="">请选择</option>
+                      <option value="紧急">紧急</option>
+                      <option value="重要">重要</option>
+                      <option value="普通">普通</option>
+                    </select>
+                  </div>
+                  <div>
+                    <label className="block text-sm text-gray-700 mb-2">发送方式</label>
+                    <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
+                      <option value="立即发送">立即发送</option>
+                      <option value="定时发送">定时发送</option>
+                    </select>
+                  </div>
+                </div>
+                <div>
+                  <label className="block text-sm text-gray-700 mb-2">接收对象 *</label>
+                  <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
+                    <option value="">请选择</option>
+                    <option value="全体用户">全体用户</option>
+                    <option value="按部门">按部门</option>
+                    <option value="按角色">按角色</option>
+                    <option value="指定用户">指定用户</option>
+                  </select>
+                </div>
+                <div>
+                  <label className="block text-sm text-gray-700 mb-2">通知内容 *</label>
+                  <textarea
+                    rows={5}
+                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
+                    placeholder="请输入通知内容"
+                    defaultValue={editingItem?.content || ''}
+                  ></textarea>
+                </div>
+              </div>
+            </div>
+
+            {/* 弹窗底部 */}
+            <div className="px-6 py-4 bg-gray-50/50 border-t border-gray-200 flex justify-end gap-3 rounded-b-2xl">
+              <button
+                onClick={() => setShowAddModal(false)}
+                className="px-5 py-2.5 text-sm text-gray-600 bg-white border border-gray-200 rounded-xl hover:bg-gray-50 transition-colors"
+              >
+                取消
+              </button>
+              <button
+                onClick={() => {
+                  console.log('保存:', editingItem);
+                  setShowAddModal(false);
+                }}
+                className="px-5 py-2.5 text-sm text-white bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all"
+              >
+                {editingItem ? '保存修改' : '确定'}
+              </button>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}
+

+ 0 - 769
src/components/PositionManagement.tsx

@@ -1,769 +0,0 @@
-import React, { useState } from 'react';
-import { Plus, Search, Edit2, Trash2, MoreVertical, ChevronRight, ChevronDown, Briefcase } from 'lucide-react';
-
-interface PositionNode {
-  id: number;
-  name: string;
-  code?: string;
-  department?: string;
-  description?: string;
-  status?: string;
-  userCount?: number;
-  createTime?: string;
-  parentId?: number;
-  children?: PositionNode[];
-}
-
-export default function PositionManagement() {
-  const [expandedPositionIds, setExpandedPositionIds] = useState<number[]>([1]); // 默认展开
-  const [selectedPositionId, setSelectedPositionId] = useState<number | null>(1); // 选中的岗位ID
-  const [searchTerm, setSearchTerm] = useState('');
-  const [showAddModal, setShowAddModal] = useState(false);
-  const [editingItem, setEditingItem] = useState<any>(null);
-
-  // 岗位树形数据
-  const positionTree: PositionNode[] = [
-    {
-      id: 1,
-      name: '管理岗位',
-      code: 'MGMT',
-      department: '总公司',
-      description: '管理类岗位',
-      status: '启用',
-      userCount: 15,
-      createTime: '2024-01-01',
-      children: [
-        {
-          id: 2,
-          name: '高级管理',
-          code: 'MGMT-HIGH',
-          department: '总公司',
-          description: '高级管理岗位',
-          status: '启用',
-          userCount: 5,
-          createTime: '2024-01-05',
-          parentId: 1,
-          children: [
-            {
-              id: 21,
-              name: 'CEO',
-              code: 'CEO',
-              department: '总公司',
-              description: '首席执行官',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 2
-            },
-            {
-              id: 22,
-              name: 'CTO',
-              code: 'CTO',
-              department: '技术中心',
-              description: '首席技术官',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 2
-            },
-            {
-              id: 23,
-              name: 'CFO',
-              code: 'CFO',
-              department: '行政中心',
-              description: '首席财务官',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 2
-            }
-          ]
-        },
-        {
-          id: 3,
-          name: '部门管理',
-          code: 'MGMT-DEPT',
-          department: '各部门',
-          description: '部门管理岗位',
-          status: '启用',
-          userCount: 8,
-          createTime: '2024-01-05',
-          parentId: 1,
-          children: [
-            {
-              id: 31,
-              name: '部门经理',
-              code: 'DEPT-MGR',
-              department: '各部门',
-              description: '部门经理',
-              status: '启用',
-              userCount: 5,
-              createTime: '2024-01-10',
-              parentId: 3
-            },
-            {
-              id: 32,
-              name: '部门副经理',
-              code: 'DEPT-DMGR',
-              department: '各部门',
-              description: '部门副经理',
-              status: '启用',
-              userCount: 3,
-              createTime: '2024-01-10',
-              parentId: 3
-            }
-          ]
-        }
-      ]
-    },
-    {
-      id: 4,
-      name: '技术岗位',
-      code: 'TECH',
-      department: '技术中心',
-      description: '技术类岗位',
-      status: '启用',
-      userCount: 45,
-      createTime: '2024-01-01',
-      children: [
-        {
-          id: 5,
-          name: '研发岗位',
-          code: 'TECH-RD',
-          department: '研发部',
-          description: '研发类岗位',
-          status: '启用',
-          userCount: 25,
-          createTime: '2024-01-05',
-          parentId: 4,
-          children: [
-            {
-              id: 51,
-              name: '高级工程师',
-              code: 'RD-SENIOR',
-              department: '研发部',
-              description: '高级研发工程师',
-              status: '启用',
-              userCount: 8,
-              createTime: '2024-01-10',
-              parentId: 5
-            },
-            {
-              id: 52,
-              name: '工程师',
-              code: 'RD-ENG',
-              department: '研发部',
-              description: '研发工程师',
-              status: '启用',
-              userCount: 12,
-              createTime: '2024-01-10',
-              parentId: 5
-            },
-            {
-              id: 53,
-              name: '助理工程师',
-              code: 'RD-ASSIST',
-              department: '研发部',
-              description: '助理研发工程师',
-              status: '启用',
-              userCount: 5,
-              createTime: '2024-01-10',
-              parentId: 5
-            }
-          ]
-        },
-        {
-          id: 6,
-          name: '测试岗位',
-          code: 'TECH-QA',
-          department: '测试部',
-          description: '测试类岗位',
-          status: '启用',
-          userCount: 12,
-          createTime: '2024-01-05',
-          parentId: 4,
-          children: [
-            {
-              id: 61,
-              name: '测试工程师',
-              code: 'QA-ENG',
-              department: '测试部',
-              description: '测试工程师',
-              status: '启用',
-              userCount: 8,
-              createTime: '2024-01-10',
-              parentId: 6
-            },
-            {
-              id: 62,
-              name: '测试助理',
-              code: 'QA-ASSIST',
-              department: '测试部',
-              description: '测试助理',
-              status: '启用',
-              userCount: 4,
-              createTime: '2024-01-10',
-              parentId: 6
-            }
-          ]
-        },
-        {
-          id: 7,
-          name: '运维岗位',
-          code: 'TECH-OPS',
-          department: '运维部',
-          description: '运维类岗位',
-          status: '启用',
-          userCount: 8,
-          createTime: '2024-01-05',
-          parentId: 4,
-          children: [
-            {
-              id: 71,
-              name: '运维工程师',
-              code: 'OPS-ENG',
-              department: '运维部',
-              description: '运维工程师',
-              status: '启用',
-              userCount: 6,
-              createTime: '2024-01-10',
-              parentId: 7
-            },
-            {
-              id: 72,
-              name: '运维助理',
-              code: 'OPS-ASSIST',
-              department: '运维部',
-              description: '运维助理',
-              status: '启用',
-              userCount: 2,
-              createTime: '2024-01-10',
-              parentId: 7
-            }
-          ]
-        }
-      ]
-    },
-    {
-      id: 8,
-      name: '行政岗位',
-      code: 'ADMIN',
-      department: '行政中心',
-      description: '行政类岗位',
-      status: '启用',
-      userCount: 25,
-      createTime: '2024-01-01',
-      children: [
-        {
-          id: 9,
-          name: '人力资源',
-          code: 'ADMIN-HR',
-          department: '人力资源部',
-          description: '人力资源岗位',
-          status: '启用',
-          userCount: 10,
-          createTime: '2024-01-05',
-          parentId: 8,
-          children: [
-            {
-              id: 91,
-              name: 'HR经理',
-              code: 'HR-MGR',
-              department: '人力资源部',
-              description: '人力资源经理',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 9
-            },
-            {
-              id: 92,
-              name: 'HR专员',
-              code: 'HR-SPEC',
-              department: '人力资源部',
-              description: '人力资源专员',
-              status: '启用',
-              userCount: 6,
-              createTime: '2024-01-10',
-              parentId: 9
-            },
-            {
-              id: 93,
-              name: 'HR助理',
-              code: 'HR-ASSIST',
-              department: '人力资源部',
-              description: '人力资源助理',
-              status: '启用',
-              userCount: 3,
-              createTime: '2024-01-10',
-              parentId: 9
-            }
-          ]
-        },
-        {
-          id: 10,
-          name: '财务岗位',
-          code: 'ADMIN-FIN',
-          department: '财务部',
-          description: '财务类岗位',
-          status: '启用',
-          userCount: 8,
-          createTime: '2024-01-05',
-          parentId: 8,
-          children: [
-            {
-              id: 101,
-              name: '财务经理',
-              code: 'FIN-MGR',
-              department: '财务部',
-              description: '财务经理',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 10
-            },
-            {
-              id: 102,
-              name: '会计',
-              code: 'FIN-ACC',
-              department: '财务部',
-              description: '会计',
-              status: '启用',
-              userCount: 4,
-              createTime: '2024-01-10',
-              parentId: 10
-            },
-            {
-              id: 103,
-              name: '出纳',
-              code: 'FIN-CASH',
-              department: '财务部',
-              description: '出纳',
-              status: '启用',
-              userCount: 3,
-              createTime: '2024-01-10',
-              parentId: 10
-            }
-          ]
-        },
-        {
-          id: 11,
-          name: '行政岗位',
-          code: 'ADMIN-OFF',
-          department: '行政部',
-          description: '行政类岗位',
-          status: '启用',
-          userCount: 7,
-          createTime: '2024-01-05',
-          parentId: 8,
-          children: [
-            {
-              id: 111,
-              name: '行政经理',
-              code: 'OFF-MGR',
-              department: '行政部',
-              description: '行政经理',
-              status: '启用',
-              userCount: 1,
-              createTime: '2024-01-10',
-              parentId: 11
-            },
-            {
-              id: 112,
-              name: '行政专员',
-              code: 'OFF-SPEC',
-              department: '行政部',
-              description: '行政专员',
-              status: '启用',
-              userCount: 4,
-              createTime: '2024-01-10',
-              parentId: 11
-            },
-            {
-              id: 113,
-              name: '前台',
-              code: 'OFF-REC',
-              department: '行政部',
-              description: '前台接待',
-              status: '启用',
-              userCount: 2,
-              createTime: '2024-01-10',
-              parentId: 11
-            }
-          ]
-        }
-      ]
-    }
-  ];
-
-  // 切换展开/收起
-  const togglePositionExpand = (id: number) => {
-    setExpandedPositionIds(prev =>
-      prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id]
-    );
-  };
-
-  // 获取所有岗位(扁平化)
-  const getAllPositions = (nodes: PositionNode[], result: PositionNode[] = []): PositionNode[] => {
-    nodes.forEach(node => {
-      result.push(node);
-      if (node.children) {
-        getAllPositions(node.children, result);
-      }
-    });
-    return result;
-  };
-
-  const allPositions = getAllPositions(positionTree);
-  
-  // 根据选中的岗位ID过滤显示的岗位列表
-  const getFilteredPositions = () => {
-    if (!selectedPositionId) return allPositions;
-    
-    // 找到选中的岗位
-    const selectedPosition = allPositions.find(p => p.id === selectedPositionId);
-    if (!selectedPosition) return allPositions;
-    
-    // 如果有子岗位,只显示直接子岗位
-    if (selectedPosition.children && selectedPosition.children.length > 0) {
-      return selectedPosition.children;
-    }
-    
-    // 如果没有子岗位,返回自己
-    return [selectedPosition];
-  };
-
-  const displayedPositions = getFilteredPositions().filter((position) =>
-    position.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
-    position.code?.toLowerCase().includes(searchTerm.toLowerCase()) ||
-    position.department?.toLowerCase().includes(searchTerm.toLowerCase())
-  );
-
-  // 递归渲染树形节点
-  const renderTreeNode = (node: PositionNode, level: number = 0) => {
-    const isExpanded = expandedPositionIds.includes(node.id);
-    const isSelected = selectedPositionId === node.id;
-    const hasChildren = node.children && node.children.length > 0;
-
-    return (
-      <div key={node.id}>
-        <div
-          className={`flex items-center gap-2 px-3 py-2.5 cursor-pointer transition-all rounded-lg ${
-            isSelected 
-              ? 'bg-blue-100 text-blue-700' 
-              : 'hover:bg-gray-100 text-gray-700'
-          }`}
-          style={{ paddingLeft: `${level * 16 + 12}px` }}
-          onClick={() => setSelectedPositionId(node.id)}
-        >
-          {hasChildren ? (
-            <button
-              onClick={(e) => {
-                e.stopPropagation();
-                togglePositionExpand(node.id);
-              }}
-              className="p-0.5 hover:bg-gray-200 rounded transition-colors"
-            >
-              {isExpanded ? (
-                <ChevronDown className="w-4 h-4" />
-              ) : (
-                <ChevronRight className="w-4 h-4" />
-              )}
-            </button>
-          ) : (
-            <span className="w-5"></span>
-          )}
-          <Briefcase className="w-4 h-4" />
-          <span className="text-sm flex-1">{node.name}</span>
-          {node.userCount && (
-            <span className="text-xs px-2 py-0.5 bg-gray-200 rounded">
-              {node.userCount}人
-            </span>
-          )}
-        </div>
-        
-        {isExpanded && hasChildren && (
-          <div>
-            {node.children!.map(child => renderTreeNode(child, level + 1))}
-          </div>
-        )}
-      </div>
-    );
-  };
-
-  const handleDelete = (id: number) => {
-    if (confirm('确定要删除这个岗位吗?')) {
-      console.log('删除岗位:', id);
-    }
-  };
-
-  const handleEdit = (item: any) => {
-    setEditingItem(item);
-    setShowAddModal(true);
-  };
-
-  // 获取所有部门(用于下拉选择)
-  const departments = ['总公司', '技术中心', '市场中心', '行政中心', '安全部', '研发部', '测试部', '运维部', '市场部', '销售部', '人力资源部', '财务部', '行政部'];
-
-  return (
-    <div className="flex gap-6 h-full">
-      {/* 左侧树形结构 */}
-      <div className="w-80 flex-shrink-0">
-        <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-4 h-full flex flex-col">
-          <div className="flex items-center justify-between mb-4 pb-3 border-b border-gray-200">
-            <h3 className="text-base text-gray-900">岗位分类</h3>
-            <button
-              onClick={() => {
-                setEditingItem(null);
-                setShowAddModal(true);
-              }}
-              className="p-2 text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
-              title="新增岗位"
-            >
-              <Plus className="w-4 h-4" />
-            </button>
-          </div>
-          
-          <div className="flex-1 overflow-y-auto">
-            {positionTree.map(node => renderTreeNode(node))}
-          </div>
-        </div>
-      </div>
-
-      {/* 右侧列表 */}
-      <div className="flex-1 space-y-6">
-        {/* 工具栏 */}
-        <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-4">
-          <div className="flex items-center justify-between">
-            <div className="relative w-80">
-              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
-              <input
-                type="text"
-                placeholder="搜索岗位..."
-                value={searchTerm}
-                onChange={(e) => setSearchTerm(e.target.value)}
-                className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-              />
-            </div>
-
-            <button
-              onClick={() => {
-                setEditingItem(null);
-                setShowAddModal(true);
-              }}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
-            >
-              <Plus className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">新增岗位</span>
-            </button>
-          </div>
-        </div>
-
-        {/* 表格 */}
-        <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
-          <div className="overflow-x-auto">
-            <table className="w-full">
-              <thead>
-                <tr className="bg-gradient-to-r from-gray-50 to-gray-100/50 border-b border-gray-200">
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '5%' }}>
-                    序号
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '15%' }}>
-                    岗位名称
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '12%' }}>
-                    岗位编码
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '15%' }}>
-                    所属部门
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '20%' }}>
-                    岗位描述
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '8%' }}>
-                    人员数
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '10%' }}>
-                    状态
-                  </th>
-                  <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '10%' }}>
-                    创建时间
-                  </th>
-                  <th className="px-6 py-4 text-center text-xs text-gray-600 uppercase tracking-wider" style={{ width: '10%' }}>
-                    操作
-                  </th>
-                </tr>
-              </thead>
-              <tbody className="divide-y divide-gray-100">
-                {displayedPositions.map((position, index) => (
-                  <tr
-                    key={position.id}
-                    className="hover:bg-blue-50/30 transition-colors"
-                  >
-                    <td className="px-6 py-4 text-sm text-gray-900">
-                      {index + 1}
-                    </td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.name}</td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.code}</td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.department}</td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.description}</td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.userCount}</td>
-                    <td className="px-6 py-4">
-                      <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${
-                        position.status === '启用' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-700'
-                      }`}>
-                        {position.status}
-                      </span>
-                    </td>
-                    <td className="px-6 py-4 text-sm text-gray-900">{position.createTime}</td>
-                    <td className="px-6 py-4">
-                      <div className="flex items-center justify-center gap-2">
-                        <button
-                          onClick={() => handleEdit(position)}
-                          className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
-                          title="编辑"
-                        >
-                          <Edit2 className="w-4 h-4" />
-                        </button>
-                        <button
-                          onClick={() => handleDelete(position.id)}
-                          className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
-                          title="删除"
-                        >
-                          <Trash2 className="w-4 h-4" />
-                        </button>
-                        <button
-                          className="p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
-                          title="更多"
-                        >
-                          <MoreVertical className="w-4 h-4" />
-                        </button>
-                      </div>
-                    </td>
-                  </tr>
-                ))}
-              </tbody>
-            </table>
-          </div>
-
-          {/* 分页 */}
-          <div className="px-6 py-4 bg-gray-50/50 border-t border-gray-200">
-            <div className="flex items-center justify-between">
-              <div className="text-sm text-gray-600">
-                共 <span className="text-blue-600">{displayedPositions.length}</span> 个岗位
-              </div>
-              <div className="flex gap-2">
-                <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
-                  上一页
-                </button>
-                <button className="px-4 py-2 text-sm text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition-colors">
-                  1
-                </button>
-                <button className="px-4 py-2 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
-                  下一页
-                </button>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      {/* 新增/编辑弹窗 */}
-      {showAddModal && (
-        <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 animate-in fade-in duration-200">
-          <div className="bg-white rounded-2xl shadow-2xl w-full max-w-2xl mx-4 animate-in zoom-in duration-200">
-            {/* 弹窗标题 */}
-            <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
-              <h3 className="text-lg text-gray-900">
-                {editingItem ? '编辑岗位' : '新增岗位'}
-              </h3>
-              <button
-                onClick={() => setShowAddModal(false)}
-                className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
-              >
-                <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
-                </svg>
-              </button>
-            </div>
-
-            {/* 弹窗内容 */}
-            <div className="px-6 py-6">
-              <div className="grid grid-cols-2 gap-4">
-                <div>
-                  <label className="block text-sm text-gray-700 mb-2">岗位名称 *</label>
-                  <input
-                    type="text"
-                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-                    placeholder="请输入岗位名称"
-                    defaultValue={editingItem?.name || ''}
-                  />
-                </div>
-                <div>
-                  <label className="block text-sm text-gray-700 mb-2">岗位编码 *</label>
-                  <input
-                    type="text"
-                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-                    placeholder="请输入岗位编码"
-                    defaultValue={editingItem?.code || ''}
-                  />
-                </div>
-                <div>
-                  <label className="block text-sm text-gray-700 mb-2">所属部门 *</label>
-                  <select 
-                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-                    defaultValue={editingItem?.department || ''}
-                  >
-                    <option value="">请选择部门</option>
-                    {departments.map(dept => (
-                      <option key={dept} value={dept}>{dept}</option>
-                    ))}
-                  </select>
-                </div>
-                <div>
-                  <label className="block text-sm text-gray-700 mb-2">状态</label>
-                  <select 
-                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-                    defaultValue={editingItem?.status || '启用'}
-                  >
-                    <option value="启用">启用</option>
-                    <option value="禁用">禁用</option>
-                  </select>
-                </div>
-                <div className="col-span-2">
-                  <label className="block text-sm text-gray-700 mb-2">岗位描述</label>
-                  <textarea
-                    rows={3}
-                    className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
-                    placeholder="请输入岗位描述"
-                    defaultValue={editingItem?.description || ''}
-                  ></textarea>
-                </div>
-              </div>
-            </div>
-
-            {/* 弹窗底部 */}
-            <div className="px-6 py-4 bg-gray-50/50 border-t border-gray-200 flex justify-end gap-3 rounded-b-2xl">
-              <button
-                onClick={() => setShowAddModal(false)}
-                className="px-5 py-2.5 text-sm text-gray-600 bg-white border border-gray-200 rounded-xl hover:bg-gray-50 transition-colors"
-              >
-                取消
-              </button>
-              <button
-                onClick={() => {
-                  console.log('保存岗位:', editingItem);
-                  setShowAddModal(false);
-                }}
-                className="px-5 py-2.5 text-sm text-white bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all"
-              >
-                确定
-              </button>
-            </div>
-          </div>
-        </div>
-      )}
-    </div>
-  );
-}
-

+ 4 - 11
src/components/SystemConfig.tsx

@@ -1,7 +1,6 @@
 import React, { useState } from 'react';
 import { Plus, Search, Edit2, Trash2, MoreVertical, ChevronRight, ChevronDown } from 'lucide-react';
 import DepartmentManagement from './DepartmentManagement';
-import PositionManagement from './PositionManagement';
 
 interface TableRow {
   id: number;
@@ -48,10 +47,9 @@ export default function SystemConfig({ subMenu }: SystemConfigProps) {
       children: [
         { id: 21, name: '菜单管理', path: '/system/menu', icon: '菜单', order: 1, status: '启用', createTime: '2025-01-02', parentId: 2 },
         { id: 22, name: '部门管理', path: '/system/dept', icon: '部门', order: 2, status: '启用', createTime: '2025-01-02', parentId: 2 },
-        { id: 26, name: '岗位管理', path: '/system/position', icon: '岗位', order: 3, status: '启用', createTime: '2025-01-02', parentId: 2 },
-        { id: 23, name: '角色管理', path: '/system/role', icon: '角色', order: 4, status: '启用', createTime: '2025-01-02', parentId: 2 },
-        { id: 24, name: '字典管理', path: '/system/dict', icon: '字典', order: 5, status: '启用', createTime: '2025-01-02', parentId: 2 },
-        { id: 25, name: '机柜管理', path: '/system/cabinet', icon: '机柜', order: 6, status: '启用', createTime: '2025-01-02', parentId: 2 },
+        { id: 23, name: '角色管理', path: '/system/role', icon: '角色', order: 3, status: '启用', createTime: '2025-01-02', parentId: 2 },
+        { id: 24, name: '字典管理', path: '/system/dict', icon: '字典', order: 4, status: '启用', createTime: '2025-01-02', parentId: 2 },
+        { id: 25, name: '机柜管理', path: '/system/cabinet', icon: '机柜', order: 5, status: '启用', createTime: '2025-01-02', parentId: 2 },
       ]
     },
     {
@@ -703,15 +701,10 @@ export default function SystemConfig({ subMenu }: SystemConfigProps) {
   }
 
   // 部门管理使用左右分栏布局
-  if (subMenu === '部门管理' || subMenu === 'departmentManagement') {
+  if (subMenu === '部门管理') {
     return <DepartmentManagement />;
   }
 
-  // 岗位管理使用左右分栏布局
-  if (subMenu === '岗位管理' || subMenu === 'positionManagement') {
-    return <PositionManagement />;
-  }
-
   // 其他菜单使用表格展示
   return (
     <div className="space-y-6">

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 249 - 703
src/components/UserManagement.tsx


+ 0 - 2
src/i18n.ts

@@ -73,7 +73,6 @@ export const translations = {
     systemConfig: {
       menuManagement: '菜单管理',
       departmentManagement: '部门管理',
-      positionManagement: '岗位管理',
       roleManagement: '角色管理',
       dictionaryManagement: '字典管理',
       cabinetManagement: '机柜管理',
@@ -168,7 +167,6 @@ export const translations = {
     systemConfig: {
       menuManagement: 'Menu',
       departmentManagement: 'Department',
-      positionManagement: 'Position',
       roleManagement: 'Role',
       dictionaryManagement: 'Dictionary',
       cabinetManagement: 'Cabinet',

+ 0 - 1
src/locales/en.json

@@ -66,7 +66,6 @@
   "systemConfig": {
     "menuManagement": "Menu",
     "departmentManagement": "Department",
-    "positionManagement": "Position",
     "roleManagement": "Role",
     "dictionaryManagement": "Dictionary",
     "cabinetManagement": "Cabinet"

+ 0 - 1
src/locales/zh.json

@@ -66,7 +66,6 @@
   "systemConfig": {
     "menuManagement": "菜单管理",
     "departmentManagement": "部门管理",
-    "positionManagement": "岗位管理",
     "roleManagement": "角色管理",
     "dictionaryManagement": "字典管理",
     "cabinetManagement": "机柜管理"

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů