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