Просмотр исходного кода

在移动端注册界面增加一项所属机构字段,用户注册时需选择所属机构,机构需要设置,可以在后台导入,用户在注册选则机构时出现二级菜单选项,例如中国银行沈阳分行为一级,二级为中华路支行

yourname 10 месяцев назад
Родитель
Сommit
6aefd4b4b8
4 измененных файлов с 246 добавлено и 107 удалено
  1. 120 54
      client/mobile/api.ts
  2. 38 3
      client/mobile/pages_register.tsx
  3. 15 0
      client/mobile/pages_tasks.tsx
  4. 73 50
      server/migrations.ts

+ 120 - 54
client/mobile/api.ts

@@ -43,7 +43,12 @@ interface AuthResponse {
 // 定义Auth API接口类型
 interface AuthAPIType {
   login: (username: string, password: string, latitude?: number, longitude?: number) => Promise<AuthLoginResponse>;
-  register: (username: string, email: string, password: string) => Promise<AuthResponse>;
+  register: (data: {
+    username: string;
+    email: string;
+    password: string;
+    organizationId?: number;
+  }) => Promise<AuthResponse>;
   logout: () => Promise<AuthResponse>;
   getCurrentUser: () => Promise<User>;
   updateUser: (userId: number, userData: Partial<User>) => Promise<User>;
@@ -71,9 +76,14 @@ export const AuthAPI: AuthAPIType = {
   },
   
   // 注册API
-  register: async (username: string, email: string, password: string) => {
+  register: async (data: {
+    username: string;
+    email: string;
+    password: string;
+    organizationId?: number;
+  }) => {
     try {
-      const response = await axios.post(`${API_BASE_URL}/auth/register`, { username, email, password });
+      const response = await axios.post(`${API_BASE_URL}/auth/register`, data);
       return response.data;
     } catch (error) {
       throw error;
@@ -141,6 +151,54 @@ export const AuthAPI: AuthAPIType = {
   }
 };
 
+// 首页相关接口响应类型
+interface HomeBannersResponse {
+  message: string;
+  data: KnowInfo[];
+}
+
+interface HomeNewsResponse {
+  message: string;
+  data: KnowInfo[];
+  pagination: {
+    total: number;
+    current: number;
+    pageSize: number;
+    totalPages: number;
+  };
+}
+
+interface HomeNoticesResponse {
+  message: string;
+  data: {
+    id: number;
+    title: string;
+    content: string;
+    created_at: string;
+  }[];
+  pagination: {
+    total: number;
+    current: number;
+    pageSize: number;
+    totalPages: number;
+  };
+}
+
+export interface LoginLocationResponse {
+  message: string;
+  data: LoginLocation[];
+}
+
+export interface LoginLocationUpdateResponse {
+  message: string;
+  data: LoginLocationDetail;
+}
+
+export interface LoginLocationDetailResponse {
+  message: string;
+  data: LoginLocationDetail;
+}
+
 // 为UserAPI添加的接口响应类型
 interface UsersResponse {
   data: User[];
@@ -491,66 +549,88 @@ export const ChartAPI = {
   // 获取文件上传统计数据
   getFileUploads: async (): Promise<ChartDataResponse<FileUploadsData[]>> => {
     try {
-      const response = await axios.get(`${API_BASE_URL}/charts/file-uploads`);
-      return response.data;
+      const res = await axios.get(`${API_BASE_URL}/charts/file-uploads`);
+      return res.data;
     } catch (error) {
       throw error;
     }
   },
 
-  // 获取文件类型分布数据
+  // 获取文件类型统计数据
   getFileTypes: async (): Promise<ChartDataResponse<FileTypesData[]>> => {
     try {
-      const response = await axios.get(`${API_BASE_URL}/charts/file-types`);
-      return response.data;
+      const res = await axios.get(`${API_BASE_URL}/charts/file-types`);
+      return res.data;
     } catch (error) {
       throw error;
     }
   },
 
-  // 获取仪表概览数据
+  // 获取仪表概览数据
   getDashboardOverview: async (): Promise<ChartDataResponse<DashboardOverviewData>> => {
     try {
-      const response = await axios.get(`${API_BASE_URL}/charts/dashboard-overview`);
-      return response.data;
+      const res = await axios.get(`${API_BASE_URL}/charts/dashboard-overview`);
+      return res.data;
     } catch (error) {
       throw error;
     }
   }
 };
-// 首页API相关类型定义
-interface HomeBannersResponse {
-  message: string;
-  data: KnowInfo[];
-}
 
-interface HomeNewsResponse {
-  message: string;
-  data: KnowInfo[];
-  pagination: {
-    total: number;
-    current: number;
-    pageSize: number;
-    totalPages: number;
-  };
-}
+// 系统管理API
+export const SysAPI = {
+  // 获取机构树形列表
+  getOrganizations: async (): Promise<{ data: any[] }> => {
+    try {
+      const res = await axios.get(`${API_BASE_URL}/sys/organizations/tree`);
+      return res.data;
+    } catch (error) {
+      throw error;
+    }
+  },
 
-interface HomeNoticesResponse {
-  message: string;
-    data: {
-      id: number;
-      title: string;
-      content: string;
-      created_at: string;
-    }[];
-  pagination: {
-    total: number;
-    current: number;
-    pageSize: number;
-    totalPages: number;
-  };
+  // 创建机构
+  createOrganization: async (data: {
+    name: string;
+    parentId?: number;
+    code?: string;
+    description?: string;
+  }) => {
+    try {
+      const res = await axios.post(`${API_BASE_URL}/sys/organizations`, data);
+      return res.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 更新机构
+  updateOrganization: async (id: number, data: {
+    name?: string;
+    parentId?: number;
+    code?: string;
+    description?: string;
+  }) => {
+    try {
+      const res = await axios.put(`${API_BASE_URL}/sys/organizations/${id}`, data);
+      return res.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 删除机构
+  deleteOrganization: async (id: number) => {
+    try {
+      const res = await axios.delete(`${API_BASE_URL}/sys/organizations/${id}`);
+      return res.data;
+    } catch (error) {
+      throw error;
+    }
+  }
 }
 
+
 // 首页API
 export const HomeAPI = {
   // 获取轮播图
@@ -592,20 +672,6 @@ export const HomeAPI = {
 };
 
 // 地图相关API的接口类型定义
-export interface LoginLocationResponse {
-  message: string;
-  data: LoginLocation[];
-}
-
-export interface LoginLocationDetailResponse {
-  message: string;
-  data: LoginLocationDetail;
-}
-
-export interface LoginLocationUpdateResponse {
-  message: string;
-  data: LoginLocationDetail;
-}
 
 
 // 地图相关API

+ 38 - 3
client/mobile/pages_register.tsx

@@ -1,8 +1,9 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
 import { useNavigate } from 'react-router';
 import { ArrowRightIcon, EnvelopeIcon, LockClosedIcon, UserIcon } from '@heroicons/react/24/outline';
-import { AuthAPI } from './api.ts';
+import { AuthAPI, SysAPI } from './api.ts';
 import { handleApiError } from './utils.ts';
+import { Select, TreeSelect } from 'antd';
 
 const RegisterPage: React.FC = () => {
   const navigate = useNavigate();
@@ -10,9 +11,24 @@ const RegisterPage: React.FC = () => {
   const [email, setEmail] = useState('');
   const [password, setPassword] = useState('');
   const [confirmPassword, setConfirmPassword] = useState('');
+  const [organizationId, setOrganizationId] = useState<number>();
+  const [organizationTree, setOrganizationTree] = useState<any[]>([]);
   const [loading, setLoading] = useState(false);
   const [error, setError] = useState<string | null>(null);
 
+  // 加载机构数据
+  useEffect(() => {
+    const loadOrganizations = async () => {
+      try {
+        const res = await SysAPI.getOrganizations();
+        setOrganizationTree(res.data);
+      } catch (err) {
+        console.error('加载机构数据失败:', err);
+      }
+    };
+    loadOrganizations();
+  }, []);
+
   const handleRegister = async (e: React.FormEvent) => {
     e.preventDefault();
     
@@ -41,7 +57,12 @@ const RegisterPage: React.FC = () => {
     setError(null);
     
     try {
-      await AuthAPI.register(username, email, password);
+      await AuthAPI.register({
+        username,
+        email,
+        password,
+        organizationId
+      });
       // 注册成功后跳转到登录页
       navigate('/mobile/login');
     } catch (err) {
@@ -134,6 +155,20 @@ const RegisterPage: React.FC = () => {
             </div>
           </div>
           
+          <div className="mb-4">
+            <label className="block text-gray-700 text-sm font-medium mb-2">
+              所属机构
+            </label>
+            <TreeSelect
+              treeData={organizationTree}
+              placeholder="请选择所属机构"
+              treeDefaultExpandAll
+              onChange={(value) => setOrganizationId(value)}
+              className="w-full"
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+            />
+          </div>
+
           <div className="mb-6">
             <label className="block text-gray-700 text-sm font-medium mb-2" htmlFor="confirmPassword">
               确认密码

+ 15 - 0
client/mobile/pages_tasks.tsx

@@ -0,0 +1,15 @@
+import React from 'react';
+import { useAuth } from './hooks.tsx';
+
+const TasksPage = () => {
+  return (
+    <div className="p-4">
+      <h1 className="text-xl font-bold mb-4">任务</h1>
+      <div className="bg-white rounded-lg shadow p-4">
+        <p className="text-gray-500">任务功能开发中...</p>
+      </div>
+    </div>
+  );
+};
+
+export default TasksPage;

+ 73 - 50
server/migrations.ts

@@ -20,6 +20,7 @@ const createUsersTable: MigrationLiveDefinition = {
       table.string('email').unique();
       table.string('nickname');
       table.string('name');
+      table.integer('organization_id').unsigned().nullable().references('id').inTable('organizations').comment('所属机构ID');
       table.integer('is_disabled').defaultTo(0);
       table.integer('is_deleted').defaultTo(0);
       table.timestamps(true, true);
@@ -52,7 +53,6 @@ const createLoginHistoryTable: MigrationLiveDefinition = {
       // 添加索引
       table.index('user_id');
       table.index('login_time');
-      // table.index(['longitude', 'latitude']);
     })
   },
   down: async (api) => {
@@ -190,6 +190,77 @@ const createSystemSettingsTable: MigrationLiveDefinition = {
   }
 };
 
+// 创建消息表迁移
+const createMessagesTable: MigrationLiveDefinition = {
+  name: "create_messages_table",
+  up: async (api) => {
+    await api.schema.createTable('messages', (table) => {
+      table.increments('id').primary().comment('消息ID');
+      table.string('title').notNullable().comment('消息标题');
+      table.text('content').notNullable().comment('消息内容');
+      table.enum('type', ['system', 'private', 'announce']).notNullable().comment('消息类型');
+      table.integer('sender_id').unsigned().references('id').inTable('users').onDelete('SET NULL').comment('发送者ID');
+      table.string('sender_name').comment('发送者名称');
+      table.timestamps(true, true);
+      
+      // 添加索引
+      table.index('type');
+      table.index('sender_id');
+    });
+  },
+  down: async (api) => {
+    await api.schema.dropTable('messages');
+  }
+};
+
+// 创建用户消息关联表迁移
+const createUserMessagesTable: MigrationLiveDefinition = {
+  name: "create_user_messages_table",
+  up: async (api) => {
+    await api.schema.createTable('user_messages', (table) => {
+      table.increments('id').primary().comment('关联ID');
+      table.integer('user_id').unsigned().references('id').inTable('users').onDelete('CASCADE').comment('用户ID');
+      table.integer('message_id').unsigned().references('id').inTable('messages').onDelete('CASCADE').comment('消息ID');
+      table.integer('status').defaultTo(0).comment('阅读状态(0=未读,1=已读)');
+      table.integer('is_deleted').defaultTo(0).comment('删除状态(0=未删除,1=已删除)');
+      table.timestamp('read_at').nullable().comment('阅读时间');
+      table.timestamps(true, true);
+      
+      // 添加复合索引
+      table.index(['user_id', 'status']);
+      table.index(['user_id', 'is_deleted']);
+      table.unique(['user_id', 'message_id']);
+    });
+  },
+  down: async (api) => {
+    await api.schema.dropTable('user_messages');
+  }
+};
+
+// 创建机构表迁移
+const createOrganizationsTable: MigrationLiveDefinition = {
+  name: "create_organizations_table",
+  up: async (api) => {
+    await api.schema.createTable('organizations', (table) => {
+      table.increments('id').primary();
+      table.string('name').notNullable().comment('机构名称');
+      table.string('code').notNullable().comment('机构代码');
+      table.integer('parent_id').unsigned().nullable().comment('父机构ID');
+      table.integer('level').defaultTo(1).comment('机构层级');
+      table.integer('is_deleted').defaultTo(DeleteStatus.NOT_DELETED).comment('是否被删除 (0否 1是)');
+      table.timestamps(true, true);
+      
+      // 添加索引
+      table.index('name');
+      table.index('code');
+      table.index('parent_id');
+    });
+  },
+  down: async (api) => {
+    await api.schema.dropTable('organizations');
+  }
+};
+
 // 初始测试数据迁移
 const seedInitialData: MigrationLiveDefinition = {
   name: "seed_initial_data",
@@ -474,55 +545,6 @@ const seedInitialData: MigrationLiveDefinition = {
   }
 };
 
-// 创建消息表迁移
-const createMessagesTable: MigrationLiveDefinition = {
-  name: "create_messages_table",
-  up: async (api) => {
-    await api.schema.createTable('messages', (table) => {
-      table.increments('id').primary().comment('消息ID');
-      table.string('title').notNullable().comment('消息标题');
-      table.text('content').notNullable().comment('消息内容');
-      table.enum('type', ['system', 'private', 'announce']).notNullable().comment('消息类型');
-      table.integer('sender_id').unsigned().references('id').inTable('users').onDelete('SET NULL').comment('发送者ID');
-      table.string('sender_name').comment('发送者名称');
-      table.timestamps(true, true);
-      
-      // 添加索引
-      table.index('type');
-      table.index('sender_id');
-    });
-  },
-  down: async (api) => {
-    await api.schema.dropTable('messages');
-  }
-};
-
-// 创建用户消息关联表迁移
-const createUserMessagesTable: MigrationLiveDefinition = {
-  name: "create_user_messages_table",
-  up: async (api) => {
-    await api.schema.createTable('user_messages', (table) => {
-      table.increments('id').primary().comment('关联ID');
-      table.integer('user_id').unsigned().references('id').inTable('users').onDelete('CASCADE').comment('用户ID');
-      table.integer('message_id').unsigned().references('id').inTable('messages').onDelete('CASCADE').comment('消息ID');
-      table.integer('status').defaultTo(0).comment('阅读状态(0=未读,1=已读)');
-      table.integer('is_deleted').defaultTo(0).comment('删除状态(0=未删除,1=已删除)');
-      table.timestamp('read_at').nullable().comment('阅读时间');
-      table.timestamps(true, true);
-      
-      // 添加复合索引
-      table.index(['user_id', 'status']);
-      table.index(['user_id', 'is_deleted']);
-      table.unique(['user_id', 'message_id']);
-    });
-  },
-  down: async (api) => {
-    await api.schema.dropTable('user_messages');
-  }
-};
-
-
-
 // 导出所有迁移
 export const migrations = [
   createUsersTable,
@@ -534,5 +556,6 @@ export const migrations = [
   createSystemSettingsTable,
   createMessagesTable,
   createUserMessagesTable,
+  createOrganizationsTable,
   seedInitialData,
 ];