From 70da1652e00de66b54e9651639c1bf6dda88b357 Mon Sep 17 00:00:00 2001
From: Your Name <123456>
Date: 星期五, 17 二月 2023 16:26:40 +0800
Subject: [PATCH] 页面

---
 src/views/basic/material/index.vue                             |  322 ++
 src/views/experiment/developing/index.vue                      |  347 ++
 src/views/basic/room/components/roomDialog.vue                 |  139 
 src/types/layout.d.ts                                          |   59 
 src/views/experiment/project/components/applyDialog.vue        |   98 
 src/views/system/home/index.ts                                 |    3 
 src/router/route.ts                                            |   32 
 src/views/basic/unit/index.ts                                  |   45 
 src/views/basic/room/index.vue                                 |  302 ++
 src/views/basic/person/index.ts                                |   52 
 src/views/basic/material/components/materialDialog.vue         |  169 +
 src/api/basic/room/index.ts                                    |   45 
 src/views/basic/equipment/components/equipmentDialog.vue       |  167 +
 src/views/experiment/project/components/selectEquipment.vue    |  208 +
 src/views/system/home/index.vue                                |  144 +
 src/utils/request.ts                                           |   12 
 src/api/basic/unit/index.ts                                    |   38 
 src/views/home/dialog.vue                                      |  272 +
 src/views/basic/unit/index.vue                                 |  313 ++
 src/views/experiment/developing/components/projectDialog.vue   |  533 +++
 src/views/basic/room/index.ts                                  |   41 
 src/views/experiment/developing/components/applyDialog.vue     |   98 
 src/views/basic/equipment/index.ts                             |   46 
 src/views/experiment/developing/components/selectMaterial.vue  |  210 +
 .env.development                                               |    6 
 src/views/basic/person/index.vue                               |  315 ++
 src/assets/style/index.scss                                    |    3 
 src/views/experiment/developing/components/applyStart.vue      |  101 
 src/views/home/index.vue                                       |  149 
 src/views/home/index.ts                                        |    3 
 src/api/experiment/project/index.ts                            |   54 
 src/views/experiment/project/components/projectDialog.vue      |  533 +++
 src/views/experiment/developing/components/selectPerson.vue    |  218 +
 src/api/basic/equipement/index.ts                              |   44 
 src/api/systemManage/role/index.ts                             |    4 
 src/views/experiment/project/index.ts                          |  150 +
 src/types/global.d.ts                                          |  111 
 src/views/experiment/developing/components/selectEquipment.vue |  208 +
 src/views/basic/equipment/index.vue                            |  319 ++
 src/views/experiment/project/components/selectMaterial.vue     |  210 +
 src/types/mitt.d.ts                                            |   38 
 src/types/views.d.ts                                           |  330 ++
 src/views/experiment/developing/index.ts                       |  150 +
 src/views/experiment/developing/components/selectDanger.vue    |  184 +
 src/types/pinia.d.ts                                           |   91 
 src/views/loginPage/component/accountLogin.vue                 |    2 
 src/views/experiment/project/components/selectDanger.vue       |  184 +
 src/views/experiment/project/index.vue                         |  324 ++
 src/views/basic/person/components/personDialog.vue             |  197 +
 src/views/experiment/project/components/applyStart.vue         |  101 
 src/views/basic/unit/components/unitDialog.vue                 |  167 +
 src/api/basic/person/index.ts                                  |   44 
 src/views/system/home/dialog.vue                               |  272 +
 src/router/backEnd.ts                                          |    1 
 src/api/systemManage/menu/index.ts                             |   11 
 src/types/axios.d.ts                                           |   13 
 src/views/basic/material/index.ts                              |   48 
 src/api/basic/material/index.ts                                |   44 
 src/views/experiment/project/components/selectPerson.vue       |  218 +
 59 files changed, 8,481 insertions(+), 61 deletions(-)

diff --git a/.env.development b/.env.development
index bb3516f..5285007 100644
--- a/.env.development
+++ b/.env.development
@@ -4,16 +4,16 @@
 #VITE_API_URL = 'http://192.168.0.35:8008'
 #李宇飞接口地址
 
-#VITE_API_URL = 'http://192.168.0.50:8008'
+VITE_API_URL = 'http://192.168.0.22:8084'
 #张凤接口地址
 
 #VITE_API_URL = 'http://192.168.0.29:8008'
 #黄振接口地址
 
-VITE_API_URL = 'http://192.168.0.5:8084'
+#VITE_API_URL = 'http://192.168.0.5:8084'
 #施正红接口地址
 
-#VITE_API_URL = 'http://192.168.0.8:8084'
+#VITE_API_URL = 'http://192.168.0.18:8084'
 #戚会山接口地址
 
 #VITE_API_URL = 'http://121.239.169.27:16006/safeplatform'
diff --git a/src/api/basic/equipement/index.ts b/src/api/basic/equipement/index.ts
new file mode 100644
index 0000000..b8a18de
--- /dev/null
+++ b/src/api/basic/equipement/index.ts
@@ -0,0 +1,44 @@
+import request from '/@/utils/request';
+
+export function equipmentApi() {
+	return {
+		getEquipmentByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/getDevicePage',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addEquipment: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/insert/insertDevice',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modEquipment: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/update/updateDevice',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deleteEquipmentById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/delete/deleteDevice',
+				method: 'post',
+				data: params
+			});
+		},
+
+		getAllEquipment: () => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/listDevice',
+				method: 'get',
+			});
+		},
+	};
+}
diff --git a/src/api/basic/material/index.ts b/src/api/basic/material/index.ts
new file mode 100644
index 0000000..c0ff740
--- /dev/null
+++ b/src/api/basic/material/index.ts
@@ -0,0 +1,44 @@
+import request from '/@/utils/request';
+
+export function materialApi() {
+	return {
+		getMaterialByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/selectStuffPage',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addMaterial: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/insert/insertStuff',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modMaterial: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/update/updateStuff',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deleteMaterialById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/delete/deleteStuff',
+				method: 'post',
+				data: params
+			});
+		},
+
+		getAllMaterial: () => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/listStuff',
+				method: 'get',
+			});
+		},
+	};
+}
diff --git a/src/api/basic/person/index.ts b/src/api/basic/person/index.ts
new file mode 100644
index 0000000..a49ed1d
--- /dev/null
+++ b/src/api/basic/person/index.ts
@@ -0,0 +1,44 @@
+import request from '/@/utils/request';
+
+export function personApi() {
+	return {
+		getPersonByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/selectPersonPage',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addPerson: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/insert/insertPerson',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modPerson: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/update/updatePerson',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deletePersonById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/delete/deletePerson',
+				method: 'post',
+				data: params
+			});
+		},
+
+		getAllPerson: () => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/listPerson',
+				method: 'get',
+			});
+		},
+	};
+}
diff --git a/src/api/basic/room/index.ts b/src/api/basic/room/index.ts
new file mode 100644
index 0000000..0e6f055
--- /dev/null
+++ b/src/api/basic/room/index.ts
@@ -0,0 +1,45 @@
+import request from '/@/utils/request';
+
+export function roomApi() {
+	return {
+		getRoomByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/selectSitePage',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addRoom: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/insert/insertSite',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modRoom: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/update/updateSite',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deleteRoomById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/delete/deleteSite',
+				method: 'post',
+				data: params
+			});
+		},
+
+		getAllRoom: () => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/listSite',
+				method: 'get',
+			});
+		},
+
+	};
+}
diff --git a/src/api/basic/unit/index.ts b/src/api/basic/unit/index.ts
new file mode 100644
index 0000000..5953dd2
--- /dev/null
+++ b/src/api/basic/unit/index.ts
@@ -0,0 +1,38 @@
+import request from '/@/utils/request';
+
+export function unitApi() {
+	return {
+		getUnitByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/select/selectRiskUnitPage',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addUnit: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/insert/insertRiskUnit',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modUnit: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/update/updateRiskUnit',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deleteUnitById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/basic/delete/deleteRiskUnit',
+				method: 'post',
+				data: params
+			});
+		},
+
+	};
+}
diff --git a/src/api/experiment/project/index.ts b/src/api/experiment/project/index.ts
new file mode 100644
index 0000000..b512600
--- /dev/null
+++ b/src/api/experiment/project/index.ts
@@ -0,0 +1,54 @@
+import request from '/@/utils/request';
+
+export function projectApi() {
+	return {
+		getProjectByList: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/list/page/project',
+				method: 'post',
+				data: params
+			});
+		},
+
+		addProject: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/save',
+				method: 'post',
+				data: params
+			});
+		},
+
+		modProject: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/mod',
+				method: 'post',
+				data: params
+			});
+		},
+
+		applyProject: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/update/develop',
+				method: 'post',
+				data: params
+			});
+		},
+
+		accessProject: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/update/apply/evaluation',
+				method: 'post',
+				data: params
+			});
+		},
+
+		deleteProjectById: (params: object) => {
+			return request({
+				url: import.meta.env.VITE_API_URL + '/experimentInfo/delete',
+				method: 'post',
+				data: params
+			});
+		},
+
+	};
+}
diff --git a/src/api/systemManage/menu/index.ts b/src/api/systemManage/menu/index.ts
index bbb0469..84e24af 100644
--- a/src/api/systemManage/menu/index.ts
+++ b/src/api/systemManage/menu/index.ts
@@ -24,7 +24,7 @@
         // v1
         addMenu: (value?: object) => {
             return request({
-                url: import.meta.env.VITE_API_URL + `/menu/add`,
+                url: import.meta.env.VITE_API_URL + `/sys/console/menu/add`,
                 method: 'post',
                 data: value
             });
@@ -32,17 +32,16 @@
         // v1
         modMenu: (value?: object) => {
             return request({
-                url: import.meta.env.VITE_API_URL + `/menu/mod`,
+                url: import.meta.env.VITE_API_URL + `/sys/console/menu/mod`,
                 method: 'post',
                 data: value
             });
         },
         // v1
-        deleteMenu: (value?: object) => {
+        deleteMenu: (value: number) => {
             return request({
-                url: import.meta.env.VITE_API_URL + `/menu/del`,
-                method: 'post',
-                data: value
+                url: import.meta.env.VITE_API_URL + `/sys/console/menu/del?menuItemId=${value}`,
+                method: 'get',
             });
         },
         // v1
diff --git a/src/api/systemManage/role/index.ts b/src/api/systemManage/role/index.ts
index 85b00cf..f099cac 100644
--- a/src/api/systemManage/role/index.ts
+++ b/src/api/systemManage/role/index.ts
@@ -5,8 +5,8 @@
         // v2
         getRoleList: () => {
             return request({
-                url: import.meta.env.VITE_API_URL + `/role/list`,
-                method: 'post'
+                url: import.meta.env.VITE_API_URL + `/account/role/find/all/active`,
+                method: 'get'
             });
         },
         // v2
diff --git a/src/assets/style/index.scss b/src/assets/style/index.scss
index 1503233..a55e6ad 100644
--- a/src/assets/style/index.scss
+++ b/src/assets/style/index.scss
@@ -17,3 +17,6 @@
 .page-position{
   float: right;
 }
+.input-length {
+  width: 90% !important;
+}
diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts
index b7750e8..12240fc 100644
--- a/src/router/backEnd.ts
+++ b/src/router/backEnd.ts
@@ -28,7 +28,6 @@
     if (window.nextLoading === undefined) NextLoading.start();
     if (!Cookies.get('token')) return false;
     const res = await getBackEndControlRoutes();
-    debugger
     await useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data.data)));
     dynamicRoutes[0].children = await backEndComponent(res.data.data);
     await setAddRoute();
diff --git a/src/router/route.ts b/src/router/route.ts
index a29e775..827dd4d 100644
--- a/src/router/route.ts
+++ b/src/router/route.ts
@@ -95,36 +95,4 @@
             isKeepAlive: false
         }
     },
-    {
-        path: '/warningScreen',
-        name: 'warningScreen',
-        component: () => import('/@/views/riskWarningSys/warningBigScreen/index.vue'),
-        meta: {
-            title: '预警预报'
-        }
-    },
-    {
-        path: '/msgDetail',
-        name: 'msgDetail',
-        component: () => import('/@/views/riskWarningSys/warningBigScreen/indexs/msgDetail.vue'),
-        meta: {
-            title: 'spi报告详情'
-        }
-    },
-    {
-        path: '/screenPage',
-        name: 'screenPage',
-        component: () => import('/@/views/riskWarningSys/warningBigScreen/indexs/index.vue'),
-        meta: {
-            title: '预警预报'
-        }
-    },
-    {
-        path: '/securities',
-        name: 'securities',
-        component: () => import('/@/views/facilityManagement/securities/index.vue'),
-        meta: {
-            title: '安全物资与设备'
-        }
-    }
 ];
diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts
new file mode 100644
index 0000000..bcd0a21
--- /dev/null
+++ b/src/types/axios.d.ts
@@ -0,0 +1,13 @@
+/* eslint-disable */
+import * as axios from 'axios';
+
+// 扩展 axios 数据返回类型,可自行扩展
+declare module 'axios' {
+	export interface AxiosResponse<T = any> {
+		code: number;
+		data: T;
+		message: string;
+		type?: string;
+		[key: string]: T;
+	}
+}
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 0000000..a46b866
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,111 @@
+// 申明外部 npm 插件模块
+declare module 'vue-grid-layout';
+declare module 'qrcodejs2-fixes';
+declare module 'splitpanes';
+declare module 'js-cookie';
+declare module '@wangeditor/editor-for-vue';
+declare module 'js-table2excel';
+declare module 'qs';
+
+// 声明一个模块,防止引入文件时报错
+declare module '*.json';
+declare module '*.png';
+declare module '*.jpg';
+declare module '*.scss';
+declare module '*.ts';
+declare module '*.js';
+
+// 声明文件,*.vue 后缀的文件交给 vue 模块来处理
+declare module '*.vue' {
+	import type { DefineComponent } from 'vue';
+	const component: DefineComponent<{}, {}, any>;
+	export default component;
+}
+
+// 声明文件,定义全局变量
+/* eslint-disable */
+declare interface Window {
+	nextLoading: boolean;
+}
+
+// 声明路由当前项类型
+declare type RouteItem<T = any> = {
+	path: string;
+	name?: string | symbol | undefined | null;
+	redirect?: string;
+	k?: T;
+	meta?: {
+		title?: string;
+		isLink?: string;
+		isHide?: boolean;
+		isKeepAlive?: boolean;
+		isAffix?: boolean;
+		isIframe?: boolean;
+		roles?: string[];
+		icon?: string;
+		isDynamic?: boolean;
+		isDynamicPath?: string;
+		isIframeOpen?: string;
+		loading?: boolean;
+	};
+	children: T[];
+	query?: { [key: string]: T };
+	params?: { [key: string]: T };
+	contextMenuClickId?: string | number;
+	commonUrl?: string;
+	isFnClick?: boolean;
+	url?: string;
+	transUrl?: string;
+	title?: string;
+	id?: string | number;
+};
+
+// 声明路由 to from
+declare interface RouteToFrom<T = any> extends RouteItem {
+	path?: string;
+	children?: T[];
+}
+
+// 声明路由当前项类型集合
+declare type RouteItems<T extends RouteItem = any> = T[];
+
+// 声明 ref
+declare type RefType<T = any> = T | null;
+
+// 声明 HTMLElement
+declare type HtmlType = HTMLElement | string | undefined | null;
+
+// 申明 children 可选
+declare type ChilType<T = any> = {
+	children?: T[];
+};
+
+// 申明 数组
+declare type EmptyArrayType<T = any> = T[];
+
+// 申明 对象
+declare type EmptyObjectType<T = any> = {
+	[key: string]: T;
+};
+
+// 申明 select option
+declare type SelectOptionType = {
+	value: string | number;
+	label: string | number;
+};
+
+// 鼠标滚轮滚动类型
+declare interface WheelEventType extends WheelEvent {
+	wheelDelta: number;
+}
+
+// table 数据格式公共类型
+declare interface TableType<T = any> {
+	total: number;
+	loading: boolean;
+	param: {
+		pageNum: number;
+		pageSize: number;
+		[key: string]: T;
+	};
+}
diff --git a/src/types/layout.d.ts b/src/types/layout.d.ts
new file mode 100644
index 0000000..82904ef
--- /dev/null
+++ b/src/types/layout.d.ts
@@ -0,0 +1,59 @@
+// aside
+declare type AsideState = {
+	menuList: RouteRecordRaw[];
+	clientWidth: number;
+};
+
+// columnsAside
+declare type ColumnsAsideState<T = any> = {
+	columnsAsideList: T[];
+	liIndex: number;
+	liOldIndex: null | number;
+	liHoverIndex: null | number;
+	liOldPath: null | string;
+	difference: number;
+	routeSplit: string[];
+};
+
+// navBars breadcrumb
+declare type BreadcrumbState<T = any> = {
+	breadcrumbList: T[];
+	routeSplit: string[];
+	routeSplitFirst: string;
+	routeSplitIndex: number;
+};
+
+// navBars search
+declare type SearchState<T = any> = {
+	isShowSearch: boolean;
+	menuQuery: string;
+	tagsViewList: T[];
+};
+
+// navBars tagsView
+declare type TagsViewState<T = any> = {
+	routeActive: string | T;
+	routePath: string | unknown;
+	dropdown: {
+		x: string | number;
+		y: string | number;
+	};
+	sortable: T;
+	tagsRefsIndex: number;
+	tagsViewList: T[];
+	tagsViewRoutesList: T[];
+};
+
+// navBars parent
+declare type ParentViewState<T = any> = {
+	refreshRouterViewKey: string;
+	iframeRefreshKey: string;
+	keepAliveNameList: string[];
+	iframeList: T[];
+};
+
+// navBars link
+declare type LinkViewState = {
+	title: string;
+	isLink: string;
+};
diff --git a/src/types/mitt.d.ts b/src/types/mitt.d.ts
new file mode 100644
index 0000000..b68b80d
--- /dev/null
+++ b/src/types/mitt.d.ts
@@ -0,0 +1,38 @@
+/**
+ * mitt 事件类型定义
+ *
+ * @method openSetingsDrawer 打开布局设置弹窗
+ * @method restoreDefault 分栏布局,鼠标移入、移出数据显示
+ * @method setSendColumnsChildren 分栏布局,鼠标移入、移出菜单数据传入到 navMenu 下的菜单中
+ * @method setSendClassicChildren 经典布局,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
+ * @method getBreadcrumbIndexSetFilterRoutes 布局设置弹窗,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
+ * @method layoutMobileResize 浏览器窗口改变时,用于适配移动端界面显示
+ * @method openOrCloseSortable 布局设置弹窗,开启 TagsView 拖拽
+ * @method openShareTagsView 布局设置弹窗,开启 TagsView 共用
+ * @method onTagsViewRefreshRouterView tagsview 刷新界面
+ * @method onCurrentContextmenuClick tagsview 右键菜单每项点击时
+ */
+declare type MittType<T = any> = {
+	openSetingsDrawer?: string;
+	restoreDefault?: string;
+	setSendColumnsChildren: T;
+	setSendClassicChildren: T;
+	getBreadcrumbIndexSetFilterRoutes?: string;
+	layoutMobileResize: T;
+	openOrCloseSortable?: string;
+	openShareTagsView?: string;
+	onTagsViewRefreshRouterView?: T;
+	onCurrentContextmenuClick?: T;
+};
+
+// mitt 参数类型定义
+declare type LayoutMobileResize = {
+	layout: string;
+	clientWidth: number;
+};
+
+// mitt 参数菜单类型
+declare type MittMenu = {
+	children: RouteRecordRaw[];
+	item?: RouteItem;
+};
diff --git a/src/types/pinia.d.ts b/src/types/pinia.d.ts
new file mode 100644
index 0000000..09c6e47
--- /dev/null
+++ b/src/types/pinia.d.ts
@@ -0,0 +1,91 @@
+/**
+ * pinia 类型定义
+ */
+
+// 用户信息
+declare interface UserInfosState<T = any> {
+	userInfos: {
+		authBtnList: string[];
+		photo: string;
+		roles: string[];
+		time: number;
+		userName: string;
+		[key: string]: T;
+	};
+}
+
+// 路由缓存列表
+declare interface KeepAliveNamesState {
+	keepAliveNames: string[];
+	cachedViews: string[];
+}
+
+// 后端返回原始路由(未处理时)
+declare interface RequestOldRoutesState {
+	requestOldRoutes: string[];
+}
+
+// TagsView 路由列表
+declare interface TagsViewRoutesState<T = any> {
+	tagsViewRoutes: T[];
+	isTagsViewCurrenFull: Boolean;
+}
+
+// 路由列表
+declare interface RoutesListState<T = any> {
+	routesList: T[];
+	isColumnsMenuHover: Boolean;
+	isColumnsNavHover: Boolean;
+}
+
+// 布局配置
+declare interface ThemeConfigState {
+	themeConfig: {
+		isDrawer: boolean;
+		primary: string;
+		topBar: string;
+		topBarColor: string;
+		isTopBarColorGradual: boolean;
+		menuBar: string;
+		menuBarColor: string;
+		menuBarActiveColor: string;
+		isMenuBarColorGradual: boolean;
+		columnsMenuBar: string;
+		columnsMenuBarColor: string;
+		isColumnsMenuBarColorGradual: boolean;
+		isColumnsMenuHoverPreload: boolean;
+		isCollapse: boolean;
+		isUniqueOpened: boolean;
+		isFixedHeader: boolean;
+		isFixedHeaderChange: boolean;
+		isClassicSplitMenu: boolean;
+		isLockScreen: boolean;
+		lockScreenTime: number;
+		isShowLogo: boolean;
+		isShowLogoChange: boolean;
+		isBreadcrumb: boolean;
+		isTagsview: boolean;
+		isBreadcrumbIcon: boolean;
+		isTagsviewIcon: boolean;
+		isCacheTagsView: boolean;
+		isSortableTagsView: boolean;
+		isShareTagsView: boolean;
+		isFooter: boolean;
+		isGrayscale: boolean;
+		isInvert: boolean;
+		isIsDark: boolean;
+		isWartermark: boolean;
+		wartermarkText: string;
+		tagsStyle: string;
+		animation: string;
+		columnsAsideStyle: string;
+		columnsAsideLayout: string;
+		layout: string;
+		isRequestRoutes: boolean;
+		globalTitle: string;
+		globalViceTitle: string;
+		globalViceTitleMsg: string;
+		globalI18n: string;
+		globalComponentSize: string;
+	};
+}
diff --git a/src/types/views.d.ts b/src/types/views.d.ts
new file mode 100644
index 0000000..2cd6faf
--- /dev/null
+++ b/src/types/views.d.ts
@@ -0,0 +1,330 @@
+/**
+ * views personal
+ */
+type NewInfo = {
+	title: string;
+	date: string;
+	link: string;
+};
+type Recommend = {
+	title: string;
+	msg: string;
+	icon: string;
+	bg: string;
+	iconColor: string;
+};
+
+declare type PersonalState = {
+	newsInfoList: NewInfo[];
+	recommendList: Recommend[];
+	personalForm: {
+		name: string;
+		email: string;
+		autograph: string;
+		occupation: string;
+		phone: string;
+		sex: string;
+	};
+};
+
+/**
+ * views visualizing
+ */
+declare type Demo2State<T = any> = {
+	time: {
+		txt: string;
+		fun: number;
+	};
+	dropdownList: T[];
+	dropdownActive: string;
+	skyList: T[];
+	dBtnList: T[];
+	chartData4Index: number;
+	dBtnActive: number;
+	earth3DBtnList: T[];
+	chartData4List: T[];
+	myCharts: T[];
+};
+
+/**
+ * views params
+ */
+declare type ParamsState = {
+	value: string;
+	tagsViewName: string;
+	tagsViewNameIsI18n: boolean;
+};
+
+/**
+ * views system
+ */
+// role
+declare interface RowRoleType {
+	roleName: string;
+	roleSign: string;
+	describe: string;
+	sort: number;
+	status: boolean;
+	createTime: string;
+}
+
+interface SysRoleTableType extends TableType {
+	data: RowRoleType[];
+}
+
+declare interface SysRoleState {
+	tableData: SysRoleTableType;
+}
+
+declare type TreeType = {
+	id: number;
+	label: string;
+	children?: TreeType[];
+};
+
+// user
+declare type RowUserType<T = any> = {
+	userName: string;
+	userNickname: string;
+	roleSign: string;
+	department: string[];
+	phone: string;
+	email: string;
+	sex: string;
+	password: string;
+	overdueTime: T;
+	status: boolean;
+	describe: string;
+	createTime: T;
+};
+
+interface SysUserTableType extends TableType {
+	data: RowUserType[];
+}
+
+declare interface SysUserState {
+	tableData: SysUserTableType;
+}
+
+declare type DeptTreeType = {
+	deptName: string;
+	createTime: string;
+	status: boolean;
+	sort: number;
+	describe: string;
+	id: number | string;
+	children?: DeptTreeType[];
+};
+
+// dept
+declare interface RowDeptType extends DeptTreeType {
+	deptLevel: string[];
+	person: string;
+	phone: string;
+	email: string;
+}
+
+interface SysDeptTableType extends TableType {
+	data: DeptTreeType[];
+}
+
+declare interface SysDeptState {
+	tableData: SysDeptTableType;
+}
+
+// dic
+type ListType = {
+	id: number;
+	label: string;
+	value: string;
+};
+
+declare interface RowDicType {
+	dicName: string;
+	fieldName: string;
+	describe: string;
+	status: boolean;
+	createTime: string;
+	list: ListType[];
+}
+
+interface SysDicTableType extends TableType {
+	data: RowDicType[];
+}
+
+declare interface SysDicState {
+	tableData: SysDicTableType;
+}
+
+/**
+ * views pages
+ */
+//  filtering
+declare type FilteringChilType = {
+	id: number | string;
+	label: string;
+	active: boolean;
+};
+
+declare type FilterListType = {
+	img: string;
+	title: string;
+	evaluate: string;
+	collection: string;
+	price: string;
+	monSales: string;
+	id: number | string;
+	loading?: boolean;
+};
+
+declare type FilteringRowType = {
+	title: string;
+	isMore: boolean;
+	isShowMore: boolean;
+	id: number | string;
+	children: FilteringChilType[];
+};
+
+// tableRules
+declare type TableRulesHeaderType = {
+	prop: string;
+	width: string | number;
+	label: string;
+	isRequired?: boolean;
+	isTooltip?: boolean;
+	type: string;
+};
+
+declare type TableRulesState = {
+	tableData: {
+		data: EmptyObjectType[];
+		header: TableRulesHeaderType[];
+		option: SelectOptionType[];
+	};
+};
+
+declare type TableRulesOneProps = {
+	name: string;
+	email: string;
+	autograph: string;
+	occupation: string;
+};
+
+// tree
+declare type RowTreeType = {
+	id: number;
+	label: string;
+	label1: string;
+	label2: string;
+	isShow: boolean;
+	children?: RowTreeType[];
+};
+
+// workflow index
+declare type NodeListState = {
+	id: string | number;
+	nodeId: string | undefined;
+	class: HTMLElement | string;
+	left: number | string;
+	top: number | string;
+	icon: string;
+	name: string;
+};
+
+declare type LineListState = {
+	sourceId: string;
+	targetId: string;
+	label: string;
+};
+
+declare type XyState = {
+	x: string | number;
+	y: string | number;
+};
+
+declare type WorkflowState<T = any> = {
+	leftNavList: T[];
+	dropdownNode: XyState;
+	dropdownLine: XyState;
+	isShow: boolean;
+	jsPlumb: T;
+	jsPlumbNodeIndex: null | number;
+	jsplumbDefaults: T;
+	jsplumbMakeSource: T;
+	jsplumbMakeTarget: T;
+	jsplumbConnect: T;
+	jsplumbData: {
+		nodeList: NodeListState[];
+		lineList: LineListState[];
+	};
+};
+
+// workflow drawer
+declare type WorkflowDrawerNodeState<T = any> = {
+	node: { [key: string]: T };
+	nodeRules: T;
+	form: T;
+	tabsActive: string;
+	loading: {
+		extend: boolean;
+	};
+};
+
+declare type WorkflowDrawerLabelType = {
+	type: string;
+	label: string;
+};
+
+declare type WorkflowDrawerState<T = any> = {
+	isOpen: boolean;
+	nodeData: {
+		type: string;
+	};
+	jsplumbConn: T;
+};
+
+/**
+ * views make
+ */
+// tableDemo
+declare type TableDemoPageType = {
+	pageNum: number;
+	pageSize: number;
+};
+
+declare type TableHeaderType = {
+	key: string;
+	width: string;
+	title: string;
+	type: string | number;
+	colWidth: string;
+	width?: string | number;
+	height?: string | number;
+	isCheck: boolean;
+};
+
+declare type TableSearchType = {
+	label: string;
+	prop: string;
+	placeholder: string;
+	required: boolean;
+	type: string;
+	options?: SelectOptionType[];
+};
+
+declare type TableDemoState = {
+	tableData: {
+		data: EmptyObjectType[];
+		header: TableHeaderType[];
+		config: {
+			total: number;
+			loading: boolean;
+			isBorder: boolean;
+			isSelection: boolean;
+			isSerialNo: boolean;
+			isOperate: boolean;
+		};
+		search: TableSearchType[];
+		param: EmptyObjectType;
+	};
+};
diff --git a/src/utils/request.ts b/src/utils/request.ts
index 21107e5..10500ec 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -15,11 +15,11 @@
 
 service.interceptors.request.use(
     (config) => {
-        for (let key in config.data) {
-            if (config.data[key] == '' && config.data[key] !== 0) {
-                config.data[key] = null;
-            }
-        }
+        // for (let key in config.data) {
+        //     if (config.data[key] == '' && config.data[key] !== 0) {
+        //         config.data[key] = null;
+        //     }
+        // }
         if (Cookies.get('token')) {
             (<any>config.headers).common['tk'] = `${Cookies.get('token')}`;
             (<any>config.headers).common['uid'] = `${Cookies.get('uid')}`;
@@ -63,7 +63,7 @@
                     Session.clear();
                     window.location.href = '/';
                 });
-        } else if (response.data.code && response.data.code === 'A0215') {
+        } else if (response.data.code && response.data.code === 405) {
             ElMessage.error('token失效');
             // logOut;
             useLoginApi()
diff --git a/src/views/basic/equipment/components/equipmentDialog.vue b/src/views/basic/equipment/components/equipmentDialog.vue
new file mode 100644
index 0000000..d52c0ef
--- /dev/null
+++ b/src/views/basic/equipment/components/equipmentDialog.vue
@@ -0,0 +1,167 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="equipmentDialogState.title" v-model="equipmentDialogState.equipmentDialogVisible" width="600px">
+            <el-form ref="EquipmentFormRef" :rules="equipmentDialogState.equipmentFormRules" :model="equipmentDialogState.equipmentForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备编号" prop="deviceCode">
+                            <el-input v-model="equipmentDialogState.equipmentForm.deviceCode" placeholder="设备编号" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备名称" prop="deviceName">
+                            <el-input v-model="equipmentDialogState.equipmentForm.deviceName" placeholder="设备名称" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备功率" prop="devicePower">
+                            <el-input v-model="equipmentDialogState.equipmentForm.devicePower" placeholder="设备功率" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="计量单位" prop="deviceUnit">
+                            <el-select v-model="equipmentDialogState.equipmentForm.deviceUnit" placeholder="计量单位" clearable class="input-length">
+                                <el-option v-for="item in equipmentDialogState.deviceUnitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="是否特种设备">
+                            <el-select v-model="equipmentDialogState.equipmentForm.specialDevice" placeholder="是否特种设备" clearable class="input-length">
+                                <el-option v-for="item in equipmentDialogState.specialDeviceList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="安全防护">
+                            <el-input type="textarea" :rows="3" v-model="equipmentDialogState.equipmentForm.safeProtect" placeholder="安全防护" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="equipmentDialogState.equipmentDialogVisible = !equipmentDialogState.equipmentDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitEquipment" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {useMenuApi} from "/@/api/systemManage/menu";
+import {ElMessage} from "element-plus";
+import {equipmentApi} from "/@/api/basic/equipement";
+
+const EquipmentFormRef = ref()
+
+const equipmentDialogState = reactive<EquipmentDialogType>({
+    title: '',
+    equipmentDialogVisible: false,
+    equipmentForm: {
+        id: null,
+        deviceCode: '',
+        deviceName: '',
+        devicePower: '',
+        deviceUnit: null,
+        safeProtect: '',
+    },
+    equipmentFormRules: {
+        deviceCode: [{ required: true, message: '请填写设备编号', trigger: 'blur' }],
+        deviceName: [{ required: true, message: '请填写设备名称', trigger: 'blur' }],
+        devicePower: [{ required: true, message: '请填写设备功率', trigger: 'blur' }],
+        deviceUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    specialDeviceList: [],
+    deviceUnitList: [
+        {id:1, name: '台'},
+        {id:2, name: '个'},
+        {id:3, name: '件'}
+    ]
+})
+
+const showEquipmentDialog = (title: string, value: EquipmentType, specialDeviceList: Type []) => {
+
+    equipmentDialogState.equipmentDialogVisible = true;
+    equipmentDialogState.specialDeviceList = specialDeviceList;
+    setTimeout(() => {
+        EquipmentFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        equipmentDialogState.title = '新增';
+        equipmentDialogState.equipmentForm = {
+            id: null,
+            deviceCode: '',
+            deviceName: '',
+            devicePower: '',
+            deviceUnit: null,
+            safeProtect: '',
+        };
+    }else{
+        equipmentDialogState.title = '编辑'
+        equipmentDialogState.equipmentForm = {
+            id: value.id,
+            deviceCode: value.deviceCode,
+            deviceName: value.deviceName,
+            devicePower: value.devicePower,
+            deviceUnit: value.deviceUnit,
+            safeProtect: value.safeProtect,
+        };
+    }
+};
+
+const onSubmitEquipment = () => {
+    EquipmentFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(equipmentDialogState.title === '新增'){
+                let res = await equipmentApi().addEquipment(equipmentDialogState.equipmentForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    equipmentDialogState.equipmentDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await equipmentApi().modEquipment(equipmentDialogState.equipmentForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    equipmentDialogState.equipmentDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+}
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showEquipmentDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/basic/equipment/index.ts b/src/views/basic/equipment/index.ts
new file mode 100644
index 0000000..27483e9
--- /dev/null
+++ b/src/views/basic/equipment/index.ts
@@ -0,0 +1,46 @@
+declare interface EquipmentStateType {
+	equipmentData: Array<EquipmentType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		deviceCode: string,
+		deviceName: string,
+		devicePower: string,
+		specialDevice: number | null,
+	}
+	total: number
+	specialDeviceList: Array<Type>,
+	deviceUnitList: Array<Type>
+}
+
+declare interface EquipmentType {
+	id: number | null,
+	deviceCode: string,
+	deviceName: string,
+	devicePower: string,
+	deviceUnit: number | null,
+	safeProtect: string,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface EquipmentDialogType {
+	title: string,
+	equipmentDialogVisible: boolean,
+	equipmentForm: {
+		id: number | null,
+		deviceCode: string,
+		deviceName: string,
+		devicePower: string,
+		deviceUnit: number | null,
+		safeProtect: string,
+	},
+	equipmentFormRules: {
+
+	},
+	specialDeviceList: Array<Type>,
+	deviceUnitList: Array<Type>
+}
diff --git a/src/views/basic/equipment/index.vue b/src/views/basic/equipment/index.vue
new file mode 100644
index 0000000..0eb0e7a
--- /dev/null
+++ b/src/views/basic/equipment/index.vue
@@ -0,0 +1,319 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>设备编码:</span>
+                    <el-input v-model="equipmentState.searchQuery.deviceCode" clearable filterable class="input-box" placeholder="设备编码">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>设备名称:</span>
+                    <el-input v-model="equipmentState.searchQuery.deviceName" clearable filterable class="input-box" placeholder="设备名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>设备功率:</span>
+                    <el-input v-model="equipmentState.searchQuery.devicePower" clearable filterable class="input-box" placeholder="设备功率">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>是否特种设备:</span>
+                    <el-select v-model="equipmentState.searchQuery.specialDevice" clearable filterable class="input-box" placeholder="是否特种设备">
+                        <el-option v-for="item in equipmentState.specialDeviceList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getEquipmentData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openEquipmentDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="equipmentState.equipmentData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="deviceCode" label="设备编号"/>
+                        <el-table-column prop="deviceName" label="设备名称" />
+                        <el-table-column prop="devicePower" label="设备功率">
+                            <template #default="scope">
+                                <span>{{`${scope.row.devicePower}${equipmentState.deviceUnitList.find(item =>item.id === scope.row.deviceUnit)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="specialDevice" label="是否特殊装备" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${equipmentState.specialDeviceList.find(item =>item.id === scope.row.specialDevice)?.name}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="150">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="Edit" @click="openEquipmentDialog('修改', scope.row)">修改</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelEquipment(scope.row)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="equipmentState.searchQuery.pageIndex" background v-model:page-size="equipmentState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="equipmentState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <equipment-dialog ref="equipmentDialogRef" @refresh="getEquipmentData"></equipment-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {equipmentApi} from "/@/api/basic/equipement";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { Edit, View, Plus, Delete } from '@element-plus/icons-vue';
+
+const EquipmentDialog = defineAsyncComponent(() => import('./components/equipmentDialog.vue'));
+
+const equipmentDialogRef = ref();
+
+const equipmentState = reactive<EquipmentStateType>({
+    equipmentData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        deviceCode: '',
+        deviceName: '',
+        devicePower: '',
+        specialDevice: null,
+    },
+    total: 0,
+    specialDeviceList: [
+        {id: 1, name: '是'},
+        {id:2, name: '否'}
+    ],
+    deviceUnitList: [
+        {id:1, name: '台'},
+        {id:2, name: '个'},
+        {id:3, name: '件'}
+    ]
+})
+
+const getEquipmentData = async () => {
+    let res = await equipmentApi().getEquipmentByList(equipmentState.searchQuery);
+    if(res.data.code === 100){
+        equipmentState.equipmentData = res.data.data;
+        equipmentState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openEquipmentDialog = (title: string, value: EquipmentType) => {
+    equipmentDialogRef.value.showEquipmentDialog(title, value, equipmentState.specialDeviceList);
+};
+
+const onDelEquipment = (val: EquipmentType) => {
+    ElMessageBox.confirm(`此操作将永久删除该设备:“${val.deviceName}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await equipmentApi().deleteEquipmentById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getEquipmentData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    equipmentState.searchQuery.pageSize = val;
+    getEquipmentData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    equipmentState.searchQuery.pageIndex = val;
+    getEquipmentData();
+};
+
+const reset = () => {
+    equipmentState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        deviceCode: '',
+        deviceName: '',
+        devicePower: '',
+        specialDevice: null,
+    }
+};
+
+onMounted(() => {
+    getEquipmentData()
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+}
+</style>
diff --git a/src/views/basic/material/components/materialDialog.vue b/src/views/basic/material/components/materialDialog.vue
new file mode 100644
index 0000000..559b711
--- /dev/null
+++ b/src/views/basic/material/components/materialDialog.vue
@@ -0,0 +1,169 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="materialDialogState.title" v-model="materialDialogState.materialDialogVisible" width="600px">
+            <el-form ref="MaterialFormRef" :rules="materialDialogState.materialFormRules" :model="materialDialogState.materialForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验材料" prop="stuffName">
+                            <el-input v-model="materialDialogState.materialForm.stuffName" placeholder="实验材料" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="编号" prop="stuffCode">
+                            <el-input v-model="materialDialogState.materialForm.stuffCode" placeholder="编号" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="材料类型" prop="stuffType">
+                            <el-select v-model="materialDialogState.materialForm.stuffType" placeholder="材料类型" clearable class="input-length">
+                                <el-option v-for="item in materialDialogState.stuffTypeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="材料储存" prop="stuffStorage">
+                            <el-select v-model="materialDialogState.materialForm.stuffStorage" placeholder="材料储存" clearable class="input-length">
+                                <el-option v-for="item in materialDialogState.stuffStorageList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="计量单位" prop="stuffUnit">
+                            <el-select v-model="materialDialogState.materialForm.stuffUnit" placeholder="计量单位" clearable class="input-length">
+                                <el-option v-for="item in materialDialogState.stuffUnitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="materialDialogState.materialDialogVisible = !materialDialogState.materialDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitMaterial" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {useMenuApi} from "/@/api/systemManage/menu";
+import {ElMessage} from "element-plus";
+import {materialApi} from "/@/api/basic/material";
+
+const MaterialFormRef = ref()
+
+const materialDialogState = reactive<MaterialDialogType>({
+    title: '',
+    materialDialogVisible: false,
+    materialForm: {
+        stuffCode: '',
+        stuffName: '',
+        stuffStorage: null,
+        stuffType: null,
+        stuffUnit: null,
+    },
+    materialFormRules: {
+        stuffCode: [{ required: true, message: '请填写实验材料', trigger: 'blur' }],
+        stuffName: [{ required: true, message: '请填写编号', trigger: 'blur' }],
+        stuffStorage: [{ required: true, message: '请选择材料类型', trigger: 'change' }],
+        stuffType: [{ required: true, message: '请选择材料储存', trigger: 'change' }],
+        stuffUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    stuffTypeList: [
+        {id: 1, name: '化学试剂'},
+        {id:2, name: '基础材料'}
+    ],
+    stuffStorageList: [
+        {id:1, name: '智能试剂柜'},
+        {id:2, name: '普通储存柜'},
+    ],
+    stuffUnitList: [
+        {id:1, name: 'g'},
+        {id:2, name: 'kg'},
+        {id:3, name: 'ml'},
+        {id:4, name: 'l'},
+    ]
+})
+
+const showMaterialDialog = (title: string, value: MaterialType) => {
+    materialDialogState.materialDialogVisible = true;
+    setTimeout(() => {
+        MaterialFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        materialDialogState.title = '新增';
+        materialDialogState.materialForm = {
+            stuffName: '',
+            stuffCode: '',
+            stuffType: null,
+            stuffStorage: null,
+            stuffUnit: null,
+        };
+    }else{
+        materialDialogState.title = '编辑'
+        materialDialogState.materialForm = {
+            id: value.id,
+            stuffName: value.stuffName,
+            stuffCode: value.stuffCode,
+            stuffType: value.stuffType,
+            stuffStorage: value.stuffStorage,
+            stuffUnit: value.stuffUnit,
+        };
+    }
+};
+
+const onSubmitMaterial = () => {
+    MaterialFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(materialDialogState.title === '新增'){
+                let res = await materialApi().addMaterial(materialDialogState.materialForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    materialDialogState.materialDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await materialApi().modMaterial(materialDialogState.materialForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    materialDialogState.materialDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+}
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showMaterialDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/basic/material/index.ts b/src/views/basic/material/index.ts
new file mode 100644
index 0000000..8be5c9a
--- /dev/null
+++ b/src/views/basic/material/index.ts
@@ -0,0 +1,48 @@
+declare interface MaterialStateType{
+	materialData: Array<MaterialType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		stuffName: string,
+		stuffCode: string,
+		stuffType: number | null,
+	}
+	total: number,
+	stuffTypeList: Array<Type>,
+	stuffStorageList: Array<Type>,
+	stuffUnitList: Array<Type>
+}
+
+
+declare interface MaterialType {
+	id?: number | null,
+	stuffName: string,
+	stuffCode: string,
+	stuffType: number | null,
+	stuffStorage: number | null,
+	stuffUnit: number | null,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface MaterialDialogType {
+	title: string,
+	materialDialogVisible: boolean,
+	materialForm: {
+		id?: number | null,
+		stuffName: string,
+		stuffCode: string,
+		stuffType: number | null,
+		stuffStorage: number | null,
+		stuffUnit: number | null,
+	},
+	materialFormRules: {
+
+	},
+	stuffTypeList: Array<Type>,
+	stuffStorageList: Array<Type>,
+	stuffUnitList: Array<Type>
+}
diff --git a/src/views/basic/material/index.vue b/src/views/basic/material/index.vue
new file mode 100644
index 0000000..1ac05d1
--- /dev/null
+++ b/src/views/basic/material/index.vue
@@ -0,0 +1,322 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>实验材料:</span>
+                    <el-input v-model="materialState.searchQuery.stuffName" clearable filterable class="input-box" placeholder="实验材料">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>耗材编号:</span>
+                    <el-input v-model="materialState.searchQuery.stuffCode" clearable filterable class="input-box" placeholder="耗材编号">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>材料类型:</span>
+                    <el-select v-model="materialState.searchQuery.stuffType" clearable filterable class="input-box" placeholder="材料类型">
+                        <el-option v-for="item in materialState.stuffTypeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getMaterialData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openMaterialDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="materialState.materialData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="stuffName" label="实验材料"/>
+                        <el-table-column prop="stuffCode" label="编号" />
+                        <el-table-column prop="stuffType" label="材料类型">
+                            <template #default="scope">
+                                <span>{{`${materialState.stuffTypeList.find(item =>item.id === scope.row.stuffType)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="stuffStorage" label="材料储存" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${materialState.stuffStorageList.find(item =>item.id === scope.row.stuffStorage)?.name  || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="stuffUnit" label="计量单位" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${materialState.stuffUnitList.find(item =>item.id === scope.row.stuffUnit)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="150">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="Edit" @click="openMaterialDialog('修改', scope.row)">修改</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelMaterial(scope.row)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="materialState.searchQuery.pageIndex" background v-model:page-size="materialState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="materialState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <material-dialog ref="materialDialogRef" @refresh="getMaterialData"></material-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import { materialApi } from "/@/api/basic/material";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { Edit, View, Plus, Delete } from '@element-plus/icons-vue';
+
+const MaterialDialog = defineAsyncComponent(() => import('./components/materialDialog.vue'));
+
+const materialDialogRef = ref();
+
+const materialState = reactive<MaterialStateType>({
+    materialData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        stuffName: '',
+        stuffCode: '',
+        stuffType: null,
+    },
+    total: 0,
+    stuffTypeList: [
+        {id: 1, name: '化学试剂'},
+        {id:2, name: '基础材料'}
+    ],
+    stuffStorageList: [
+        {id:1, name: '智能试剂柜'},
+        {id:2, name: '普通储存柜'},
+    ],
+    stuffUnitList: [
+        {id:1, name: 'g'},
+        {id:2, name: 'kg'},
+        {id:3, name: 'ml'},
+        {id:4, name: 'l'},
+    ]
+})
+
+const getMaterialData = async () => {
+    let res = await materialApi().getMaterialByList(materialState.searchQuery);
+    if(res.data.code === 100){
+        materialState.materialData = res.data.data;
+        materialState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openMaterialDialog = (title: string, value: MaterialType) => {
+    materialDialogRef.value.showMaterialDialog(title, value);
+};
+
+const onDelMaterial = (val: MaterialType) => {
+    ElMessageBox.confirm(`此操作将永久删除该设备:“${val.stuffName}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await materialApi().deleteMaterialById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getMaterialData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    materialState.searchQuery.pageSize = val;
+    getMaterialData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    materialState.searchQuery.pageIndex = val;
+    getMaterialData();
+};
+
+const reset = () => {
+    materialState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        stuffName: '',
+        stuffCode: '',
+        stuffType: null,
+    }
+};
+
+onMounted(() => {
+    getMaterialData()
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+}
+</style>
diff --git a/src/views/basic/person/components/personDialog.vue b/src/views/basic/person/components/personDialog.vue
new file mode 100644
index 0000000..65e7a44
--- /dev/null
+++ b/src/views/basic/person/components/personDialog.vue
@@ -0,0 +1,197 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="personDialogState.title" v-model="personDialogState.personDialogVisible" width="600px">
+            <el-form ref="PersonFormRef" :rules="personDialogState.personFormRules" :model="personDialogState.personForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="人员名称" prop="personName">
+                            <el-input v-model="personDialogState.personForm.personName" placeholder="人员名称" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="人员年龄" prop="personAge">
+                            <el-input @input="onVerifiyNumberInteger($event, 'age')" v-model="personDialogState.personForm.personAge" placeholder="人员年龄" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="人员性别" prop="personGender">
+                            <el-select v-model="personDialogState.personForm.personGender" placeholder="人员性别" clearable class="input-length">
+                                <el-option v-for="item in personDialogState.personGenderList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="专业" prop="personMajor">
+                            <el-input v-model="personDialogState.personForm.personMajor" placeholder="专业" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="部门名称" prop="depName">
+                            <el-input v-model="personDialogState.personForm.depName" placeholder="部门名称" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="手机号" prop="phone">
+                            <el-input @input="onVerifiyNumberInteger($event, 'phone')" v-model="personDialogState.personForm.phone" placeholder="手机号" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="相关资质" prop="aptitude">
+                            <el-input v-model="personDialogState.personForm.aptitude" placeholder="相关资质" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="培训情况" prop="training">
+                            <el-select v-model="personDialogState.personForm.training" placeholder="培训情况" clearable class="input-length">
+                                <el-option v-for="item in personDialogState.trainingList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="personDialogState.personDialogVisible = !personDialogState.personDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitPerson" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {useMenuApi} from "/@/api/systemManage/menu";
+import {ElMessage} from "element-plus";
+import {personApi} from "/@/api/basic/person";
+import { verifiyNumberInteger } from '../../../../utils/toolsValidate'
+
+const PersonFormRef = ref()
+
+const personDialogState = reactive<PersonDialogType>({
+    title: '',
+    personDialogVisible: false,
+    personForm: {
+        personName: '',
+        personAge: null,
+        personGender: null,
+        personMajor: '',
+        depName: '',
+        phone: null,
+        aptitude: '',
+        training: null,
+    },
+    personFormRules: {
+        personName: [{ required: true, message: '请填写人员名称', trigger: 'blur' }],
+        personMajor: [{ required: true, message: '请填写专业', trigger: 'blur' }],
+        aptitude: [{ required: true, message: '请填写相关资质', trigger: 'blur' }],
+        personAge: [{ required: true, message: '请填写人员年龄', trigger: 'blur' }],
+        personGender: [{ required: true, message: '请选择人员性别', trigger: 'change' }],
+        phone: [{ required: true, message: '请填写手机号', trigger: 'blur' }],
+        training: [{ required: true, message: '请选择培训情况', trigger: 'change' }]
+    },
+    personGenderList: [
+        {id:1, name: '男'},
+        {id:2, name: '女'},
+    ],
+    trainingList: [
+        {id:1, name: '已完成当期安全培训'},
+        {id:2, name: '未完成当期安全培训'},
+    ]
+})
+
+const showPersonDialog = (title: string, value: PersonType,) => {
+
+    personDialogState.personDialogVisible = true;
+    setTimeout(() => {
+        PersonFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        personDialogState.title = '新增';
+        personDialogState.personForm = {
+            personName: '',
+            personAge: null,
+            personGender: null,
+            personMajor: '',
+            depName: '',
+            phone: null,
+            aptitude: '',
+            training: null,
+        };
+    }else{
+        personDialogState.title = '编辑'
+        personDialogState.personForm = {
+            id: value.id,
+            personName: value.personName,
+            personAge: value.personAge,
+            personGender: value.personGender,
+            personMajor: value.personMajor,
+            depName: value.depName,
+            phone: value.phone,
+            aptitude: value.aptitude,
+            training: value.training,
+        };
+    }
+};
+
+const onSubmitPerson = () => {
+    PersonFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(personDialogState.title === '新增'){
+                let res = await personApi().addPerson(personDialogState.personForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    personDialogState.personDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await personApi().modPerson(personDialogState.personForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    personDialogState.personDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const onVerifiyNumberInteger = (val: number, title: string) => {
+    if (title === 'age') {
+        personDialogState.personForm.personAge = Number(verifiyNumberInteger(val.toString())) === 0 ? null : Number(verifiyNumberInteger(val.toString()));
+    } else if (title === 'phone') {
+        personDialogState.personForm.phone = Number(verifiyNumberInteger(val.toString())) === 0 ? null : Number(verifiyNumberInteger(val.toString()));
+    }
+};
+
+const emit = defineEmits(['refresh']);
+
+defineExpose({
+    showPersonDialog
+});
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/basic/person/index.ts b/src/views/basic/person/index.ts
new file mode 100644
index 0000000..c1de227
--- /dev/null
+++ b/src/views/basic/person/index.ts
@@ -0,0 +1,52 @@
+declare interface PersonStateType extends PublicType{
+	personData: Array<PersonType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		personName: string,
+		training: number | null,
+		depName: string,
+	}
+	total: number
+}
+
+declare interface PersonType {
+	id?: number | null,
+	personName: string,
+	personAge: number | null,
+	personGender: number | null,
+	personMajor: string,
+	depName: string,
+	phone: number | null,
+	aptitude: string,
+	training: number | null,
+}
+
+declare interface PublicType {
+	personGenderList: Array<Type>,
+	trainingList: Array<Type>
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface PersonDialogType extends PublicType{
+	title: string,
+	personDialogVisible: boolean,
+	personForm: {
+		id?: number | null,
+		personName: string,
+		personAge: number | null,
+		personGender: number | null,
+		personMajor: string,
+		depName: string,
+		phone: number | null,
+		aptitude: string,
+		training: number | null,
+	},
+	personFormRules: {
+
+	},
+}
diff --git a/src/views/basic/person/index.vue b/src/views/basic/person/index.vue
new file mode 100644
index 0000000..0492573
--- /dev/null
+++ b/src/views/basic/person/index.vue
@@ -0,0 +1,315 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>人员名称:</span>
+                    <el-input v-model="personState.searchQuery.personName" clearable filterable class="input-box" placeholder="人员名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>部门名称:</span>
+                    <el-input v-model="personState.searchQuery.depName" clearable filterable class="input-box" placeholder="部门名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>培训情况:</span>
+                    <el-select v-model="personState.searchQuery.training"  clearable filterable class="input-box" placeholder="培训情况">
+                        <el-option v-for="item in personState.trainingList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getPersonData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openPersonDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="personState.personData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="personName" label="人员名称"/>
+                        <el-table-column prop="personAge" label="年龄" />
+                        <el-table-column prop="personGender" label="性别">
+                            <template #default="scope">
+                                <span>{{`${personState.personGenderList.find(item =>item.id === scope.row.personGender)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="personMajor" label="专业" show-overflow-tooltip/>
+                        <el-table-column prop="depName" label="部门名称" show-overflow-tooltip/>
+                        <el-table-column prop="phone" label="手机号" show-overflow-tooltip/>
+                        <el-table-column prop="aptitude" label="相关资质" show-overflow-tooltip/>
+                        <el-table-column prop="training" label="培训情况" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${personState.trainingList.find(item =>item.id === scope.row.training)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="150">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="Edit" @click="openPersonDialog('修改', scope.row)">修改</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelPerson(scope.row)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="personState.searchQuery.pageIndex" background v-model:page-size="personState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="personState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <person-dialog ref="personDialogRef" @refresh="getPersonData"></person-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {personApi} from "/@/api/basic/person";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { Edit, View, Plus, Delete } from '@element-plus/icons-vue';
+
+const PersonDialog = defineAsyncComponent(() => import('./components/personDialog.vue'));
+
+const personDialogRef = ref();
+
+const personState = reactive<PersonStateType>({
+    personData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        personName: '',
+        training: null,
+        depName: '',
+    },
+    total: 0,
+    personGenderList: [
+        {id:1, name: '男'},
+        {id:2, name: '女'},
+    ],
+    trainingList: [
+        {id: 1, name: '已完成当期安全培训'},
+        {id:2, name: '未完成当期安全培训'}
+    ],
+})
+
+const getPersonData = async () => {
+    let res = await personApi().getPersonByList(personState.searchQuery);
+    if(res.data.code === 100){
+        personState.personData = res.data.data;
+        personState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openPersonDialog = (title: string, value: PersonType) => {
+    personDialogRef.value.showPersonDialog(title, value);
+};
+
+const onDelPerson = (val: PersonType) => {
+    ElMessageBox.confirm(`此操作将永久删除该设备:“${val.personName}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await personApi().deletePersonById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getPersonData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    personState.searchQuery.pageSize = val;
+    getPersonData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    personState.searchQuery.pageIndex = val;
+    getPersonData();
+};
+
+const reset = () => {
+    personState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        personName: '',
+        training: null,
+        depName: '',
+    }
+};
+
+onMounted(() => {
+    getPersonData()
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+}
+</style>
diff --git a/src/views/basic/room/components/roomDialog.vue b/src/views/basic/room/components/roomDialog.vue
new file mode 100644
index 0000000..51ccec4
--- /dev/null
+++ b/src/views/basic/room/components/roomDialog.vue
@@ -0,0 +1,139 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="roomDialogState.title" v-model="roomDialogState.roomDialogVisible" width="600px">
+            <el-form ref="roomFormRef" :rules="roomDialogState.roomFormRules" :model="roomDialogState.roomForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="场所名称" prop="siteName">
+                            <el-input v-model="roomDialogState.roomForm.siteName" placeholder="场所名称" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验室所在楼" prop="floor">
+                            <el-input v-model="roomDialogState.roomForm.floor" placeholder="实验室所在楼" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验室所在房间" prop="room">
+                            <el-input v-model="roomDialogState.roomForm.room" placeholder="实验室所在房间" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="roomDialogState.roomDialogVisible = !roomDialogState.roomDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitRoom" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {roomApi} from "/@/api/basic/room";
+
+const roomFormRef = ref()
+
+const roomDialogState = reactive<RoomDialogType>({
+    title: '',
+    roomDialogVisible: false,
+    roomForm: {
+        siteName: '',
+        floor: '',
+        room: '',
+    },
+    roomFormRules: {
+        deviceCode: [{ required: true, message: '请填写设备编号', trigger: 'blur' }],
+        deviceName: [{ required: true, message: '请填写设备名称', trigger: 'blur' }],
+        devicePower: [{ required: true, message: '请填写设备功率', trigger: 'blur' }],
+        deviceUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    specialDeviceList: [],
+    deviceUnitList: [
+        {id:1, name: '台'},
+        {id:2, name: '个'},
+        {id:3, name: '件'}
+    ]
+})
+
+const showroomDialog = (title: string, value: RoomType, specialDeviceList: Type []) => {
+
+    roomDialogState.roomDialogVisible = true;
+    roomDialogState.specialDeviceList = specialDeviceList;
+    setTimeout(() => {
+        roomFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        roomDialogState.title = '新增';
+        roomDialogState.roomForm = {
+            siteName: '',
+            floor: '',
+            room: '',
+        };
+    }else{
+        roomDialogState.title = '编辑'
+        roomDialogState.roomForm = {
+            id: value.id,
+            siteName: value.siteName,
+            floor: value.floor,
+            room: value.room,
+        };
+    }
+};
+
+const onSubmitRoom = () => {
+    roomFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(roomDialogState.title === '新增'){
+                let res = await roomApi().addRoom(roomDialogState.roomForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    roomDialogState.roomDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await roomApi().modRoom(roomDialogState.roomForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    roomDialogState.roomDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+}
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showroomDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/basic/room/index.ts b/src/views/basic/room/index.ts
new file mode 100644
index 0000000..677de72
--- /dev/null
+++ b/src/views/basic/room/index.ts
@@ -0,0 +1,41 @@
+declare interface RoomStateType {
+	roomData: Array<RoomType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		siteName: string,
+		floor: string,
+		room: string,
+	}
+	total: number
+	specialDeviceList: Array<Type>,
+	deviceUnitList: Array<Type>
+}
+
+declare interface RoomType {
+	id?: number | null,
+	siteName: string,
+	floor: string,
+	room: string,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface RoomDialogType {
+	title: string,
+	roomDialogVisible: boolean,
+	roomForm: {
+		id?: number | null,
+		siteName: string,
+		floor: string,
+		room: string,
+	},
+	roomFormRules: {
+
+	},
+	specialDeviceList: Array<Type>,
+	deviceUnitList: Array<Type>
+}
diff --git a/src/views/basic/room/index.vue b/src/views/basic/room/index.vue
new file mode 100644
index 0000000..0af9501
--- /dev/null
+++ b/src/views/basic/room/index.vue
@@ -0,0 +1,302 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>场所名称:</span>
+                    <el-input v-model="roomState.searchQuery.siteName" clearable filterable class="input-box" placeholder="场所名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>楼:</span>
+                    <el-input v-model="roomState.searchQuery.floor" clearable filterable class="input-box" placeholder="楼">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>房间号:</span>
+                    <el-input v-model="roomState.searchQuery.room" clearable filterable class="input-box" placeholder="房间号">
+                    </el-input>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getRoomData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openRoomDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="roomState.roomData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="siteName" label="场所名称"/>
+                        <el-table-column prop="floor" label="实验室所在楼" />
+                        <el-table-column prop="room" label="实验室所在房间"/>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="150">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="Edit" @click="openRoomDialog('修改', scope.row)">修改</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelRoom(scope.row)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="roomState.searchQuery.pageIndex" background v-model:page-size="roomState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="roomState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <room-dialog ref="roomDialogRef" @refresh="getRoomData"></room-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {roomApi} from "/@/api/basic/room";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { Edit, View, Plus, Delete } from '@element-plus/icons-vue';
+
+const RoomDialog = defineAsyncComponent(() => import('./components/roomDialog.vue'));
+
+const roomDialogRef = ref();
+
+const roomState = reactive<RoomStateType>({
+    roomData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        siteName: '',
+        floor: '',
+        room: '',
+    },
+    total: 0,
+    specialDeviceList: [
+        {id: 1, name: '是'},
+        {id:2, name: '否'}
+    ],
+    deviceUnitList: [
+        {id:1, name: '台'},
+        {id:2, name: '个'},
+        {id:3, name: '件'}
+    ]
+})
+
+const getRoomData = async () => {
+    let res = await roomApi().getRoomByList(roomState.searchQuery);
+    if(res.data.code === 100){
+        roomState.roomData = res.data.data;
+        roomState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openRoomDialog = (title: string, value: RoomType) => {
+    roomDialogRef.value.showroomDialog(title, value, roomState.specialDeviceList);
+};
+
+const onDelRoom = (val: RoomType) => {
+    ElMessageBox.confirm(`此操作将永久删除该设备:“${val.siteName}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await roomApi().deleteRoomById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getRoomData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    roomState.searchQuery.pageSize = val;
+    getRoomData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    roomState.searchQuery.pageIndex = val;
+    getRoomData();
+};
+
+const reset = () => {
+    roomState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        siteName: '',
+        floor: '',
+        room: '',
+    }
+};
+
+onMounted(() => {
+    getRoomData()
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+}
+</style>
diff --git a/src/views/basic/unit/components/unitDialog.vue b/src/views/basic/unit/components/unitDialog.vue
new file mode 100644
index 0000000..00341fe
--- /dev/null
+++ b/src/views/basic/unit/components/unitDialog.vue
@@ -0,0 +1,167 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="unitDialogState.title" v-model="unitDialogState.unitDialogVisible" width="600px">
+            <el-form ref="UnitFormRef" :rules="unitDialogState.unitFormRules" :model="unitDialogState.unitForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备编号" prop="deviceCode">
+                            <el-input v-model="unitDialogState.unitForm.deviceCode" placeholder="设备编号" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备名称" prop="deviceName">
+                            <el-input v-model="unitDialogState.unitForm.deviceName" placeholder="设备名称" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="设备功率" prop="devicePower">
+                            <el-input v-model="unitDialogState.unitForm.devicePower" placeholder="设备功率" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="计量单位" prop="deviceUnit">
+                            <el-select v-model="unitDialogState.unitForm.deviceUnit" placeholder="计量单位" clearable class="input-length">
+                                <el-option v-for="item in unitDialogState.deviceUnitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="是否特种设备">
+                            <el-select v-model="unitDialogState.unitForm.specialDevice" placeholder="是否特种设备" clearable class="input-length">
+                                <el-option v-for="item in unitDialogState.specialDeviceList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="安全防护">
+                            <el-input type="textarea" :rows="3" v-model="unitDialogState.unitForm.safeProtect" placeholder="安全防护" clearable class="input-length"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="unitDialogState.unitDialogVisible = !unitDialogState.unitDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitUnit" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {unitApi} from "/@/api/basic/unit";
+
+const UnitFormRef = ref()
+
+const unitDialogState = reactive<UnitDialogType>({
+    title: '',
+    unitDialogVisible: false,
+    unitForm: {
+        id: null,
+        deviceCode: '',
+        deviceName: '',
+        devicePower: '',
+        deviceUnit: null,
+        safeProtect: '',
+    },
+    unitFormRules: {
+        deviceCode: [{ required: true, message: '请填写设备编号', trigger: 'blur' }],
+        deviceName: [{ required: true, message: '请填写设备名称', trigger: 'blur' }],
+        devicePower: [{ required: true, message: '请填写设备功率', trigger: 'blur' }],
+        deviceUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    riskSourceTypeList: [
+        {id: 1, name: '区域、实验场所'},
+        {id:2, name: '设施设备'},
+        {id:3, name: '固定工艺节点'},
+    ],
+    evaluateStatusList: [
+        {id: 1, name: '未评价'},
+        {id:2, name: '已评价'},
+    ]
+})
+
+const showUnitDialog = (title: string, value: UnitType) => {
+    unitDialogState.unitDialogVisible = true;
+    setTimeout(() => {
+        UnitFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        unitDialogState.title = '新增';
+        unitDialogState.unitForm = {
+            id: null,
+            deviceCode: '',
+            deviceName: '',
+            devicePower: '',
+            deviceUnit: null,
+            safeProtect: '',
+        };
+    }else{
+        unitDialogState.title = '编辑'
+        unitDialogState.unitForm = {
+            id: value.id,
+            deviceCode: value.deviceCode,
+            deviceName: value.deviceName,
+            devicePower: value.devicePower,
+            deviceUnit: value.deviceUnit,
+            safeProtect: value.safeProtect,
+        };
+    }
+};
+
+const onSubmitUnit = () => {
+    UnitFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(unitDialogState.title === '新增'){
+                let res = await unitApi().addUnit(unitDialogState.unitForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    unitDialogState.unitDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await unitApi().modUnit(unitDialogState.unitForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    unitDialogState.unitDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+}
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showUnitDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/basic/unit/index.ts b/src/views/basic/unit/index.ts
new file mode 100644
index 0000000..ae9436d
--- /dev/null
+++ b/src/views/basic/unit/index.ts
@@ -0,0 +1,45 @@
+declare interface UnitStateType {
+	unitData: Array<UnitType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		riskCode: string,
+		riskName: string,
+		riskSourceType: number | null,
+	}
+	total: number
+	riskSourceTypeList: Array<Type>,
+	evaluateStatusList: Array<Type>,
+}
+
+declare interface UnitType {
+	id: number | null,
+	deviceCode: string,
+	deviceName: string,
+	devicePower: string,
+	deviceUnit: number | null,
+	safeProtect: string,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface UnitDialogType {
+	title: string,
+	unitDialogVisible: boolean,
+	unitForm: {
+		id: number | null,
+		deviceCode: string,
+		deviceName: string,
+		devicePower: string,
+		deviceUnit: number | null,
+		safeProtect: string,
+	},
+	unitFormRules: {
+
+	},
+	riskSourceTypeList: Array<Type>,
+	evaluateStatusList: Array<Type>,
+}
diff --git a/src/views/basic/unit/index.vue b/src/views/basic/unit/index.vue
new file mode 100644
index 0000000..c7788af
--- /dev/null
+++ b/src/views/basic/unit/index.vue
@@ -0,0 +1,313 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>风险单元编号:</span>
+                    <el-input v-model="unitState.searchQuery.riskCode" clearable filterable class="input-box" placeholder="风险单元编号">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>风险单元名称:</span>
+                    <el-input v-model="unitState.searchQuery.riskName" clearable filterable class="input-box" placeholder="风险单元名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>风险源风险类型:</span>
+                    <el-select v-model="unitState.searchQuery.riskSourceType" clearable filterable class="input-box" placeholder="风险源风险类型">
+                        <el-option v-for="item in unitState.riskSourceTypeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getUnitData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openunitDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="unitState.unitData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="riskCode" label="风险单元编号" show-overflow-tooltip/>
+                        <el-table-column prop="riskName" label="风险单元名称" show-overflow-tooltip/>
+                        <el-table-column prop="riskSourceType" label="风险源风险类型" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${unitState.riskSourceTypeList.find(item =>item.id === scope.row.riskSourceType)?.name || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="evaluateStatus" label="评价状态" show-overflow-tooltip>
+                            <template #default="scope">
+                                <span>{{`${unitState.evaluateStatusList.find(item =>item.id === scope.row.evaluateStatus)?.name  || ''}`}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="liabilityUserName" label="责任人" show-overflow-tooltip/>
+                        <el-table-column prop="liabilityDep" label="责任部门" show-overflow-tooltip/>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="150">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="Edit" @click="openUnitDialog('修改', scope.row)">修改</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelUnit(scope.row)">删除</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="unitState.searchQuery.pageIndex" background v-model:page-size="unitState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="unitState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <unit-dialog ref="unitDialogRef" @refresh="getUnitData"></unit-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {unitApi} from "/@/api/basic/unit";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { Edit, View, Plus, Delete } from '@element-plus/icons-vue';
+
+const UnitDialog = defineAsyncComponent(() => import('./components/unitDialog.vue'));
+
+const unitDialogRef = ref();
+
+const unitState = reactive<UnitStateType>({
+    unitData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        riskCode: '',
+        riskName: '',
+        riskSourceType: null,
+    },
+    total: 0,
+    riskSourceTypeList: [
+        {id: 1, name: '区域、实验场所'},
+        {id:2, name: '设施设备'},
+        {id:3, name: '固定工艺节点'},
+    ],
+    evaluateStatusList: [
+        {id: 1, name: '未评价'},
+        {id:2, name: '已评价'},
+    ]
+})
+
+const getUnitData = async () => {
+    let res = await unitApi().getUnitByList(unitState.searchQuery);
+    if(res.data.code === 100){
+        unitState.unitData = res.data.data;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openUnitDialog = (title: string, value: UnitType) => {
+    unitDialogRef.value.showUnitDialog(title, value);
+};
+
+const onDelUnit = (val: UnitType) => {
+    ElMessageBox.confirm(`此操作将永久删除该设备:“${val.deviceName}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await unitApi().deleteUnitById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getUnitData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    unitState.searchQuery.pageSize = val;
+    getUnitData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    unitState.searchQuery.pageIndex = val;
+    getUnitData();
+};
+
+const reset = () => {
+    unitState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        riskCode: '',
+        riskName: '',
+        riskSourceType: null,
+    }
+};
+
+onMounted(() => {
+    getUnitData()
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+}
+</style>
diff --git a/src/views/experiment/developing/components/applyDialog.vue b/src/views/experiment/developing/components/applyDialog.vue
new file mode 100644
index 0000000..b65f862
--- /dev/null
+++ b/src/views/experiment/developing/components/applyDialog.vue
@@ -0,0 +1,98 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="applyStartDialogState.title" v-model="applyStartDialogState.applyStartDialogVisible" width="600px">
+            <el-form ref="applyStartFormRef" :rules="applyStartDialogState.applyStartFormRules" :model="applyStartDialogState.applyStartForm" size="default" label-width="160px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验开始时间" prop="startTime">
+                            <el-date-picker type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="applyStartDialogState.applyStartForm.startTime" class="input-length"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="是否是安全信息化系统 " prop="sisStatus">
+                            <el-radio-group v-model="applyStartDialogState.applyStartForm.sisStatus">
+                                <el-radio :label="1">是</el-radio>
+                                <el-radio :label="2">否</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="安全信息化系统" prop="safeInformationSystem">
+                            <el-input v-model="applyStartDialogState.applyStartForm.safeInformationSystem" placeholder="材料类型" class="input-length">
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="applyStartDialogState.applyStartDialogVisible = !applyStartDialogState.applyStartDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitApplyStart" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+
+const applyStartFormRef = ref()
+
+const applyStartDialogState = reactive<ApplyStartDialogType>({
+    title: '',
+    applyStartDialogVisible: false,
+    applyStartForm: {
+        id: null,
+        sisStatus: null,
+        safeInformationSystem: '',
+        startTime: '',
+    },
+    applyStartFormRules: {
+
+    },
+})
+
+const showApplyStartDialog = (value: ProjectType) => {
+    applyStartDialogState.applyStartDialogVisible = true;
+    applyStartDialogState.applyStartForm.id = <number>value.id
+};
+
+const onSubmitApplyStart = () => {
+    applyStartFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            let res = await projectApi().applyProject([applyStartDialogState.applyStartForm]);
+            if(res.data.code === 100){
+                emit('refresh')
+                applyStartDialogState.applyStartDialogVisible = false;
+                ElMessage({
+                    type: 'success',
+                    message: '申请开展成功'
+                })
+            }else{
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg,
+                });
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showApplyStartDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/experiment/developing/components/applyStart.vue b/src/views/experiment/developing/components/applyStart.vue
new file mode 100644
index 0000000..8906f6e
--- /dev/null
+++ b/src/views/experiment/developing/components/applyStart.vue
@@ -0,0 +1,101 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="applyStartDialogState.title" v-model="applyStartDialogState.applyStartDialogVisible" width="600px">
+            <el-form ref="applyStartFormRef" :rules="applyStartDialogState.applyStartFormRules" :model="applyStartDialogState.applyStartForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验材料" prop="stuffName">
+                            <el-date-picker type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="applyStartDialogState.applyStartForm.startTime" class="input-length"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="编号" prop="sisStatus">
+                            <el-radio-group v-model="applyStartDialogState.applyStartForm.sisStatus">
+                                <el-radio :label="1">是</el-radio>
+                                <el-radio :label="2">否</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="材料类型" prop="stuffType">
+                            <el-select v-model="applyStartDialogState.applyStartForm.safeInformationSystem" placeholder="材料类型" class="input-length">
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="applyStartDialogState.applyStartDialogVisible = !applyStartDialogState.applyStartDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitApplyStart" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+
+const applyStartFormRef = ref()
+
+const applyStartDialogState = reactive<ApplyStartDialogType>({
+    title: '',
+    applyStartDialogVisible: false,
+    applyStartForm: {
+        id: null,
+        sisStatus: null,
+        safeInformationSystem: '',
+        startTime: '',
+    },
+    applyStartFormRules: {
+        stuffCode: [{ required: true, message: '请填写实验材料', trigger: 'blur' }],
+        stuffName: [{ required: true, message: '请填写编号', trigger: 'blur' }],
+        stuffStorage: [{ required: true, message: '请选择材料类型', trigger: 'change' }],
+        stuffType: [{ required: true, message: '请选择材料储存', trigger: 'change' }],
+        stuffUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+})
+
+const showApplyStartDialog = (value: ProjectType) => {
+    applyStartDialogState.applyStartDialogVisible = true;
+};
+
+const onSubmitApplyStart = () => {
+    applyStartFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            let res = await projectApi().applyProject(applyStartDialogState.applyStartForm);
+            if(res.data.code === 100){
+                emit('refresh')
+                applyStartDialogState.applyStartDialogVisible = false;
+                ElMessage({
+                    type: 'success',
+                    message: '申请开展成功'
+                })
+            }else{
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg,
+                });
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showApplyStartDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/experiment/developing/components/projectDialog.vue b/src/views/experiment/developing/components/projectDialog.vue
new file mode 100644
index 0000000..b621833
--- /dev/null
+++ b/src/views/experiment/developing/components/projectDialog.vue
@@ -0,0 +1,533 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="projectDialogState.title" v-model="projectDialogState.projectDialogVisible" width="70%">
+            <el-form ref="ProjectFormRef" :rules="projectDialogState.projectFormRules" :model="projectDialogState.projectForm" size="default" label-width="0">
+                <table class="report-table">
+                    <th class="m-color b-font" style="text-align: center">***研究所/***大学<br />科学研究实验项目安全风险基础信息录入表(已开展B)</th>
+                    <tr>
+                        <td class="w-25 m-color">实验名称</td>
+                        <td class="w-75 m-color">
+                            <el-form-item prop="experimentName">
+                                <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentName" placeholder="请输入实验名称" />
+                            </el-form-item>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验类型</td>
+                        <td class="w-75 m-color">
+
+                            <el-radio-group style="text-align: center" :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentType">
+                                <el-radio :label="1">化学类</el-radio>
+                                <el-radio :label="2">生物类</el-radio>
+                                <el-radio :label="3">辐射类</el-radio>
+                                <el-radio :label="4">机电类</el-radio>
+                                <el-radio :label="5">特种设备类</el-radio>
+                                <el-radio :label="6">其他类</el-radio>
+                            </el-radio-group>
+
+
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">部门</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.dep" />
+                        </td>
+                        <td class="w-25 m-color">负责人</td>
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.liabilityUserId" clearable filterable>
+                                <el-option
+                                    v-for="item in projectDialogState.allPersonList"
+                                    :key="item.id"
+                                    :value="item.id"
+                                    :label="item.personName"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">立项时间</td>
+                        <td class="w-25 m-color">
+                            <el-date-picker :disabled="projectDialogState.disabled"  type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="projectDialogState.projectForm.expectStartTime" />
+                        </td>
+<!--                        <td class="w-16 m-color">开展时间</td>-->
+<!--                        <td class="w-16 m-color">-->
+<!--                            <el-date-picker v-model="projectDialogState.projectForm.startTime" />-->
+<!--                        </td>-->
+                        <td class="w-25 m-color">安全负责人</td>
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.safeLiabilityUserId" clearable filterable>
+                                <el-option
+                                v-for="item in projectDialogState.allPersonList"
+                                :key="item.id"
+                                :value="item.id"
+                                :label="item.personName"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="text-align: center">实验场所</tr>
+<!--                    <tr>-->
+<!--                        <td class="w-25 m-color">楼宇</td>-->
+<!--                        <td class="w-75 m-color">-->
+<!--                            <el-input v-model="projectDialogState.projectForm.building" placeholder="请输入楼栋名称" />-->
+<!--                        </td>-->
+<!--                    </tr>-->
+                    <tr>
+                        <td class="w-25 m-color">房间号</td>
+<!--                        <td class="w-75 m-color">-->
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.siteId" placeholder="请输入房间号" >
+                                <el-option
+                                    v-for="item in projectDialogState.allRoomList"
+                                    :key="item.id"
+                                    :value="item.id"
+                                    :label="item.room"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <select-material ref="selectMaterialRef" v-model:disabled="projectDialogState.disabled" v-model:data="projectDialogState.projectForm.stuffList"></select-material>
+                    <select-equipment ref="selectEquipmentRef" v-model:disabled="projectDialogState.disabled" v-model:data="projectDialogState.projectForm.deviceList"></select-equipment>
+                    <tr>
+                        <td class="w-25 m-color">实验步骤</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentStep" placeholder="请输入实验步骤" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">操作方法</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentMethod" placeholder="请输入操作方法" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">工艺过程</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.process" placeholder="请输入工艺过程" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">特殊/关键过程</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.keyProcess" placeholder="请输入特殊/关键过程" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">是否存在过夜、老化实验</td>
+                        <td class="w-25 m-color">
+                            <el-radio-group :disabled="projectDialogState.disabled"  v-model="projectDialogState.projectForm.timeout">
+                                <el-radio :label="1">存在</el-radio>
+                                <el-radio :label="2">不存在</el-radio>
+                            </el-radio-group>
+                        </td>
+                        <td class="w-25 m-color">过夜、老化保障措施</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.timeoutManager" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">是否在封闭条件下</td>
+                        <td class="w-25 m-color">
+                            <el-radio-group :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.closed">
+                                <el-radio :label="1">存在</el-radio>
+                                <el-radio :label="2">不存在</el-radio>
+                            </el-radio-group>
+                        </td>
+                        <td class="w-25 m-color">封闭条件保障措施</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.unclosedManager" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防爆措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.explosionProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防火措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.fireProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防中毒措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.poisonProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+
+
+                    <select-danger ref="selectDangerRef" v-model:data="projectDialogState.projectForm.hazardousWasteList" v-model:disabled="projectDialogState.disabled"></select-danger>
+                    <select-person ref="selectPersonRef" v-model:data="projectDialogState.projectForm.persons" v-model:disabled="projectDialogState.disabled"></select-person>
+
+
+<!--                    <tr>-->
+<!--                        <td class="w-25 m-color">安全信息化系统使用情况</td>-->
+<!--                        <td class="w-25 m-color">-->
+<!--                            <el-radio-group v-model="projectDialogState.projectForm.useIT">-->
+<!--                                <el-radio value="1">是</el-radio>-->
+<!--                                <el-radio value="2">否</el-radio>-->
+<!--                            </el-radio-group>-->
+<!--                        </td>-->
+<!--                        <td class="w-25 m-color">系统名称</td>-->
+<!--                        <td class="w-25 m-color">-->
+<!--                            <el-input v-model="projectDialogState.projectForm.systemName" placeholder="请输入" />-->
+<!--                        </td>-->
+<!--                    </tr>-->
+                    <tr>
+                        <td class="w-25 m-color">安全管理制度</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.safeManagerMethod" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="text-align: center">应急预案/应急演练</tr>
+                    <tr>
+                        <td class="w-25 m-color">有无预案</td>
+                        <td class="w-25 m-color">
+                            预案名称
+                        </td>
+                        <td class="w-25 m-color">是否演练</td>
+                        <td class="w-25 m-color">
+                            演练情况
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyPlan" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyPlanName" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyDrillStatus" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyDrill" />
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="  text-align: center">实验人员</tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所是否需要分区隔断</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.partitionCondition" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">其它基础信息(详细描述)</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.note" placeholder="请输入" />
+                        </td>
+                    </tr>
+                </table>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer" style="padding-top:10px;text-align: center !important;">
+					<el-button @click="projectDialogState.projectDialogVisible = !projectDialogState.projectDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" v-if="!projectDialogState.disabled" @click="onSubmitProject" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, nextTick, onMounted, reactive, ref} from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+import {personApi} from "/@/api/basic/person";
+
+const SelectEquipment = defineAsyncComponent(() => import('./selectEquipment.vue'))
+const SelectMaterial = defineAsyncComponent(() => import('./selectMaterial.vue'))
+const SelectDanger = defineAsyncComponent(() => import('./selectDanger.vue'))
+const SelectPerson = defineAsyncComponent(() => import('./selectPerson.vue'))
+const ProjectFormRef = ref()
+const selectPersonRef = ref()
+const selectEquipmentRef = ref()
+const selectMaterialRef = ref()
+const selectDangerRef = ref()
+
+const projectDialogState = reactive<ProjectDialogType>({
+    title: '',
+    disabled: false,
+    projectDialogVisible: false,
+    projectForm:   {
+        id: null,
+        experimentName: "",
+        experimentType: null,
+        liabilityUserId: null,
+        safeLiabilityUserId: null,
+        dep: "",
+        siteId: null,
+        experimentStep: "",
+        experimentMethod: "",
+        process: "",
+        keyProcess: "",
+        timeout: null,
+        timeoutManager: "",
+        closed: null,
+        unclosedManager: "",
+        explosionProof: "",
+        fireProof: "",
+        poisonProof: "",
+        hazardousWaste: null,
+        safeManagerMethod: "",
+        emergencyPlan: "",
+        emergencyDrill: "",
+        emergencyPlanName: "",
+        emergencyDrillStatus: "",
+        partitionCondition: "",
+        note: "",
+        expectStartTime: null,
+        persons: [
+        ],
+        deviceList: [
+        ],
+        stuffList: [
+        ],
+        hazardousWasteList: [
+        ]
+    },
+    projectFormRules: {
+        experimentName: [{ required: true, message: '请填写设备编号', trigger: 'blur' }],
+        deviceName: [{ required: true, message: '请填写设备名称', trigger: 'blur' }],
+        devicePower: [{ required: true, message: '请填写设备功率', trigger: 'blur' }],
+        deviceUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    allPersonList: [],
+    allRoomList: [],
+})
+
+const showProjectDialog = (title: string, value: ProjectType, allRoomList: RoomType []) => {
+    projectDialogState.projectDialogVisible = true;
+    projectDialogState.allRoomList = allRoomList
+    setTimeout(() => {
+        ProjectFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        projectDialogState.disabled = false
+        projectDialogState.title = '新增';
+        projectDialogState.projectForm = {
+            id: null,
+            experimentName: "",
+            experimentType: null,
+            liabilityUserId: null,
+            safeLiabilityUserId: null,
+            dep: "",
+            siteId: null,
+            experimentStep: "",
+            experimentMethod: "",
+            process: "",
+            keyProcess: "",
+            timeout: null,
+            timeoutManager: "",
+            closed: null,
+            unclosedManager: "",
+            explosionProof: "",
+            fireProof: "",
+            poisonProof: "",
+            hazardousWaste: 1,
+            safeManagerMethod: "",
+            emergencyPlan: "",
+            emergencyDrill: "",
+            emergencyPlanName: "",
+            emergencyDrillStatus: "",
+            partitionCondition: "",
+            note: "",
+            expectStartTime: null,
+            persons: [
+            ],
+            deviceList: [
+            ],
+            stuffList: [
+            ],
+            hazardousWasteList: [
+            ]
+        };
+    }else{
+        projectDialogState.title = '查看';
+        projectDialogState.disabled = true
+        for(let i in projectDialogState.projectForm) {
+            if(isValidKey(i, projectDialogState.projectForm)) {
+                projectDialogState.projectForm[i] = value[i];
+            }
+        }
+    }
+};
+
+const isValidKey = (key: string | number | symbol, object:object): key is keyof typeof object =>{
+    return key in object;
+};
+
+const onSubmitProject = () => {
+    ProjectFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(projectDialogState.title === '新增'){
+                projectDialogState.projectForm.persons = selectPersonRef.value.dataList
+                projectDialogState.projectForm.hazardousWasteList = selectDangerRef.value.dataList
+                projectDialogState.projectForm.stuffList = selectMaterialRef.value.dataList
+                projectDialogState.projectForm.deviceList = selectEquipmentRef.value.dataList
+                let res = await projectApi().addProject(projectDialogState.projectForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    projectDialogState.projectDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await projectApi().modProject(projectDialogState.projectForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    projectDialogState.projectDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const getAllPersonList = async () => {
+    let res = await personApi().getAllPerson();
+    if(res.data.code === 100){
+        projectDialogState.allPersonList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const emit = defineEmits(['refresh']);
+
+defineExpose({
+    showProjectDialog,
+});
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+th {
+    padding: 10px 0;
+    border: 1px solid #337ecc;
+    border-left: none;
+}
+
+tr {
+    width: 100%;
+    height: 44px;
+    line-height: 42px;
+    border-bottom: 1px solid #ccc;
+
+&:last-of-type {
+     border-bottom: none;
+ }
+
+td {
+    border-right: 1px solid #ccc;
+    display: inline-block;
+    height: 44px;
+    vertical-align: middle;
+    text-align: center;
+    line-height: 42px;
+
+&:last-of-type {
+     border-right: none;
+ }
+
+&.w-14 {
+     width: calc((100/7)/100 * 100%);
+ }
+
+&.w-16 {
+     width: calc((100/6)/100 * 100%);
+ }
+
+&.w-18 {
+     width: 16.59%;
+ }
+
+&.w-20 {
+     width: 20%;
+ }
+
+&.w-25 {
+     width: 25%;
+ }
+
+&.w-50 {
+     width: 50%;
+ }
+
+&.w-75 {
+     width: 75%;
+ }
+
+.ant-input {
+    height: 100%;
+    border: none;
+    background: #f5f7fa;
+}
+
+.ant-picker {
+    width: 100%;
+    height: 100%;
+}
+}
+}
+
+.b-font {
+    font-size: 16px;
+    font-weight: bolder;
+}
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+    margin-top: 6px;
+}
+:deep(.el-dialog__footer){
+    padding-top: 20px;
+    display: flex;
+    justify-content: center;
+}
+
+
+</style>
diff --git a/src/views/experiment/developing/components/selectDanger.vue b/src/views/experiment/developing/components/selectDanger.vue
new file mode 100644
index 0000000..8ef010c
--- /dev/null
+++ b/src/views/experiment/developing/components/selectDanger.vue
@@ -0,0 +1,184 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">危废情况</tr>
+    <tr>
+        <td class="w-20 m-color">序号</td>
+        <td class="w-20 m-color">废弃物分类</td>
+        <td class="w-20 m-color">存储方式</td>
+        <td class="w-20 m-color">预估处理量</td>
+        <td class="w-20 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectDangerState.wasteList" :key="index">
+        <td class="w-20">
+            {{ index + 1 }}
+        </td>
+        <td class="w-20">
+            <el-select :disabled="selectDangerState.disabled" v-model="item.classify" clearable filterable>
+                <el-option v-for="item in selectDangerState.classifyList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+        </td>
+        <td class="w-20">
+            <el-select :disabled="selectDangerState.disabled" v-model="item.wasteStorage" clearable filterable>
+                <el-option v-for="item in selectDangerState.wasteStorageList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+        </td>
+        <td class="w-20">
+            <el-input type="number" v-model="item.handAmount"></el-input>
+        </td>
+        <td class="w-20">
+            <el-button :disabled="selectDangerState.disabled" type="danger" @click="deleteDangerItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectDangerState.disabled" type="primary" shape="round" @click="addDangerItem()">
+            添加行
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {reactive, watchEffect} from "vue";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<WasteType>
+});
+
+const selectDangerState = reactive<SelectDangerType>({
+    disabled: false,
+    wasteList: [
+    ],
+    classifyList:[
+        {id:1, name: '有机'},
+        {id:2, name: '酸'},
+        {id:3, name: '碱性'},
+        {id:4, name: '固体废弃物'},
+        {id:5, name: '医疗废弃物'},
+        {id:6, name: '过期化学品'},
+        {id:7, name: '其他'}
+    ],
+    wasteStorageList: [
+        {id:1, name: '吨袋'},
+        {id:2, name: '吨桶'},
+        {id:3, name: '小桶'},
+        {id:4, name: '托盘'},
+        {id:5, name: '其他'},
+    ]
+})
+
+watchEffect(() => {
+    selectDangerState.wasteList = props.data as Array<WasteType>
+    selectDangerState.disabled = props.disabled
+});
+
+const addDangerItem = () => {
+    selectDangerState.wasteList.push({classify: null, wasteStorage: null, handAmount: null,});
+};
+
+const deleteDangerItem = (index: number) => {
+    selectDangerState.wasteList.splice(index,1);
+};
+
+const formatList = (formatList: Array<WasteType>) => {
+    selectDangerState.wasteList = formatList
+}
+
+defineExpose({
+    formatList,
+    dataList: selectDangerState.wasteList,
+});
+
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/developing/components/selectEquipment.vue b/src/views/experiment/developing/components/selectEquipment.vue
new file mode 100644
index 0000000..c86e11f
--- /dev/null
+++ b/src/views/experiment/developing/components/selectEquipment.vue
@@ -0,0 +1,208 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验所用的仪器/设备</tr>
+    <tr>
+        <td class="w-16 m-color">设备名称</td>
+        <td class="w-16 m-color">编号</td>
+        <td class="w-16 m-color">设备功率</td>
+        <td class="w-16 m-color">是否特种</td>
+        <td class="w-16 m-color">设备数量</td>
+        <td class="w-16 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectEquipmentState.equipmentList" :key="index">
+        <td class="w-16">
+            <el-select :disabled="selectEquipmentState.disabled" filterable v-model="item.deviceId" @change="giveOtherEquipmentValue($event, index)">
+                <el-option
+                    v-for="item in selectEquipmentState.allEquipmentList"
+                    :key="item.id"
+                    :value="item.id"
+                    :label="item.deviceName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-16">
+            <el-input :disabled="selectEquipmentState.disabled" v-model="item.deviceCode" placeholder="请输入数量" />
+        </td>
+        <td class="w-16">
+            <el-input :disabled="selectEquipmentState.disabled" v-model="item.devicePower" />
+        </td>
+        <td class="w-16">
+            <el-radio-group :disabled="selectEquipmentState.disabled" v-model="item.specialDevice">
+                <el-radio :label="1">是</el-radio>
+                <el-radio :label="2">否</el-radio>
+            </el-radio-group>
+        </td>
+        <td class="w-16">
+            <el-input type="number" v-model="item.deviceUseCount" />
+        </td>
+        <td class="w-16">
+            <el-button :disabled="selectEquipmentState.disabled" type="danger" @click="deleteEquipmentItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectEquipmentState.disabled" type="primary" shape="round" @click="addEquipmentItem()">
+            选择实验仪器
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {onMounted, reactive, watchEffect} from "vue";
+import {ElMessage} from "element-plus";
+import { equipmentApi } from "/@/api/basic/equipement";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllEquipmentListType>
+});
+
+const selectEquipmentState = reactive<SelectEquipmentType>({
+    disabled: false,
+    equipmentList: [],
+    allEquipmentList: [],
+});
+
+watchEffect(() => {
+    selectEquipmentState.equipmentList = props.data as Array<AllEquipmentListType>
+    selectEquipmentState.disabled = props.disabled
+});
+
+const addEquipmentItem = () => {
+    selectEquipmentState.equipmentList.push({deviceId: null, deviceUseCount: null, deviceCode: '', deviceName: '', devicePower: '', specialDevice: '',});
+};
+
+const deleteEquipmentItem = (index: number) => {
+    selectEquipmentState.equipmentList.splice(index,1);
+};
+
+const getAllEquipmentList = async () => {
+    let res = await equipmentApi().getAllEquipment();
+    if(res.data.code === 100){
+        selectEquipmentState.allEquipmentList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherEquipmentValue = (value: number, index:number) => {
+    const data = selectEquipmentState.allEquipmentList.find(item => item.id === value) as AllEquipmentListType
+    selectEquipmentState.equipmentList[index] = {
+        deviceId: data.id,
+        deviceUseCount: null,
+        deviceCode: data.deviceCode,
+        deviceName: data.deviceName,
+        devicePower: data.devicePower,
+        specialDevice: data.specialDevice
+    }
+};
+
+const formatList = (formatList: Array<AllEquipmentListType>) => {
+    selectEquipmentState.equipmentList = formatList
+}
+
+defineExpose({
+    dataList: selectEquipmentState.equipmentList,
+    formatList
+});
+
+onMounted(() => {
+    getAllEquipmentList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/developing/components/selectMaterial.vue b/src/views/experiment/developing/components/selectMaterial.vue
new file mode 100644
index 0000000..bb29d5f
--- /dev/null
+++ b/src/views/experiment/developing/components/selectMaterial.vue
@@ -0,0 +1,210 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验所用的试剂或材料</tr>
+    <tr>
+        <td class="w-14 m-color">实验材料</td>
+        <td class="w-14 m-color">耗材ID</td>
+        <td class="w-14 m-color">材料类型</td>
+        <td class="w-14 m-color">材料储存</td>
+        <td class="w-14 m-color">计量单位</td>
+        <td class="w-14 m-color">使用数量</td>
+        <td class="w-14 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectMaterialState.materialList" :key="index">
+        <td class="w-14">
+            <el-select :disabled="selectMaterialState.disabled" filterable v-model="item.stuffId" @change="giveOtherMaterialValue($event, index)">
+                <el-option
+                    v-for="item in selectMaterialState.allMaterialList"
+                    :key="item.id"
+                    :value="item.id"
+                    :label="item.stuffName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffCode" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffType" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffStorage" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffUnit" />
+        </td>
+        <td class="w-14">
+            <el-input type="number" v-model="item.stuffUseCount" />
+        </td>
+        <td class="w-14">
+            <el-button type="danger" :disabled="selectMaterialState.disabled" @click="deleteMaterialItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectMaterialState.disabled" type="primary" shape="round" @click="addMaterialItem()">
+            选择实验材料
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {onMounted, reactive, watchEffect} from "vue";
+import { materialApi } from "/@/api/basic/material";
+import {ElMessage} from "element-plus";
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllMaterialListType>
+});
+
+const selectMaterialState = reactive<SelectMaterialType>({
+    disabled: false,
+    materialList: [],
+    allMaterialList: [],
+})
+
+const addMaterialItem = () => {
+    selectMaterialState.materialList.push({stuffId: null, stuffUseCount: null, stuffName: '',stuffCode:'',stuffType: '', stuffStorage: '', stuffUnit: ''});
+};
+
+watchEffect(() => {
+    selectMaterialState.materialList = props.data as Array<AllMaterialListType>
+    selectMaterialState.disabled = props.disabled
+});
+
+const deleteMaterialItem = (index: number) => {
+    selectMaterialState.materialList.splice(index,1);
+};
+
+const getAllPersonList = async () => {
+    let res = await materialApi().getAllMaterial();
+    if(res.data.code === 100){
+        selectMaterialState.allMaterialList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherMaterialValue = (value: number, index:number) => {
+    const data = selectMaterialState.allMaterialList.find(item => item.id === value) as AllMaterialListType
+    selectMaterialState.materialList[index] = {
+        stuffId: data.id,
+        stuffUseCount: data.stuffUseCount,
+        stuffName: data.stuffName,
+        stuffCode: data.stuffCode,
+        stuffType: data.stuffType,
+        stuffStorage: data.stuffStorage,
+        stuffUnit: data.stuffUnit
+    };
+};
+
+const formatList = (formatList: Array<AllMaterialListType>) => {
+    selectMaterialState.materialList = formatList
+};
+
+defineExpose({
+    dataList: selectMaterialState.materialList,
+    formatList
+});
+
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/developing/components/selectPerson.vue b/src/views/experiment/developing/components/selectPerson.vue
new file mode 100644
index 0000000..e0359b8
--- /dev/null
+++ b/src/views/experiment/developing/components/selectPerson.vue
@@ -0,0 +1,218 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验人员</tr>
+    <tr>
+        <td class="w-14 m-color">姓名</td>
+        <td class="w-14 m-color">年龄</td>
+        <td class="w-14 m-color">性别</td>
+        <td class="w-14 m-color">专业</td>
+        <td class="w-14 m-color">部门</td>
+        <td class="w-14 m-color">相关资质</td>
+        <td class="w-14 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectPersonState.personList" :key="index">
+        <td class="w-14">
+            <el-select filterable :disabled="selectPersonState.disabled" v-model="item.personId" @change="giveOtherPersonValue($event, index)">
+                <el-option
+                v-for="item in selectPersonState.allPersonList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.personName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personAge" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personGender" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personMajor" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.depName" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.aptitude" />
+        </td>
+        <td class="w-14">
+            <el-button type="danger" :disabled="selectPersonState.disabled" @click="deletePersonItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectPersonState.disabled" type="primary" shape="round" @click="addPersonItem()">
+            选择实验人员
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {nextTick, onMounted, reactive, watchEffect} from "vue";
+import { personApi } from "/@/api/basic/person";
+import {ElMessage} from "element-plus";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllPersonListType>
+
+});
+
+const selectPersonState = reactive<SelectPersonType>({
+    disabled: false,
+    personList: [],
+    allPersonList: [
+    ],
+});
+
+watchEffect(() => {
+    selectPersonState.personList = props.data as Array<AllPersonListType>
+    selectPersonState.disabled = props.disabled
+});
+
+const addPersonItem = () => {
+    selectPersonState.personList.push({personId: null, personName: null, personAge: null, personGender:'',personMajor:'',depName:'',phone:'',aptitude:'',training:''});
+};
+
+const deletePersonItem = (index: number) => {
+    selectPersonState.personList.splice(index,1);
+};
+
+const getAllPersonList = async () => {
+    let res = await personApi().getAllPerson();
+    if(res.data.code === 100){
+        selectPersonState.allPersonList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherPersonValue = (value: number, index:number) => {
+    // selectPersonState.personList[index] = selectPersonState.allPersonList.find(item => item.id === value) as AllPersonListType
+    const data = selectPersonState.allPersonList.find(item => item.id === value) as AllPersonListType
+    selectPersonState.personList[index] = {
+        personId: data.id,
+        personName: data.personName,
+        personAge: data.personAge,
+        personGender: data.personGender,
+        personMajor: data.personMajor,
+        depName: data.depName,
+        phone: data.phone,
+        aptitude: data.aptitude,
+        training: data.training,
+    };
+};
+
+const formatList = (formatList: Array<AllPersonListType>) => {
+    nextTick(() => {
+        selectPersonState.personList = formatList
+    })
+
+};
+
+defineExpose({
+    formatList,
+    dataList: selectPersonState.personList,
+});
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/developing/index.ts b/src/views/experiment/developing/index.ts
new file mode 100644
index 0000000..c06feb7
--- /dev/null
+++ b/src/views/experiment/developing/index.ts
@@ -0,0 +1,150 @@
+declare interface ProjectStateType {
+	projectData: Array<ProjectType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		searchParams: {
+			experimentName: string,
+			experimentType: null | number,
+		}
+	},
+	total: 0,
+	experimentTypeList: Type []
+	allRoomList: RoomType []
+}
+
+declare interface ProjectType {
+	id?: number | null,
+	experimentCode: string,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface ProjectDialogType {
+	title: string,
+	disabled: boolean,
+	projectDialogVisible: boolean,
+	projectForm: {
+		id: null | number,
+		experimentName: string,
+		experimentType: null | number,
+		liabilityUserId: null | number,
+		safeLiabilityUserId: null | number,
+		dep: string,
+		siteId: null | number,
+		experimentStep: string,
+		experimentMethod: string,
+		process: string,
+		keyProcess: string,
+		timeout: null | number,
+		timeoutManager: string,
+		closed: null | number,
+		unclosedManager: string,
+		explosionProof: string,
+		fireProof: string,
+		poisonProof: string,
+		hazardousWaste: null | number,
+		safeManagerMethod: string,
+		emergencyPlan: string,
+		emergencyDrill: string,
+		emergencyPlanName: string,
+		emergencyDrillStatus: string,
+		partitionCondition: string,
+		note: string,
+		expectStartTime: null | number,
+		persons: SelectPersonType [],
+		deviceList: SelectEquipmentType [],
+		stuffList: SelectMaterialType [],
+		hazardousWasteList: SelectDangerType []
+	},
+	projectFormRules: {
+
+	},
+	allPersonList: Array<AllPersonListType>
+	allRoomList: Array<RoomType>
+}
+
+declare interface SelectDangerType {
+	disabled: boolean
+	wasteList: Array<WasteType>
+	classifyList: Array<Type>
+	wasteStorageList: Array<Type>
+}
+
+declare interface WasteType {
+	classify: null | number,
+	wasteStorage: null | number,
+	handAmount: null | number,
+}
+
+
+declare interface SelectPersonType {
+	disabled: boolean
+	personList: Array<AllPersonListType>
+	allPersonList: Array<AllPersonListType>
+}
+
+declare interface AllPersonListType {
+	id: null | number,
+	personId?: null | number,
+	personName: null,
+	personAge: null,
+	personGender:'',
+	personMajor:'',
+	depName:'',
+	phone:'',
+	aptitude:'',
+	training:''
+}
+
+declare interface SelectEquipmentType {
+	disabled: boolean
+	equipmentList: Array<AllEquipmentListType>,
+	allEquipmentList: Array<AllEquipmentListType>,
+}
+
+declare interface AllEquipmentListType {
+	id?: null | number,
+	deviceId?: null | number,
+	deviceUseCount: null | number,
+	deviceCode: string,
+	deviceName: string,
+	devicePower: string,
+	specialDevice: string,
+}
+
+
+declare interface SelectMaterialType {
+	disabled: boolean
+	materialList: Array<AllMaterialListType>,
+	allMaterialList: Array<AllMaterialListType>,
+}
+
+declare interface AllMaterialListType {
+	id?: null | number,
+	stuffId?: null | number,
+	stuffUseCount: null | number,
+	stuffName: string,
+	stuffCode:string,
+	stuffType: string,
+	stuffStorage: string,
+	stuffUnit: string
+}
+
+
+declare interface ApplyStartDialogType {
+	title: string,
+	applyStartDialogVisible: boolean,
+	applyStartForm: {
+		id: number | null,
+		sisStatus: number | null,
+		safeInformationSystem: string,
+		startTime: string,
+	},
+	applyStartFormRules: {
+
+	},
+}
diff --git a/src/views/experiment/developing/index.vue b/src/views/experiment/developing/index.vue
new file mode 100644
index 0000000..ba7ab0b
--- /dev/null
+++ b/src/views/experiment/developing/index.vue
@@ -0,0 +1,347 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>实验名称:</span>
+                    <el-input v-model="projectState.searchQuery.searchParams.experimentName" clearable filterable class="input-box" placeholder="实验名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>实验类型:</span>
+                    <el-select v-model="projectState.searchQuery.searchParams.experimentType" clearable filterable class="input-box" placeholder="实验类型">
+                        <el-option v-for="item in projectState.experimentTypeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getProjectData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openProjectDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="projectState.projectData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="experimentCode" label="实验编号"/>
+                        <el-table-column prop="expectStartTime" label="立项时间" />
+                        <el-table-column prop="liabilityUser" label="负责人"/>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="250">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="View" @click="openProjectDialog('查看', scope.row)">查看</el-button>
+                                <el-button size="small" text type="primary" :icon="Edit" @click="applyAccess( scope.row)">申请评估</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="projectState.searchQuery.pageIndex" background v-model:page-size="projectState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="projectState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <project-dialog ref="ProjectDialogRef" @refresh="getProjectData"></project-dialog>
+<!--        <apply-start ref="ApplyStartRef"></apply-start>-->
+        <test ref="ApplyStartRef" @refresh="getProjectData"></test>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {projectApi} from "/@/api/experiment/project";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { View,Edit, Plus, Delete } from '@element-plus/icons-vue';
+import {roomApi} from "/@/api/basic/room";
+
+const ProjectDialog = defineAsyncComponent(() => import('./components/projectDialog.vue'));
+const Test = defineAsyncComponent(() => import('./components/applyDialog.vue'))
+
+const ProjectDialogRef = ref();
+const ApplyStartRef = ref();
+
+const projectState = reactive<ProjectStateType>({
+    projectData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        searchParams: {
+            experimentName: '',
+            experimentType: null,
+        }
+    },
+    total: 0,
+    experimentTypeList: [
+        {id: 1, name: '化学类'},
+        {id: 2, name: '生物类'},
+        {id: 3, name: '辐射类'},
+        {id: 4, name: '机电类'},
+        {id: 5, name: '特种设备类'},
+        {id: 6, name: '其它类'},
+    ],
+    allRoomList: []
+})
+
+const getProjectData = async () => {
+    let res = await projectApi().getProjectByList(projectState.searchQuery);
+    if(res.data.code === 100){
+        projectState.projectData = res.data.data;
+        projectState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openProjectDialog = (title: string, value: ProjectType) => {
+    ProjectDialogRef.value.showProjectDialog(title, value, projectState.allRoomList);
+};
+
+const applyAccess = (val: ProjectType) => {
+    debugger
+    ElMessageBox.confirm(`此操作将申请评估该实验:“${val.experimentCode}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await projectApi().accessProject([val.id]);
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '申请成功'
+                });
+                await getProjectData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onDelProject = (val: ProjectType) => {
+    ElMessageBox.confirm(`此操作将永久删除该实验:“${val.experimentCode}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            debugger
+            let res = await projectApi().deleteProjectById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getProjectData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    projectState.searchQuery.pageSize = val;
+    getProjectData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    projectState.searchQuery.pageIndex = val;
+    getProjectData();
+};
+
+const reset = () => {
+    projectState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        searchParams: {
+            experimentName: '',
+            experimentType: null,
+        }
+    }
+};
+
+const getRoomData = async () => {
+    let res = await roomApi().getAllRoom();
+    if(res.data.code === 100){
+        projectState.allRoomList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+onMounted(() => {
+    getProjectData();
+    getRoomData();
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/project/components/applyDialog.vue b/src/views/experiment/project/components/applyDialog.vue
new file mode 100644
index 0000000..b65f862
--- /dev/null
+++ b/src/views/experiment/project/components/applyDialog.vue
@@ -0,0 +1,98 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="applyStartDialogState.title" v-model="applyStartDialogState.applyStartDialogVisible" width="600px">
+            <el-form ref="applyStartFormRef" :rules="applyStartDialogState.applyStartFormRules" :model="applyStartDialogState.applyStartForm" size="default" label-width="160px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验开始时间" prop="startTime">
+                            <el-date-picker type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="applyStartDialogState.applyStartForm.startTime" class="input-length"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="是否是安全信息化系统 " prop="sisStatus">
+                            <el-radio-group v-model="applyStartDialogState.applyStartForm.sisStatus">
+                                <el-radio :label="1">是</el-radio>
+                                <el-radio :label="2">否</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="安全信息化系统" prop="safeInformationSystem">
+                            <el-input v-model="applyStartDialogState.applyStartForm.safeInformationSystem" placeholder="材料类型" class="input-length">
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="applyStartDialogState.applyStartDialogVisible = !applyStartDialogState.applyStartDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitApplyStart" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+
+const applyStartFormRef = ref()
+
+const applyStartDialogState = reactive<ApplyStartDialogType>({
+    title: '',
+    applyStartDialogVisible: false,
+    applyStartForm: {
+        id: null,
+        sisStatus: null,
+        safeInformationSystem: '',
+        startTime: '',
+    },
+    applyStartFormRules: {
+
+    },
+})
+
+const showApplyStartDialog = (value: ProjectType) => {
+    applyStartDialogState.applyStartDialogVisible = true;
+    applyStartDialogState.applyStartForm.id = <number>value.id
+};
+
+const onSubmitApplyStart = () => {
+    applyStartFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            let res = await projectApi().applyProject([applyStartDialogState.applyStartForm]);
+            if(res.data.code === 100){
+                emit('refresh')
+                applyStartDialogState.applyStartDialogVisible = false;
+                ElMessage({
+                    type: 'success',
+                    message: '申请开展成功'
+                })
+            }else{
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg,
+                });
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showApplyStartDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/experiment/project/components/applyStart.vue b/src/views/experiment/project/components/applyStart.vue
new file mode 100644
index 0000000..8906f6e
--- /dev/null
+++ b/src/views/experiment/project/components/applyStart.vue
@@ -0,0 +1,101 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="applyStartDialogState.title" v-model="applyStartDialogState.applyStartDialogVisible" width="600px">
+            <el-form ref="applyStartFormRef" :rules="applyStartDialogState.applyStartFormRules" :model="applyStartDialogState.applyStartForm" size="default" label-width="120px">
+                <el-row :gutter="35">
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="实验材料" prop="stuffName">
+                            <el-date-picker type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="applyStartDialogState.applyStartForm.startTime" class="input-length"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="编号" prop="sisStatus">
+                            <el-radio-group v-model="applyStartDialogState.applyStartForm.sisStatus">
+                                <el-radio :label="1">是</el-radio>
+                                <el-radio :label="2">否</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+                        <el-form-item label="材料类型" prop="stuffType">
+                            <el-select v-model="applyStartDialogState.applyStartForm.safeInformationSystem" placeholder="材料类型" class="input-length">
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="applyStartDialogState.applyStartDialogVisible = !applyStartDialogState.applyStartDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmitApplyStart" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+
+const applyStartFormRef = ref()
+
+const applyStartDialogState = reactive<ApplyStartDialogType>({
+    title: '',
+    applyStartDialogVisible: false,
+    applyStartForm: {
+        id: null,
+        sisStatus: null,
+        safeInformationSystem: '',
+        startTime: '',
+    },
+    applyStartFormRules: {
+        stuffCode: [{ required: true, message: '请填写实验材料', trigger: 'blur' }],
+        stuffName: [{ required: true, message: '请填写编号', trigger: 'blur' }],
+        stuffStorage: [{ required: true, message: '请选择材料类型', trigger: 'change' }],
+        stuffType: [{ required: true, message: '请选择材料储存', trigger: 'change' }],
+        stuffUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+})
+
+const showApplyStartDialog = (value: ProjectType) => {
+    applyStartDialogState.applyStartDialogVisible = true;
+};
+
+const onSubmitApplyStart = () => {
+    applyStartFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            let res = await projectApi().applyProject(applyStartDialogState.applyStartForm);
+            if(res.data.code === 100){
+                emit('refresh')
+                applyStartDialogState.applyStartDialogVisible = false;
+                ElMessage({
+                    type: 'success',
+                    message: '申请开展成功'
+                })
+            }else{
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg,
+                });
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const emit = defineEmits(['refresh'])
+
+defineExpose({
+    showApplyStartDialog
+})
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/experiment/project/components/projectDialog.vue b/src/views/experiment/project/components/projectDialog.vue
new file mode 100644
index 0000000..b621833
--- /dev/null
+++ b/src/views/experiment/project/components/projectDialog.vue
@@ -0,0 +1,533 @@
+<template>
+    <div class="system-menu-dialog-container">
+        <el-dialog :title="projectDialogState.title" v-model="projectDialogState.projectDialogVisible" width="70%">
+            <el-form ref="ProjectFormRef" :rules="projectDialogState.projectFormRules" :model="projectDialogState.projectForm" size="default" label-width="0">
+                <table class="report-table">
+                    <th class="m-color b-font" style="text-align: center">***研究所/***大学<br />科学研究实验项目安全风险基础信息录入表(已开展B)</th>
+                    <tr>
+                        <td class="w-25 m-color">实验名称</td>
+                        <td class="w-75 m-color">
+                            <el-form-item prop="experimentName">
+                                <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentName" placeholder="请输入实验名称" />
+                            </el-form-item>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验类型</td>
+                        <td class="w-75 m-color">
+
+                            <el-radio-group style="text-align: center" :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentType">
+                                <el-radio :label="1">化学类</el-radio>
+                                <el-radio :label="2">生物类</el-radio>
+                                <el-radio :label="3">辐射类</el-radio>
+                                <el-radio :label="4">机电类</el-radio>
+                                <el-radio :label="5">特种设备类</el-radio>
+                                <el-radio :label="6">其他类</el-radio>
+                            </el-radio-group>
+
+
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">部门</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.dep" />
+                        </td>
+                        <td class="w-25 m-color">负责人</td>
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.liabilityUserId" clearable filterable>
+                                <el-option
+                                    v-for="item in projectDialogState.allPersonList"
+                                    :key="item.id"
+                                    :value="item.id"
+                                    :label="item.personName"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">立项时间</td>
+                        <td class="w-25 m-color">
+                            <el-date-picker :disabled="projectDialogState.disabled"  type="datetime" format="YYYY/MM/DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" v-model="projectDialogState.projectForm.expectStartTime" />
+                        </td>
+<!--                        <td class="w-16 m-color">开展时间</td>-->
+<!--                        <td class="w-16 m-color">-->
+<!--                            <el-date-picker v-model="projectDialogState.projectForm.startTime" />-->
+<!--                        </td>-->
+                        <td class="w-25 m-color">安全负责人</td>
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.safeLiabilityUserId" clearable filterable>
+                                <el-option
+                                v-for="item in projectDialogState.allPersonList"
+                                :key="item.id"
+                                :value="item.id"
+                                :label="item.personName"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="text-align: center">实验场所</tr>
+<!--                    <tr>-->
+<!--                        <td class="w-25 m-color">楼宇</td>-->
+<!--                        <td class="w-75 m-color">-->
+<!--                            <el-input v-model="projectDialogState.projectForm.building" placeholder="请输入楼栋名称" />-->
+<!--                        </td>-->
+<!--                    </tr>-->
+                    <tr>
+                        <td class="w-25 m-color">房间号</td>
+<!--                        <td class="w-75 m-color">-->
+                        <td class="w-16 m-color">
+                            <el-select :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.siteId" placeholder="请输入房间号" >
+                                <el-option
+                                    v-for="item in projectDialogState.allRoomList"
+                                    :key="item.id"
+                                    :value="item.id"
+                                    :label="item.room"
+                                ></el-option>
+                            </el-select>
+                        </td>
+                    </tr>
+                    <select-material ref="selectMaterialRef" v-model:disabled="projectDialogState.disabled" v-model:data="projectDialogState.projectForm.stuffList"></select-material>
+                    <select-equipment ref="selectEquipmentRef" v-model:disabled="projectDialogState.disabled" v-model:data="projectDialogState.projectForm.deviceList"></select-equipment>
+                    <tr>
+                        <td class="w-25 m-color">实验步骤</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentStep" placeholder="请输入实验步骤" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">操作方法</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.experimentMethod" placeholder="请输入操作方法" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">工艺过程</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.process" placeholder="请输入工艺过程" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">特殊/关键过程</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.keyProcess" placeholder="请输入特殊/关键过程" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">是否存在过夜、老化实验</td>
+                        <td class="w-25 m-color">
+                            <el-radio-group :disabled="projectDialogState.disabled"  v-model="projectDialogState.projectForm.timeout">
+                                <el-radio :label="1">存在</el-radio>
+                                <el-radio :label="2">不存在</el-radio>
+                            </el-radio-group>
+                        </td>
+                        <td class="w-25 m-color">过夜、老化保障措施</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.timeoutManager" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">是否在封闭条件下</td>
+                        <td class="w-25 m-color">
+                            <el-radio-group :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.closed">
+                                <el-radio :label="1">存在</el-radio>
+                                <el-radio :label="2">不存在</el-radio>
+                            </el-radio-group>
+                        </td>
+                        <td class="w-25 m-color">封闭条件保障措施</td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.unclosedManager" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防爆措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.explosionProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防火措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.fireProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所防中毒措施条件和设施情况</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.poisonProof" placeholder="请输入" />
+                        </td>
+                    </tr>
+
+
+                    <select-danger ref="selectDangerRef" v-model:data="projectDialogState.projectForm.hazardousWasteList" v-model:disabled="projectDialogState.disabled"></select-danger>
+                    <select-person ref="selectPersonRef" v-model:data="projectDialogState.projectForm.persons" v-model:disabled="projectDialogState.disabled"></select-person>
+
+
+<!--                    <tr>-->
+<!--                        <td class="w-25 m-color">安全信息化系统使用情况</td>-->
+<!--                        <td class="w-25 m-color">-->
+<!--                            <el-radio-group v-model="projectDialogState.projectForm.useIT">-->
+<!--                                <el-radio value="1">是</el-radio>-->
+<!--                                <el-radio value="2">否</el-radio>-->
+<!--                            </el-radio-group>-->
+<!--                        </td>-->
+<!--                        <td class="w-25 m-color">系统名称</td>-->
+<!--                        <td class="w-25 m-color">-->
+<!--                            <el-input v-model="projectDialogState.projectForm.systemName" placeholder="请输入" />-->
+<!--                        </td>-->
+<!--                    </tr>-->
+                    <tr>
+                        <td class="w-25 m-color">安全管理制度</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.safeManagerMethod" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="text-align: center">应急预案/应急演练</tr>
+                    <tr>
+                        <td class="w-25 m-color">有无预案</td>
+                        <td class="w-25 m-color">
+                            预案名称
+                        </td>
+                        <td class="w-25 m-color">是否演练</td>
+                        <td class="w-25 m-color">
+                            演练情况
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyPlan" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyPlanName" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyDrillStatus" />
+                        </td>
+                        <td class="w-25 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.emergencyDrill" />
+                        </td>
+                    </tr>
+                    <tr class="m-color b-font" style="  text-align: center">实验人员</tr>
+                    <tr>
+                        <td class="w-25 m-color">实验场所是否需要分区隔断</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.partitionCondition" placeholder="请输入" />
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="w-25 m-color">其它基础信息(详细描述)</td>
+                        <td class="w-75 m-color">
+                            <el-input :disabled="projectDialogState.disabled" v-model="projectDialogState.projectForm.note" placeholder="请输入" />
+                        </td>
+                    </tr>
+                </table>
+            </el-form>
+            <template #footer>
+				<span class="dialog-footer" style="padding-top:10px;text-align: center !important;">
+					<el-button @click="projectDialogState.projectDialogVisible = !projectDialogState.projectDialogVisible" size="default">取 消</el-button>
+					<el-button type="primary" v-if="!projectDialogState.disabled" @click="onSubmitProject" size="default">确定</el-button>
+				</span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, nextTick, onMounted, reactive, ref} from "vue";
+import {ElMessage} from "element-plus";
+import {projectApi} from "/@/api/experiment/project";
+import {personApi} from "/@/api/basic/person";
+
+const SelectEquipment = defineAsyncComponent(() => import('./selectEquipment.vue'))
+const SelectMaterial = defineAsyncComponent(() => import('./selectMaterial.vue'))
+const SelectDanger = defineAsyncComponent(() => import('./selectDanger.vue'))
+const SelectPerson = defineAsyncComponent(() => import('./selectPerson.vue'))
+const ProjectFormRef = ref()
+const selectPersonRef = ref()
+const selectEquipmentRef = ref()
+const selectMaterialRef = ref()
+const selectDangerRef = ref()
+
+const projectDialogState = reactive<ProjectDialogType>({
+    title: '',
+    disabled: false,
+    projectDialogVisible: false,
+    projectForm:   {
+        id: null,
+        experimentName: "",
+        experimentType: null,
+        liabilityUserId: null,
+        safeLiabilityUserId: null,
+        dep: "",
+        siteId: null,
+        experimentStep: "",
+        experimentMethod: "",
+        process: "",
+        keyProcess: "",
+        timeout: null,
+        timeoutManager: "",
+        closed: null,
+        unclosedManager: "",
+        explosionProof: "",
+        fireProof: "",
+        poisonProof: "",
+        hazardousWaste: null,
+        safeManagerMethod: "",
+        emergencyPlan: "",
+        emergencyDrill: "",
+        emergencyPlanName: "",
+        emergencyDrillStatus: "",
+        partitionCondition: "",
+        note: "",
+        expectStartTime: null,
+        persons: [
+        ],
+        deviceList: [
+        ],
+        stuffList: [
+        ],
+        hazardousWasteList: [
+        ]
+    },
+    projectFormRules: {
+        experimentName: [{ required: true, message: '请填写设备编号', trigger: 'blur' }],
+        deviceName: [{ required: true, message: '请填写设备名称', trigger: 'blur' }],
+        devicePower: [{ required: true, message: '请填写设备功率', trigger: 'blur' }],
+        deviceUnit: [{ required: true, message: '请选择计量单位', trigger: 'change' }]
+    },
+    allPersonList: [],
+    allRoomList: [],
+})
+
+const showProjectDialog = (title: string, value: ProjectType, allRoomList: RoomType []) => {
+    projectDialogState.projectDialogVisible = true;
+    projectDialogState.allRoomList = allRoomList
+    setTimeout(() => {
+        ProjectFormRef.value.clearValidate();
+    });
+    if(title === '新增'){
+        projectDialogState.disabled = false
+        projectDialogState.title = '新增';
+        projectDialogState.projectForm = {
+            id: null,
+            experimentName: "",
+            experimentType: null,
+            liabilityUserId: null,
+            safeLiabilityUserId: null,
+            dep: "",
+            siteId: null,
+            experimentStep: "",
+            experimentMethod: "",
+            process: "",
+            keyProcess: "",
+            timeout: null,
+            timeoutManager: "",
+            closed: null,
+            unclosedManager: "",
+            explosionProof: "",
+            fireProof: "",
+            poisonProof: "",
+            hazardousWaste: 1,
+            safeManagerMethod: "",
+            emergencyPlan: "",
+            emergencyDrill: "",
+            emergencyPlanName: "",
+            emergencyDrillStatus: "",
+            partitionCondition: "",
+            note: "",
+            expectStartTime: null,
+            persons: [
+            ],
+            deviceList: [
+            ],
+            stuffList: [
+            ],
+            hazardousWasteList: [
+            ]
+        };
+    }else{
+        projectDialogState.title = '查看';
+        projectDialogState.disabled = true
+        for(let i in projectDialogState.projectForm) {
+            if(isValidKey(i, projectDialogState.projectForm)) {
+                projectDialogState.projectForm[i] = value[i];
+            }
+        }
+    }
+};
+
+const isValidKey = (key: string | number | symbol, object:object): key is keyof typeof object =>{
+    return key in object;
+};
+
+const onSubmitProject = () => {
+    ProjectFormRef.value.validate(async(valid: boolean) => {
+        if(valid){
+            if(projectDialogState.title === '新增'){
+                projectDialogState.projectForm.persons = selectPersonRef.value.dataList
+                projectDialogState.projectForm.hazardousWasteList = selectDangerRef.value.dataList
+                projectDialogState.projectForm.stuffList = selectMaterialRef.value.dataList
+                projectDialogState.projectForm.deviceList = selectEquipmentRef.value.dataList
+                let res = await projectApi().addProject(projectDialogState.projectForm);
+                if(res.data.code === 100){
+                    emit('refresh')
+                    projectDialogState.projectDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await projectApi().modProject(projectDialogState.projectForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    projectDialogState.projectDialogVisible = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const getAllPersonList = async () => {
+    let res = await personApi().getAllPerson();
+    if(res.data.code === 100){
+        projectDialogState.allPersonList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const emit = defineEmits(['refresh']);
+
+defineExpose({
+    showProjectDialog,
+});
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+th {
+    padding: 10px 0;
+    border: 1px solid #337ecc;
+    border-left: none;
+}
+
+tr {
+    width: 100%;
+    height: 44px;
+    line-height: 42px;
+    border-bottom: 1px solid #ccc;
+
+&:last-of-type {
+     border-bottom: none;
+ }
+
+td {
+    border-right: 1px solid #ccc;
+    display: inline-block;
+    height: 44px;
+    vertical-align: middle;
+    text-align: center;
+    line-height: 42px;
+
+&:last-of-type {
+     border-right: none;
+ }
+
+&.w-14 {
+     width: calc((100/7)/100 * 100%);
+ }
+
+&.w-16 {
+     width: calc((100/6)/100 * 100%);
+ }
+
+&.w-18 {
+     width: 16.59%;
+ }
+
+&.w-20 {
+     width: 20%;
+ }
+
+&.w-25 {
+     width: 25%;
+ }
+
+&.w-50 {
+     width: 50%;
+ }
+
+&.w-75 {
+     width: 75%;
+ }
+
+.ant-input {
+    height: 100%;
+    border: none;
+    background: #f5f7fa;
+}
+
+.ant-picker {
+    width: 100%;
+    height: 100%;
+}
+}
+}
+
+.b-font {
+    font-size: 16px;
+    font-weight: bolder;
+}
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+    margin-top: 6px;
+}
+:deep(.el-dialog__footer){
+    padding-top: 20px;
+    display: flex;
+    justify-content: center;
+}
+
+
+</style>
diff --git a/src/views/experiment/project/components/selectDanger.vue b/src/views/experiment/project/components/selectDanger.vue
new file mode 100644
index 0000000..8ef010c
--- /dev/null
+++ b/src/views/experiment/project/components/selectDanger.vue
@@ -0,0 +1,184 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">危废情况</tr>
+    <tr>
+        <td class="w-20 m-color">序号</td>
+        <td class="w-20 m-color">废弃物分类</td>
+        <td class="w-20 m-color">存储方式</td>
+        <td class="w-20 m-color">预估处理量</td>
+        <td class="w-20 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectDangerState.wasteList" :key="index">
+        <td class="w-20">
+            {{ index + 1 }}
+        </td>
+        <td class="w-20">
+            <el-select :disabled="selectDangerState.disabled" v-model="item.classify" clearable filterable>
+                <el-option v-for="item in selectDangerState.classifyList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+        </td>
+        <td class="w-20">
+            <el-select :disabled="selectDangerState.disabled" v-model="item.wasteStorage" clearable filterable>
+                <el-option v-for="item in selectDangerState.wasteStorageList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+            </el-select>
+        </td>
+        <td class="w-20">
+            <el-input type="number" v-model="item.handAmount"></el-input>
+        </td>
+        <td class="w-20">
+            <el-button :disabled="selectDangerState.disabled" type="danger" @click="deleteDangerItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectDangerState.disabled" type="primary" shape="round" @click="addDangerItem()">
+            添加行
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {reactive, watchEffect} from "vue";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<WasteType>
+});
+
+const selectDangerState = reactive<SelectDangerType>({
+    disabled: false,
+    wasteList: [
+    ],
+    classifyList:[
+        {id:1, name: '有机'},
+        {id:2, name: '酸'},
+        {id:3, name: '碱性'},
+        {id:4, name: '固体废弃物'},
+        {id:5, name: '医疗废弃物'},
+        {id:6, name: '过期化学品'},
+        {id:7, name: '其他'}
+    ],
+    wasteStorageList: [
+        {id:1, name: '吨袋'},
+        {id:2, name: '吨桶'},
+        {id:3, name: '小桶'},
+        {id:4, name: '托盘'},
+        {id:5, name: '其他'},
+    ]
+})
+
+watchEffect(() => {
+    selectDangerState.wasteList = props.data as Array<WasteType>
+    selectDangerState.disabled = props.disabled
+});
+
+const addDangerItem = () => {
+    selectDangerState.wasteList.push({classify: null, wasteStorage: null, handAmount: null,});
+};
+
+const deleteDangerItem = (index: number) => {
+    selectDangerState.wasteList.splice(index,1);
+};
+
+const formatList = (formatList: Array<WasteType>) => {
+    selectDangerState.wasteList = formatList
+}
+
+defineExpose({
+    formatList,
+    dataList: selectDangerState.wasteList,
+});
+
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/project/components/selectEquipment.vue b/src/views/experiment/project/components/selectEquipment.vue
new file mode 100644
index 0000000..c86e11f
--- /dev/null
+++ b/src/views/experiment/project/components/selectEquipment.vue
@@ -0,0 +1,208 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验所用的仪器/设备</tr>
+    <tr>
+        <td class="w-16 m-color">设备名称</td>
+        <td class="w-16 m-color">编号</td>
+        <td class="w-16 m-color">设备功率</td>
+        <td class="w-16 m-color">是否特种</td>
+        <td class="w-16 m-color">设备数量</td>
+        <td class="w-16 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectEquipmentState.equipmentList" :key="index">
+        <td class="w-16">
+            <el-select :disabled="selectEquipmentState.disabled" filterable v-model="item.deviceId" @change="giveOtherEquipmentValue($event, index)">
+                <el-option
+                    v-for="item in selectEquipmentState.allEquipmentList"
+                    :key="item.id"
+                    :value="item.id"
+                    :label="item.deviceName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-16">
+            <el-input :disabled="selectEquipmentState.disabled" v-model="item.deviceCode" placeholder="请输入数量" />
+        </td>
+        <td class="w-16">
+            <el-input :disabled="selectEquipmentState.disabled" v-model="item.devicePower" />
+        </td>
+        <td class="w-16">
+            <el-radio-group :disabled="selectEquipmentState.disabled" v-model="item.specialDevice">
+                <el-radio :label="1">是</el-radio>
+                <el-radio :label="2">否</el-radio>
+            </el-radio-group>
+        </td>
+        <td class="w-16">
+            <el-input type="number" v-model="item.deviceUseCount" />
+        </td>
+        <td class="w-16">
+            <el-button :disabled="selectEquipmentState.disabled" type="danger" @click="deleteEquipmentItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectEquipmentState.disabled" type="primary" shape="round" @click="addEquipmentItem()">
+            选择实验仪器
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {onMounted, reactive, watchEffect} from "vue";
+import {ElMessage} from "element-plus";
+import { equipmentApi } from "/@/api/basic/equipement";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllEquipmentListType>
+});
+
+const selectEquipmentState = reactive<SelectEquipmentType>({
+    disabled: false,
+    equipmentList: [],
+    allEquipmentList: [],
+});
+
+watchEffect(() => {
+    selectEquipmentState.equipmentList = props.data as Array<AllEquipmentListType>
+    selectEquipmentState.disabled = props.disabled
+});
+
+const addEquipmentItem = () => {
+    selectEquipmentState.equipmentList.push({deviceId: null, deviceUseCount: null, deviceCode: '', deviceName: '', devicePower: '', specialDevice: '',});
+};
+
+const deleteEquipmentItem = (index: number) => {
+    selectEquipmentState.equipmentList.splice(index,1);
+};
+
+const getAllEquipmentList = async () => {
+    let res = await equipmentApi().getAllEquipment();
+    if(res.data.code === 100){
+        selectEquipmentState.allEquipmentList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherEquipmentValue = (value: number, index:number) => {
+    const data = selectEquipmentState.allEquipmentList.find(item => item.id === value) as AllEquipmentListType
+    selectEquipmentState.equipmentList[index] = {
+        deviceId: data.id,
+        deviceUseCount: null,
+        deviceCode: data.deviceCode,
+        deviceName: data.deviceName,
+        devicePower: data.devicePower,
+        specialDevice: data.specialDevice
+    }
+};
+
+const formatList = (formatList: Array<AllEquipmentListType>) => {
+    selectEquipmentState.equipmentList = formatList
+}
+
+defineExpose({
+    dataList: selectEquipmentState.equipmentList,
+    formatList
+});
+
+onMounted(() => {
+    getAllEquipmentList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/project/components/selectMaterial.vue b/src/views/experiment/project/components/selectMaterial.vue
new file mode 100644
index 0000000..bb29d5f
--- /dev/null
+++ b/src/views/experiment/project/components/selectMaterial.vue
@@ -0,0 +1,210 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验所用的试剂或材料</tr>
+    <tr>
+        <td class="w-14 m-color">实验材料</td>
+        <td class="w-14 m-color">耗材ID</td>
+        <td class="w-14 m-color">材料类型</td>
+        <td class="w-14 m-color">材料储存</td>
+        <td class="w-14 m-color">计量单位</td>
+        <td class="w-14 m-color">使用数量</td>
+        <td class="w-14 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectMaterialState.materialList" :key="index">
+        <td class="w-14">
+            <el-select :disabled="selectMaterialState.disabled" filterable v-model="item.stuffId" @change="giveOtherMaterialValue($event, index)">
+                <el-option
+                    v-for="item in selectMaterialState.allMaterialList"
+                    :key="item.id"
+                    :value="item.id"
+                    :label="item.stuffName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffCode" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffType" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffStorage" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectMaterialState.disabled" v-model="item.stuffUnit" />
+        </td>
+        <td class="w-14">
+            <el-input type="number" v-model="item.stuffUseCount" />
+        </td>
+        <td class="w-14">
+            <el-button type="danger" :disabled="selectMaterialState.disabled" @click="deleteMaterialItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectMaterialState.disabled" type="primary" shape="round" @click="addMaterialItem()">
+            选择实验材料
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {onMounted, reactive, watchEffect} from "vue";
+import { materialApi } from "/@/api/basic/material";
+import {ElMessage} from "element-plus";
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllMaterialListType>
+});
+
+const selectMaterialState = reactive<SelectMaterialType>({
+    disabled: false,
+    materialList: [],
+    allMaterialList: [],
+})
+
+const addMaterialItem = () => {
+    selectMaterialState.materialList.push({stuffId: null, stuffUseCount: null, stuffName: '',stuffCode:'',stuffType: '', stuffStorage: '', stuffUnit: ''});
+};
+
+watchEffect(() => {
+    selectMaterialState.materialList = props.data as Array<AllMaterialListType>
+    selectMaterialState.disabled = props.disabled
+});
+
+const deleteMaterialItem = (index: number) => {
+    selectMaterialState.materialList.splice(index,1);
+};
+
+const getAllPersonList = async () => {
+    let res = await materialApi().getAllMaterial();
+    if(res.data.code === 100){
+        selectMaterialState.allMaterialList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherMaterialValue = (value: number, index:number) => {
+    const data = selectMaterialState.allMaterialList.find(item => item.id === value) as AllMaterialListType
+    selectMaterialState.materialList[index] = {
+        stuffId: data.id,
+        stuffUseCount: data.stuffUseCount,
+        stuffName: data.stuffName,
+        stuffCode: data.stuffCode,
+        stuffType: data.stuffType,
+        stuffStorage: data.stuffStorage,
+        stuffUnit: data.stuffUnit
+    };
+};
+
+const formatList = (formatList: Array<AllMaterialListType>) => {
+    selectMaterialState.materialList = formatList
+};
+
+defineExpose({
+    dataList: selectMaterialState.materialList,
+    formatList
+});
+
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/project/components/selectPerson.vue b/src/views/experiment/project/components/selectPerson.vue
new file mode 100644
index 0000000..e0359b8
--- /dev/null
+++ b/src/views/experiment/project/components/selectPerson.vue
@@ -0,0 +1,218 @@
+<template>
+    <tr class="m-color b-font" style="text-align: center">实验人员</tr>
+    <tr>
+        <td class="w-14 m-color">姓名</td>
+        <td class="w-14 m-color">年龄</td>
+        <td class="w-14 m-color">性别</td>
+        <td class="w-14 m-color">专业</td>
+        <td class="w-14 m-color">部门</td>
+        <td class="w-14 m-color">相关资质</td>
+        <td class="w-14 m-color">操作</td>
+    </tr>
+    <tr v-for="(item,index) in selectPersonState.personList" :key="index">
+        <td class="w-14">
+            <el-select filterable :disabled="selectPersonState.disabled" v-model="item.personId" @change="giveOtherPersonValue($event, index)">
+                <el-option
+                v-for="item in selectPersonState.allPersonList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.personName"
+                >
+                </el-option>
+            </el-select>
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personAge" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personGender" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.personMajor" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.depName" />
+        </td>
+        <td class="w-14">
+            <el-input :disabled="selectPersonState.disabled" v-model="item.aptitude" />
+        </td>
+        <td class="w-14">
+            <el-button type="danger" :disabled="selectPersonState.disabled" @click="deletePersonItem(index)">删除</el-button>
+        </td>
+    </tr>
+    <tr style="text-align: center">
+        <el-button :disabled="selectPersonState.disabled" type="primary" shape="round" @click="addPersonItem()">
+            选择实验人员
+        </el-button>
+    </tr>
+</template>
+
+<script setup lang="ts">
+import {nextTick, onMounted, reactive, watchEffect} from "vue";
+import { personApi } from "/@/api/basic/person";
+import {ElMessage} from "element-plus";
+
+let props = defineProps({
+    disabled: Boolean,
+    data: Array<AllPersonListType>
+
+});
+
+const selectPersonState = reactive<SelectPersonType>({
+    disabled: false,
+    personList: [],
+    allPersonList: [
+    ],
+});
+
+watchEffect(() => {
+    selectPersonState.personList = props.data as Array<AllPersonListType>
+    selectPersonState.disabled = props.disabled
+});
+
+const addPersonItem = () => {
+    selectPersonState.personList.push({personId: null, personName: null, personAge: null, personGender:'',personMajor:'',depName:'',phone:'',aptitude:'',training:''});
+};
+
+const deletePersonItem = (index: number) => {
+    selectPersonState.personList.splice(index,1);
+};
+
+const getAllPersonList = async () => {
+    let res = await personApi().getAllPerson();
+    if(res.data.code === 100){
+        selectPersonState.allPersonList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        })
+    }
+};
+
+const giveOtherPersonValue = (value: number, index:number) => {
+    // selectPersonState.personList[index] = selectPersonState.allPersonList.find(item => item.id === value) as AllPersonListType
+    const data = selectPersonState.allPersonList.find(item => item.id === value) as AllPersonListType
+    selectPersonState.personList[index] = {
+        personId: data.id,
+        personName: data.personName,
+        personAge: data.personAge,
+        personGender: data.personGender,
+        personMajor: data.personMajor,
+        depName: data.depName,
+        phone: data.phone,
+        aptitude: data.aptitude,
+        training: data.training,
+    };
+};
+
+const formatList = (formatList: Array<AllPersonListType>) => {
+    nextTick(() => {
+        selectPersonState.personList = formatList
+    })
+
+};
+
+defineExpose({
+    formatList,
+    dataList: selectPersonState.personList,
+});
+
+onMounted(() => {
+    getAllPersonList();
+});
+</script>
+
+<style scoped lang="scss">
+.site-layout-background {
+    background: #fff;
+}
+
+.report-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #337ecc;
+    margin: 20px 0;
+
+    th {
+        padding: 10px 0;
+        border: 1px solid #337ecc;
+        border-left: none;
+    }
+
+    tr {
+        width: 100%;
+        height: 44px;
+        line-height: 42px;
+        border-bottom: 1px solid #ccc;
+
+        &:last-of-type {
+            border-bottom: none;
+        }
+
+        td {
+            border-right: 1px solid #ccc;
+            display: inline-block;
+            height: 44px;
+            vertical-align: middle;
+            text-align: center;
+            line-height: 42px;
+
+            &:last-of-type {
+                border-right: none;
+            }
+
+            &.w-14 {
+                width: calc((100/7)/100 * 100%);
+            }
+
+            &.w-16 {
+                width: calc((100/6)/100 * 100%);
+            }
+
+            &.w-18 {
+                width: 16.59%;
+            }
+
+            &.w-20 {
+                width: 20%;
+            }
+
+            &.w-25 {
+                width: 25%;
+            }
+
+            &.w-50 {
+                width: 50%;
+            }
+
+            &.w-75 {
+                width: 75%;
+            }
+
+            .ant-input {
+                height: 100%;
+                border: none;
+                background: #f5f7fa;
+            }
+
+            .ant-picker {
+                width: 100%;
+                height: 100%;
+            }
+        }
+    }
+
+    .b-font {
+        font-size: 16px;
+        font-weight: bolder;
+    }
+}
+
+.m-color {
+    color: #0c4995;
+}
+:deep(.el-input__wrapper ){
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/experiment/project/index.ts b/src/views/experiment/project/index.ts
new file mode 100644
index 0000000..c06feb7
--- /dev/null
+++ b/src/views/experiment/project/index.ts
@@ -0,0 +1,150 @@
+declare interface ProjectStateType {
+	projectData: Array<ProjectType>
+	searchQuery: {
+		pageIndex: number,
+		pageSize: number,
+		searchParams: {
+			experimentName: string,
+			experimentType: null | number,
+		}
+	},
+	total: 0,
+	experimentTypeList: Type []
+	allRoomList: RoomType []
+}
+
+declare interface ProjectType {
+	id?: number | null,
+	experimentCode: string,
+}
+
+declare interface Type {
+	id: number,
+	name: string,
+}
+
+declare interface ProjectDialogType {
+	title: string,
+	disabled: boolean,
+	projectDialogVisible: boolean,
+	projectForm: {
+		id: null | number,
+		experimentName: string,
+		experimentType: null | number,
+		liabilityUserId: null | number,
+		safeLiabilityUserId: null | number,
+		dep: string,
+		siteId: null | number,
+		experimentStep: string,
+		experimentMethod: string,
+		process: string,
+		keyProcess: string,
+		timeout: null | number,
+		timeoutManager: string,
+		closed: null | number,
+		unclosedManager: string,
+		explosionProof: string,
+		fireProof: string,
+		poisonProof: string,
+		hazardousWaste: null | number,
+		safeManagerMethod: string,
+		emergencyPlan: string,
+		emergencyDrill: string,
+		emergencyPlanName: string,
+		emergencyDrillStatus: string,
+		partitionCondition: string,
+		note: string,
+		expectStartTime: null | number,
+		persons: SelectPersonType [],
+		deviceList: SelectEquipmentType [],
+		stuffList: SelectMaterialType [],
+		hazardousWasteList: SelectDangerType []
+	},
+	projectFormRules: {
+
+	},
+	allPersonList: Array<AllPersonListType>
+	allRoomList: Array<RoomType>
+}
+
+declare interface SelectDangerType {
+	disabled: boolean
+	wasteList: Array<WasteType>
+	classifyList: Array<Type>
+	wasteStorageList: Array<Type>
+}
+
+declare interface WasteType {
+	classify: null | number,
+	wasteStorage: null | number,
+	handAmount: null | number,
+}
+
+
+declare interface SelectPersonType {
+	disabled: boolean
+	personList: Array<AllPersonListType>
+	allPersonList: Array<AllPersonListType>
+}
+
+declare interface AllPersonListType {
+	id: null | number,
+	personId?: null | number,
+	personName: null,
+	personAge: null,
+	personGender:'',
+	personMajor:'',
+	depName:'',
+	phone:'',
+	aptitude:'',
+	training:''
+}
+
+declare interface SelectEquipmentType {
+	disabled: boolean
+	equipmentList: Array<AllEquipmentListType>,
+	allEquipmentList: Array<AllEquipmentListType>,
+}
+
+declare interface AllEquipmentListType {
+	id?: null | number,
+	deviceId?: null | number,
+	deviceUseCount: null | number,
+	deviceCode: string,
+	deviceName: string,
+	devicePower: string,
+	specialDevice: string,
+}
+
+
+declare interface SelectMaterialType {
+	disabled: boolean
+	materialList: Array<AllMaterialListType>,
+	allMaterialList: Array<AllMaterialListType>,
+}
+
+declare interface AllMaterialListType {
+	id?: null | number,
+	stuffId?: null | number,
+	stuffUseCount: null | number,
+	stuffName: string,
+	stuffCode:string,
+	stuffType: string,
+	stuffStorage: string,
+	stuffUnit: string
+}
+
+
+declare interface ApplyStartDialogType {
+	title: string,
+	applyStartDialogVisible: boolean,
+	applyStartForm: {
+		id: number | null,
+		sisStatus: number | null,
+		safeInformationSystem: string,
+		startTime: string,
+	},
+	applyStartFormRules: {
+
+	},
+}
diff --git a/src/views/experiment/project/index.vue b/src/views/experiment/project/index.vue
new file mode 100644
index 0000000..9c6c50c
--- /dev/null
+++ b/src/views/experiment/project/index.vue
@@ -0,0 +1,324 @@
+<template>
+    <div class="home-container">
+        <div style="height: 100%">
+            <el-row class="homeCard">
+                <div class="basic-line">
+                    <span>实验名称:</span>
+                    <el-input v-model="projectState.searchQuery.searchParams.experimentName" clearable filterable class="input-box" placeholder="实验名称">
+                    </el-input>
+                </div>
+                <div class="basic-line">
+                    <span>实验类型:</span>
+                    <el-select v-model="projectState.searchQuery.searchParams.experimentType" clearable filterable class="input-box" placeholder="实验类型">
+                        <el-option v-for="item in projectState.experimentTypeList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                    </el-select>
+                </div>
+                <div style="padding-bottom: 10px">
+                    <el-button type="primary" @click="getProjectData">查询</el-button>
+                    <el-button plain @click="reset">重置</el-button>
+                </div>
+            </el-row>
+            <div class="homeCard">
+                <div class="main-card">
+                    <el-row class="cardTop">
+                        <el-col :span="12" class="mainCardBtn">
+                            <el-button type="primary" :icon="Plus" size="default" @click="openProjectDialog('新增', {})">新增</el-button>
+                            <!--                            <el-button type="danger" :icon="Delete" size="default" plain>删除</el-button>-->
+                        </el-col>
+<!--                        <el-button type="primary" :icon="Refresh" size="default" />-->
+                    </el-row>
+                    <el-table ref="multipleTableRef" :data="projectState.projectData" style="width: 100%" height="calc(100% - 100px)" :header-cell-style="{ background: '#fafafa' }">
+                        <el-table-column prop="experimentCode" label="实验编号"/>
+                        <el-table-column prop="expectStartTime" label="立项时间" />
+                        <el-table-column prop="liabilityUser" label="负责人"/>
+                        <el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="createByUserName" label="创建人" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateTime" label="最后修改时间" show-overflow-tooltip></el-table-column>
+                        <el-table-column prop="updateByUserName" label="最后修改人" show-overflow-tooltip></el-table-column>
+                        <el-table-column label="操作" width="250">
+                            <template #default="scope">
+                                <el-button size="small" text type="primary" :icon="View" @click="openProjectDialog('查看', scope.row)">查看</el-button>
+                                <el-button size="small" text type="primary" :icon="Edit" @click="applyStart('申请开展', scope.row)">申请开展</el-button>
+                                <el-button size="small" text type="danger" :icon="Delete" @click="onDelProject(scope.row)">删除</el-button>                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="pageBtn">
+                        <el-pagination @size-change="onHandleSizeChange" @current-change="onHandleCurrentChange" :pager-count="5" :page-sizes="[10, 20, 30]" v-model:current-page="projectState.searchQuery.pageIndex" background v-model:page-size="projectState.searchQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="projectState.total" class="page-position"> </el-pagination>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <project-dialog ref="ProjectDialogRef" @refresh="getProjectData"></project-dialog>
+<!--        <apply-start ref="ApplyStartRef"></apply-start>-->
+        <test ref="ApplyStartRef" @refresh="getProjectData"></test>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, onMounted, reactive, ref} from "vue";
+import {projectApi} from "/@/api/experiment/project";
+import {ElMessage, ElMessageBox} from "element-plus";
+import { View,Edit, Plus, Delete } from '@element-plus/icons-vue';
+import {roomApi} from "/@/api/basic/room";
+
+const ProjectDialog = defineAsyncComponent(() => import('./components/projectDialog.vue'));
+const Test = defineAsyncComponent(() => import('./components/applyDialog.vue'))
+
+const ProjectDialogRef = ref();
+const ApplyStartRef = ref();
+
+const projectState = reactive<ProjectStateType>({
+    projectData: [],
+    searchQuery: {
+        pageIndex: 1,
+        pageSize: 10,
+        searchParams: {
+            experimentName: '',
+            experimentType: null,
+        }
+    },
+    total: 0,
+    experimentTypeList: [
+        {id: 1, name: '化学类'},
+        {id: 2, name: '生物类'},
+        {id: 3, name: '辐射类'},
+        {id: 4, name: '机电类'},
+        {id: 5, name: '特种设备类'},
+        {id: 6, name: '其它类'},
+    ],
+    allRoomList: []
+})
+
+const getProjectData = async () => {
+    let res = await projectApi().getProjectByList(projectState.searchQuery);
+    if(res.data.code === 100){
+        projectState.projectData = res.data.data;
+        projectState.total = res.data.total;
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+const openProjectDialog = (title: string, value: ProjectType) => {
+    ProjectDialogRef.value.showProjectDialog(title, value, projectState.allRoomList);
+};
+
+const applyStart = (title: string, value: ProjectType) => {
+    ApplyStartRef.value.showApplyStartDialog(value);
+};
+
+const onDelProject = (val: ProjectType) => {
+    ElMessageBox.confirm(`此操作将永久删除该实验:“${val.experimentCode}”,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            debugger
+            let res = await projectApi().deleteProjectById({ id: val.id });
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getProjectData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+        });
+}
+
+const onHandleSizeChange = (val: number) => {
+    projectState.searchQuery.pageSize = val;
+    getProjectData();
+};
+
+const onHandleCurrentChange = (val: number) => {
+    projectState.searchQuery.pageIndex = val;
+    getProjectData();
+};
+
+const reset = () => {
+    projectState.searchQuery = {
+        pageIndex: 1,
+        pageSize: 10,
+        searchParams: {
+            experimentName: '',
+            experimentType: null,
+        }
+    }
+};
+
+const getRoomData = async () => {
+    let res = await roomApi().getAllRoom();
+    if(res.data.code === 100){
+        projectState.allRoomList = JSON.parse(JSON.stringify(res.data.data));
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+
+onMounted(() => {
+    getProjectData();
+    getRoomData();
+})
+
+</script>
+
+<style scoped lang="scss">
+$homeNavLengh: 8;
+.home-container {
+    height: calc(100vh - 144px);
+    box-sizing: border-box;
+    overflow: hidden;
+    .homeCard {
+        width: 100%;
+        padding: 20px;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 4px;
+
+        .main-card {
+            width: 100%;
+            height: 100%;
+            .cardTop {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-bottom: 20px;
+                .mainCardBtn {
+                    margin: 0;
+                }
+            }
+            .pageBtn {
+                height: 60px;
+                display: flex;
+                align-items: center;
+                justify-content: right;
+
+                .demo-pagination-block + .demo-pagination-block {
+                    margin-top: 10px;
+                }
+                .demo-pagination-block .demonstration {
+                    margin-bottom: 16px;
+                }
+            }
+        }
+        &:last-of-type {
+            height: calc(100% - 100px);
+        }
+    }
+    .el-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        &:last-child {
+            margin-bottom: 0;
+        }
+        .grid-content {
+            align-items: center;
+            min-height: 36px;
+        }
+
+        .topInfo {
+            display: flex;
+            align-items: center;
+            font-size: 16px;
+            font-weight: bold;
+
+            & > div {
+                white-space: nowrap;
+                margin-right: 20px;
+            }
+        }
+    }
+}
+.stepItem {
+    width: 100%;
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 30px;
+    margin-left: 30px;
+    padding-bottom: 30px;
+    border-left: 2px solid #ccc;
+    &:first-of-type {
+        margin-top: 30px;
+    }
+    &:last-of-type {
+        margin-bottom: 0;
+        border-left: none;
+    }
+    .stepNum {
+        width: 30px;
+        height: 30px;
+        border-radius: 15px;
+        box-sizing: border-box;
+        color: #333;
+        border: 1px solid #999;
+        line-height: 28px;
+        text-align: center;
+        margin-right: 10px;
+        margin-left: -16px;
+        margin-top: -30px;
+    }
+    .stepCard {
+        width: 100%;
+        margin-top: -30px;
+
+        .box-card {
+            width: 100%;
+            &:deep(.el-card__header) {
+                padding: 10px 15px;
+            }
+            .card-header {
+                width: 100%;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                & > div:first-of-type {
+                    margin-right: 80px;
+                    font-size: 18px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+    &:hover .card-header {
+        color: #0098f5;
+    }
+    &:hover .stepNum {
+        border: 2px solid #0098f5;
+        color: #0098f5;
+    }
+}
+
+:deep(.el-date-editor) {
+    width: 100%;
+}
+.el-select {
+    width: 100%;
+}
+:deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: var(--el-card-bg-color);
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__inner) {
+    color: var(--el-input-text-color, var(--el-text-color-regular));
+}
+:deep(.el-input.is-disabled .el-input__wrapper) {
+    background-color: var(--el-card-bg-color);
+    box-shadow: none;
+}
+</style>
diff --git a/src/views/home/dialog.vue b/src/views/home/dialog.vue
new file mode 100644
index 0000000..b1da6ca
--- /dev/null
+++ b/src/views/home/dialog.vue
@@ -0,0 +1,272 @@
+<template>
+	<div class="system-menu-dialog-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
+			<el-form ref="menuDialogFormRef" :model="state.ruleForm" size="default" label-width="80px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级菜单">
+                            <el-cascader
+                                :options="state.menuData"
+                                :props="{ emitPath: false, checkStrictly: true, value: 'id', label: 'title' }"
+                                placeholder="请选择上级菜单"
+                                clearable
+                                class="w100"
+                                @change="test"
+                                v-model="state.ruleForm.parentId">
+                            </el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单名称">
+							<el-input v-model="state.ruleForm.meta.title" placeholder="格式:message.router.xxx" clearable></el-input>
+						</el-form-item>
+					</el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="路由名称">
+                            <el-input v-model="state.ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="路由路径">
+                            <el-input v-model="state.ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="重定向">
+                            <el-input v-model="state.ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="菜单图标">
+                            <IconSelector placeholder="请输入菜单图标" v-model="state.ruleForm.meta.icon" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="组件路径">
+                            <el-input v-model="state.ruleForm.component" placeholder="组件路径" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="链接地址">
+                            <el-input
+                                v-model="state.ruleForm.meta.isLink"
+                                placeholder="外链/内嵌时链接地址(http:xxx.com)"
+                                clearable
+                            >
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="权限标识">
+                            <el-select v-model="state.ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
+                                <el-option v-for="item in state.roleList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单排序">
+							<el-input-number v-model="state.ruleForm.priority" controls-position="right" placeholder="请输入排序" class="w100" />
+						</el-form-item>
+					</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+							<el-form-item label="是否隐藏">
+								<el-radio-group v-model="state.ruleForm.meta.isHide">
+									<el-radio :label="true">隐藏</el-radio>
+									<el-radio :label="false">不隐藏</el-radio>
+								</el-radio-group>
+							</el-form-item>
+						</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="systemMenuDialog">
+import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { i18n } from '/@/i18n/index';
+import {ElMessage} from "element-plus";
+import { useMenuApi } from "/@/api/systemManage/menu";
+import {useRoleApi} from "/@/api/systemManage/role";
+// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+
+// 引入组件
+const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
+
+// 定义变量内容
+const menuDialogFormRef = ref();
+const stores = useRoutesList();
+const { routesList } = storeToRefs(stores);
+const state = reactive({
+    roleList:[],
+	// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
+	ruleForm: {
+        id: null,
+        parentId: null,
+        description: '',
+        name: '',
+        component: '',
+        priority: 0,
+        path: '',
+        redirect: '',
+        publicable: null,
+		meta: {
+			title: '',
+			icon: '',
+			isHide: false,
+			isKeepAlive: true,
+			isAffix: false,
+			isLink: '',
+			isIframe: false,
+			roles: '',
+		},
+	},
+	menuData: [] as RouteItems [], // 上级菜单数据
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+});
+
+const test = () => {
+    console.log(state.menuData)
+}
+
+// 获取 pinia 中的路由
+const getMenuData = (routes: RouteItems) => {
+	const arr: RouteItems = [];
+	routes.map((val: RouteItem) => {
+		val['title'] = i18n.global.t(val.meta?.title as string);
+		arr.push({ ...val });
+		if (val.children) getMenuData(val.children);
+	});
+	return arr;
+};
+// 打开弹窗
+const openDialog = (type: string, row?: any) => {
+	if (type === 'edit') {
+        for(let i in state.ruleForm){
+            state.ruleForm[i] = row[i]
+        }
+		state.dialog.title = '修改菜单';
+		state.dialog.submitTxt = '修 改';
+	} else {
+		state.dialog.title = '新增菜单';
+		state.dialog.submitTxt = '新 增';
+        state.ruleForm = {
+            id: null,
+            parentId: null,
+            description: '',
+            name: '',
+            component: '',
+            priority: 0,
+            path: '',
+            redirect: '',
+            publicable: null,
+            meta: {
+                title: '',
+                icon: '',
+                isHide: false,
+                isKeepAlive: true,
+                isAffix: false,
+                isLink: '',
+                isIframe: false,
+                roles: '',
+            },
+        }
+		// 清空表单,此项需加表单验证才能使用
+		// nextTick(() => {
+		// 	menuDialogFormRef.value.resetFields();
+		// });
+	}
+	state.dialog.type = type;
+	state.dialog.isShowDialog = true;
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+// 提交
+const onSubmit = () => {
+    menuDialogFormRef.value.validate(async (valid: boolean) => {
+        if(valid){
+            if(state.dialog.title === '新增菜单'){
+                let res = await useMenuApi().addMenu(state.ruleForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    state.dialog.isShowDialog = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await useMenuApi().modMenu(state.ruleForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    state.dialog.isShowDialog = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const getRoles = async () => {
+    let res = await useRoleApi().getRoleList()
+    if(res.data.code === 100){
+       state.roleList = res.data.data
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.message
+        })
+    }
+}
+// 页面加载时
+onMounted(() => {
+	state.menuData = getMenuData(routesList.value);
+    getRoles()
+});
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
diff --git a/src/views/home/index.ts b/src/views/home/index.ts
new file mode 100644
index 0000000..9742bbb
--- /dev/null
+++ b/src/views/home/index.ts
@@ -0,0 +1,3 @@
+interface EquipmentStateType {
+
+}
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
index 0771f90..91d40aa 100644
--- a/src/views/home/index.vue
+++ b/src/views/home/index.vue
@@ -1,15 +1,144 @@
 <template>
-<div>
-    123
-</div>
+	<div class="system-menu-container layout-pd">
+		<el-card shadow="hover">
+			<div class="system-menu-search mb15">
+				<el-input size="default" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenAddMenu">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增菜单
+				</el-button>
+			</div>
+			<el-table
+				:data="state.tableData.data"
+				v-loading="state.tableData.loading"
+				style="width: 100%"
+				row-key="path"
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+			>
+				<el-table-column label="菜单名称" show-overflow-tooltip>
+					<template #default="scope">
+						<SvgIcon :name="scope.row.meta.icon" />
+						<span class="ml10">{{ $t(scope.row.meta.title) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column prop="path" label="路由路径" show-overflow-tooltip></el-table-column>
+				<el-table-column label="组件路径" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.component }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="权限标识" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.meta.roles }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="排序" show-overflow-tooltip width="80">
+					<template #default="scope">
+						{{ scope.$index }}
+					</template>
+				</el-table-column>
+				<el-table-column label="类型" show-overflow-tooltip width="80">
+					<template #default="scope">
+						<el-tag type="success" size="small">{{ scope.row.xx }}菜单</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column label="操作" show-overflow-tooltip width="140">
+					<template #default="scope">
+						<el-button size="small" text type="primary" @click="onOpenAddMenu('add')">新增</el-button>
+						<el-button size="small" text type="primary" @click="onOpenEditMenu('edit', scope.row)">修改</el-button>
+						<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+		<MenuDialog ref="menuDialogRef" @refresh="getTableData()" />
+	</div>
 </template>
 
-<script>
-export default {
-    name: "index"
+<script setup lang="ts" name="systemMenu">
+import { defineAsyncComponent, ref, onMounted, reactive } from 'vue';
+import { RouteRecordRaw } from 'vue-router';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useMenuApi } from "/@/api/systemManage/menu";
+import { initBackEndControlRoutes } from "/@/router/backEnd";
+import {roomApi} from "/@/api/basic/room";
+// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
+
+// 引入组件
+const MenuDialog = defineAsyncComponent(() => import('/@/views/home/dialog.vue'));
+
+// 定义变量内容
+const stores = useRoutesList();
+const { routesList } = storeToRefs(stores);
+const menuDialogRef = ref();
+const state = reactive({
+	tableData: {
+        data: [] as RouteRecordRaw[],
+		loading: false,
+	},
+});
+
+// 获取路由数据,真实请从接口获取
+const getTableData = async () => {
+    let res = await useMenuApi().getMenuAdmin();
+    if (res.data.code === 100) {
+        state.tableData.data = res.data.data;
+        await initBackEndControlRoutes()
+    } else {
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+// 打开新增菜单弹窗
+const onOpenAddMenu = (type: string) => {
+	menuDialogRef.value.openDialog(type);
+};
+// 打开编辑菜单弹窗
+const onOpenEditMenu = (type: string, row: RouteRecordRaw) => {
+	menuDialogRef.value.openDialog(type, row);
+};
+// 删除当前行
+const onTabelRowDel = (row: RouteRecordRaw) => {
+    ElMessageBox.confirm(`此操作将永久删除该菜单,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await useMenuApi().deleteMenu(row.id );
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getTableData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+
+        });
 }
+
+// 页面加载时
+onMounted(() => {
+	getTableData();
+});
 </script>
-
-<style scoped>
-
-</style>
diff --git a/src/views/loginPage/component/accountLogin.vue b/src/views/loginPage/component/accountLogin.vue
index 96d52e9..7726cac 100644
--- a/src/views/loginPage/component/accountLogin.vue
+++ b/src/views/loginPage/component/accountLogin.vue
@@ -233,7 +233,7 @@
             // 登录成功,跳到转首页
             // 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
             // if (route.json.query?.redirect) {
-            router.push('/newHome');
+            router.push('/home');
             // router.push({
             // 	path: <string>route.json.query?.redirect,
             // 	query: Object.keys(<string>route.json.query?.params).length > 0 ? JSON.parse(<string>route.json.query?.params) : '',
diff --git a/src/views/system/home/dialog.vue b/src/views/system/home/dialog.vue
new file mode 100644
index 0000000..b1da6ca
--- /dev/null
+++ b/src/views/system/home/dialog.vue
@@ -0,0 +1,272 @@
+<template>
+	<div class="system-menu-dialog-container">
+		<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
+			<el-form ref="menuDialogFormRef" :model="state.ruleForm" size="default" label-width="80px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级菜单">
+                            <el-cascader
+                                :options="state.menuData"
+                                :props="{ emitPath: false, checkStrictly: true, value: 'id', label: 'title' }"
+                                placeholder="请选择上级菜单"
+                                clearable
+                                class="w100"
+                                @change="test"
+                                v-model="state.ruleForm.parentId">
+                            </el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单名称">
+							<el-input v-model="state.ruleForm.meta.title" placeholder="格式:message.router.xxx" clearable></el-input>
+						</el-form-item>
+					</el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="路由名称">
+                            <el-input v-model="state.ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="路由路径">
+                            <el-input v-model="state.ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="重定向">
+                            <el-input v-model="state.ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="菜单图标">
+                            <IconSelector placeholder="请输入菜单图标" v-model="state.ruleForm.meta.icon" />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="组件路径">
+                            <el-input v-model="state.ruleForm.component" placeholder="组件路径" clearable></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="链接地址">
+                            <el-input
+                                v-model="state.ruleForm.meta.isLink"
+                                placeholder="外链/内嵌时链接地址(http:xxx.com)"
+                                clearable
+                            >
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                        <el-form-item label="权限标识">
+                            <el-select v-model="state.ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
+                                <el-option v-for="item in state.roleList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="菜单排序">
+							<el-input-number v-model="state.ruleForm.priority" controls-position="right" placeholder="请输入排序" class="w100" />
+						</el-form-item>
+					</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+							<el-form-item label="是否隐藏">
+								<el-radio-group v-model="state.ruleForm.meta.isHide">
+									<el-radio :label="true">隐藏</el-radio>
+									<el-radio :label="false">不隐藏</el-radio>
+								</el-radio-group>
+							</el-form-item>
+						</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup lang="ts" name="systemMenuDialog">
+import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { i18n } from '/@/i18n/index';
+import {ElMessage} from "element-plus";
+import { useMenuApi } from "/@/api/systemManage/menu";
+import {useRoleApi} from "/@/api/systemManage/role";
+// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['refresh']);
+
+// 引入组件
+const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
+
+// 定义变量内容
+const menuDialogFormRef = ref();
+const stores = useRoutesList();
+const { routesList } = storeToRefs(stores);
+const state = reactive({
+    roleList:[],
+	// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
+	ruleForm: {
+        id: null,
+        parentId: null,
+        description: '',
+        name: '',
+        component: '',
+        priority: 0,
+        path: '',
+        redirect: '',
+        publicable: null,
+		meta: {
+			title: '',
+			icon: '',
+			isHide: false,
+			isKeepAlive: true,
+			isAffix: false,
+			isLink: '',
+			isIframe: false,
+			roles: '',
+		},
+	},
+	menuData: [] as RouteItems [], // 上级菜单数据
+	dialog: {
+		isShowDialog: false,
+		type: '',
+		title: '',
+		submitTxt: '',
+	},
+});
+
+const test = () => {
+    console.log(state.menuData)
+}
+
+// 获取 pinia 中的路由
+const getMenuData = (routes: RouteItems) => {
+	const arr: RouteItems = [];
+	routes.map((val: RouteItem) => {
+		val['title'] = i18n.global.t(val.meta?.title as string);
+		arr.push({ ...val });
+		if (val.children) getMenuData(val.children);
+	});
+	return arr;
+};
+// 打开弹窗
+const openDialog = (type: string, row?: any) => {
+	if (type === 'edit') {
+        for(let i in state.ruleForm){
+            state.ruleForm[i] = row[i]
+        }
+		state.dialog.title = '修改菜单';
+		state.dialog.submitTxt = '修 改';
+	} else {
+		state.dialog.title = '新增菜单';
+		state.dialog.submitTxt = '新 增';
+        state.ruleForm = {
+            id: null,
+            parentId: null,
+            description: '',
+            name: '',
+            component: '',
+            priority: 0,
+            path: '',
+            redirect: '',
+            publicable: null,
+            meta: {
+                title: '',
+                icon: '',
+                isHide: false,
+                isKeepAlive: true,
+                isAffix: false,
+                isLink: '',
+                isIframe: false,
+                roles: '',
+            },
+        }
+		// 清空表单,此项需加表单验证才能使用
+		// nextTick(() => {
+		// 	menuDialogFormRef.value.resetFields();
+		// });
+	}
+	state.dialog.type = type;
+	state.dialog.isShowDialog = true;
+};
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialog.isShowDialog = false;
+};
+
+// 取消
+const onCancel = () => {
+	closeDialog();
+};
+// 提交
+const onSubmit = () => {
+    menuDialogFormRef.value.validate(async (valid: boolean) => {
+        if(valid){
+            if(state.dialog.title === '新增菜单'){
+                let res = await useMenuApi().addMenu(state.ruleForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    state.dialog.isShowDialog = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '新增成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }else{
+                let res = await useMenuApi().modMenu(state.ruleForm)
+                if(res.data.code === 100){
+                    emit('refresh')
+                    state.dialog.isShowDialog = false;
+                    ElMessage({
+                        type: 'success',
+                        message: '编辑成功'
+                    })
+                }else{
+                    ElMessage({
+                        type: 'warning',
+                        message: res.data.msg,
+                    });
+                }
+            }
+        }else{
+            ElMessage({
+                type: 'warning',
+                message: '请完善基本信息',
+            });
+        }
+    })
+};
+
+const getRoles = async () => {
+    let res = await useRoleApi().getRoleList()
+    if(res.data.code === 100){
+       state.roleList = res.data.data
+    }else{
+        ElMessage({
+            type: 'warning',
+            message: res.data.message
+        })
+    }
+}
+// 页面加载时
+onMounted(() => {
+	state.menuData = getMenuData(routesList.value);
+    getRoles()
+});
+
+// 暴露变量
+defineExpose({
+	openDialog,
+});
+</script>
diff --git a/src/views/system/home/index.ts b/src/views/system/home/index.ts
new file mode 100644
index 0000000..9742bbb
--- /dev/null
+++ b/src/views/system/home/index.ts
@@ -0,0 +1,3 @@
+interface EquipmentStateType {
+
+}
diff --git a/src/views/system/home/index.vue b/src/views/system/home/index.vue
new file mode 100644
index 0000000..91d40aa
--- /dev/null
+++ b/src/views/system/home/index.vue
@@ -0,0 +1,144 @@
+<template>
+	<div class="system-menu-container layout-pd">
+		<el-card shadow="hover">
+			<div class="system-menu-search mb15">
+				<el-input size="default" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
+				<el-button size="default" type="primary" class="ml10">
+					<el-icon>
+						<ele-Search />
+					</el-icon>
+					查询
+				</el-button>
+				<el-button size="default" type="success" class="ml10" @click="onOpenAddMenu">
+					<el-icon>
+						<ele-FolderAdd />
+					</el-icon>
+					新增菜单
+				</el-button>
+			</div>
+			<el-table
+				:data="state.tableData.data"
+				v-loading="state.tableData.loading"
+				style="width: 100%"
+				row-key="path"
+				:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+			>
+				<el-table-column label="菜单名称" show-overflow-tooltip>
+					<template #default="scope">
+						<SvgIcon :name="scope.row.meta.icon" />
+						<span class="ml10">{{ $t(scope.row.meta.title) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column prop="path" label="路由路径" show-overflow-tooltip></el-table-column>
+				<el-table-column label="组件路径" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.component }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="权限标识" show-overflow-tooltip>
+					<template #default="scope">
+						<span>{{ scope.row.meta.roles }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column label="排序" show-overflow-tooltip width="80">
+					<template #default="scope">
+						{{ scope.$index }}
+					</template>
+				</el-table-column>
+				<el-table-column label="类型" show-overflow-tooltip width="80">
+					<template #default="scope">
+						<el-tag type="success" size="small">{{ scope.row.xx }}菜单</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column label="操作" show-overflow-tooltip width="140">
+					<template #default="scope">
+						<el-button size="small" text type="primary" @click="onOpenAddMenu('add')">新增</el-button>
+						<el-button size="small" text type="primary" @click="onOpenEditMenu('edit', scope.row)">修改</el-button>
+						<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+		</el-card>
+		<MenuDialog ref="menuDialogRef" @refresh="getTableData()" />
+	</div>
+</template>
+
+<script setup lang="ts" name="systemMenu">
+import { defineAsyncComponent, ref, onMounted, reactive } from 'vue';
+import { RouteRecordRaw } from 'vue-router';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import { storeToRefs } from 'pinia';
+import { useRoutesList } from '/@/stores/routesList';
+import { useMenuApi } from "/@/api/systemManage/menu";
+import { initBackEndControlRoutes } from "/@/router/backEnd";
+import {roomApi} from "/@/api/basic/room";
+// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
+
+// 引入组件
+const MenuDialog = defineAsyncComponent(() => import('/@/views/home/dialog.vue'));
+
+// 定义变量内容
+const stores = useRoutesList();
+const { routesList } = storeToRefs(stores);
+const menuDialogRef = ref();
+const state = reactive({
+	tableData: {
+        data: [] as RouteRecordRaw[],
+		loading: false,
+	},
+});
+
+// 获取路由数据,真实请从接口获取
+const getTableData = async () => {
+    let res = await useMenuApi().getMenuAdmin();
+    if (res.data.code === 100) {
+        state.tableData.data = res.data.data;
+        await initBackEndControlRoutes()
+    } else {
+        ElMessage({
+            type: 'warning',
+            message: res.data.msg
+        });
+    }
+};
+// 打开新增菜单弹窗
+const onOpenAddMenu = (type: string) => {
+	menuDialogRef.value.openDialog(type);
+};
+// 打开编辑菜单弹窗
+const onOpenEditMenu = (type: string, row: RouteRecordRaw) => {
+	menuDialogRef.value.openDialog(type, row);
+};
+// 删除当前行
+const onTabelRowDel = (row: RouteRecordRaw) => {
+    ElMessageBox.confirm(`此操作将永久删除该菜单,是否继续?`, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    })
+        .then(async () => {
+            let res = await useMenuApi().deleteMenu(row.id );
+            if (res.data.code === 100) {
+                ElMessage({
+                    type: 'success',
+                    duration: 2000,
+                    message: '删除成功'
+                });
+                await getTableData();
+            } else {
+                ElMessage({
+                    type: 'warning',
+                    message: res.data.msg
+                });
+            }
+        })
+        .catch((error) => {
+
+        });
+}
+
+// 页面加载时
+onMounted(() => {
+	getTableData();
+});
+</script>

--
Gitblit v1.9.2