Browse Source

feat: 增加登录

main
betaqi 4 weeks ago
parent
commit
f79b7526ef
  1. 3
      .env
  2. 3
      .gitignore
  3. 124
      .idea/workspace.xml
  4. 9
      global.types/components.d.ts
  5. 3
      package.json
  6. 8
      pnpm-lock.yaml
  7. 24
      public/commun_config_modbustcp/commun_channel_moebustcp.json
  8. 34
      public/commun_config_modbustcp/commun_dev_modbustcp.json
  9. 22
      public/commun_config_modbustcp/index.json
  10. 61
      public/commun_config_modbustcp/point_table_modbus_01.json
  11. 61
      public/commun_config_modbustcp/point_table_modbus_02.json
  12. 61
      public/commun_config_modbustcp/point_table_modbus_03.json
  13. 53
      public/commun_config_modbustcp/工程说明.md
  14. 6
      src/api/module/engineering/index.ts
  15. 3
      src/api/module/index.ts
  16. 57
      src/api/module/system/dept/index.ts
  17. 68
      src/api/module/system/dict/dict.data.ts
  18. 65
      src/api/module/system/dict/dict.type.ts
  19. 50
      src/api/module/system/file/index.ts
  20. 106
      src/api/module/system/login/index.ts
  21. 33
      src/api/module/system/loginLog/index.ts
  22. 63
      src/api/module/system/menu/index.ts
  23. 51
      src/api/module/system/operatelog/index.ts
  24. 54
      src/api/module/system/permission/index.ts
  25. 64
      src/api/module/system/post/index.ts
  26. 79
      src/api/module/system/role/index.ts
  27. 80
      src/api/module/system/tenant/index.ts
  28. 58
      src/api/module/system/tenantPackage/index.ts
  29. 121
      src/api/module/system/user/index.ts
  30. 75
      src/api/module/system/user/profile.ts
  31. 30
      src/api/module/system/user/socialUser.ts
  32. 4
      src/api/server/axiosInstance.ts
  33. 4
      src/api/server/config.ts
  34. BIN
      src/assets/images/login/avatar.png
  35. BIN
      src/assets/images/login/bg.png
  36. BIN
      src/assets/images/login/logo.png
  37. 38
      src/hooks/useCache.ts
  38. 148
      src/hooks/useMessage.ts
  39. 31
      src/router/index.ts
  40. 2
      src/stores/engineering.ts
  41. 38
      src/utils/auth.ts
  42. 124
      src/utils/hooks.ts
  43. 2
      src/views/engineering/config/index.vue
  44. 60
      src/views/layout/index.vue
  45. 244
      src/views/login/index.vue

3
.env

@ -1,3 +1,4 @@
VITE_BASE_API = '/remoteServer/admin-api/' VITE_BASE_API = '/remoteServer/admin-api/'
VITE_BASE_API_SYSTEM = '/remote/admin-api/system/'
VITE_SHOW_ONLINE_DEVICE = true VITE_SHOW_ONLINE_DEVICE = true
VITE_BASE_URL = 'http://43.140.245.32:48081' VITE_BASE_URL = 'http://43.140.245.32:48089'

3
.gitignore vendored

@ -28,4 +28,5 @@ yarn-error.log*
/node_modules/ /node_modules/
/*.tar /*.tar
.idea .idea
test.js

124
.idea/workspace.xml

@ -4,7 +4,7 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="1a5a317b-2539-4ca3-a13a-6db181725745" name="Changes" comment="feat: 样式优化" /> <list default="true" id="1a5a317b-2539-4ca3-a13a-6db181725745" name="Changes" comment="feat: 功能调整" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -18,6 +18,9 @@
</list> </list>
</option> </option>
</component> </component>
<component name="FormatOnSaveOptions">
<option name="myRunOnSave" value="true" />
</component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY"> <option name="RECENT_BRANCH_BY_REPOSITORY">
<map> <map>
@ -54,6 +57,9 @@
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="file://$USER_HOME$/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_0wujhfg3eau322_5d75/msg/file/2025-11/1.json" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$USER_HOME$/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_0wujhfg3eau322_5d75/msg/file/2025-11/1.json" root0="FORCE_HIGHLIGHTING" />
</component> </component>
<component name="OptimizeOnSaveOptions">
<option name="myRunOnSave" value="true" />
</component>
<component name="ProblemsViewState"> <component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" /> <option name="selectedTabId" value="CurrentFile" />
</component> </component>
@ -71,36 +77,39 @@
<option name="showModules" value="false" /> <option name="showModules" value="false" />
<option name="showScratchesAndConsoles" value="false" /> <option name="showScratchesAndConsoles" value="false" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true", &quot;ASKED_MARK_IGNORED_FILES_AS_EXCLUDED&quot;: &quot;true&quot;,
"ModuleVcsDetector.initialDetectionPerformed": "true", &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", &quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
"RunOnceActivity.git.unshallow": "true", &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultAutoModeForALLUsers.v1": "true", &quot;code.cleanup.on.save&quot;: &quot;true&quot;,
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", &quot;com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultAutoModeForALLUsers.v1&quot;: &quot;true&quot;,
"git-widget-placeholder": "main", &quot;com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1&quot;: &quot;true&quot;,
"junie.onboarding.icon.badge.shown": "true", &quot;git-widget-placeholder&quot;: &quot;main&quot;,
"last_opened_file_path": "/Users/taqibe/worker/EDFS-EPM", &quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
"node.js.detected.package.eslint": "true", &quot;last_opened_file_path&quot;: &quot;/Users/taqibe/worker/EDFS-EPM/public&quot;,
"node.js.detected.package.tslint": "true", &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
"node.js.selected.package.eslint": "(autodetect)", &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
"node.js.selected.package.tslint": "(autodetect)", &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
"nodejs_package_manager_path": "npm", &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
"settings.editor.selected.configurable": "editing.templates", &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
"to.speed.mode.migration.done": "true", &quot;rearrange.code.on.save&quot;: &quot;true&quot;,
"ts.external.directory.path": "/Users/taqibe/worker/EDFS-EPM/node_modules/typescript/lib", &quot;settings.editor.selected.configurable&quot;: &quot;preferences.general&quot;,
"vue.rearranger.settings.migration": "true" &quot;to.speed.mode.migration.done&quot;: &quot;true&quot;,
&quot;ts.external.directory.path&quot;: &quot;/Users/taqibe/worker/EDFS-EPM/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}, },
"keyToStringList": { &quot;keyToStringList&quot;: {
"vue.recent.templates": [ &quot;vue.recent.templates&quot;: [
"Vue Composition API Component" &quot;Vue Composition API Component&quot;
] ]
} }
}]]></component> }</component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/public" />
<recent name="$PROJECT_DIR$/src/views" /> <recent name="$PROJECT_DIR$/src/views" />
</key> </key>
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
@ -110,7 +119,7 @@
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-js-predefined-d6986cc7102b-3aa1da707db6-JavaScript-WS-252.27397.92" /> <option value="bundled-js-predefined-d6986cc7102b-a71380e98a7c-JavaScript-WS-252.28238.10" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>
@ -167,6 +176,15 @@
<workItem from="1763543655623" duration="1880000" /> <workItem from="1763543655623" duration="1880000" />
<workItem from="1763610355529" duration="670000" /> <workItem from="1763610355529" duration="670000" />
<workItem from="1763611329249" duration="1960000" /> <workItem from="1763611329249" duration="1960000" />
<workItem from="1764038430996" duration="1494000" />
<workItem from="1764060663947" duration="4257000" />
<workItem from="1764128227656" duration="1924000" />
<workItem from="1764555160345" duration="1897000" />
<workItem from="1764903079877" duration="8217000" />
<workItem from="1765175892880" duration="1684000" />
<workItem from="1765187147072" duration="460000" />
<workItem from="1765264069898" duration="42000" />
<workItem from="1765270558674" duration="670000" />
</task> </task>
<task id="LOCAL-00001" summary="fix: 一些调整"> <task id="LOCAL-00001" summary="fix: 一些调整">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -416,7 +434,39 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1763358207970</updated> <updated>1763358207970</updated>
</task> </task>
<option name="localTasksCounter" value="32" /> <task id="LOCAL-00032" summary="feat: 基础功能添加">
<option name="closed" value="true" />
<created>1764142225202</created>
<option name="number" value="00032" />
<option name="presentableId" value="LOCAL-00032" />
<option name="project" value="LOCAL" />
<updated>1764142225202</updated>
</task>
<task id="LOCAL-00033" summary="feat: 基础功能添加">
<option name="closed" value="true" />
<created>1764142233905</created>
<option name="number" value="00033" />
<option name="presentableId" value="LOCAL-00033" />
<option name="project" value="LOCAL" />
<updated>1764142233905</updated>
</task>
<task id="LOCAL-00034" summary="feat: 功能完成">
<option name="closed" value="true" />
<created>1765187239302</created>
<option name="number" value="00034" />
<option name="presentableId" value="LOCAL-00034" />
<option name="project" value="LOCAL" />
<updated>1765187239302</updated>
</task>
<task id="LOCAL-00035" summary="feat: 功能调整">
<option name="closed" value="true" />
<created>1765264099923</created>
<option name="number" value="00035" />
<option name="presentableId" value="LOCAL-00035" />
<option name="project" value="LOCAL" />
<updated>1765264099923</updated>
</task>
<option name="localTasksCounter" value="36" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -428,8 +478,18 @@
</option> </option>
</component> </component>
<component name="Vcs.Log.Tabs.Properties"> <component name="Vcs.Log.Tabs.Properties">
<option name="OPEN_GENERIC_TABS">
<map>
<entry key="13fad90b-77dc-405d-8c54-81c3aa558114" value="TOOL_WINDOW" />
</map>
</option>
<option name="TAB_STATES"> <option name="TAB_STATES">
<map> <map>
<entry key="13fad90b-77dc-405d-8c54-81c3aa558114">
<value>
<State />
</value>
</entry>
<entry key="MAIN"> <entry key="MAIN">
<value> <value>
<State /> <State />
@ -439,9 +499,6 @@
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="feat: 迁移drawer loading 逻辑修改" />
<MESSAGE value="feat: 增加设备类型并增加icon" />
<MESSAGE value="feat: emu 设备数据点位值保留小数修改" />
<MESSAGE value="feat: emu 设备批量选择增加全选" /> <MESSAGE value="feat: emu 设备批量选择增加全选" />
<MESSAGE value="feat: 点位数据不显示数据为空的" /> <MESSAGE value="feat: 点位数据不显示数据为空的" />
<MESSAGE value="feat: 使用时间对齐作为折线图x轴" /> <MESSAGE value="feat: 使用时间对齐作为折线图x轴" />
@ -464,7 +521,10 @@
<MESSAGE value="feat: 点位数据空值不不处理" /> <MESSAGE value="feat: 点位数据空值不不处理" />
<MESSAGE value="feat: topo 点位Total 调整" /> <MESSAGE value="feat: topo 点位Total 调整" />
<MESSAGE value="feat: 样式优化" /> <MESSAGE value="feat: 样式优化" />
<option name="LAST_COMMIT_MESSAGE" value="feat: 样式优化" /> <MESSAGE value="feat: 基础功能添加" />
<MESSAGE value="feat: 功能完成" />
<MESSAGE value="feat: 功能调整" />
<option name="LAST_COMMIT_MESSAGE" value="feat: 功能调整" />
</component> </component>
<component name="github-copilot-workspace"> <component name="github-copilot-workspace">
<instructionFileLocations> <instructionFileLocations>

9
global.types/components.d.ts vendored

@ -21,26 +21,24 @@ declare module 'vue' {
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog'] 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'] ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader'] ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader'] ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElStep: typeof import('element-plus/es')['ElStep'] ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps'] 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'] ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
@ -52,7 +50,6 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
} }
export interface GlobalDirectives { export interface GlobalDirectives {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
vLoading: typeof import('element-plus/es')['ElLoadingDirective'] vLoading: typeof import('element-plus/es')['ElLoadingDirective']
} }
} }

3
package.json

@ -31,7 +31,8 @@
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-router": "^4.5.0" "vue-router": "^4.5.0",
"web-storage-cache": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.310", "@iconify/json": "^2.2.310",

8
pnpm-lock.yaml

@ -71,6 +71,9 @@ importers:
vue-router: vue-router:
specifier: ^4.5.0 specifier: ^4.5.0
version: 4.6.3(vue@3.5.25(typescript@5.7.3)) version: 4.6.3(vue@3.5.25(typescript@5.7.3))
web-storage-cache:
specifier: ^1.1.1
version: 1.1.1
devDependencies: devDependencies:
'@iconify/json': '@iconify/json':
specifier: ^2.2.310 specifier: ^2.2.310
@ -2469,6 +2472,9 @@ packages:
resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
web-storage-cache@1.1.1:
resolution: {integrity: sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==}
webpack-sources@3.3.3: webpack-sources@3.3.3:
resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
@ -5036,6 +5042,8 @@ snapshots:
glob-to-regexp: 0.4.1 glob-to-regexp: 0.4.1
graceful-fs: 4.2.11 graceful-fs: 4.2.11
web-storage-cache@1.1.1: {}
webpack-sources@3.3.3: {} webpack-sources@3.3.3: {}
webpack-virtual-modules@0.6.2: {} webpack-virtual-modules@0.6.2: {}

24
public/commun_config_modbustcp/commun_channel_moebustcp.json

@ -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
}
]
}

34
public/commun_config_modbustcp/commun_dev_modbustcp.json

@ -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"
}
]
}

22
public/commun_config_modbustcp/index.json

@ -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"
}
}
}

61
public/commun_config_modbustcp/point_table_modbus_01.json

@ -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,
"点位段名称"
]
]
}

61
public/commun_config_modbustcp/point_table_modbus_02.json

@ -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,
"点位段名称"
]
]
}

61
public/commun_config_modbustcp/point_table_modbus_03.json

@ -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,
"点位段名称"
]
]
}

53
public/commun_config_modbustcp/工程说明.md

@ -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
```

6
src/api/module/engineering/index.ts

@ -13,10 +13,8 @@ export const createEngineering = (params: IEngineeringOV) => {
export const getEngineeringList = ( export const getEngineeringList = (
params?: Pick<IEngineeringOV, 'name' | 'isCreate'>, params?: Pick<IEngineeringOV, 'name' | 'isCreate'>,
) => { ) => {
return globalServer<{ return globalServer<IEngineeringOV[]>({
list: IEngineeringOV[] url: '/project/list',
}>({
url: '/project/page',
method: 'get', method: 'get',
params, params,
}) })

3
src/api/module/index.ts

@ -2,5 +2,6 @@ import axiosInstance from '@/api/server/axiosInstance'
import { API_Config } from '@/api/server/config' import { API_Config } from '@/api/server/config'
const globalServer = axiosInstance('global', API_Config.global) const globalServer = axiosInstance('global', API_Config.global)
const systemServer = axiosInstance('system', API_Config.system)
export { globalServer } export { globalServer, systemServer }

57
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<DeptVO[]>({
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',
})

68
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',
})

65
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',
})

50
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<FilePresignedUrlRespVO>({
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' },
})
}

106
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<number>({
url: '/tenant/get-id-by-name',
method: 'get',
params: {
name,
},
})
export const login = (data: any) =>
systemServer<LoginResponse>({
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 })
// }

33
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',
})
}

63
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',
})

51
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',
})
}

54
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,
})

64
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<PostVO[]>({
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',
})

79
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<RoleVO[]>({
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',
})

80
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',
})

58
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',
})

121
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<UserSimple[]>({
url: '/user/simple-list',
method: 'get',
})
}

75
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',
},
})

30
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',
})

4
src/api/server/axiosInstance.ts

@ -67,6 +67,10 @@ const createAxiosInstance = (module: APIConfigKeys, config: Config) => {
(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
// config.headers.Authorization = basicHeader.token() // config.headers.Authorization = basicHeader.token()
// config.headers['tenant-id'] = basicHeader.tenant() // config.headers['tenant-id'] = basicHeader.tenant()
if (config.url?.includes('/login')) {
config.headers['tenant-id'] = '1'
}
const params = config.params || {} const params = config.params || {}
const data = config.data || false const data = config.data || false

4
src/api/server/config.ts

@ -1,5 +1,6 @@
export interface APIConfig { export interface APIConfig {
global: Config global: Config
system: Config
} }
interface Config { interface Config {
@ -11,6 +12,9 @@ const API_Config: APIConfig = {
global: { global: {
baseAPI: import.meta.env.VITE_BASE_API, baseAPI: import.meta.env.VITE_BASE_API,
}, },
system: {
baseAPI: import.meta.env.VITE_BASE_API_SYSTEM,
},
} }

BIN
src/assets/images/login/avatar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
src/assets/images/login/bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

BIN
src/assets/images/login/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

38
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)
}

148
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<any>, msg?: string) {
if (res.code === Keys.CODE_SUCCEED || res.status === 200 || res.code === 200)
return false
ElMessage.error(res?.msg ?? msg)
return true
}

31
src/router/index.ts

@ -1,6 +1,17 @@
import { getToken } from '@/utils/auth';
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
export const defaultRouter = [ export const defaultRouter = [
{
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'Login',
meta: {
hidden: true,
title: '登录',
noTagsView: true,
},
},
{ {
path: '/', path: '/',
name: 'dashboard', name: 'dashboard',
@ -58,4 +69,24 @@ const router = createRouter({
routes: defaultRouter, 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 export default router

2
src/stores/engineering.ts

@ -13,7 +13,7 @@ export const useEngineeringStore = defineStore('engineering', () => {
loading.value = true loading.value = true
try { try {
const res = await getEngineeringList() const res = await getEngineeringList()
engineeringList.value = Array.isArray(res.data?.list) ? res.data.list : [] engineeringList.value = Array.isArray(res.data) ? res.data : []
} catch (error) { } catch (error) {
console.error(error) console.error(error)
engineeringList.value = [] engineeringList.value = []

38
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);

124
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<any>, 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<T extends ItemWithChildren, K extends keyof T>(
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[]
}

2
src/views/engineering/config/index.vue

@ -112,7 +112,7 @@ const router = useRouter()
const route = useRoute() const route = useRoute()
const projectName = computed(() => route.query.name as string) 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' type Step = 'channel' | 'category' | 'device'
const steps: Step[] = ['channel', 'category', 'device'] const steps: Step[] = ['channel', 'category', 'device']

60
src/views/layout/index.vue

@ -78,6 +78,23 @@
{{ currentTime }} {{ currentTime }}
</div> </div>
</div> </div>
<div class="right">
<el-dropdown
style="width: 100%; height: 100%"
@command="command => handleCommand(command)"
>
<div class="user-avatar">
<img :src="Avatar" height="32" />
<!-- <div class="user-name" :title="userName">{{ userName }}</div> -->
</div>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item command="personal">个人中心</el-dropdown-item> -->
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header> </el-header>
<main class="main-wrap"> <main class="main-wrap">
<RouterView :key="currentRoute.fullPath" /> <RouterView :key="currentRoute.fullPath" />
@ -93,6 +110,10 @@ import { defaultRouter } from '@/router'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useEngineeringStore } from '@/stores/engineering' import { useEngineeringStore } from '@/stores/engineering'
import { storeToRefs } from 'pinia' 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 unfold = 'i-icon-park-outline:menu-unfold'
const fold = 'i-icon-park-outline:menu-fold' const fold = 'i-icon-park-outline:menu-fold'
@ -103,7 +124,7 @@ const engineeringStore = useEngineeringStore()
const { engineeringList } = storeToRefs(engineeringStore) const { engineeringList } = storeToRefs(engineeringStore)
const menuList = computed(() => { 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') const engRoute = routes.find((r: any) => r.path === '/engineering')
if (engRoute) { if (engRoute) {
// Initialize children array // 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 isCollapse = ref(false)
const getIconClass = (icon: string) => {
return icon
}
const { push, currentRoute } = useRouter() const { push, currentRoute } = useRouter()
const activeMenu = computed(() => { const activeMenu = computed(() => {
const { meta, path, query } = unref(currentRoute) const { meta, path, query } = unref(currentRoute)
@ -183,6 +197,24 @@ const updateTime = () => {
requestAnimationFrame(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(() => { onMounted(() => {
updateTime() updateTime()
engineeringStore.fetchEngineeringList() engineeringStore.fetchEngineeringList()
@ -279,8 +311,16 @@ onMounted(() => {
} }
} }
.avatar { .right {
@apply w-24 h-24; @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 { .menu-icon {

244
src/views/login/index.vue

@ -0,0 +1,244 @@
<template>
<div class="main">
<div class="header">
<img :src="Logo" class="icon" />
<div class="sys-title">
<span class="title-cn">比特电科-工程管理平台</span>
<span class="title-en">BTDK-ES Engineering Management Platform</span>
</div>
</div>
<div class="body">
<img :src="bg" class="bg" alt="" />
<div class="form">
<div class="title">账号登录</div>
<el-form label-position="top" :model="account">
<el-form-item class="label">
<el-input v-model="account.username" class="input" placeholder="请输入账号" />
</el-form-item>
<el-form-item class="label">
<el-input
type="password"
v-model="account.password"
class="input"
placeholder="请输入密码"
/>
</el-form-item>
</el-form>
<el-button color="#08a4f0" class="login-btn" @click="onLogin" :loading="isLoading"
>登录</el-button
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {
login,
getTenantId,
sendSmsCode,
smsLogin,
type LoginRequestData,
} from '@/api/module/system/login'
import Logo from '@/assets/images/login/logo.png'
import bg from '@/assets/images/login/bg.png'
import { setRefreshToken, setTenantId } from '@/utils/auth'
import { setToken } from '@/utils/auth'
import { isResError, useMessage } from '@/hooks/useMessage'
import { useWindowKeyEnter } from '@/utils/hooks'
import { useRouter } from 'vue-router'
const isLoading = ref(false)
const router = useRouter()
const message = useMessage()
const activeName = ref('account')
const account = reactive<LoginRequestData>({
username: '',
password: '',
})
const mobile = reactive({
tenantName: '',
number: '',
code: '',
captchaVerification: '',
rememberMe: true,
})
async function onLogin() {
isLoading.value = true
try {
switch (activeName.value) {
case 'account':
await loginByAccount()
break
case 'mobile':
await loginByMobile()
break
}
} catch (e) {
return
}
isLoading.value = false
router.push('/engineering')
}
async function loginByAccount() {
if (!account.username || !account.password) {
message.error('用户名或密码不能为空')
isLoading.value = false
return Promise.reject()
}
const loginRes = await login({
username: account.username,
password: account.password,
})
if (isResError(loginRes)) {
isLoading.value = false
return Promise.reject(loginRes)
}
setToken(loginRes.data.accessToken)
setRefreshToken(loginRes.data.refreshToken)
}
const smsVO = reactive({
smsCode: {
mobile: '',
scene: 21,
},
loginSms: {
mobile: '',
code: '',
},
})
const mobileCodeTimer = ref(0)
const getSmsCode = async () => {
const tenantRes = await getTenantId(mobile.tenantName)
if (isResError(tenantRes)) {
isLoading.value = false
return
}
smsVO.smsCode.mobile = mobile.number
const res = await sendSmsCode(smsVO.smsCode)
if (isResError(res)) {
return
}
message.success('验证码已发送')
mobileCodeTimer.value = 60
let msgTimer = setInterval(() => {
mobileCodeTimer.value = mobileCodeTimer.value - 1
if (mobileCodeTimer.value <= 0) {
clearInterval(msgTimer)
}
}, 1000)
}
async function loginByMobile() {
smsVO.loginSms.mobile = mobile.number
smsVO.loginSms.code = mobile.code
const res = await smsLogin(smsVO.loginSms)
if (isResError(res)) {
isLoading.value = false
return Promise.reject(res)
}
setToken(res.data.accessToken)
setRefreshToken(res.data.refreshToken)
}
async function settingTenantId(tenantName: string) {
const tenantRes = await getTenantId(tenantName)
if (isResError(tenantRes)) {
isLoading.value = false
return Promise.reject()
}
setTenantId(tenantRes.data)
}
useWindowKeyEnter(onLogin)
onMounted(() => {
// verifyCode.value.showCode()
// getVerCode()
})
</script>
<style scoped lang="scss">
.main {
position: relative;
width: 100%;
height: 100%;
background: linear-gradient(to bottom right, #08a4f0, #7f80fe);
.header {
display: flex;
align-items: center;
height: 110px;
padding-left: 26px;
.icon {
width: 52px;
height: 52px;
margin-right: 12px;
}
.sys-title {
display: flex;
flex-direction: column;
font-size: 26px;
color: #ffffff;
font-weight: 700;
.title-cn {
margin-bottom: 8px;
}
.title-en {
font-size: 16px;
}
}
}
.body {
height: calc(100% - 110px);
display: flex;
justify-content: space-around;
.bg {
// max-width: 1010px;
// max-height: 1010px;
}
}
.form {
width: 440px;
padding: 64px 44px;
background-color: #ffffff;
border-radius: 3px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
margin: auto 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
.title {
font-size: 24px;
font-weight: 600;
text-align: center;
color: #333333;
margin-bottom: 48px;
}
:deep(.el-form-item) {
margin-bottom: 26px;
}
:deep(.el-input) {
height: 40px;
}
:deep(.el-button) {
height: 40px;
}
}
}
</style>
Loading…
Cancel
Save