diff --git a/.env b/.env index 41bc483..fad912c 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ VITE_BASE_API = '/remoteServer/admin-api/' +VITE_BASE_API_SYSTEM = '/remote/admin-api/system/' VITE_SHOW_ONLINE_DEVICE = true -VITE_BASE_URL = 'http://43.140.245.32:48081' +VITE_BASE_URL = 'http://43.140.245.32:48089' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2b9b15a..08a84d4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ yarn-error.log* /node_modules/ /*.tar -.idea \ No newline at end of file +.idea +test.js \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 284d99b..baaf73b 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,7 +4,7 @@ + + - { + "keyToString": { + "ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true", + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", + "RunOnceActivity.git.unshallow": "true", + "code.cleanup.on.save": "true", + "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultAutoModeForALLUsers.v1": "true", + "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", + "git-widget-placeholder": "main", + "junie.onboarding.icon.badge.shown": "true", + "last_opened_file_path": "/Users/taqibe/worker/EDFS-EPM/public", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "rearrange.code.on.save": "true", + "settings.editor.selected.configurable": "preferences.general", + "to.speed.mode.migration.done": "true", + "ts.external.directory.path": "/Users/taqibe/worker/EDFS-EPM/node_modules/typescript/lib", + "vue.rearranger.settings.migration": "true" }, - "keyToStringList": { - "vue.recent.templates": [ - "Vue Composition API Component" + "keyToStringList": { + "vue.recent.templates": [ + "Vue Composition API Component" ] } -}]]> +} + @@ -110,7 +119,7 @@ - @@ -167,6 +176,15 @@ + + + + + + + + + - @@ -428,8 +478,18 @@ + - - - @@ -464,7 +521,10 @@ - diff --git a/global.types/components.d.ts b/global.types/components.d.ts index e7d0f23..137173c 100644 --- a/global.types/components.d.ts +++ b/global.types/components.d.ts @@ -21,26 +21,24 @@ declare module 'vue' { ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDialog: typeof import('element-plus/es')['ElDialog'] + ElDropdown: typeof import('element-plus/es')['ElDropdown'] + ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] + ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElHeader: typeof import('element-plus/es')['ElHeader'] ElIcon: typeof import('element-plus/es')['ElIcon'] ElInput: typeof import('element-plus/es')['ElInput'] - ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElOption: typeof import('element-plus/es')['ElOption'] ElPageHeader: typeof import('element-plus/es')['ElPageHeader'] - ElProgress: typeof import('element-plus/es')['ElProgress'] ElRow: typeof import('element-plus/es')['ElRow'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElStep: typeof import('element-plus/es')['ElStep'] ElSteps: typeof import('element-plus/es')['ElSteps'] - ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] - ElTable: typeof import('element-plus/es')['ElTable'] - ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] @@ -52,7 +50,6 @@ declare module 'vue' { RouterView: typeof import('vue-router')['RouterView'] } export interface GlobalDirectives { - vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'] vLoading: typeof import('element-plus/es')['ElLoadingDirective'] } } diff --git a/package.json b/package.json index 26ef7e5..ce97000 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "uuid": "^11.1.0", "vue": "^3.5.13", "vue-echarts": "^7.0.3", - "vue-router": "^4.5.0" + "vue-router": "^4.5.0", + "web-storage-cache": "^1.1.1" }, "devDependencies": { "@iconify/json": "^2.2.310", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f131fa7..e1bff6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: vue-router: specifier: ^4.5.0 version: 4.6.3(vue@3.5.25(typescript@5.7.3)) + web-storage-cache: + specifier: ^1.1.1 + version: 1.1.1 devDependencies: '@iconify/json': specifier: ^2.2.310 @@ -2469,6 +2472,9 @@ packages: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} + web-storage-cache@1.1.1: + resolution: {integrity: sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==} + webpack-sources@3.3.3: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} @@ -5036,6 +5042,8 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + web-storage-cache@1.1.1: {} + webpack-sources@3.3.3: {} webpack-virtual-modules@0.6.2: {} diff --git a/public/commun_config_modbustcp/commun_channel_moebustcp.json b/public/commun_config_modbustcp/commun_channel_moebustcp.json deleted file mode 100644 index 43ea954..0000000 --- a/public/commun_config_modbustcp/commun_channel_moebustcp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "channel": [ - { - "name": "名称,唯一标识不可重复,不能为中文或特殊符号,长度32字节", - "ip": "设备IP地址,字符串", - "port": "端口号,整形数字,0~65535,一般为502" - }, - { - "name": "modbustcp_ch1", - "ip": "192.168.10.123", - "port": 502 - }, - { - "name": "xxxx", - "ip": "192.168.10.133", - "port": 1502 - }, - { - "name": "xxxxxx", - "ip": "192.168.10.143", - "port": 2502 - } - ] -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/commun_dev_modbustcp.json b/public/commun_config_modbustcp/commun_dev_modbustcp.json deleted file mode 100644 index 644adb7..0000000 --- a/public/commun_config_modbustcp/commun_dev_modbustcp.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "dev": [ - { - "name": "名称,唯一标识不可重复,不能为中文或特殊符号,长度32字节", - "ch": "所在通道名称,特指ModbusRtu通道的名称,字符串", - "point": "使用点表名称,特指Modbus点表的名称,字符串", - "addr": "站地址,字符串,0 ~ 255" - }, - { - "name": "xxxx1", - "ch": "modbustcp_ch1", - "point": "pcs", - "addr": "1" - }, - { - "name": "xxxx2", - "ch": "modbustcp_ch1", - "point": "pcs", - "addr": "2" - }, - { - "name": "xxxx3", - "ch": "xxxx", - "point": "bms", - "addr": "1" - }, - { - "name": "xxxx4", - "ch": "xxxxxx", - "point": "tms", - "addr": "1" - } - ] -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/index.json b/public/commun_config_modbustcp/index.json deleted file mode 100644 index d16e67d..0000000 --- a/public/commun_config_modbustcp/index.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "工程名xxxx", - "commun_channel": { - "通讯通道类型,固定如下": "通讯通道配置文件路径", - "modbustcp": "commun_channel_moebustcp.json" - }, - "commun_dev": { - "通讯设备类型,固定如下": "通讯设备配置文件路径", - "modbustcp": "commun_dev_moebustcp.json" - }, - "point_table": { - "通讯点表类型,固定如下": { - "名称": "路径" - }, - "modbus": { - "modbus点表名称,自定义,非中文、非特殊字符,32字节": "modbus点表文件路径", - "pcs2": "point_table_modbus_01.json", - "bms": "point_table_modbus_02.json", - "tms": "point_table_modbus_03.json" - } - } -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/point_table_modbus_01.json b/public/commun_config_modbustcp/point_table_modbus_01.json deleted file mode 100644 index 550aa8c..0000000 --- a/public/commun_config_modbustcp/point_table_modbus_01.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "p12312cs", - "point": [ - [ - "点起始地址,进制字符串, 范围 0x0000~0xFFFF 或 0~65535", - "点数量,字符串, 范围 0x0001~0x10000 或 1~65536", - "功能码,字符串,可选项: '0x01' '0x02' '0x03' '0x04' 或 '1' '2' '3' '4'", - "数据类型,字符串,可选 'U16' 'S16' 'U32' 'S32'", - "点读取周期ms,整形数字,-1时标志该点位未启用", - "点位段名称,字符串,可设为空字符串" - ], - [ - "0x0001", - "0x0010", - "0x03", - "U16", - 1000, - "点位段名称1" - ], - [ - "0x0100", - "0x0020", - "0x03", - "U16", - 1000, - "点位段名称2" - ], - [ - "0x0120", - "0x0020", - "0x03", - "U32", - 1000, - "" - ], - [ - "0x0000", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ], - [ - "0x0020", - "0x0010", - "0x04", - "U32", - 1000, - "-" - ], - [ - "0x0040", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ] - ] -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/point_table_modbus_02.json b/public/commun_config_modbustcp/point_table_modbus_02.json deleted file mode 100644 index f694b7b..0000000 --- a/public/commun_config_modbustcp/point_table_modbus_02.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "xxxx", - "point": [ - [ - "点起始地址,进制字符串, 范围 0x0000~0xFFFF 或 0~65535", - "点数量,字符串, 范围 0x0001~0x10000 或 1~65536", - "功能码,字符串,可选项: '0x01' '0x02' '0x03' '0x04' 或 '1' '2' '3' '4'", - "数据类型,字符串,可选 'U16' 'S16' 'U32' 'S32'", - "点读取周期ms,整形数字,-1时标志该点位未启用", - "点位段名称,字符串,可设为空字符串" - ], - [ - "0x0001", - "0x0010", - "0x03", - "U16", - 1000, - "点位段名称1" - ], - [ - "0x0100", - "0x0020", - "0x03", - "U16", - 1000, - "点位段名称2" - ], - [ - "0x0120", - "0x0020", - "0x03", - "U32", - 1000, - "" - ], - [ - "0x0000", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ], - [ - "0x0020", - "0x0010", - "0x04", - "U32", - 1000, - "-" - ], - [ - "0x0040", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ] - ] -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/point_table_modbus_03.json b/public/commun_config_modbustcp/point_table_modbus_03.json deleted file mode 100644 index f694b7b..0000000 --- a/public/commun_config_modbustcp/point_table_modbus_03.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "xxxx", - "point": [ - [ - "点起始地址,进制字符串, 范围 0x0000~0xFFFF 或 0~65535", - "点数量,字符串, 范围 0x0001~0x10000 或 1~65536", - "功能码,字符串,可选项: '0x01' '0x02' '0x03' '0x04' 或 '1' '2' '3' '4'", - "数据类型,字符串,可选 'U16' 'S16' 'U32' 'S32'", - "点读取周期ms,整形数字,-1时标志该点位未启用", - "点位段名称,字符串,可设为空字符串" - ], - [ - "0x0001", - "0x0010", - "0x03", - "U16", - 1000, - "点位段名称1" - ], - [ - "0x0100", - "0x0020", - "0x03", - "U16", - 1000, - "点位段名称2" - ], - [ - "0x0120", - "0x0020", - "0x03", - "U32", - 1000, - "" - ], - [ - "0x0000", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ], - [ - "0x0020", - "0x0010", - "0x04", - "U32", - 1000, - "-" - ], - [ - "0x0040", - "0x0020", - "0x04", - "U16", - 1000, - "点位段名称" - ] - ] -} \ No newline at end of file diff --git a/public/commun_config_modbustcp/工程说明.md b/public/commun_config_modbustcp/工程说明.md deleted file mode 100644 index 9e253dd..0000000 --- a/public/commun_config_modbustcp/工程说明.md +++ /dev/null @@ -1,53 +0,0 @@ - - -## 创建工程 - -填写工程名,用户自定义输入 - - - -## 通讯点表 - -1. 展示目前工程中已有的通讯点表 - -2. 导入新的通讯点表 - - ``` - 自定义名称:设置点表自定义名字,用户自定义输入,需检查唯一性,禁止中文、特殊字符 - 通讯点表类型:通过下拉菜单选择,目前只支持ModbusTcp(预留支持其他类型,不同类型的点表需要分类保存) - ``` - - - -## 通讯通道 - -1. 展示目前工程中已有的通讯通道 - -2. 创建新的通讯通道 - - ``` - 自定义名称:设置通讯通道自定义名字,用户自定义输入,需检查唯一性,禁止中文、特殊字符 - 通讯通道类型:通过下拉菜单选择,目前只支持ModbusTcp(预留支持其他类型,不同类型的通讯通道分类保存) - 设备IP地址:用户自定义输入,须符合IP地址的格式,如192.168.10.123 - 设备端口:用户自定义输入,范围0~65535,一般为502 - ``` - - - -## 通讯设备 - -1. 展示目前工程中已有的通讯设备 - -2. 创建新的通讯设备 - - ``` - 自定义名称:设置通讯设备自定义名字,用户自定义输入,需检查唯一性,禁止中文、特殊字符 - 通讯设备类型:通过下拉菜单选择,目前只支持ModbusTcp(预留支持其他类型,不同类型的通讯设备分类保存) - 通讯通道选择:通过下拉菜单选择,选项为已选择的类型(目前只有ModbusTcp)的所有通讯通道(是否支持在此处新建通道) - 通讯点表选择:通过下拉菜单选择,选项为已选择的类型(目前只有ModbusTcp)的所有通讯点表(是否支持在此处新建点表) - 通讯地址:用户自定义输入通讯站地址,范围0~255,一般为1 - ``` - - - - \ No newline at end of file diff --git a/src/api/module/engineering/index.ts b/src/api/module/engineering/index.ts index 0e92bcc..8cd8a6e 100644 --- a/src/api/module/engineering/index.ts +++ b/src/api/module/engineering/index.ts @@ -13,10 +13,8 @@ export const createEngineering = (params: IEngineeringOV) => { export const getEngineeringList = ( params?: Pick, ) => { - return globalServer<{ - list: IEngineeringOV[] - }>({ - url: '/project/page', + return globalServer({ + url: '/project/list', method: 'get', params, }) diff --git a/src/api/module/index.ts b/src/api/module/index.ts index ca6b36c..43c5ddc 100644 --- a/src/api/module/index.ts +++ b/src/api/module/index.ts @@ -2,5 +2,6 @@ import axiosInstance from '@/api/server/axiosInstance' import { API_Config } from '@/api/server/config' const globalServer = axiosInstance('global', API_Config.global) +const systemServer = axiosInstance('system', API_Config.system) -export { globalServer } +export { globalServer, systemServer } diff --git a/src/api/module/system/dept/index.ts b/src/api/module/system/dept/index.ts new file mode 100644 index 0000000..c344de7 --- /dev/null +++ b/src/api/module/system/dept/index.ts @@ -0,0 +1,57 @@ +import { systemServer } from '../../index' +export interface DeptVO { + id?: number + name: string + parentId: number + status: number + sort: number + leaderUserId: number + phone: string + email: string + createTime: Date +} + +// 查询部门(精简)列表 +export const getSimpleDeptList = async () => + systemServer({ + url: '/dept/simple-list', + method: 'get', + }) + +// 查询部门列表 +export const getDeptPage = async (params: PageParam) => + systemServer({ + url: '/dept/page', + method: 'get', + params, + }) + +// 查询部门详情 +export const getDept = async (id: number) => + systemServer({ + url: '/dept/get?id=' + id, + method: 'get', + }) + +// 新增部门 +export const createDept = async (data: DeptVO) => + systemServer({ + url: '/dept/create', + method: 'post', + data, + }) + +// 修改部门 +export const updateDept = async (params: DeptVO) => + systemServer({ + url: '/dept/update', + method: 'put', + data: params, + }) + +// 删除部门 +export const deleteDept = async (id: number) => + systemServer({ + url: '/dept/delete?id=' + id, + method: 'delete', + }) diff --git a/src/api/module/system/dict/dict.data.ts b/src/api/module/system/dict/dict.data.ts new file mode 100644 index 0000000..ada29de --- /dev/null +++ b/src/api/module/system/dict/dict.data.ts @@ -0,0 +1,68 @@ + +import { systemServer } from '../../index' + +export type DictDataVO = { + id: number | undefined + sort: number | undefined + label: string + value: string + dictType: string + status: number + colorType: string + cssClass: string + remark: string + createTime: Date +} + +// 查询字典数据(精简)列表 +export const getSimpleDictDataList = () => + systemServer({ + url: '/dict-data/simple-list', + method: 'get', + }) + +// 查询字典数据列表 +export const getDictDataPage = (params: PageParam) => + systemServer({ + url: '/dict-data/page', + method: 'get', + params, + }) +// 查询字典数据详情 +export const getDictData = (id: number) => + systemServer({ + url: '/dict-data/get?id=' + id, + method: 'get', + }) + +// 新增字典数据 +export const createDictData = (data: DictDataVO) => + systemServer({ + url: '/dict-data/create', + method: 'post', + data, + }) + +// 修改字典数据 +export const updateDictData = (data: DictDataVO) => + systemServer({ + url: '/dict-data/update', + method: 'put', + data, + }) + +// 删除字典数据 +export const deleteDictData = (id: number) => + systemServer({ + url: '/dict-data/delete?id=' + id, + method: 'delete', + }) + +// 导出字典类型数据 +export const exportDictData = (params:any) => + systemServer({ + url: '/dict-data/export', + method: 'get', + params, + responseType: 'blob', + }) \ No newline at end of file diff --git a/src/api/module/system/dict/dict.type.ts b/src/api/module/system/dict/dict.type.ts new file mode 100644 index 0000000..b7da000 --- /dev/null +++ b/src/api/module/system/dict/dict.type.ts @@ -0,0 +1,65 @@ + +import { systemServer } from '../../index' + +export type DictTypeVO = { + id: number | undefined + name: string + type: string + status: number + remark: string + createTime: Date +} + +// 查询字典(精简)列表 +export const getSimpleDictTypeList = () => + systemServer({ + url: '/dict-type/list-all-simple', + method: 'get', + }) + +// 查询字典列表 +export const getDictTypePage = (params: PageParam) => + systemServer({ + url: '/dict-type/page', + method: 'get', + params, + }) + +// 查询字典详情 +export const getDictType = (id: number) => + systemServer({ + url: '/dict-type/get?id=' + id, + method: 'get', + }) + +// 新增字典 +export const createDictType = (data: DictTypeVO) => + systemServer({ + url: '/dict-type/create', + method: 'post', + data, + }) + +// 修改字典 +export const updateDictType = (data: DictTypeVO) => + systemServer({ + url: '/dict-type/update', + method: 'put', + data, + }) + +// 删除字典 +export const deleteDictType = (id: number) => + systemServer({ + url: '/dict-type/delete?id=' + id, + method: 'delete', + }) + +// 导出字典类型 +export const exportDictType = (params: any) => + systemServer({ + url: '/dict-type/export', + method: 'get', + params, + responseType: 'blob', + }) diff --git a/src/api/module/system/file/index.ts b/src/api/module/system/file/index.ts new file mode 100644 index 0000000..77c9210 --- /dev/null +++ b/src/api/module/system/file/index.ts @@ -0,0 +1,50 @@ +import { systemServer } from '../../index' +export interface FilePageReqVO extends PageParam { + path?: string + type?: string + createTime?: Date[] +} + +// 文件预签名地址 Response VO +export interface FilePresignedUrlRespVO { + // 文件配置编号 + configId: number + // 文件上传 URL + uploadUrl: string + // 文件 URL + url: string +} + +// 查询文件列表 +export const getFilePage = (params: FilePageReqVO) => { + return systemServer({ url: '/infra/file/page', params, method: 'get' }) +} + +// 删除文件 +export const deleteFile = (id: number) => { + return systemServer({ url: '/infra/file/delete?id=' + id, method: 'delete' }) +} + +// 获取文件预签名地址 +export const getFilePresignedUrl = (path: string) => { + return systemServer({ + url: '/infra/file/presigned-url', + params: { path }, + method: 'get', + }) +} + +// 创建文件 +export const createFile = (data: any) => { + return systemServer({ url: '/infra/file/create', data, method: 'post' }) +} + +// 上传文件 +export const updateFile = (data: any) => { + return systemServer({ + url: '/infra/file/upload', + data, + method: 'post', + headers: { 'Content-Type': 'multipart/form-data' }, + }) +} diff --git a/src/api/module/system/login/index.ts b/src/api/module/system/login/index.ts new file mode 100644 index 0000000..658e462 --- /dev/null +++ b/src/api/module/system/login/index.ts @@ -0,0 +1,106 @@ +import { getRefreshToken } from '@/utils/auth' +import { systemServer } from '../../index' + +type UserLoginVO = { + username: string + password: string + captchaVerification: string + socialType?: string + socialCode?: string + socialState?: string +} + +export interface LoginRequestData { + username: string + password: string + // tenantName: string + // captchaVerification: string + // rememberMe: boolean +} +export interface SmsCodeVO { + mobile: string + scene: number +} + +export interface SmsLoginVO { + mobile: string + code: string +} +interface LoginResponse { + accessToken: string + expiresTime: number + refreshToken: string + userId: number +} + +export const getTenantId = (name: string) => + systemServer({ + url: '/tenant/get-id-by-name', + method: 'get', + params: { + name, + }, + }) + +export const login = (data: any) => + systemServer({ + method: 'post', + url: '/auth/login', + data, + }) + +// 刷新访问令牌 +export const refreshToken = () => + systemServer({ + url: '/auth/refresh-token?refreshToken=' + getRefreshToken(), + method: 'post', + }) + +// // 使用租户域名,获得租户信息 +// export const getTenantByWebsite = (website: string) => { +// return request.get({ url: '/tenant/get-by-website?website=' + website }) +// } + +// // 登出 +export const loginOut = () => systemServer({ url: '/auth/logout', method: 'post' }) + +export const getInfo = () => + systemServer({ url: '/auth/get-permission-info', method: 'get' }) + +// //获取登录验证码 +export const sendSmsCode = (data: SmsCodeVO) => { + return systemServer({ url: '/auth/send-sms-code', method: 'post', data }) +} + +// // 短信验证码登录 +export const smsLogin = (data: SmsLoginVO) => { + return systemServer({ url: '/auth/sms-login', method: 'post', data }) +} + +// // 社交快捷登录,使用 code 授权码 +// export function socialLogin(type: string, code: string, state: string) { +// return request.post({ +// url: '/auth/social-login', +// data: { +// type, +// code, +// state +// } +// }) +// } + +// // 社交授权的跳转 +// export const socialAuthRedirect = (type: number, redirectUri: string) => { +// return request.get({ +// url: '/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri +// }) +// } +// // 获取验证图片以及 token +// export const getCode = (data) => { +// return request.postOriginal({ url: 'system/captcha/get', data }) +// } + +// // 滑动或者点选验证 +// export const reqCheck = (data) => { +// return request.postOriginal({ url: 'system/captcha/check', data }) +// } diff --git a/src/api/module/system/loginLog/index.ts b/src/api/module/system/loginLog/index.ts new file mode 100644 index 0000000..29299e2 --- /dev/null +++ b/src/api/module/system/loginLog/index.ts @@ -0,0 +1,33 @@ +import { systemServer } from '../../index' +export interface LoginLogVO { + id: number + logType: number + traceId: number + userId: number + userType: number + username: string + result: number + status: number + userIp: string + userAgent: string + createTime: Date +} + +// 查询登录日志列表 +export const getLoginLogPage = (params: PageParam) => { + return systemServer({ + url: '/login-log/page', + method: 'get', + params, + }) +} + +// 导出登录日志 +export const exportLoginLog = (params: any) => { + return systemServer({ + url: '/login-log/export', + method: 'get', + params, + responseType: 'blob', + }) +} diff --git a/src/api/module/system/menu/index.ts b/src/api/module/system/menu/index.ts new file mode 100644 index 0000000..7942111 --- /dev/null +++ b/src/api/module/system/menu/index.ts @@ -0,0 +1,63 @@ +import { systemServer } from '../../index' +export interface MenuVO { + id: number + name: string + permission: string + type: number + sort: number + parentId: number + path: string + icon: string + component: string + componentName?: string + status: number + visible: boolean + keepAlive: boolean + alwaysShow?: boolean + createTime: Date +} + +// 查询菜单(精简)列表 +export const getSimpleMenusList = () => + systemServer({ + url: '/menu/simple-list', + method: 'get', + }) + +// 查询菜单列表 +export const getMenuList = (params: any) => + systemServer({ + url: '/menu/list', + method: 'get', + params, + }) + +// 获取菜单详情 +export const getMenu = (id: number) => + systemServer({ + url: '/menu/get?id=' + id, + method: 'get', + }) + +// 新增菜单 +export const createMenu = (data: MenuVO) => + systemServer({ + url: '/menu/create', + method: 'post', + data, + }) + +// 修改菜单 +export const updateMenu = (data: MenuVO) => + systemServer({ + url: '/menu/update', + method: 'put', + data, + }) + +// 删除菜单 +export const deleteMenu = (id: number) => + systemServer({ + url: '/menu/delete?id=' + id, + method: 'delete', + }) diff --git a/src/api/module/system/operatelog/index.ts b/src/api/module/system/operatelog/index.ts new file mode 100644 index 0000000..e4c2d6e --- /dev/null +++ b/src/api/module/system/operatelog/index.ts @@ -0,0 +1,51 @@ + +import { systemServer } from '../../index' + +export enum Action { + //用户个人操作 + USER_LOGIN = '登录', + USER_LOGOUT = '登出', + //用户管理 + USERMGR_ADD = '添加用户', + USERMGR_DEL = '删除用户', + USERMGR_MODIFY = '更改用户', + //zmq命令 + ZMQ_CMD_PUBLISH = '设备命令下发', + //ssh + SSH_LOGIN = 'SSH连接', + SSH_LOGOUT = 'SSH中断连接' +} + +export type OperateLogVO = { + id: number + traceId: string + userType: number + userId: number + userName: string + type: string + subType: string + bizId: number + action: string + extra: string + requestMethod: string + requestUrl: string + userIp: string + userAgent: string + creator: string + creatorName: string + createTime: Date +} + +// 查询操作日志列表 +export const getOperateLogPage = (params: PageParam) => { + return systemServer({ url: '/operate-log/page', method: 'get', params }) +} +// 导出操作日志 +export const exportOperateLog = (params: any) => { + return systemServer({ + url: '/operate-log/export', + method: 'get', + params, + responseType: 'blob', + }) +} diff --git a/src/api/module/system/permission/index.ts b/src/api/module/system/permission/index.ts new file mode 100644 index 0000000..eb4e6db --- /dev/null +++ b/src/api/module/system/permission/index.ts @@ -0,0 +1,54 @@ +import { systemServer } from '../../index' +export interface PermissionAssignUserRoleReqVO { + userId: number + roleIds: number[] +} + +export interface PermissionAssignRoleMenuReqVO { + roleId: number + menuIds: number[] +} + +export interface PermissionAssignRoleDataScopeReqVO { + roleId: number + dataScope: number + dataScopeDeptIds: number[] +} + +// 查询角色拥有的菜单权限 +export const getRoleMenuList = async (roleId: number) => + systemServer({ + url: '/permission/list-role-menus?roleId=' + roleId, + method: 'get', + }) + +// 赋予角色菜单权限 +export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => + systemServer({ + url: '/permission/assign-role-menu', + method: 'post', + data, + }) + +// 赋予角色数据权限 +export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => + systemServer({ + url: '/permission/assign-role-data-scope', + method: 'post', + data, + }) + +// 查询用户拥有的角色数组 +export const getUserRoleList = async (userId: number) => + systemServer({ + url: '/permission/list-user-roles?userId=' + userId, + method: 'get', + }) + +// 赋予用户角色 +export const assignUserRole = async (data: PermissionAssignUserRoleReqVO) => + systemServer({ + url: '/permission/assign-user-role', + method: 'post', + data, + }) diff --git a/src/api/module/system/post/index.ts b/src/api/module/system/post/index.ts new file mode 100644 index 0000000..f577d12 --- /dev/null +++ b/src/api/module/system/post/index.ts @@ -0,0 +1,64 @@ +import { systemServer } from '../../index' +export interface PostVO { + id?: number + name: string + code: string + sort: number + status: number + remark: string + createTime?: Date +} + +// 查询岗位列表 +export const getPostPage = async (params: PageParam) => + systemServer({ + url: '/post/page', + method: 'get', + params, + }) + +// 获取岗位精简信息列表 +export const getSimplePostList = async () => + systemServer({ + url: '/post/simple-list', + method: 'get', + }) + +// 查询岗位详情 +export const getPost = async (id: number) => + systemServer({ + url: '/post/get?id=' + id, + method: 'get', + }) + +// 新增岗位 +export const createPost = async (data: PostVO) => + systemServer({ + url: '/post/create', + method: 'post', + data, + }) + +// 修改岗位 +export const updatePost = async (data: PostVO) => + systemServer({ + url: '/post/update', + method: 'put', + data, + }) + +// 删除岗位 +export const deletePost = async (id: number) => + systemServer({ + url: '/post/delete?id=' + id, + method: 'delete', + }) + +// 导出岗位 +export const exportPost = async (params: any) => + systemServer({ + url: '/post/export', + method: 'get', + params, + responseType: 'blob', + }) diff --git a/src/api/module/system/role/index.ts b/src/api/module/system/role/index.ts new file mode 100644 index 0000000..d92def9 --- /dev/null +++ b/src/api/module/system/role/index.ts @@ -0,0 +1,79 @@ +import { systemServer } from '../../index' +export interface RoleVO { + id: number + name: string + code: string + sort: number + status: number + type: number + dataScope: number + dataScopeDeptIds: number[] + createTime: Date +} + +export interface UpdateStatusReqVO { + id: number + status: number +} + +// 查询角色列表 +export const getRolePage = async (params: PageParam) => + systemServer({ + url: '/role/page', + method: 'get', + params, + }) + +// 查询角色(精简)列表 +export const getSimpleRoleList = async () => + systemServer({ + url: '/role/simple-list', + method: 'get', + }) + +// 查询角色详情 +export const getRole = async (id: number) => + systemServer({ + url: '/role/get?id=' + id, + method: 'get', + }) + +// 新增角色 +export const createRole = async (data: RoleVO) => + systemServer({ + url: '/role/create', + method: 'post', + data, + }) + +// 修改角色 +export const updateRole = async (data: RoleVO) => + systemServer({ + url: '/role/update', + method: 'put', + data, + }) + +// 修改角色状态 +export const updateRoleStatus = async (data: UpdateStatusReqVO) => + systemServer({ + url: '/role/update-status', + method: 'put', + data, + }) + +// 删除角色 +export const deleteRole = async (id: number) => + systemServer({ + url: '/role/delete?id=' + id, + method: 'delete', + }) + +// 导出角色 +export const exportRole = (params: any) => + systemServer({ + url: '/role/export-excel', + method: 'get', + params, + responseType: 'blob', + }) diff --git a/src/api/module/system/tenant/index.ts b/src/api/module/system/tenant/index.ts new file mode 100644 index 0000000..6d1444b --- /dev/null +++ b/src/api/module/system/tenant/index.ts @@ -0,0 +1,80 @@ +import { systemServer } from '../../index' + + +export interface TenantVO { + id: number + name: string + contactName: string + contactMobile: string + status: number + domain: string + packageId: number + username: string + password: string + expireTime: Date + accountCount: number + createTime: Date +} + +export interface TenantPageReqVO extends PageParam { + name?: string + contactName?: string + contactMobile?: string + status?: number + createTime?: Date[] +} + +export interface TenantExportReqVO { + name?: string + contactName?: string + contactMobile?: string + status?: number + createTime?: Date[] +} + +// 查询租户列表 +export const getTenantPage = (params: TenantPageReqVO) => + systemServer({ + url: '/tenant/page', + method: 'get', + params, + }) + +// 查询租户详情 +export const getTenant = (id: number) => + systemServer({ + url: '/tenant/get?id=' + id, + method: 'get', + }) + +// 新增租户 +export const createTenant = (data: TenantVO) => + systemServer({ + url: '/tenant/create', + method: 'post', + data, + }) + +// 修改租户 +export const updateTenant = (data: TenantVO) => + systemServer({ + url: '/tenant/update', + method: 'put', + data, + }) + +// 删除租户 +export const deleteTenant = (id: number) => + systemServer({ + url: '/tenant/delete?id=' + id, + method: 'delete', + }) + +// 导出租户 +export const exportTenant = (params: TenantExportReqVO) => + systemServer({ + url: '/tenant/export-excel', + method: 'get', + params, + responseType: 'blob', + }) diff --git a/src/api/module/system/tenantPackage/index.ts b/src/api/module/system/tenantPackage/index.ts new file mode 100644 index 0000000..34847d6 --- /dev/null +++ b/src/api/module/system/tenantPackage/index.ts @@ -0,0 +1,58 @@ +import { systemServer } from '../../index' + +export interface TenantPackageVO { + id: number + name: string + status: number + remark: string + creator: string + updater: string + updateTime: string + menuIds: number[] + createTime: Date +} + +// 查询租户套餐列表 +export const getTenantPackagePage = (params: PageParam) => + systemServer({ + url: '/tenant-package/page', + method: 'get', + params, + }) + +// 获得租户 +export const getTenantPackage = (id: number) => + systemServer({ + url: '/tenant-package/get?id=' + id, + method: 'get', + }) + +// 新增租户套餐 +export const createTenantPackage = (data: TenantPackageVO) => + systemServer({ + url: '/tenant-package/create', + method: 'post', + data, + }) + +// 修改租户套餐 +export const updateTenantPackage = (data: TenantPackageVO) => + systemServer({ + url: '/tenant-package/update', + method: 'put', + data, + }) + +// 删除租户套餐 +export const deleteTenantPackage = (id: number) => + systemServer({ + url: '/tenant-package/delete?id=' + id, + method: 'delete', + }) + +// 获取租户套餐精简信息列表 +export const getTenantPackageList = () => + systemServer({ + url: '/tenant-package/simple-list', + method: 'get', + }) diff --git a/src/api/module/system/user/index.ts b/src/api/module/system/user/index.ts new file mode 100644 index 0000000..c226d5c --- /dev/null +++ b/src/api/module/system/user/index.ts @@ -0,0 +1,121 @@ +import { systemServer } from '../../index' + +export interface UserVO { + id: number + username: string + nickname: string + deptId: number + postIds: string[] + email: string + mobile: string + sex: number + avatar: string + loginIp: string + status: number + remark: string + loginDate: Date + createTime: Date +} + +export interface UserSimple { + id: number; + nickname: string; + deptId: number; + deptName: string; +} + +// 查询用户管理列表 +export const getUserPage = (params: PageParam) => + systemServer({ + url: '/user/page', + method: 'get', + params, + }) + +// 查询所有用户列表 +export const getAllUser = () => + systemServer({ + url: '/user/all', + method: 'get', + }) + +// 查询用户详情 +export const getUser = (id: number) => + systemServer({ + url: '/user/get?id=' + id, + method: 'get', + }) + +// 新增用户 +export const createUser = (data: UserVO) => + systemServer({ + url: '/user/create', + method: 'post', + data, + }) + +// 修改用户 +export const updateUser = (data: UserVO) => + systemServer({ + url: '/user/update', + method: 'put', + data, + }) + +// 删除用户 +export const deleteUser = (id: number) => + systemServer({ + url: '/user/delete?id=' + id, + method: 'delete', + }) + +// 导出用户 +export const exportUser = (params: any) => + systemServer({ + url: '/user/export', + method: 'get', + responseType: 'blob', + params, + }) + +// 下载用户导入模板 +export const importUserTemplate = () => + systemServer({ + url: '/user/get-import-template', + method: 'get', + responseType: 'blob', + }) + +// 用户密码重置 +export const resetUserPwd = (id: number, password: string) => { + const data = { + id, + password, + } + return systemServer({ + url: '/user/reset-password', + method: 'put', + data, + }) +} + +// 用户状态修改 +export const updateUserStatus = (id: number, status: number) => { + const data = { + id, + status, + } + return systemServer({ + url: '/user/update-status', + method: 'put', + data, + }) +} + +// 获取用户精简信息列表 +export const getSimpleUserList = () => { + return systemServer({ + url: '/user/simple-list', + method: 'get', + }) +} diff --git a/src/api/module/system/user/profile.ts b/src/api/module/system/user/profile.ts new file mode 100644 index 0000000..59e70bd --- /dev/null +++ b/src/api/module/system/user/profile.ts @@ -0,0 +1,75 @@ +import { systemServer } from '../../index' +export interface ProfileVO { + id: number + username: string + nickname: string + dept: { + id: number + name: string + } + roles: { + id: number + name: string + }[] + posts: { + id: number + name: string + }[] + socialUsers: { + type: number + openid: string + }[] + email: string + mobile: string + sex: number + avatar: string + status: number + remark: string + loginIp: string + loginDate: Date + createTime: Date +} + +export interface UserProfileUpdateReqVO { + nickname: string + email: string + mobile: string + sex: number +} + +// 查询用户个人信息 +export const getUserProfile = () => + systemServer({ + url: '/user/profile/get', + method: 'get', + }) + +// 修改用户个人信息 +export const updateUserProfile = (data: UserProfileUpdateReqVO) => + systemServer({ + url: '/user/profile/update', + method: 'put', + data, + }) + +// 用户密码重置 +export const updateUserPassword = (oldPassword: string, newPassword: string) => + systemServer({ + url: '/user/profile/update-password', + method: 'put', + data: { + oldPassword, + newPassword, + }, + }) + +// 用户头像上传 +export const uploadAvatar = (data: any) => + systemServer({ + url: '/user/profile/update-avatar', + data, + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) diff --git a/src/api/module/system/user/socialUser.ts b/src/api/module/system/user/socialUser.ts new file mode 100644 index 0000000..398f0a0 --- /dev/null +++ b/src/api/module/system/user/socialUser.ts @@ -0,0 +1,30 @@ +import { systemServer } from '../../index' +// 社交绑定,使用 code 授权码 +export const socialBind = (type: any, code: any, state: any) => + systemServer({ + url: '/social-user/bind', + method: 'post', + data: { + type, + code, + state, + }, + }) + +// 取消社交绑定 +export const socialUnbind = (type: any, openid: any) => + systemServer({ + url: '/social-user/unbind', + method: 'delete', + data: { + type, + openid, + }, + }) + +// 社交授权的跳转 +export const socialAuthRedirect = (type: any, redirectUri: any) => + systemServer({ + url: '/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri, + method: 'get', + }) diff --git a/src/api/server/axiosInstance.ts b/src/api/server/axiosInstance.ts index 8fb273d..040c83e 100644 --- a/src/api/server/axiosInstance.ts +++ b/src/api/server/axiosInstance.ts @@ -67,6 +67,10 @@ const createAxiosInstance = (module: APIConfigKeys, config: Config) => { (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { // config.headers.Authorization = basicHeader.token() // config.headers['tenant-id'] = basicHeader.tenant() + if (config.url?.includes('/login')) { + config.headers['tenant-id'] = '1' + } + const params = config.params || {} const data = config.data || false diff --git a/src/api/server/config.ts b/src/api/server/config.ts index cee8eca..d36f6d4 100644 --- a/src/api/server/config.ts +++ b/src/api/server/config.ts @@ -1,5 +1,6 @@ export interface APIConfig { global: Config + system: Config } interface Config { @@ -11,6 +12,9 @@ const API_Config: APIConfig = { global: { baseAPI: import.meta.env.VITE_BASE_API, }, + system: { + baseAPI: import.meta.env.VITE_BASE_API_SYSTEM, + }, } diff --git a/src/assets/images/login/avatar.png b/src/assets/images/login/avatar.png new file mode 100644 index 0000000..720f7d2 Binary files /dev/null and b/src/assets/images/login/avatar.png differ diff --git a/src/assets/images/login/bg.png b/src/assets/images/login/bg.png new file mode 100644 index 0000000..b9ef171 Binary files /dev/null and b/src/assets/images/login/bg.png differ diff --git a/src/assets/images/login/logo.png b/src/assets/images/login/logo.png new file mode 100644 index 0000000..0439741 Binary files /dev/null and b/src/assets/images/login/logo.png differ diff --git a/src/hooks/useCache.ts b/src/hooks/useCache.ts new file mode 100644 index 0000000..1902b8d --- /dev/null +++ b/src/hooks/useCache.ts @@ -0,0 +1,38 @@ +/** + * 配置浏览器本地存储的方式,可直接存储对象数组。 + */ + +import Keys from '@/api/Keys' +import WebStorageCache from 'web-storage-cache' + +type CacheType = 'localStorage' | 'sessionStorage' + +export const useCache = (type: CacheType = 'localStorage') => { + const wsCache: WebStorageCache = new WebStorageCache({ + storage: type, + }) + + return { + wsCache, + } +} + +export const deleteUserCache = () => { + const { wsCache } = useCache() + wsCache.delete(Keys.STORAGE_USER_INFO) + wsCache.delete(Keys.STORAGE_ROLE_ROUTERS) + wsCache.delete(Keys.STORAGE_STATIONID) +} + +export function getCacheStationId( + stationId: number | string, + type: 'get' | 'set' = 'get' +) { + const { wsCache } = useCache() + const id = wsCache.get(Keys.STORAGE_STATIONID) + if (!id || type === 'set') { + wsCache.set(Keys.STORAGE_STATIONID, stationId) + return Number(stationId) + } + return Number(id) +} diff --git a/src/hooks/useMessage.ts b/src/hooks/useMessage.ts new file mode 100644 index 0000000..51ce7c3 --- /dev/null +++ b/src/hooks/useMessage.ts @@ -0,0 +1,148 @@ +import Keys from '@/api/Keys' +import type { Result } from '@/api/basic/httpTypes' +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' +export const useMessage = () => { + return { + // 消息提示 + info(content: string) { + ElMessage.info(content) + }, + // 错误消息 + error(content: string) { + ElMessage.error(content) + }, + // 成功消息 + success(content: string) { + ElMessage.success(content) + }, + // 警告消息 + warning(content: string) { + ElMessage.warning(content) + }, + // 弹出提示 + alert(content: string) { + ElMessageBox.alert(content, '系统提示') + }, + // 错误提示 + alertError(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'error' }) + }, + // 成功提示 + alertSuccess(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'success' }) + }, + // 警告提示 + alertWarning(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'warning' }) + }, + // 通知提示 + notify(content: string) { + ElNotification.info(content) + }, + // 错误通知 + notifyError(content: string, tip?: string) { + ElNotification.error({ + title: tip ? tip : '系统提示', + message: content, + }) + }, + // 成功通知 + notifySuccess(content: string) { + ElNotification.success(content) + }, + // 警告通知 + notifyWarning(content: string) { + ElNotification.warning(content) + }, + // 确认窗体 + confirm(content: string, tip?: string) { + return ElMessageBox.confirm(content, tip ? tip : '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + type: 'warning', + }) + }, + forceConfirm(content: string, tip?: string, buttonText?: string) { + return ElMessageBox.confirm(content, tip ? tip : '系统提示', { + confirmButtonText: buttonText ?? '确定', + showCancelButton: false, + closeOnClickModal: false, + closeOnPressEscape: false, + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + showClose: false, + type: 'warning', + }) + }, + // 删除窗体 + delConfirm(content?: string, tip?: string) { + return new Promise((resolve, reject) => { + ElMessageBox.confirm( + content ? content : '是否确认删除数据项?', + tip ? tip : '系统提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + } + ) + .then(() => { + resolve('') + }) + .catch(() => { + reject('') + }) + }) + }, + // 导出窗体 + exportConfirm(content?: string, tip?: string) { + return ElMessageBox.confirm( + content ? content : '是否确认导出数据项?', + tip ? tip : '系统提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + type: 'warning', + } + ) + }, + // 提交内容 + prompt(content: string, tip: string) { + return ElMessageBox.prompt(content, tip, { + confirmButtonText: '确定', + cancelButtonText: '取消', + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + type: 'warning', + }) + }, + + promptVerify(content: string, tip: string, pattern: string, inputErrorMessage = '') { + const PatternRegExp = new RegExp( + `^${pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}$` + ) + return ElMessageBox.prompt(content, tip, { + confirmButtonText: '确定', + cancelButtonText: '取消', + confirmButtonClass: 'el-button--success', + cancelButtonClass: 'el-button--default', + inputPattern: PatternRegExp, + inputErrorMessage: inputErrorMessage, + type: 'warning', + }) + }, + } +} + +export function isResError(res: Result, msg?: string) { + if (res.code === Keys.CODE_SUCCEED || res.status === 200 || res.code === 200) + return false + ElMessage.error(res?.msg ?? msg) + return true +} diff --git a/src/router/index.ts b/src/router/index.ts index 9b5a9c6..48452f2 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,6 +1,17 @@ +import { getToken } from '@/utils/auth'; import { createRouter, createWebHistory } from 'vue-router' export const defaultRouter = [ + { + path: '/login', + component: () => import('@/views/login/index.vue'), + name: 'Login', + meta: { + hidden: true, + title: '登录', + noTagsView: true, + }, + }, { path: '/', name: 'dashboard', @@ -58,4 +69,24 @@ const router = createRouter({ routes: defaultRouter, }) + +const whiteList = ['/login'] + +router.beforeEach(async (to, from, next) => { + if (!getToken()) { + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + next('/login') + } + return + } + + if (to.path === '/login' || to.path === '/') { + next('/engineering') + return + } + next() +}) + export default router diff --git a/src/stores/engineering.ts b/src/stores/engineering.ts index f8cde81..cdec045 100644 --- a/src/stores/engineering.ts +++ b/src/stores/engineering.ts @@ -13,7 +13,7 @@ export const useEngineeringStore = defineStore('engineering', () => { loading.value = true try { const res = await getEngineeringList() - engineeringList.value = Array.isArray(res.data?.list) ? res.data.list : [] + engineeringList.value = Array.isArray(res.data) ? res.data : [] } catch (error) { console.error(error) engineeringList.value = [] diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 0000000..01fb0a3 --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,38 @@ +import Keys from "@/api/Keys"; +// @ts-ignore +import { useCache } from "@/hooks/useCache"; +const { wsCache } = useCache(); + +export const getToken = () => wsCache.get(Keys.STORAGE_TOKEN); + +export const setToken = (token: string) => { + wsCache.set(Keys.STORAGE_TOKEN, token); +}; + +export const setRefreshToken = (refreshToken: any) => { + wsCache.set(Keys.STORAGE_REFRESH_TOKEN, refreshToken); +}; + +export const getRefreshToken = () => wsCache.get(Keys.STORAGE_REFRESH_TOKEN); + +export const removeToken = () => { + wsCache.delete(Keys.STORAGE_TOKEN); + wsCache.delete(Keys.STORAGE_REFRESH_TOKEN); +}; + +export const getLocalUserInfo = () => wsCache.get(Keys.STORAGE_USER_INFO); + +export const setUserInfo = (userInfo: any) => + wsCache.set(Keys.STORAGE_USER_INFO, userInfo); + +// ========== 权限路由相关 ========== +export const getRoleRouters = () => wsCache.get(Keys.STORAGE_ROLE_ROUTERS); + +export const setRoleRouters = (roleRouters: any) => + wsCache.set(Keys.STORAGE_ROLE_ROUTERS, roleRouters); + +// ========== 租户相关 ========== +export const getTenantId = () => wsCache.get(Keys.STORAGE_TENANT_ID); + +export const setTenantId = (tenantId: number) => + wsCache.set(Keys.STORAGE_TENANT_ID, tenantId); diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts new file mode 100644 index 0000000..6b68afc --- /dev/null +++ b/src/utils/hooks.ts @@ -0,0 +1,124 @@ +import { debounce } from 'lodash-es' +import { onScopeDispose } from 'vue' + +export function useWindowKeyEnter(fn: Function) { + let isComposing = false + + window.addEventListener('compositionstart', () => { + isComposing = true + }) + window.addEventListener('compositionend', () => { + isComposing = false + }) + window.addEventListener('keydown', handleKeyDown) + + function handleKeyDown(e: KeyboardEvent) { + if (!isComposing && e.key === 'Enter') { + fn() + } + } + + onScopeDispose(() => { + window.removeEventListener('compositionstart', () => { + isComposing = true + }) + window.removeEventListener('compositionend', () => { + isComposing = false + }) + window.removeEventListener('keydown', handleKeyDown) + }) +} + +export function useWindowResize(fn: (...args: any[]) => any, delay: number = 300) { + const handleResize = debounce(fn, delay) + + window.addEventListener('resize', handleResize) + + return () => { + window.removeEventListener('resize', handleResize) + } +} + +export function useFlatArray(data: Array, flatKey: string = 'children') { + const result: any[] = [] + + function flat(items: any[]) { + for (const item of items) { + result.push(item) + if (item[flatKey] && item[flatKey].length > 0) { + flat(item[flatKey]) + } + } + } + + flat(data) + return result +} + +export const ipPattern = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ + +export function getColor(percentageStr: string) { + let percentage + if (!percentageStr.includes('%')) { + percentage = parseFloat(percentageStr.replace('%', '')) + } else { + percentage = parseFloat(percentageStr) + } + + if (percentage >= 0 && percentage <= 33.33) { + return '#D51B60' + } else if (percentage > 33.33 && percentage <= 66.66) { + return '#FC861B' + } else if (percentage > 66.66 && percentage <= 100) { + return '#00CD35' + } else { + return null // 超出范围 + } +} + +export function useParseHexToJson(hexString: string): object { + // 如果十六进制字符串长度是奇数,则在前面加零 + if (hexString.length % 2 !== 0) { + hexString = '0' + hexString + } + + // 将十六进制字符串转换为 ASCII 字符串 + function hexToAscii(hex: string) { + let asciiStr = '' + for (let i = 0; i < hex.length; i += 2) { + asciiStr += String.fromCharCode(parseInt(hex.substring(i, i + 2), 16)) + } + return asciiStr + } + + // 解析十六进制字符串并转换为 JSON 对象 + const asciiString = hexToAscii(hexString) + return JSON.parse(asciiString) +} + +interface ItemWithChildren { + children?: ItemWithChildren[] +} + +export function useFilterArray( + data: T[], + idToFilter: T[K], + idKey: K +): T[] { + return data + .map(item => { + if (item[idKey] === idToFilter) { + return null + } + + // 递归处理子节点 + const filteredChildren = Array.isArray(item.children) + ? useFilterArray(item.children as T[], idToFilter, idKey) + : null + + // 返回一个新的对象,包含过滤后的子节点 + return { ...item, children: filteredChildren } + }) + .filter(Boolean) as T[] +} diff --git a/src/views/engineering/config/index.vue b/src/views/engineering/config/index.vue index ffec9f1..7e4a77c 100644 --- a/src/views/engineering/config/index.vue +++ b/src/views/engineering/config/index.vue @@ -112,7 +112,7 @@ const router = useRouter() const route = useRoute() const projectName = computed(() => route.query.name as string) -const isCreate = computed(() => route.query.isCreate as string) +const isCreate = computed(() => route.query.type === 'create') type Step = 'channel' | 'category' | 'device' const steps: Step[] = ['channel', 'category', 'device'] diff --git a/src/views/layout/index.vue b/src/views/layout/index.vue index c992a1b..0bc9618 100644 --- a/src/views/layout/index.vue +++ b/src/views/layout/index.vue @@ -78,6 +78,23 @@ {{ currentTime }} +
+ +
+ + +
+ +
+
@@ -93,6 +110,10 @@ import { defaultRouter } from '@/router' import dayjs from 'dayjs' import { useEngineeringStore } from '@/stores/engineering' import { storeToRefs } from 'pinia' +import Avatar from '@/assets/images/login/avatar.png' +import { loginOut } from '@/api/module/system/login' +import { removeToken } from '@/utils/auth' +import { deleteUserCache } from '@/hooks/useCache' const unfold = 'i-icon-park-outline:menu-unfold' const fold = 'i-icon-park-outline:menu-fold' @@ -103,7 +124,7 @@ const engineeringStore = useEngineeringStore() const { engineeringList } = storeToRefs(engineeringStore) const menuList = computed(() => { - const routes = JSON.parse(JSON.stringify(defaultRouter[0].children)) + const routes = defaultRouter.find(r => r.name === 'dashboard')!.children as any[] const engRoute = routes.find((r: any) => r.path === '/engineering') if (engRoute) { // Initialize children array @@ -147,15 +168,8 @@ const engineeringTreeData = computed(() => [ }, ]) -const circleUrl = ref( - 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png', -) const isCollapse = ref(false) -const getIconClass = (icon: string) => { - return icon -} - const { push, currentRoute } = useRouter() const activeMenu = computed(() => { const { meta, path, query } = unref(currentRoute) @@ -183,6 +197,24 @@ const updateTime = () => { requestAnimationFrame(updateTime) } +function handleCommand(command: string) { + if (command === 'logout') { + logOut() + } +} +async function logOut() { + try { + await ElMessageBox.confirm('是否退出本系统?', '温馨提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + await loginOut() + removeToken() + deleteUserCache() + push('/login') + } catch {} +} onMounted(() => { updateTime() engineeringStore.fetchEngineeringList() @@ -279,8 +311,16 @@ onMounted(() => { } } - .avatar { - @apply w-24 h-24; + .right { + @apply h-full flex items-center; + + .user-avatar { + @apply flex items-center cursor-pointer outline-none; + + img { + @apply w-32px h-32px rounded-full; + } + } } .menu-icon { diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..f924e17 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,244 @@ + + + + +