瀏覽代碼

fist commit Reservation

tom 2 年之前
父節點
當前提交
e33d4503b8
共有 70 個文件被更改,包括 66127 次插入1 次删除
  1. 9 0
      .editorconfig
  2. 4 0
      .eslintignore
  3. 15 0
      .eslintrc.cjs
  4. 30 0
      .gitignore
  5. 6 0
      .prettierignore
  6. 4 0
      .prettierrc.yaml
  7. 3 0
      .vscode/extensions.json
  8. 34 1
      README.md
  9. 12 0
      build/entitlements.mac.plist
  10. 二進制
      build/icon.icns
  11. 二進制
      build/icon.ico
  12. 二進制
      build/icon.png
  13. 42 0
      electron-builder.yml
  14. 21 0
      electron.vite.config.js
  15. 423 0
      out/main/index.js
  16. 15 0
      out/preload/index.js
  17. 7 0
      out/renderer/assets/index-90535596.css
  18. 60558 0
      out/renderer/assets/index-e95a9740.js
  19. 19 0
      out/renderer/index.html
  20. 48 0
      package.json
  21. 二進制
      resources/icon.png
  22. 1 0
      set_utf8.bat
  23. 101 0
      src/main/index.js
  24. 456 0
      src/main/utils/comm.js
  25. 71 0
      src/main/utils/varsub.js
  26. 20 0
      src/preload/index.js
  27. 17 0
      src/renderer/index.html
  28. 16 0
      src/renderer/src/App.vue
  29. 205 0
      src/renderer/src/assets/css/styles.less
  30. 34 0
      src/renderer/src/assets/icons.svg
  31. 二進制
      src/renderer/src/assets/img/Add.png
  32. 二進制
      src/renderer/src/assets/img/AddMsg.png
  33. 二進制
      src/renderer/src/assets/img/CasePut.png
  34. 二進制
      src/renderer/src/assets/img/DelMsg.png
  35. 二進制
      src/renderer/src/assets/img/Delete.png
  36. 二進制
      src/renderer/src/assets/img/Down.png
  37. 二進制
      src/renderer/src/assets/img/Edit.png
  38. 二進制
      src/renderer/src/assets/img/Gas.png
  39. 二進制
      src/renderer/src/assets/img/GetMgt.png
  40. 二進制
      src/renderer/src/assets/img/GroupAdd.png
  41. 二進制
      src/renderer/src/assets/img/Money.png
  42. 二進制
      src/renderer/src/assets/img/MsgList.png
  43. 二進制
      src/renderer/src/assets/img/MsgLog.png
  44. 二進制
      src/renderer/src/assets/img/MsgMgt.png
  45. 二進制
      src/renderer/src/assets/img/Note.png
  46. 二進制
      src/renderer/src/assets/img/NoteSet.png
  47. 二進制
      src/renderer/src/assets/img/OnePerson.png
  48. 二進制
      src/renderer/src/assets/img/Package.png
  49. 二進制
      src/renderer/src/assets/img/Records.png
  50. 二進制
      src/renderer/src/assets/img/Repair.png
  51. 二進制
      src/renderer/src/assets/img/Search.png
  52. 二進制
      src/renderer/src/assets/img/Table.png
  53. 二進制
      src/renderer/src/assets/img/TimeDs.png
  54. 二進制
      src/renderer/src/assets/img/TwoPerson.png
  55. 二進制
      src/renderer/src/assets/img/UpMsg.png
  56. 二進制
      src/renderer/src/assets/img/White.png
  57. 二進制
      src/renderer/src/assets/img/addorder.png
  58. 二進制
      src/renderer/src/assets/img/back.png
  59. 二進制
      src/renderer/src/assets/img/bgreen.png
  60. 二進制
      src/renderer/src/assets/img/bgrey.png
  61. 二進制
      src/renderer/src/assets/img/qttitle.png
  62. 54 0
      src/renderer/src/components/BackGround.vue
  63. 141 0
      src/renderer/src/components/LoginPage.vue
  64. 114 0
      src/renderer/src/components/ReservMain.vue
  65. 14 0
      src/renderer/src/components/Versions.vue
  66. 9 0
      src/renderer/src/main.js
  67. 13 0
      src/renderer/src/router/index.js
  68. 15 0
      src/renderer/src/stores/reservdata.js
  69. 14 0
      src/renderer/src/stores/savedata.js
  70. 3582 0
      yarn.lock

+ 9 - 0
.editorconfig 查看文件

@@ -0,0 +1,9 @@
1
+root = true
2
+
3
+[*]
4
+charset = utf-8
5
+indent_style = space
6
+indent_size = 2
7
+end_of_line = lf
8
+insert_final_newline = true
9
+trim_trailing_whitespace = true

+ 4 - 0
.eslintignore 查看文件

@@ -0,0 +1,4 @@
1
+node_modules
2
+dist
3
+out
4
+.gitignore

+ 15 - 0
.eslintrc.cjs 查看文件

@@ -0,0 +1,15 @@
1
+/* eslint-env node */
2
+require('@rushstack/eslint-patch/modern-module-resolution')
3
+
4
+module.exports = {
5
+  extends: [
6
+    'eslint:recommended',
7
+    'plugin:vue/vue3-recommended',
8
+    '@electron-toolkit',
9
+    '@vue/eslint-config-prettier'
10
+  ],
11
+  rules: {
12
+    'vue/require-default-prop': 'off',
13
+    'vue/multi-word-component-names': 'off'
14
+  }
15
+}

+ 30 - 0
.gitignore 查看文件

@@ -0,0 +1,30 @@
1
+# Logs
2
+logs
3
+*.log
4
+npm-debug.log*
5
+yarn-debug.log*
6
+yarn-error.log*
7
+pnpm-debug.log*
8
+lerna-debug.log*
9
+
10
+# local env files
11
+.env.local
12
+.env.*.local
13
+
14
+node_modules
15
+dist
16
+dist-ssr
17
+*.local
18
+
19
+# Editor directories and files
20
+.vscode/*
21
+!.vscode/extensions.json
22
+.idea
23
+.DS_Store
24
+*.suo
25
+*.ntvs*
26
+*.njsproj
27
+*.sln
28
+*.sw?
29
+*.rar
30
+*.zip

+ 6 - 0
.prettierignore 查看文件

@@ -0,0 +1,6 @@
1
+out
2
+dist
3
+pnpm-lock.yaml
4
+LICENSE.md
5
+tsconfig.json
6
+tsconfig.*.json

+ 4 - 0
.prettierrc.yaml 查看文件

@@ -0,0 +1,4 @@
1
+singleQuote: true
2
+semi: false
3
+printWidth: 100
4
+trailingComma: none

+ 3 - 0
.vscode/extensions.json 查看文件

@@ -0,0 +1,3 @@
1
+{
2
+  "recommendations": ["dbaeumer.vscode-eslint"]
3
+}

+ 34 - 1
README.md 查看文件

@@ -1,3 +1,36 @@
1 1
 # Reservation
2 2
 
3
-預約系統電腦版
3
+預約系統電腦版
4
+
5
+An Electron application with Vue
6
+
7
+## Recommended IDE Setup
8
+
9
+- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
10
+
11
+## Project Setup
12
+
13
+### Install
14
+
15
+```bash
16
+$ yarn
17
+```
18
+
19
+### Development
20
+
21
+```bash
22
+$ yarn dev
23
+```
24
+
25
+### Build
26
+
27
+```bash
28
+# For windows
29
+$ yarn build:win
30
+
31
+# For macOS
32
+$ yarn build:mac
33
+
34
+# For Linux
35
+$ yarn build:linux
36
+```

+ 12 - 0
build/entitlements.mac.plist 查看文件

@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+  <dict>
5
+    <key>com.apple.security.cs.allow-jit</key>
6
+    <true/>
7
+    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
8
+    <true/>
9
+    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
10
+    <true/>
11
+  </dict>
12
+</plist>

二進制
build/icon.icns 查看文件


二進制
build/icon.ico 查看文件


二進制
build/icon.png 查看文件


+ 42 - 0
electron-builder.yml 查看文件

@@ -0,0 +1,42 @@
1
+appId: com.electron.app
2
+productName: reservation
3
+directories:
4
+  buildResources: build
5
+files:
6
+  - '!**/.vscode/*'
7
+  - '!src/*'
8
+  - '!electron.vite.config.{js,ts,mjs,cjs}'
9
+  - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
10
+  - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
11
+asarUnpack:
12
+  - resources/**
13
+win:
14
+  executableName: reservation
15
+nsis:
16
+  artifactName: ${name}-${version}-setup.${ext}
17
+  shortcutName: ${productName}
18
+  uninstallDisplayName: ${productName}
19
+  createDesktopShortcut: always
20
+mac:
21
+  entitlementsInherit: build/entitlements.mac.plist
22
+  extendInfo:
23
+    - NSCameraUsageDescription: Application requests access to the device's camera.
24
+    - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
25
+    - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
26
+    - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
27
+  notarize: false
28
+dmg:
29
+  artifactName: ${name}-${version}.${ext}
30
+linux:
31
+  target:
32
+    - AppImage
33
+    - snap
34
+    - deb
35
+  maintainer: electronjs.org
36
+  category: Utility
37
+appImage:
38
+  artifactName: ${name}-${version}.${ext}
39
+npmRebuild: false
40
+publish:
41
+  provider: generic
42
+  url: https://example.com/auto-updates

+ 21 - 0
electron.vite.config.js 查看文件

@@ -0,0 +1,21 @@
1
+import { resolve } from 'path'
2
+import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
3
+import vue from '@vitejs/plugin-vue'
4
+
5
+export default defineConfig({
6
+  main: {
7
+    plugins: [externalizeDepsPlugin()]
8
+  },
9
+  preload: {
10
+    plugins: [externalizeDepsPlugin()]
11
+  },
12
+  renderer: {
13
+    resolve: {
14
+      alias: {
15
+        '@renderer': resolve('src/renderer/src'),
16
+        '@main': resolve('src/main')
17
+      }
18
+    },
19
+    plugins: [vue()]
20
+  }
21
+})

+ 423 - 0
out/main/index.js 查看文件

@@ -0,0 +1,423 @@
1
+"use strict";
2
+const electron = require("electron");
3
+const path = require("path");
4
+const utils = require("@electron-toolkit/utils");
5
+const axios = require("axios");
6
+const mqtt = require("mqtt");
7
+const events$1 = require("events");
8
+const dayjs = require("dayjs");
9
+const utc = require("dayjs/plugin/utc");
10
+const AsyncLock = require("async-lock");
11
+var client;
12
+const sub_topic1 = "20005000A0001";
13
+var lock = new AsyncLock();
14
+var em = new events$1.EventEmitter();
15
+function mqttClient(mqtt_address, mywindow, hmap2) {
16
+  const clientId = "mqttjs_giga" + Math.random().toString(16).substr(2, 8);
17
+  const host = "mqtt://" + mqtt_address;
18
+  console.log("=======>>>> MQTT Client Start " + host);
19
+  const options = {
20
+    keepalive: 30,
21
+    clientId,
22
+    protocolId: "MQTT",
23
+    protocolVersion: 4,
24
+    clean: true,
25
+    reconnectPeriod: 1e3,
26
+    connectTimeout: 30 * 1e3,
27
+    will: {
28
+      topic: "WillMsg",
29
+      payload: "Connection Closed abnormally..!",
30
+      qos: 0,
31
+      retain: false
32
+    },
33
+    rejectUnauthorized: false
34
+  };
35
+  console.log("start connecting mqtt client....");
36
+  client = mqtt.connect(host, options);
37
+  client.on("error", (err) => {
38
+    console.log("mqtt Connection error: ", err);
39
+    client.end();
40
+  });
41
+  client.on("reconnect", () => {
42
+    console.error("MQTT reconnect");
43
+  });
44
+  client.on("connect", () => {
45
+    console.log("MQTT Client connected : " + clientId + " OK");
46
+    client.subscribe(sub_topic1, {
47
+      qos: 2
48
+    });
49
+    var param = {
50
+      success: true,
51
+      action: "mqttconnect",
52
+      data: {
53
+        status: "0"
54
+      }
55
+    };
56
+    console.log("Send mqtt:connectReply");
57
+    mywindow.webContents.send("mqtt:connectReply", param);
58
+  });
59
+  client.on("offline", () => {
60
+    console.log("MQTT offline");
61
+    var param = {
62
+      success: false,
63
+      action: "mqttconnect",
64
+      data: {
65
+        status: "0"
66
+      }
67
+    };
68
+    console.log("Send mqtt:connectReply");
69
+    mywindow.webContents.send("mqtt:connectReply", param);
70
+  });
71
+  client.on("message", (topic, payload) => {
72
+    console.log("Received Message:", topic, payload.toString());
73
+    var obj = JSON.parse(payload.toString());
74
+    lock.acquire("hmap", () => {
75
+      var hobj = hmap2.get(obj.TimeStamp);
76
+      if (hobj !== void 0) {
77
+        hobj.data = payload.toString();
78
+        hobj.stat = 1;
79
+        hmap2.set(obj.TimeStamp, hobj);
80
+      }
81
+    }).then(function() {
82
+    });
83
+  });
84
+  electron.ipcMain.on("mqtt:publish", function(event, param) {
85
+    console.log("mqttclient(ipcMain) 接收到 mqtt:publish : " + JSON.stringify(param));
86
+    client.publish(param.topic, param.message, {
87
+      qos: 2,
88
+      retain: false
89
+    });
90
+  });
91
+  em.on("mqtt:publish", (param) => {
92
+    console.log("mqttclient(eventEmitter)  接收到 mqtt:publish : " + JSON.stringify(param));
93
+    client.publish(param.topic, param.message, {
94
+      qos: 2,
95
+      retain: false
96
+    });
97
+  });
98
+}
99
+function cmsHttpRequest(mywindow, hmap2) {
100
+  electron.ipcMain.on("cms:getMasterDeviceSN", function(event, param) {
101
+    console.log("cmsHttpRequest 接收到 cms:getMasterDeviceSN : " + JSON.stringify(param));
102
+    var url;
103
+    if (param.data.cid.slice(0, 6) == "886235")
104
+      url = "http://intercom.nxt.tw:8006";
105
+    else
106
+      url = "http://aws.gigatech.tw:8006";
107
+    axios.get(url + "/Common/CloudCMS/devices/devicelist.ashx?CID=" + param.data.cid).then((res) => {
108
+      if (res.status == 200 && res.data.success) {
109
+        res.data.data.every((element) => {
110
+          console.log(element);
111
+          var data = element.split(";");
112
+          if (data.length > 3) {
113
+            if (data[2] === "00000000-1") {
114
+              console.log("Master Device SN : " + data[0]);
115
+              return false;
116
+            }
117
+          }
118
+          return true;
119
+        });
120
+      }
121
+    });
122
+  });
123
+  electron.ipcMain.on("cms:loginVerify", function(event, param) {
124
+    console.log("cmsHttpRequest 接收到 cms:loginVerify : " + JSON.stringify(param));
125
+    setImmediate((param2, event2, hmap3, mywindow2) => {
126
+      em.emit("loginverify:Step1", param2, event2, hmap3, mywindow2);
127
+    }, param, event, hmap2, mywindow);
128
+  });
129
+}
130
+em.on("convertStep1", (param, num) => {
131
+  console.log("convertStep1...." + param.cid + " , " + param.userID + " , " + param.passwd);
132
+  console.log(num);
133
+});
134
+em.on("loginverify:Step1", (param, event, hmap2, mywindow) => {
135
+  var url;
136
+  if (param.data.cid.slice(0, 6) == "886235")
137
+    url = "http://intercom.nxt.tw:8006";
138
+  else
139
+    url = "http://aws.gigatech.tw:8006";
140
+  axios.get(url + "/Common/CloudCMS/devices/devicelist.ashx?CID=" + param.data.cid).then((res) => {
141
+    if (res.status == 200 && res.data.success) {
142
+      res.data.data.every((element) => {
143
+        console.log(element);
144
+        var data = element.split(";");
145
+        if (data.length > 3) {
146
+          if (data[2] === "00000000-1") {
147
+            console.log("Master Device SN : " + data[0]);
148
+            dayjs.extend(utc);
149
+            const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString();
150
+            var querydata = {
151
+              topic: data[0],
152
+              message: '{"action":"mqttqueryMobileList","data":{"Command":"CID=' + param.data.cid + '&ID=00000000","SerialNo":"' + sub_topic1 + '","TimeStamp":"' + ts + '","Token":"FFFFFGIGATECH0529FFFFF"}}'
153
+            };
154
+            var cb_param = {
155
+              stat: 0,
156
+              data: ""
157
+            };
158
+            hmap2.set(ts, cb_param);
159
+            client.publish(querydata.topic, querydata.message, {
160
+              qos: 2,
161
+              retain: false
162
+            });
163
+            var inparam = {
164
+              ts,
165
+              retrycnt: 0,
166
+              cid: param.data.cid,
167
+              userID: param.data.userID,
168
+              vpasswd: param.data.vpasswd,
169
+              passwd: "",
170
+              topic: querydata.topic
171
+            };
172
+            var chkflg = setInterval(function(inparam2) {
173
+              lock.acquire("hmap", () => {
174
+                var hobj = hmap2.get(inparam2.ts);
175
+                if (hobj !== void 0) {
176
+                  if (hobj.stat == 1) {
177
+                    clearInterval(chkflg);
178
+                    var dobj = JSON.parse(hobj.data);
179
+                    console.log("===>>>" + dobj.data[0].Password);
180
+                    inparam2.passwd = dobj.data[0].Password;
181
+                    hmap2.delete(inparam2.ts);
182
+                    setImmediate((inparam3, event2, hmap3, mywindow2) => {
183
+                      em.emit("loginverify:Step2", inparam3, event2, hmap3, mywindow2);
184
+                    }, inparam2, event, hmap2, mywindow);
185
+                  } else {
186
+                    if (inparam2.retrycnt > 50) {
187
+                      clearInterval(chkflg);
188
+                      hmap2.delete(inparam2.ts);
189
+                      var lparam2 = {
190
+                        action: "loginVerifyReply",
191
+                        data: {
192
+                          code: 404,
193
+                          token: ""
194
+                        }
195
+                      };
196
+                      setImmediate((lparam3, event2, hmap3, mywindow2) => {
197
+                        em.emit("loginverify:Step3", lparam3, event2, hmap3, mywindow2);
198
+                      }, lparam2, event, hmap2, mywindow);
199
+                    } else
200
+                      inparam2.retrycnt++;
201
+                  }
202
+                } else {
203
+                  clearInterval(chkflg);
204
+                }
205
+              }).then(function() {
206
+              });
207
+            }, 100, inparam);
208
+            return false;
209
+          }
210
+        }
211
+        return true;
212
+      });
213
+    } else {
214
+      var lparam = {
215
+        action: "loginVerifyReply",
216
+        data: {
217
+          code: 404,
218
+          token: ""
219
+        }
220
+      };
221
+      setImmediate((lparam2, event2, hmap3, mywindow2) => {
222
+        em.emit("loginverify:Step3", lparam2, event2, hmap3, mywindow2);
223
+      }, lparam, event, hmap2, mywindow);
224
+    }
225
+  });
226
+});
227
+em.on("loginverify:Step2", (param, event, hmap2, mywindow) => {
228
+  console.log("loginverify:Step2 -> " + param.cid + " , " + param.userID + " , " + param.passwd);
229
+  dayjs.extend(utc);
230
+  const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString();
231
+  var querydata = {
232
+    topic: param.topic,
233
+    message: '{"action":"mqttverifyRemoteKeyAccount","data":{"Command":"User=' + param.userID + "&Account=" + param.cid + "_" + param.userID + "&PassWord=" + param.passwd + "&WebPassword=" + param.vpasswd + '","SerialNo":"' + sub_topic1 + '","TimeStamp":"' + ts + '","Token":"FFFFFGIGATECH0529FFFFF"}}'
234
+  };
235
+  var cb_param = {
236
+    stat: 0,
237
+    data: ""
238
+  };
239
+  hmap2.set(ts, cb_param);
240
+  client.publish(querydata.topic, querydata.message, {
241
+    qos: 2,
242
+    retain: false
243
+  });
244
+  var inparam = {
245
+    ts,
246
+    retrycnt: 0,
247
+    cid: param.cid,
248
+    userID: param.userID,
249
+    vpasswd: param.vpasswd,
250
+    passwd: param.passwd,
251
+    topic: param.topic
252
+  };
253
+  var chkflg = setInterval(function(inparam2) {
254
+    lock.acquire("hmap", () => {
255
+      var hobj = hmap2.get(inparam2.ts);
256
+      if (hobj !== void 0) {
257
+        if (hobj.stat == 1) {
258
+          clearInterval(chkflg);
259
+          console.log(hobj.data);
260
+          var dobj = JSON.parse(hobj.data);
261
+          hmap2.delete(inparam2.ts);
262
+          var lparam = {
263
+            action: "loginVerifyReply",
264
+            data: {
265
+              code: 0,
266
+              token: ""
267
+            }
268
+          };
269
+          if (dobj.success && dobj.data.code == 200) {
270
+            lparam.data.code = dobj.data.code;
271
+            lparam.data.token = dobj.data.token;
272
+          } else {
273
+            lparam.data.code = dobj.data.code;
274
+          }
275
+          setImmediate((lparam2, event2, hmap3, mywindow2) => {
276
+            em.emit("loginverify:Step3", lparam2, event2, hmap3, mywindow2);
277
+          }, lparam, event, hmap2, mywindow);
278
+        } else {
279
+          if (inparam2.retrycnt > 50) {
280
+            clearInterval(chkflg);
281
+            hmap2.delete(lparam.ts);
282
+            var lparam = {
283
+              action: "loginVerifyReply",
284
+              data: {
285
+                code: 404,
286
+                token: ""
287
+              }
288
+            };
289
+            setImmediate((lparam2, event2, hmap3, mywindow2) => {
290
+              em.emit("loginverify:Step3", lparam2, event2, hmap3, mywindow2);
291
+            }, lparam, event, hmap2, mywindow);
292
+          } else
293
+            inparam2.retrycnt++;
294
+        }
295
+      } else {
296
+        clearInterval(chkflg);
297
+      }
298
+    }).then(function() {
299
+    });
300
+  }, 100, inparam);
301
+});
302
+em.on("loginverify:Step3", (param, event, hmap2, mywindow) => {
303
+  console.log("loginverify:Step3 -> ", param);
304
+  mywindow.webContents.send("cms:loginVerifyReply", param);
305
+});
306
+const events = require("events");
307
+var varmap = new Array();
308
+function varSubProc(mywindow) {
309
+  console.log("varSubProc run.....");
310
+  var em2 = new events.EventEmitter();
311
+  em2.on("em:setDate", function(cmd) {
312
+    console.log(cmd);
313
+    var index = cmd.data.name + "-" + cmd.data.user;
314
+    if (cmd.data.type == "Number") {
315
+      varmap[index] = parseInt(cmd.data.value);
316
+    } else if (cmd.data.type == "Float") {
317
+      varmap[index] = parseFloat(cmd.data.value);
318
+    } else {
319
+      varmap[index] = cmd.data.value;
320
+    }
321
+  });
322
+  em2.on("em:getDate", function(cmd) {
323
+    console.log(cmd);
324
+  });
325
+  electron.ipcMain.on("ipc:setData", function(event, cmd) {
326
+    console.log(cmd);
327
+    var index = cmd.data.name + "-" + cmd.data.user;
328
+    if (cmd.data.type == "Number") {
329
+      varmap[index] = parseInt(cmd.data.value);
330
+    } else if (cmd.data.type == "Float") {
331
+      varmap[index] = parseFloat(cmd.data.value);
332
+    } else {
333
+      varmap[index] = cmd.data.value;
334
+    }
335
+  });
336
+  electron.ipcMain.on("ipc:getData", function(event, cmd) {
337
+    var retv;
338
+    console.log(cmd);
339
+    var index = cmd.data.name + "-" + cmd.data.user;
340
+    if (varmap[index] == null) {
341
+      if (cmd.data.type == "Number") {
342
+        retv = 0;
343
+      } else {
344
+        retv = "";
345
+      }
346
+    } else {
347
+      retv = varmap[index];
348
+    }
349
+    var param = {
350
+      success: true,
351
+      action: "getData",
352
+      data: {
353
+        name: cmd.data.name,
354
+        user: cmd.data.user,
355
+        type: cmd.data.type,
356
+        value: retv
357
+      }
358
+    };
359
+    event.sender.send("ipc:getDataReply", param);
360
+  });
361
+}
362
+const icon = path.join(__dirname, "../../resources/icon.png");
363
+const remote = require("@electron/remote/main");
364
+const HashMap = require("hashmap");
365
+remote.initialize();
366
+var hmap = new HashMap();
367
+function createWindow() {
368
+  const mainWindow = new electron.BrowserWindow({
369
+    width: 900,
370
+    height: 670,
371
+    show: false,
372
+    autoHideMenuBar: true,
373
+    ...process.platform === "linux" ? { icon } : {},
374
+    webPreferences: {
375
+      nodeIntegration: true,
376
+      contextIsolation: false,
377
+      enableRemoteModule: true,
378
+      preload: path.join(__dirname, "../preload/index.js"),
379
+      sandbox: false
380
+    }
381
+  });
382
+  mainWindow.on("ready-to-show", () => {
383
+    mainWindow.show();
384
+  });
385
+  mainWindow.webContents.setWindowOpenHandler((details) => {
386
+    electron.shell.openExternal(details.url);
387
+    return { action: "deny" };
388
+  });
389
+  if (utils.is.dev && process.env["ELECTRON_RENDERER_URL"]) {
390
+    mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
391
+  } else {
392
+    mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
393
+  }
394
+  remote.enable(mainWindow.webContents);
395
+  varSubProc();
396
+  cmsHttpRequest(mainWindow, hmap);
397
+  electron.ipcMain.on("debug-log", (event, message) => {
398
+    console.log(message);
399
+  });
400
+  electron.ipcMain.on("renderer:cmd", (event, message) => {
401
+    if (message === "onMounted") {
402
+      setTimeout(() => {
403
+        mqttClient("iotmqtt.gigatech.tw:1883", mainWindow, hmap);
404
+      }, "1000");
405
+    }
406
+  });
407
+}
408
+electron.app.whenReady().then(() => {
409
+  utils.electronApp.setAppUserModelId("com.electron");
410
+  electron.app.on("browser-window-created", (_, window) => {
411
+    utils.optimizer.watchWindowShortcuts(window);
412
+  });
413
+  createWindow();
414
+  electron.app.on("activate", function() {
415
+    if (electron.BrowserWindow.getAllWindows().length === 0)
416
+      createWindow();
417
+  });
418
+});
419
+electron.app.on("window-all-closed", () => {
420
+  if (process.platform !== "darwin") {
421
+    electron.app.quit();
422
+  }
423
+});

+ 15 - 0
out/preload/index.js 查看文件

@@ -0,0 +1,15 @@
1
+"use strict";
2
+const electron = require("electron");
3
+const preload = require("@electron-toolkit/preload");
4
+const api = {};
5
+if (process.contextIsolated) {
6
+  try {
7
+    electron.contextBridge.exposeInMainWorld("electron", preload.electronAPI);
8
+    electron.contextBridge.exposeInMainWorld("api", api);
9
+  } catch (error) {
10
+    console.error(error);
11
+  }
12
+} else {
13
+  window.electron = preload.electronAPI;
14
+  window.api = api;
15
+}

文件差異過大導致無法顯示
+ 7 - 0
out/renderer/assets/index-90535596.css


文件差異過大導致無法顯示
+ 60558 - 0
out/renderer/assets/index-e95a9740.js


+ 19 - 0
out/renderer/index.html 查看文件

@@ -0,0 +1,19 @@
1
+<!doctype html>
2
+<html>
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <title>Electron</title>
6
+    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
7
+    <meta
8
+      http-equiv="Content-Security-Policy"
9
+      content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
10
+    />
11
+    <script type="module" crossorigin src="./assets/index-e95a9740.js"></script>
12
+    <link rel="stylesheet" href="./assets/index-90535596.css">
13
+  </head>
14
+
15
+  <body>
16
+    <div id="app"></div>
17
+    
18
+  </body>
19
+</html>

+ 48 - 0
package.json 查看文件

@@ -0,0 +1,48 @@
1
+{
2
+  "name": "reservation",
3
+  "version": "1.0.0",
4
+  "description": "An Electron application with Vue",
5
+  "main": "./out/main/index.js",
6
+  "author": "example.com",
7
+  "homepage": "https://www.electronjs.org",
8
+  "scripts": {
9
+    "format": "prettier --write .",
10
+    "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
11
+    "start": "electron-vite preview",
12
+    "dev": "electron-vite dev",
13
+    "build": "electron-vite build",
14
+    "postinstall": "electron-builder install-app-deps",
15
+    "build:win": "npm run build && electron-builder --win --config",
16
+    "build:mac": "npm run build && electron-builder --mac --config",
17
+    "build:linux": "npm run build && electron-builder --linux --config"
18
+  },
19
+  "dependencies": {
20
+    "@electron-toolkit/preload": "^2.0.0",
21
+    "@electron-toolkit/utils": "^2.0.0",
22
+    "@electron/remote": "^2.0.12",
23
+    "async-lock": "^1.4.0",
24
+    "axios": "^1.6.2",
25
+    "dayjs": "^1.11.7",
26
+    "element-plus": "^2.3.14",
27
+    "hashmap": "^2.4.0",
28
+    "mqtt": "^5.0.3",
29
+    "pinia": "^2.1.7",
30
+    "pinia-plugin-persistedstate": "^3.2.1",
31
+    "vue-router": "^4.0.0-0"
32
+  },
33
+  "devDependencies": {
34
+    "@electron-toolkit/eslint-config": "^1.0.1",
35
+    "@rushstack/eslint-patch": "^1.3.3",
36
+    "@vitejs/plugin-vue": "^4.3.1",
37
+    "@vue/eslint-config-prettier": "^8.0.0",
38
+    "electron": "^25.6.0",
39
+    "electron-builder": "^24.6.3",
40
+    "electron-vite": "^1.0.27",
41
+    "eslint": "^8.47.0",
42
+    "eslint-plugin-vue": "^9.17.0",
43
+    "less": "^4.2.0",
44
+    "prettier": "^3.0.2",
45
+    "vite": "^4.4.9",
46
+    "vue": "^3.3.4"
47
+  }
48
+}

二進制
resources/icon.png 查看文件


+ 1 - 0
set_utf8.bat 查看文件

@@ -0,0 +1 @@
1
+chcp 65001

+ 101 - 0
src/main/index.js 查看文件

@@ -0,0 +1,101 @@
1
+import { app, shell, BrowserWindow, ipcMain } from 'electron'
2
+import { join } from 'path'
3
+import { electronApp, optimizer, is } from '@electron-toolkit/utils'
4
+import { mqttClient, cmsHttpRequest } from './utils/comm'
5
+import { varSubProc } from './utils/varsub'
6
+import icon from '../../resources/icon.png?asset'
7
+const remote = require("@electron/remote/main")
8
+const HashMap = require('hashmap')
9
+
10
+remote.initialize()
11
+var hmap = new HashMap()
12
+
13
+function createWindow() {
14
+  // Create the browser window.
15
+  const mainWindow = new BrowserWindow({
16
+    width: 900,
17
+    height: 670,
18
+    show: false,
19
+    autoHideMenuBar: true,
20
+    ...(process.platform === 'linux' ? { icon } : {}),
21
+    webPreferences: {
22
+      nodeIntegration: true,
23
+      contextIsolation: false,
24
+      enableRemoteModule: true,      
25
+      preload: join(__dirname, '../preload/index.js'),
26
+      sandbox: false
27
+    }
28
+  })
29
+
30
+  mainWindow.on('ready-to-show', () => {
31
+    mainWindow.show()
32
+  })
33
+
34
+  mainWindow.webContents.setWindowOpenHandler((details) => {
35
+    shell.openExternal(details.url)
36
+    return { action: 'deny' }
37
+  })
38
+
39
+  // HMR for renderer base on electron-vite cli.
40
+  // Load the remote URL for development or the local html file for production.
41
+  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
42
+    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
43
+  } else {
44
+    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
45
+  }
46
+  remote.enable(mainWindow.webContents)
47
+
48
+  //mainWindow.webContents.openDevTools()
49
+
50
+  varSubProc(mainWindow)
51
+  cmsHttpRequest(mainWindow, hmap)
52
+
53
+  ipcMain.on('debug-log', (event, message) => {
54
+      console.log(message)
55
+  })  
56
+
57
+  ipcMain.on('renderer:cmd', (event, message) => {
58
+      if (message==='onMounted') {
59
+          setTimeout(() => {
60
+              // console.log("Delayed for 3 second.");
61
+              mqttClient('iotmqtt.gigatech.tw:1883', mainWindow, hmap)
62
+          }, "1000");
63
+      }
64
+  })  
65
+
66
+}
67
+
68
+// This method will be called when Electron has finished
69
+// initialization and is ready to create browser windows.
70
+// Some APIs can only be used after this event occurs.
71
+app.whenReady().then(() => {
72
+  // Set app user model id for windows
73
+  electronApp.setAppUserModelId('com.electron')
74
+
75
+  // Default open or close DevTools by F12 in development
76
+  // and ignore CommandOrControl + R in production.
77
+  // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
78
+  app.on('browser-window-created', (_, window) => {
79
+    optimizer.watchWindowShortcuts(window)
80
+  })
81
+
82
+  createWindow()
83
+
84
+  app.on('activate', function () {
85
+    // On macOS it's common to re-create a window in the app when the
86
+    // dock icon is clicked and there are no other windows open.
87
+    if (BrowserWindow.getAllWindows().length === 0) createWindow()
88
+  })
89
+})
90
+
91
+// Quit when all windows are closed, except on macOS. There, it's common
92
+// for applications and their menu bar to stay active until the user quits
93
+// explicitly with Cmd + Q.
94
+app.on('window-all-closed', () => {
95
+  if (process.platform !== 'darwin') {
96
+    app.quit()
97
+  }
98
+})
99
+
100
+// In this file you can include the rest of your app"s specific main process
101
+// code. You can also put them in separate files and require them here.

+ 456 - 0
src/main/utils/comm.js 查看文件

@@ -0,0 +1,456 @@
1
+import { ipcMain } from 'electron'
2
+import axios from 'axios';
3
+const mqtt = require('mqtt')
4
+const events = require('events');
5
+const dayjs = require('dayjs')
6
+const utc = require('dayjs/plugin/utc')
7
+const AsyncLock = require('async-lock')
8
+
9
+var contentFolder
10
+var fwdir
11
+var client
12
+var timeoutID
13
+const sub_topic1 = '20005000A0001'
14
+
15
+var lock = new AsyncLock()
16
+var em=new events.EventEmitter();
17
+
18
+export function mqttClient(mqtt_address, mywindow, hmap) {
19
+    const clientId = 'mqttjs_giga' + Math.random().toString(16).substr(2, 8);
20
+    const host = 'mqtt://'+mqtt_address;
21
+
22
+    console.log('=======>>>> MQTT Client Start '+host);
23
+
24
+    const options = {
25
+        keepalive: 30,
26
+        clientId: clientId,
27
+        protocolId: 'MQTT',
28
+        protocolVersion: 4,
29
+        clean: true,
30
+        reconnectPeriod: 1000,
31
+        connectTimeout: 30 * 1000,
32
+        will: {
33
+            topic: 'WillMsg',
34
+            payload: 'Connection Closed abnormally..!',
35
+            qos: 0,
36
+            retain: false,
37
+        },
38
+        rejectUnauthorized: false,
39
+    } 
40
+    console.log('start connecting mqtt client....');
41
+    client = mqtt.connect(host, options);
42
+
43
+    client.on('error', (err) => {
44
+        console.log('mqtt Connection error: ', err)
45
+        client.end()
46
+    });
47
+
48
+    client.on('reconnect', () => {
49
+        console.error('MQTT reconnect')
50
+        // console.log('mqtt Reconnecting....')
51
+        // var param = {
52
+        //     success: true,
53
+        //     action: 'mqttconnect',
54
+        //     data: {
55
+        //         status: '1'
56
+        //     }
57
+        // }        
58
+
59
+        // console.log('Send mqtt:connectReply')
60
+        // mywindow.webContents.send('mqtt:connectReply', param)        
61
+    });
62
+      
63
+    client.on('connect', () => {
64
+        console.log('MQTT Client connected : ' + clientId + ' OK')
65
+        client.subscribe(sub_topic1, {
66
+            qos: 2,
67
+        })
68
+        var param = {
69
+            success: true,
70
+            action: 'mqttconnect',
71
+            data: {
72
+                status: '0'
73
+            }
74
+        }        
75
+
76
+        console.log('Send mqtt:connectReply')
77
+        mywindow.webContents.send('mqtt:connectReply', param)
78
+    });
79
+
80
+    // client.on('close', () => {
81
+    //     console.log('MQTT close');
82
+    // });
83
+
84
+    // client.on('disconnect', () => {
85
+    //     console.log('MQTT disconnect');
86
+    // });    
87
+
88
+    client.on('offline', () => {
89
+        console.log('MQTT offline');
90
+        var param = {
91
+            success: false,
92
+            action: 'mqttconnect',
93
+            data: {
94
+                status: '0'
95
+            }
96
+        }        
97
+
98
+        console.log('Send mqtt:connectReply')
99
+        mywindow.webContents.send('mqtt:connectReply', param)        
100
+    });    
101
+
102
+    // client.on('end', () => {
103
+    //     console.log('MQTT end');
104
+    // });   
105
+
106
+    client.on('message', (topic, payload) => {
107
+        console.log('Received Message:', topic, payload.toString())
108
+        var obj = JSON.parse(payload.toString());
109
+        //console.log(obj.TimeStamp)
110
+
111
+        lock.acquire('hmap', ()=>{
112
+            // return value or promise
113
+            //console.log('get hmap lock');
114
+            var hobj = hmap.get(obj.TimeStamp)
115
+            if (hobj!==undefined) {
116
+                hobj.data = payload.toString()
117
+                hobj.stat = 1
118
+                hmap.set(obj.TimeStamp, hobj)
119
+            }
120
+        }).then(function() {
121
+            // lock released
122
+            //console.log('get hmap lock released');
123
+        });
124
+
125
+    })
126
+
127
+    ipcMain.on('mqtt:publish', function(event, param) {
128
+        console.log('mqttclient(ipcMain) 接收到 mqtt:publish : ' + JSON.stringify(param))
129
+        client.publish(param.topic, param.message, {
130
+            qos: 2,
131
+            retain: false,
132
+        })        
133
+    })
134
+
135
+    
136
+    em.on ('mqtt:publish',(param) => {
137
+        console.log('mqttclient(eventEmitter)  接收到 mqtt:publish : ' + JSON.stringify(param))
138
+        client.publish(param.topic, param.message, {
139
+            qos: 2,
140
+            retain: false,
141
+        })
142
+    })
143
+
144
+}
145
+
146
+export function cmsHttpRequest(mywindow, hmap) {
147
+    ipcMain.on('cms:getMasterDeviceSN', function(event, param) {
148
+        console.log('cmsHttpRequest 接收到 cms:getMasterDeviceSN : ' + JSON.stringify(param))
149
+        var url
150
+        if (param.data.cid.slice(0,6)=='886235') url='http://intercom.nxt.tw:8006'
151
+        else url = 'http://aws.gigatech.tw:8006'
152
+        axios.get(url+'/Common/CloudCMS/devices/devicelist.ashx?CID='+param.data.cid)
153
+        .then(res => {
154
+            if (res.status==200 && res.data.success) {
155
+                //console.log(res.data.data)
156
+                res.data.data.every((element) => {
157
+                    console.log(element)
158
+                    var data = element.split(';')
159
+                    if (data.length > 3) {
160
+                        if (data[2]==='00000000-1') {
161
+                            console.log('Master Device SN : '+data[0])
162
+                            return false
163
+                        }
164
+                    }
165
+                    return true
166
+                })
167
+            }
168
+            else {
169
+
170
+            }
171
+        })
172
+    })
173
+
174
+    ipcMain.on('cms:loginVerify', function(event, param) {
175
+        console.log('cmsHttpRequest 接收到 cms:loginVerify : ' + JSON.stringify(param))
176
+        setImmediate((param, event, hmap, mywindow)=> {
177
+            em.emit('loginverify:Step1', param, event, hmap, mywindow);
178
+        }, param, event, hmap, mywindow);
179
+        // var url
180
+        // if (param.data.cid.slice(0,6)=='886235') url='http://intercom.nxt.tw:8006'
181
+        // else url = 'http://aws.gigatech.tw:8006'
182
+        // axios.get(url+'/Common/CloudCMS/devices/devicelist.ashx?CID='+param.data.cid)
183
+        // .then(res => {
184
+        //     if (res.status==200 && res.data.success) {
185
+        //         //console.log(res.data.data)
186
+        //         res.data.data.every((element) => {
187
+        //             console.log(element)
188
+        //             var data = element.split(';')
189
+        //             if (data.length > 3) {
190
+        //                 if (data[2]==='00000000-1') {
191
+        //                     console.log('Master Device SN : '+data[0])
192
+        //                     dayjs.extend(utc)
193
+        //                     const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString()
194
+        //                     var querydata = {
195
+        //                         topic: data[0],
196
+        //                         message:'{"action":"mqttqueryMobileList","data":{"Command":"CID='+param.data.cid+'&ID=00000000","SerialNo":"'+sub_topic1+'","TimeStamp":"'+ts+'","Token":"FFFFFGIGATECH0529FFFFF"}}'
197
+        //                     }
198
+
199
+        //                     var cb_param = {
200
+        //                         stat: 0,
201
+        //                         data: ''
202
+        //                     }
203
+        //                     hmap.set(ts, cb_param)
204
+        //                     client.publish(querydata.topic, querydata.message, {
205
+        //                         qos: 2,
206
+        //                         retain: false,
207
+        //                     })   
208
+
209
+        //                     var inparam = {
210
+        //                         ts: ts,
211
+        //                         cid: param.data.cid,
212
+        //                         userID: param.data.userID,
213
+        //                         passwd: ''
214
+        //                     }
215
+        //                     var chkflg = setInterval(function (inparam) {
216
+        //                         lock.acquire('hmap', ()=>{
217
+        //                             // return value or promise
218
+        //                             //console.log('get hmap lock');
219
+        //                             var hobj = hmap.get(inparam.ts)
220
+        //                             //console.log(hobj)
221
+        //                             if (hobj!==undefined) {
222
+        //                                 if (hobj.stat == 1) {
223
+        //                                     clearInterval(chkflg)
224
+        //                                     var dobj = JSON.parse(hobj.data);
225
+        //                                     console.log('===>>>'+dobj.data[0].Password)
226
+        //                                     inparam.passwd = dobj.data[0].Password
227
+        //                                     setImmediate((inparam)=> {
228
+        //                                         em.emit('convertStep1', inparam, 123);
229
+        //                                     }, inparam);
230
+        //                                 }
231
+        //                             }
232
+        //                             else {
233
+        //                                 clearInterval(chkflg)
234
+        //                             }
235
+        //                         }).then(function() {
236
+        //                             // lock released
237
+        //                             //console.log('get hmap lock released');
238
+        //                         });                                
239
+        //                     }, 100, inparam)       
240
+        //                     return false
241
+        //                 }
242
+        //             }
243
+        //             return true
244
+        //         })
245
+        //     }
246
+        //     else {
247
+
248
+        //     }
249
+        // })        
250
+    })
251
+}    
252
+
253
+
254
+em.on('convertStep1', (param, num) => {
255
+    console.log('convertStep1....'+param.cid+' , '+param.userID+' , '+param.passwd)
256
+    console.log(num)
257
+})
258
+
259
+
260
+em.on('loginverify:Step1', (param, event, hmap, mywindow)=>{
261
+    var url
262
+    if (param.data.cid.slice(0,6)=='886235') url='http://intercom.nxt.tw:8006'
263
+    else url = 'http://aws.gigatech.tw:8006'
264
+    axios.get(url+'/Common/CloudCMS/devices/devicelist.ashx?CID='+param.data.cid)
265
+    .then(res => {
266
+        if (res.status==200 && res.data.success) {
267
+            //console.log(res.data.data)
268
+            res.data.data.every((element) => {
269
+                console.log(element)
270
+                var data = element.split(';')
271
+                if (data.length > 3) {
272
+                    if (data[2]==='00000000-1') {
273
+                        console.log('Master Device SN : '+data[0])
274
+                        dayjs.extend(utc)
275
+                        const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString()
276
+                        var querydata = {
277
+                            topic: data[0],
278
+                            message:'{"action":"mqttqueryMobileList","data":{"Command":"CID='+param.data.cid+'&ID=00000000","SerialNo":"'+sub_topic1+'","TimeStamp":"'+ts+'","Token":"FFFFFGIGATECH0529FFFFF"}}'
279
+                        }
280
+
281
+                        var cb_param = {
282
+                            stat: 0,
283
+                            data: ''
284
+                        }
285
+                        hmap.set(ts, cb_param)
286
+                        client.publish(querydata.topic, querydata.message, {
287
+                            qos: 2,
288
+                            retain: false,
289
+                        })   
290
+
291
+                        var inparam = {
292
+                            ts: ts,
293
+                            retrycnt: 0,
294
+                            cid: param.data.cid,
295
+                            userID: param.data.userID,
296
+                            vpasswd: param.data.vpasswd,
297
+                            passwd: '',
298
+                            topic: querydata.topic,
299
+                        }
300
+                        var chkflg = setInterval(function (inparam) {
301
+                            lock.acquire('hmap', ()=>{
302
+                                // return value or promise
303
+                                //console.log('get hmap lock');
304
+                                var hobj = hmap.get(inparam.ts)
305
+                                //console.log(hobj)
306
+                                if (hobj!==undefined) {
307
+                                    if (hobj.stat == 1) {
308
+                                        clearInterval(chkflg)
309
+                                        var dobj = JSON.parse(hobj.data);
310
+                                        console.log('===>>>'+dobj.data[0].Password)
311
+                                        inparam.passwd = dobj.data[0].Password
312
+                                        hmap.delete(inparam.ts);
313
+                                        setImmediate((inparam, event, hmap, mywindow)=> {
314
+                                            em.emit('loginverify:Step2', inparam, event, hmap, mywindow);
315
+                                        }, inparam, event, hmap, mywindow);
316
+                                    }
317
+                                    else {
318
+                                        if (inparam.retrycnt>50) {
319
+                                            // 大於 5 Sec沒有回應
320
+                                            clearInterval(chkflg)
321
+                                            hmap.delete(inparam.ts);
322
+                                            var lparam = {
323
+                                                action: 'loginVerifyReply',
324
+                                                data: {
325
+                                                    code: 404,
326
+                                                    token: ''
327
+                                                }
328
+                                            }  
329
+                                            setImmediate((lparam, event, hmap, mywindow)=> {
330
+                                                em.emit('loginverify:Step3', lparam, event, hmap, mywindow);
331
+                                            }, lparam, event, hmap, mywindow);                                                
332
+                                        }
333
+                                        else inparam.retrycnt++
334
+                                    }
335
+                                }
336
+                                else {
337
+                                    clearInterval(chkflg)
338
+                                }
339
+                            }).then(function() {
340
+                                // lock released
341
+                                //console.log('get hmap lock released');
342
+                            });                                
343
+                        }, 100, inparam)       
344
+                        return false
345
+                    }
346
+                }
347
+                return true
348
+            })
349
+        }
350
+        else {
351
+            var lparam = {
352
+                action: 'loginVerifyReply',
353
+                data: {
354
+                    code: 404,
355
+                    token: ''
356
+                }
357
+            }  
358
+            setImmediate((lparam, event, hmap, mywindow)=> {
359
+                em.emit('loginverify:Step3', lparam, event, hmap, mywindow);
360
+            }, lparam, event, hmap, mywindow);  
361
+        }
362
+    })        
363
+})
364
+
365
+
366
+em.on('loginverify:Step2', (param, event, hmap, mywindow)=>{
367
+    //console.log('loginverify:Step2')
368
+    console.log('loginverify:Step2 -> '+param.cid+' , '+param.userID+' , '+param.passwd)
369
+    dayjs.extend(utc)
370
+    const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString()
371
+    var querydata = {
372
+        topic: param.topic,
373
+        message:'{"action":"mqttverifyRemoteKeyAccount","data":{"Command":"User='+param.userID+'&Account='+param.cid+'_'+param.userID+'&PassWord='+param.passwd+'&WebPassword='+param.vpasswd+'","SerialNo":"'+sub_topic1+'","TimeStamp":"'+ts+'","Token":"FFFFFGIGATECH0529FFFFF"}}'
374
+    }
375
+
376
+    var cb_param = {
377
+        stat: 0,
378
+        data: ''
379
+    }    
380
+    hmap.set(ts, cb_param)
381
+    client.publish(querydata.topic, querydata.message, {
382
+        qos: 2,
383
+        retain: false,
384
+    })      
385
+    var inparam = {
386
+        ts: ts,
387
+        retrycnt: 0,
388
+        cid: param.cid,
389
+        userID: param.userID,
390
+        vpasswd: param.vpasswd,
391
+        passwd: param.passwd,
392
+        topic: param.topic,
393
+    }
394
+    //console.log(inparam)
395
+    var chkflg = setInterval(function (inparam) {
396
+        lock.acquire('hmap', ()=>{
397
+            var hobj = hmap.get(inparam.ts)
398
+            if (hobj!==undefined) {
399
+                if (hobj.stat == 1) {
400
+                    clearInterval(chkflg)
401
+                    console.log(hobj.data)
402
+                    var dobj = JSON.parse(hobj.data);
403
+                    hmap.delete(inparam.ts);
404
+                    var lparam = {
405
+                        action: 'loginVerifyReply',
406
+                        data: {
407
+                            code: 0,
408
+                            token: ''
409
+                        }
410
+                    }
411
+                    if (dobj.success && dobj.data.code==200) {
412
+                        //console.log(dobj.data.token)
413
+                        lparam.data.code = dobj.data.code
414
+                        lparam.data.token = dobj.data.token
415
+                    }
416
+                    else {
417
+                        lparam.data.code = dobj.data.code
418
+                    }
419
+                    setImmediate((lparam, event, hmap, mywindow)=> {
420
+                        em.emit('loginverify:Step3', lparam, event, hmap, mywindow);
421
+                    }, lparam, event, hmap, mywindow);
422
+                }
423
+                else {
424
+                    if (inparam.retrycnt>50) {
425
+                        // 大於 5 Sec沒有回應
426
+                        clearInterval(chkflg)
427
+                        hmap.delete(lparam.ts);
428
+                        var lparam = {
429
+                            action: 'loginVerifyReply',
430
+                            data: {
431
+                                code: 404,
432
+                                token: ''
433
+                            }
434
+                        }  
435
+                        setImmediate((lparam, event, hmap, mywindow)=> {
436
+                            em.emit('loginverify:Step3', lparam, event, hmap, mywindow);
437
+                        }, lparam, event, hmap, mywindow);                      
438
+                    }
439
+                    else inparam.retrycnt++
440
+                }
441
+            }
442
+            else {
443
+                clearInterval(chkflg)
444
+            }
445
+        }).then(function() {
446
+            // lock released
447
+            //console.log('get hmap lock released');
448
+        }); 
449
+    }, 100, inparam)
450
+})
451
+
452
+
453
+em.on('loginverify:Step3', (param, event, hmap, mywindow)=>{
454
+    console.log('loginverify:Step3 -> ', param)
455
+    mywindow.webContents.send('cms:loginVerifyReply', param)  
456
+})

+ 71 - 0
src/main/utils/varsub.js 查看文件

@@ -0,0 +1,71 @@
1
+import { ipcMain } from 'electron'
2
+const events = require('events');
3
+
4
+var varmap = new Array();
5
+
6
+export function varSubProc (mywindow) {
7
+    console.log('varSubProc run.....');
8
+    var em=new events.EventEmitter();
9
+
10
+
11
+    em.on('em:setDate',function(cmd){
12
+        console.log(cmd);
13
+        var index = cmd.data.name+'-'+cmd.data.user;
14
+        if (cmd.data.type=="Number") {
15
+            varmap[index] = parseInt(cmd.data.value);
16
+        }
17
+        else if (cmd.data.type=="Float") {
18
+            varmap[index] = parseFloat(cmd.data.value);
19
+        }
20
+        else {
21
+            varmap[index] = cmd.data.value;
22
+        }          
23
+    });
24
+
25
+    em.on('em:getDate',function(cmd){
26
+        console.log(cmd);
27
+    });    
28
+
29
+    ipcMain.on('ipc:setData', function(event, cmd) {
30
+        console.log(cmd);
31
+        //event.sender.send('asynchronous-reply', 'pong');
32
+        var index = cmd.data.name+'-'+cmd.data.user;
33
+        if (cmd.data.type=="Number") {
34
+            varmap[index] = parseInt(cmd.data.value);
35
+        }
36
+        else if (cmd.data.type=="Float") {
37
+            varmap[index] = parseFloat(cmd.data.value);
38
+        }
39
+        else {
40
+            varmap[index] = cmd.data.value;
41
+        }        
42
+    });
43
+
44
+    ipcMain.on('ipc:getData', function(event, cmd) {
45
+        var retv;
46
+        console.log(cmd);
47
+        var index = cmd.data.name+'-'+cmd.data.user;
48
+        if (varmap[index]==null) {
49
+            if (cmd.data.type=='Number') {
50
+                retv = 0;
51
+            }
52
+            else{
53
+                retv = '';
54
+            }
55
+        }
56
+        else {
57
+            retv = varmap[index];
58
+        }
59
+        var param = {
60
+            success: true,
61
+            action: 'getData',
62
+            data: {
63
+                name: cmd.data.name,
64
+                user: cmd.data.user,
65
+                type: cmd.data.type,
66
+                value: retv
67
+            }
68
+        }
69
+        event.sender.send('ipc:getDataReply', param);
70
+    });
71
+}

+ 20 - 0
src/preload/index.js 查看文件

@@ -0,0 +1,20 @@
1
+import { contextBridge } from 'electron'
2
+import { electronAPI } from '@electron-toolkit/preload'
3
+
4
+// Custom APIs for renderer
5
+const api = {}
6
+
7
+// Use `contextBridge` APIs to expose Electron APIs to
8
+// renderer only if context isolation is enabled, otherwise
9
+// just add to the DOM global.
10
+if (process.contextIsolated) {
11
+  try {
12
+    contextBridge.exposeInMainWorld('electron', electronAPI)
13
+    contextBridge.exposeInMainWorld('api', api)
14
+  } catch (error) {
15
+    console.error(error)
16
+  }
17
+} else {
18
+  window.electron = electronAPI
19
+  window.api = api
20
+}

+ 17 - 0
src/renderer/index.html 查看文件

@@ -0,0 +1,17 @@
1
+<!doctype html>
2
+<html>
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <title>Electron</title>
6
+    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
7
+    <meta
8
+      http-equiv="Content-Security-Policy"
9
+      content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
10
+    />
11
+  </head>
12
+
13
+  <body>
14
+    <div id="app"></div>
15
+    <script type="module" src="/src/main.js"></script>
16
+  </body>
17
+</html>

+ 16 - 0
src/renderer/src/App.vue 查看文件

@@ -0,0 +1,16 @@
1
+<script setup>
2
+import BackGround from './components/BackGround.vue'
3
+//import Reserv from './components/ReservMain.vue'
4
+//import Login from './components/LoginPage.vue'
5
+</script>
6
+
7
+<template>
8
+  <back-ground></back-ground>
9
+  <!-- <Reserv></Reserv> -->
10
+  <!-- <Login></Login> -->
11
+  <router-view></router-view>
12
+</template>
13
+
14
+<style lang="less">
15
+//@import './assets/css/styles.less';
16
+</style>

+ 205 - 0
src/renderer/src/assets/css/styles.less 查看文件

@@ -0,0 +1,205 @@
1
+body {
2
+  display: flex;
3
+  flex-direction: column;
4
+  font-family:
5
+    Roboto,
6
+    -apple-system,
7
+    BlinkMacSystemFont,
8
+    'Helvetica Neue',
9
+    'Segoe UI',
10
+    'Oxygen',
11
+    'Ubuntu',
12
+    'Cantarell',
13
+    'Open Sans',
14
+    sans-serif;
15
+  color: #86a5b1;
16
+  background-color: #2f3241;
17
+}
18
+
19
+* {
20
+  padding: 0;
21
+  margin: 0;
22
+}
23
+
24
+ul {
25
+  list-style: none;
26
+}
27
+
28
+code {
29
+  font-weight: 600;
30
+  padding: 3px 5px;
31
+  border-radius: 2px;
32
+  background-color: #26282e;
33
+  font-family:
34
+    ui-monospace,
35
+    SFMono-Regular,
36
+    SF Mono,
37
+    Menlo,
38
+    Consolas,
39
+    Liberation Mono,
40
+    monospace;
41
+  font-size: 85%;
42
+}
43
+
44
+a {
45
+  color: #9feaf9;
46
+  font-weight: 600;
47
+  cursor: pointer;
48
+  text-decoration: none;
49
+  outline: none;
50
+}
51
+
52
+a:hover {
53
+  border-bottom: 1px solid;
54
+}
55
+
56
+#app {
57
+  flex: 1;
58
+  display: flex;
59
+  flex-direction: column;
60
+  max-width: 840px;
61
+  margin: 0 auto;
62
+  padding: 15px 30px 0 30px;
63
+}
64
+
65
+.versions {
66
+  margin: 0 auto;
67
+  float: none;
68
+  clear: both;
69
+  overflow: hidden;
70
+  font-family: 'Menlo', 'Lucida Console', monospace;
71
+  color: #c2f5ff;
72
+  line-height: 1;
73
+  transition: all 0.3s;
74
+
75
+  li {
76
+    display: block;
77
+    float: left;
78
+    border-right: 1px solid rgba(194, 245, 255, 0.4);
79
+    padding: 0 20px;
80
+    font-size: 13px;
81
+    opacity: 0.8;
82
+
83
+    &:last-child {
84
+      border: none;
85
+    }
86
+  }
87
+}
88
+
89
+.hero-logo {
90
+  margin-top: -0.4rem;
91
+  transition: all 0.3s;
92
+}
93
+
94
+@media (max-width: 840px) {
95
+  .versions {
96
+    display: none;
97
+  }
98
+
99
+  .hero-logo {
100
+    margin-top: -1.5rem;
101
+  }
102
+}
103
+
104
+.hero-text {
105
+  font-weight: 400;
106
+  color: #c2f5ff;
107
+  text-align: center;
108
+  margin-top: -0.5rem;
109
+  margin-bottom: 10px;
110
+}
111
+
112
+@media (max-width: 660px) {
113
+  .hero-logo {
114
+    display: none;
115
+  }
116
+
117
+  .hero-text {
118
+    margin-top: 20px;
119
+  }
120
+}
121
+
122
+.hero-tagline {
123
+  text-align: center;
124
+  margin-bottom: 14px;
125
+}
126
+
127
+.links {
128
+  display: flex;
129
+  align-items: center;
130
+  justify-content: center;
131
+  margin-bottom: 24px;
132
+  font-size: 18px;
133
+  font-weight: 500;
134
+
135
+  a {
136
+    font-weight: 500;
137
+  }
138
+
139
+  .link-item {
140
+    padding: 0 4px;
141
+  }
142
+}
143
+
144
+.features {
145
+  display: flex;
146
+  flex-wrap: wrap;
147
+  margin: -6px;
148
+
149
+  .feature-item {
150
+    width: 33.33%;
151
+    box-sizing: border-box;
152
+    padding: 6px;
153
+  }
154
+
155
+  article {
156
+    background-color: rgba(194, 245, 255, 0.1);
157
+    border-radius: 8px;
158
+    box-sizing: border-box;
159
+    padding: 12px;
160
+    height: 100%;
161
+  }
162
+
163
+  span {
164
+    color: #d4e8ef;
165
+    word-break: break-all;
166
+  }
167
+
168
+  .title {
169
+    font-size: 17px;
170
+    font-weight: 500;
171
+    color: #c2f5ff;
172
+    line-height: 22px;
173
+    overflow: hidden;
174
+    text-overflow: ellipsis;
175
+    white-space: nowrap;
176
+  }
177
+
178
+  .detail {
179
+    font-size: 14px;
180
+    font-weight: 500;
181
+    line-height: 22px;
182
+    margin-top: 6px;
183
+  }
184
+}
185
+
186
+@media (max-width: 660px) {
187
+  .features .feature-item {
188
+    width: 50%;
189
+  }
190
+}
191
+
192
+@media (max-width: 480px) {
193
+  .links {
194
+    flex-direction: column;
195
+    line-height: 32px;
196
+
197
+    .link-dot {
198
+      display: none;
199
+    }
200
+  }
201
+
202
+  .features .feature-item {
203
+    width: 100%;
204
+  }
205
+}

+ 34 - 0
src/renderer/src/assets/icons.svg 查看文件

@@ -0,0 +1,34 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2
+  <symbol id="electron" viewBox="0 0 900 300">
3
+    <g fill="none" fill-rule="evenodd">
4
+      <g class="hero-apps" style="fill: #71abb7;">
5
+        <path d="M15 138l-4.9-.64L8 133l-2.1 4.36L1 138l3.6 3.26-.93 4.74L8 143.67l4.33 2.33-.93-4.74z"></path>
6
+        <path d="M897.2 114.0912l-5.2 3.63v-2.72c0-.55-.45-1-1-1h-8c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-2.72l5.2 3.63c.33.23.8 0 .8-.41v-10c0-.41-.47-.64-.8-.41z"></path>
7
+        <path d="M65.4 188.625h-1.6c.88 0 1.6-.7313 1.6-1.625v-1.625c0-.8937-.72-1.625-1.6-1.625h-1.6c-.88 0-1.6.7313-1.6 1.625V187c0 .8937.72 1.625 1.6 1.625h-1.6c-.88 0-1.6.7313-1.6 1.625v3.25h1.6v4.875c0 .8937.72 1.625 1.6 1.625h1.6c.88 0 1.6-.7313 1.6-1.625V193.5H67v-3.25c0-.8937-.72-1.625-1.6-1.625zm-3.2-3.25h1.6V187h-1.6v-1.625zm3.2 6.5h-1.6v6.5h-1.6v-6.5h-1.6v-1.625h4.8v1.625zm3.344-5.6875c0-3.2175-2.576-5.8337-5.744-5.8337-3.168 0-5.744 2.6162-5.744 5.8337 0 .455.048.8937.144 1.3162v3.2175c-.976-1.2512-1.6-2.8112-1.6-4.55 0-4.03 3.232-7.3125 7.2-7.3125s7.2 3.2825 7.2 7.3125c0 1.7225-.624 3.2988-1.6 4.55v-3.2175c.096-.4387.144-.8612.144-1.3162zm6.256 0c0 4.68-2.608 8.7425-6.4 10.7738v-1.7063c2.976-1.885 4.944-5.2325 4.944-9.0675 0-5.915-4.72-10.7087-10.544-10.7087-5.824 0-10.544 4.7937-10.544 10.7087 0 3.835 1.968 7.1825 4.944 9.0675v1.7063c-3.792-2.0313-6.4-6.0938-6.4-10.7738C51 179.46 56.376 174 63 174s12 5.46 12 12.1875z"></path>
8
+        <path d="M830.7143 142.3333c-.8643 0-1.5714.7125-1.5714 1.5834v3.1666c0 .871.707 1.5834 1.5713 1.5834h12.5714c.8643 0 1.5714-.7125 1.5714-1.5834v-3.1666c0-.871-.707-1.5834-1.5713-1.5834h-12.5714zm12.5714 2.771l-1.9643 1.979h-2.357L837 145.1043l-1.9643 1.979h-2.357l-1.9644-1.979v-1.1876h1.1786l1.964 1.979 1.9644-1.979h2.3572l1.9643 1.979 1.964-1.979h1.1787v1.1875zm-9.4286 5.1457h6.286v1.5833h-6.286V150.25zM837 136c-6.0657 0-11 4.6075-11 10.2917v7.125c0 .8708.707 1.5833 1.5714 1.5833h18.8572c.8643 0 1.5714-.7125 1.5714-1.5833v-7.125C848 140.6075 843.0657 136 837 136zm9.4286 17.4167h-18.8572v-7.125c0-4.8925 4.1486-8.851 9.4286-8.851 5.28 0 9.4286 3.9585 9.4286 8.851v7.125z"></path>
9
+        <path d="M75 91.8065V96h4.1935L90.376 84.8174l-4.1934-4.1935L75 91.8064zm4.1935 2.7957h-2.7957v-2.7957h1.398v1.3978h1.3977v1.398zM93.591 81.6024l-1.817 1.817-4.1935-4.1934 1.817-1.817c.5453-.5453 1.426-.5453 1.971 0l2.2226 2.2224c.5453.5452.5453 1.4258 0 1.971z"></path>
10
+        <path d="M797 187h4v4h-4v-4zm12-1v19c0 1.1-.9 2-2 2h-20c-1.1 0-2-.9-2-2v-24c0-1.1.9-2 2-2h15l7 7zm-2 1l-6-6h-14v22l6-10 4 8 4-4 6 6v-16z"></path>
11
+        <path d="M138 125c-6.62 0-12 5-12 11 0 9.04 12 21 12 21s12-11.96 12-21c0-6-5.38-11-12-11zm0 29.1c-3.72-4.06-10-12.22-10-18.1 0-4.96 4.5-9 10-9 2.68 0 5.22.96 7.12 2.72 1.84 1.72 2.88 3.94 2.88 6.28 0 5.88-6.28 14.04-10 18.1zm4-18.1c0 2.22-1.78 4-4 4-2.22 0-4-1.78-4-4 0-2.22 1.78-4 4-4 2.22 0 4 1.78 4 4z"></path>
12
+        <path d="M771 82h8v2h-8v-2zm0 6h8v-2h-8v2zm0 4h8v-2h-8v2zm22-10h-8v2h8v-2zm0 4h-8v2h8v-2zm0 4h-8v2h8v-2zm4-12v18c0 1.1-.9 2-2 2h-11l-2 2-2-2h-11c-1.1 0-2-.9-2-2V78c0-1.1.9-2 2-2h11l2 2 2-2h11c1.1 0 2 .9 2 2zm-16 1l-1-1h-11v18h12V79zm14-1h-11l-1 1v17h12V78z"></path>
13
+        <path d="M176 203h-24c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v7l7-7h13c1.1 0 2-.9 2-2v-16c0-1.1-.9-2-2-2zm0 18h-14l-4 4v-4h-6v-16h24v16z"></path>
14
+        <path d="M673 88.921c0 2.18-.9 4.18-2.34 5.66l-1.34-1.34c1.1-1.12 1.78-2.62 1.78-4.32 0-1.7-.68-3.22-1.78-4.32l1.34-1.34c1.44 1.44 2.34 3.44 2.34 5.66zm-8.56-11.48l-7.44 7.44h-4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h4l7.44 7.44c.94.94 2.56.28 2.56-1.06v-20.76c0-1.34-1.62-2-2.56-1.06zm11.88.16l-1.34 1.34c2.56 2.56 4.12 6.06 4.12 9.96 0 3.88-1.56 7.4-4.12 9.96l1.34 1.34c2.9-2.9 4.68-6.9 4.68-11.32 0-4.44-1.78-8.44-4.68-11.32v.04zm-2.82 2.82l-1.38 1.34c1.84 1.84 2.96 4.38 2.96 7.16 0 2.78-1.12 5.32-2.96 7.12l1.38 1.34c2.16-2.16 3.5-5.16 3.5-8.46 0-3.3-1.34-6.32-3.5-8.5z"></path>
15
+        <path d="M226 79h-16c0-1.1-.9-2-2-2h-8c-1.1 0-2 .9-2 2-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V81c0-1.1-.9-2-2-2zm-18 4h-8v-2h8v2zm9 14c-3.88 0-7-3.12-7-7s3.12-7 7-7 7 3.12 7 7-3.12 7-7 7zm5-7c0 2.76-2.26 5-5 5s-5-2.26-5-5 2.26-5 5-5 5 2.26 5 5z"></path>
16
+        <path d="M725.8393 157h-15.6498c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h15.6298C727 153 727 153.82 727 155c0 1.18 0 2-1.1807 2h.02zm-11.6473-10c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h11.6273C727 143 727 143.82 727 145c0 1.18 0 2-1.1807 2H714.192zM695 146.82l2.8218-2.6 3.182 3.18 8.185-8.4 2.8218 2.82-11.0068 11-6.0038-6zM710.1895 163h15.6298C727 163 727 163.82 727 165c0 1.18 0 2-1.1807 2h-15.6298c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2z"></path>
17
+        <path d="M223 152v24c0 1.65 1.35 3 3 3h36c1.65 0 3-1.35 3-3v-24c0-1.65-1.35-3-3-3h-36c-1.65 0-3 1.35-3 3zm39 0l-18 15-18-15h36zm-36 4.5l12 9-12 9v-18zm3 19.5l10.5-9 4.5 4.5 4.5-4.5 10.5 9h-30zm33-1.5l-12-9 12-9v18z"></path>
18
+        <path d="M648 182h-3v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-9v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-3c-1.65 0-3 1.35-3 3v33c0 1.65 1.35 3 3 3h33c1.65 0 3-1.35 3-3v-33c0-1.65-1.35-3-3-3zm0 36h-33v-27h33v27zm-24-33h-3v-6h3v6zm18 0h-3v-6h3v6zm-15 12h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3z"></path>
19
+      </g>
20
+      <g class="hero-icons" style="fill: #c2f5ff;">
21
+        <path d="M441.1132 69.724c7.681 0 13.9075-6.207 13.9075-13.8636 0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9076 6.207-13.9076 13.8634 0 7.6566 6.2266 13.8635 13.9076 13.8635zm0-5.7932c-4.4713 0-8.096-3.6132-8.096-8.0704 0-4.457 3.6247-8.0703 8.096-8.0703 4.4712 0 8.096 3.6133 8.096 8.0704 0 4.4572-3.6248 8.0704-8.096 8.0704z"></path>
22
+        <path d="M354.8995 220.2693c7.681 0 13.9075-6.207 13.9075-13.8635s-6.2266-13.8634-13.9075-13.8634c-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635zm0-5.793c-4.4713 0-8.096-3.6133-8.096-8.0705 0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703c0 4.4572-3.6247 8.0704-8.096 8.0704z"></path>
23
+        <path d="M541.0343 206.4058c0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635 7.681 0 13.9075-6.207 13.9075-13.8635zm-5.8115 0c0 4.4572-3.6247 8.0704-8.096 8.0704s-8.096-3.6132-8.096-8.0704c0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703z"></path>
24
+        <path d="M397.6943 214.5258c9.7012 27.0033 25.5723 43.629 43.419 43.629 13.0157 0 25.0578-8.8443 34.4482-24.4154.827-1.371.3822-3.1507-.9932-3.975-1.3755-.824-3.1607-.3808-3.9876.9902-8.439 13.9938-18.8052 21.6072-29.4675 21.6072-14.8247 0-28.9803-14.8288-37.9476-39.7892-.541-1.506-2.2044-2.2897-3.7153-1.7504-1.511.5394-2.297 2.1975-1.756 3.7036z"></path>
25
+        <path d="M514.124 163.4733c18.5545-21.85 25.033-43.826 16.122-59.2117-6.557-11.321-20.419-17.2982-38.841-17.537-1.6047-.021-2.9225 1.259-2.9434 2.8586-.0208 1.5996 1.263 2.9132 2.8678 2.934 16.5683.2148 28.5106 5.3642 33.8836 14.641 7.4018 12.7797 1.6243 32.3774-15.5247 52.5722-1.037 1.221-.8844 3.0487.3405 4.0822 1.2248 1.0336 3.0584.8817 4.0952-.3393z"></path>
26
+        <path d="M411.5672 88.457c-28.3373-5.1448-50.7424.24-59.672 15.6575-6.6635 11.505-4.7588 26.7585 4.6193 43.0637.7982 1.3878 2.574 1.8678 3.966 1.072 1.3923-.7956 1.874-2.5656 1.0756-3.9534-8.4477-14.688-10.0915-27.8524-4.628-37.2857 7.418-12.8074 27.403-17.6105 53.5978-12.8546 1.579.2866 3.092-.7568 3.3794-2.3307.2876-1.5738-.7592-3.082-2.338-3.3687z"></path>
27
+        <path d="M486.3075 209.2436c5.022-15.998 7.7194-34.453 7.7194-53.6842 0-47.9875-16.849-89.3545-40.8478-99.977-1.4667-.649-3.1837.0098-3.835 1.472-.6512 1.462.01 3.1735 1.4766 3.8227 21.404 9.474 37.3945 48.7337 37.3945 94.6824 0 18.6574-2.612 36.5297-7.454 51.954-.4794 1.5268.3736 3.1518 1.9052 3.6295s3.1617-.3727 3.641-1.8994z"></path>
28
+        <path d="M466.439 89.4215c-16.7763 3.583-34.6332 10.5886-51.7827 20.4585-42.434 24.4216-70.1147 60.4323-66.2703 86.5432.233 1.5828 1.709 2.6776 3.297 2.4453 1.5877-.2323 2.686-1.7037 2.453-3.2865-3.4135-23.1838 22.825-57.3183 63.426-80.685 16.6365-9.5746 33.9267-16.3578 50.0946-19.811 1.5692-.335 2.5687-1.8748 2.2325-3.439-.336-1.5642-1.8807-2.5606-3.45-2.2255z"></path>
29
+        <path d="M371.2508 166.997c11.458 12.5516 26.3438 24.3243 43.3203 34.0947 41.106 23.6572 84.866 29.9805 106.4328 15.3217 1.326-.9013 1.668-2.7033.7638-4.025-.904-1.3217-2.712-1.6626-4.0378-.7614-19.302 13.1195-60.871 7.1128-100.253-15.5523-16.469-9.4783-30.8834-20.8782-41.9277-32.9767-1.08-1.1832-2.9178-1.2695-4.1048-.1928-1.187 1.0766-1.2735 2.9086-.1934 4.0918z"></path>
30
+        <path d="M443.2374 165.3634c-5.432 1.17-10.7838-2.2712-11.9598-7.686-1.1714-5.415 2.2785-10.7498 7.7106-11.922 5.432-1.17 10.7838 2.2712 11.9598 7.686 1.1737 5.415-2.2785 10.7498-7.7106 11.922z"></path>
31
+      </g>
32
+    </g>
33
+  </symbol>
34
+</svg>

二進制
src/renderer/src/assets/img/Add.png 查看文件


二進制
src/renderer/src/assets/img/AddMsg.png 查看文件


二進制
src/renderer/src/assets/img/CasePut.png 查看文件


二進制
src/renderer/src/assets/img/DelMsg.png 查看文件


二進制
src/renderer/src/assets/img/Delete.png 查看文件


二進制
src/renderer/src/assets/img/Down.png 查看文件


二進制
src/renderer/src/assets/img/Edit.png 查看文件


二進制
src/renderer/src/assets/img/Gas.png 查看文件


二進制
src/renderer/src/assets/img/GetMgt.png 查看文件


二進制
src/renderer/src/assets/img/GroupAdd.png 查看文件


二進制
src/renderer/src/assets/img/Money.png 查看文件


二進制
src/renderer/src/assets/img/MsgList.png 查看文件


二進制
src/renderer/src/assets/img/MsgLog.png 查看文件


二進制
src/renderer/src/assets/img/MsgMgt.png 查看文件


二進制
src/renderer/src/assets/img/Note.png 查看文件


二進制
src/renderer/src/assets/img/NoteSet.png 查看文件


二進制
src/renderer/src/assets/img/OnePerson.png 查看文件


二進制
src/renderer/src/assets/img/Package.png 查看文件


二進制
src/renderer/src/assets/img/Records.png 查看文件


二進制
src/renderer/src/assets/img/Repair.png 查看文件


二進制
src/renderer/src/assets/img/Search.png 查看文件


二進制
src/renderer/src/assets/img/Table.png 查看文件


二進制
src/renderer/src/assets/img/TimeDs.png 查看文件


二進制
src/renderer/src/assets/img/TwoPerson.png 查看文件


二進制
src/renderer/src/assets/img/UpMsg.png 查看文件


二進制
src/renderer/src/assets/img/White.png 查看文件


二進制
src/renderer/src/assets/img/addorder.png 查看文件


二進制
src/renderer/src/assets/img/back.png 查看文件


二進制
src/renderer/src/assets/img/bgreen.png 查看文件


二進制
src/renderer/src/assets/img/bgrey.png 查看文件


二進制
src/renderer/src/assets/img/qttitle.png 查看文件


+ 54 - 0
src/renderer/src/components/BackGround.vue 查看文件

@@ -0,0 +1,54 @@
1
+<template>
2
+    <div>
3
+
4
+    </div>
5
+</template>
6
+
7
+<script setup>
8
+    import { ref, reactive, onMounted } from 'vue'
9
+    import { ElMessage, ElMessageBox } from 'element-plus'
10
+    import { useReservStore } from '../stores/reservdata'
11
+    const { ipcRenderer } = window.require('electron')
12
+    const { dialog } = require('@electron/remote')
13
+    const currentWindow = require('@electron/remote').getCurrentWindow()
14
+    const fs = require('fs')
15
+
16
+    console.log('BackGround run ......')
17
+
18
+    const reservstore = useReservStore()
19
+
20
+    onMounted (()=> {
21
+        console.log('onMounted...')
22
+        ipcRenderer.send('renderer:cmd', 'onMounted'); 
23
+    })
24
+
25
+    const DebugLog = (item) => {
26
+        ipcRenderer.send('debug-log',item)
27
+    }         
28
+
29
+    ipcRenderer.on('mqtt:connectReply',async (event, param) => {
30
+        DebugLog('ReservMain  接收到 mqtt:connectReply : ' + JSON.stringify(param))
31
+        console.log('ReservMain  接收到 mqtt:connectReply : ' + JSON.stringify(param))
32
+        if (param.action=='mqttconnect') {
33
+            if (param.success) {
34
+                if (!reservstore.mqttstat) {
35
+                    ElMessage({
36
+                    type: 'success',
37
+                    message: 'MQTT連線 成功',
38
+                    })
39
+                    reservstore.mqttstat = true 
40
+                }            
41
+            }
42
+            else {
43
+                if (reservstore.mqttstat) {
44
+                    ElMessage.error('MQTT連線 失敗')
45
+                    reservstore.mqttstat = false
46
+                }
47
+            }
48
+        }
49
+    })      
50
+</script>
51
+
52
+<style lang="less" scoped>
53
+
54
+</style>

+ 141 - 0
src/renderer/src/components/LoginPage.vue 查看文件

@@ -0,0 +1,141 @@
1
+<template>
2
+    <el-container class="main-container">
3
+        <el-col :span="8">
4
+            <el-card class="card-wrapper">
5
+                <el-form>
6
+                    <h1>預約系統登錄</h1>
7
+                    <el-form-item label="Username">
8
+                        <el-input v-model="username" placeholder="Enter your username"></el-input>
9
+                    </el-form-item>
10
+                    <el-form-item label="Password">
11
+                        <el-input v-model="password" placeholder="Enter your password" type="password" show-password></el-input>
12
+                    </el-form-item>
13
+                    <el-form-item>
14
+                        <el-button type="primary" :disabled="!reservstore.mqttstat||loginStat!=0" @click="login">Login</el-button>
15
+                    </el-form-item>     
16
+                    <el-form-item class="submit-form-item">
17
+                        <div id="chkbox">
18
+                            <el-checkbox v-model="isSave" label="保存設置" size="large"/>
19
+                        </div>                          
20
+                    </el-form-item>               
21
+                </el-form>
22
+            </el-card>
23
+        </el-col>        
24
+    </el-container>        
25
+</template>
26
+
27
+<script setup>
28
+    import { ref, reactive, onMounted, watch } from 'vue'
29
+    import { ElMessage, ElMessageBox } from 'element-plus'
30
+    const { ipcRenderer } = window.require('electron')
31
+    import { useRouter } from 'vue-router'
32
+    import { useReservStore } from '../stores/reservdata'
33
+    import { useSaveStore } from '../stores/savedata'
34
+    const dayjs = require('dayjs')
35
+    const utc = require('dayjs/plugin/utc')
36
+
37
+    //首先在setup中定义
38
+    const router = useRouter()
39
+    const reservstore = useReservStore()
40
+    const savestore = useSaveStore()
41
+
42
+    // const username = ref('886235000025_1')
43
+    const username = ref('')
44
+    const password = ref('')
45
+    const loginStat = ref(0)
46
+    const isSave = ref(false)
47
+
48
+    const sub_topic = '20005000A0001'
49
+
50
+
51
+    onMounted(() => {
52
+        console.log('=====>>>>>> '+savestore.saveData.isSave)
53
+        isSave.value = savestore.saveData.isSave
54
+        if (isSave.value) {
55
+            username.value = savestore.saveData.userName
56
+            password.value = savestore.saveData.passWord
57
+        }
58
+    })
59
+
60
+
61
+    watch(isSave, (newValue, oldValue) => {
62
+        console.log(newValue, oldValue)
63
+        savestore.saveData.isSave = isSave.value
64
+        if (!isSave.value) {
65
+            savestore.saveData.userName = ''
66
+            savestore.saveData.passWord = ''        
67
+        }
68
+    })
69
+
70
+    const login = () => {
71
+        if (isSave.value) {
72
+            savestore.saveData.userName = username.value
73
+            savestore.saveData.passWord = password.value
74
+        }        
75
+        console.log(reservstore.mqttstat)
76
+        dayjs.extend(utc)
77
+        const ts = dayjs().utc().format("YYYY/MM/DD_HH:mm:ss").toString()
78
+        // var cmd1 = {
79
+        //     //topic:'2500120010004',
80
+        //     //message:'{"action":"mqttgetConfig","data":{"configName":"PDV5703Condition","SerialNo":"'+sub_topic+'","TimeStamp":"'+ts+'","Token":"FFFFFGIGATECH0529FFFFF"}}'
81
+        //     topic:'886235000025',
82
+        //     message:'{"action":"mqtthpgetSystem","data":{"Command":"","SerialNo":"'+sub_topic+'","TimeStamp":"'+ts+'","Token":"FFFFFGIGATECH0529FFFFF"}}'
83
+        // }
84
+        // ipcRenderer.send('mqtt:publish', cmd1);
85
+        var data = username.value.split('_')
86
+        if (data.length==2) {
87
+            var cmd2 = {
88
+                action: 'loginVerify',
89
+                data: {
90
+                    cid: data[0],
91
+                    userID: data[1],
92
+                    vpasswd: password.value,
93
+                }
94
+            }
95
+            loginStat.value = 1
96
+            ipcRenderer.send('cms:loginVerify', cmd2);
97
+            //router.push('reserv')
98
+        }
99
+    }
100
+
101
+    ipcRenderer.on('cms:loginVerifyReply', (event, param) => {
102
+        DebugLog('LoginPage  接收到 cms:loginVerifyReply : ' + JSON.stringify(param))
103
+        if (param.data.code==200) {
104
+            ElMessage({
105
+                type: 'success',
106
+                message: '登錄 成功',
107
+            })
108
+            reservstore.mqttToken = param.data.token
109
+            loginStat.value = 2
110
+            router.push('reserv')
111
+        }
112
+        else {
113
+            ElMessage.error('登錄 失敗')
114
+            loginStat.value = 0
115
+        }
116
+    })
117
+
118
+    const DebugLog = (item) => {
119
+        ipcRenderer.send('debug-log',item)
120
+    }
121
+
122
+</script>
123
+
124
+<style lang="less" scoped>
125
+.custom-container {
126
+    display: flex;
127
+    justify-content: center;
128
+    align-items: center;
129
+    height: 90vh;
130
+}
131
+
132
+.main-container {
133
+    display: flex;
134
+    // 水平置中 
135
+    justify-content: center;    
136
+    // 垂直置中
137
+    align-content: center;      
138
+    flex-wrap: wrap;
139
+    margin-top: 70px;
140
+}
141
+</style>

+ 114 - 0
src/renderer/src/components/ReservMain.vue 查看文件

@@ -0,0 +1,114 @@
1
+
2
+<template>
3
+    <div>
4
+        <el-container>
5
+            <el-header class="header"> 
6
+                <img  class="qrimage" src="../assets/img/qttitle.png">
7
+                <h2>預約 QR Code</h2>
8
+            </el-header>
9
+            <el-main class="classtable">
10
+                <el-table :show-header="false" :data="tableData" stripe style="width: 100%" size="large" border @cell-click="presshandle">
11
+                    <el-table-column prop="classlabel" label="className" align="center"/>
12
+                </el-table>                
13
+            </el-main>
14
+            <el-footer>{{reservstore.vertext}}</el-footer>
15
+        </el-container>        
16
+    </div>
17
+</template>
18
+
19
+<script setup>
20
+    import { ref, reactive, onMounted } from 'vue'
21
+    import { ElMessage, ElMessageBox } from 'element-plus'
22
+    import { useReservStore } from '../stores/reservdata'
23
+    const { ipcRenderer } = window.require('electron')
24
+    const { dialog } = require('@electron/remote')
25
+    const currentWindow = require('@electron/remote').getCurrentWindow()
26
+    const fs = require('fs')
27
+    
28
+    const reservstore = useReservStore()
29
+
30
+    const tableData = [
31
+        {index: 0, classlabel: '3500000'},
32
+        {index: 1, classlabel: '三合建設'},
33
+        {index: 2, classlabel: '3502100'},
34
+        {index: 3, classlabel: '3502200'},
35
+        {index: 4, classlabel: '353教室'},
36
+        {index: 5, classlabel: '竹林電子'},
37
+        {index: 6, classlabel: '大洋興業'},
38
+        {index: 7, classlabel: '3504000'},
39
+        {index: 8, classlabel: '3504200'},
40
+        {index: 9, classlabel: '3505000'},
41
+    ]
42
+
43
+
44
+    onMounted(() => {
45
+        DebugLog('MQTT Token : ' + reservstore.mqttToken)
46
+    })
47
+
48
+    const presshandle = (row,column,cell,event) => {
49
+        console.log(row)
50
+        DebugLog('Press row: '+ row.index + ' , Class : '+row.classlabel)
51
+    }
52
+
53
+
54
+    const btnLogin = async(e)=>{
55
+        console.log('btn press');
56
+        reservstore.increment()
57
+        var cmd = {
58
+            action: 'setData',
59
+            data: {
60
+                name: 'mqttstat',
61
+                user: '',
62
+                type: 'Number',
63
+                value: '12345'					
64
+            }
65
+        }
66
+        ipcRenderer.send('ipc:setData', cmd);
67
+        
68
+        var cmd1 = {
69
+            action: 'getData',
70
+            data: {
71
+                name: 'mqttstat',
72
+                user: '',
73
+                type: 'Number',				
74
+            }
75
+        }    
76
+        ipcRenderer.send('ipc:getData', cmd1);    
77
+    }   
78
+
79
+    const DebugLog = (item) => {
80
+        ipcRenderer.send('debug-log',item)
81
+    }      
82
+
83
+    ipcRenderer.on('ipc:getDataReply',async (event, param) => {
84
+        DebugLog('ReservMain  接收到 ipc:getDataReply : ' + JSON.stringify(param))
85
+    })
86
+
87
+
88
+</script>
89
+
90
+<style lang="less" scoped>
91
+    .header {
92
+        display: flex;
93
+        // 水平置中 
94
+        justify-content: center;    
95
+        // 垂直置中
96
+        align-content: center;      
97
+        flex-wrap: wrap;
98
+        background-color: rgb(211,211,211);
99
+        border-radius: 4px 4px 0px 0px;
100
+        .qrimage {
101
+            width : 40px;
102
+            height : 40px;
103
+            margin-top: 16px;
104
+            margin-right: 4px;
105
+        }
106
+    }
107
+    .classtable {
108
+        padding-top: 0px;
109
+        padding-bottom: 0px;
110
+        padding-left: 0px;
111
+        padding-right: 0px;
112
+    }
113
+
114
+</style>

+ 14 - 0
src/renderer/src/components/Versions.vue 查看文件

@@ -0,0 +1,14 @@
1
+<script setup>
2
+import { reactive } from 'vue'
3
+
4
+const versions = reactive({ ...window.electron.process.versions })
5
+</script>
6
+
7
+<template>
8
+  <ul class="versions">
9
+    <li class="electron-version">Electron v{{ versions.electron }}</li>
10
+    <li class="chrome-version">Chromium v{{ versions.chrome }}</li>
11
+    <li class="node-version">Node v{{ versions.node }}</li>
12
+    <li class="v8-version">V8 v{{ versions.v8 }}</li>
13
+  </ul>
14
+</template>

+ 9 - 0
src/renderer/src/main.js 查看文件

@@ -0,0 +1,9 @@
1
+import { createApp } from 'vue'
2
+import { createPinia } from 'pinia'
3
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
4
+import { router } from './router'
5
+import ElementPlus from 'element-plus'
6
+import 'element-plus/dist/index.css'
7
+import App from './App.vue'
8
+
9
+createApp(App).use(createPinia().use(piniaPluginPersistedstate)).use(router).use(ElementPlus).mount('#app')

+ 13 - 0
src/renderer/src/router/index.js 查看文件

@@ -0,0 +1,13 @@
1
+import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
2
+
3
+import Login from '../components/LoginPage.vue'
4
+import ReservMain from '../components/ReservMain.vue'
5
+
6
+export const router = createRouter({
7
+    //history: createWebHistory(),
8
+    history: createWebHashHistory(),
9
+    routes: [
10
+        {path: '/', name: 'home', component: Login},
11
+        {path: '/reserv', name: 'reserv', component: ReservMain}
12
+    ],
13
+});

+ 15 - 0
src/renderer/src/stores/reservdata.js 查看文件

@@ -0,0 +1,15 @@
1
+import { ref, reactive, computed } from 'vue'
2
+import { defineStore } from 'pinia'
3
+
4
+export const useReservStore = defineStore ('reservdata', ()=> {
5
+    const count = ref(0)
6
+    const mqttstat = ref(false)
7
+    const mqttToken = ref('')
8
+    const vertext = ref('v1.0.00-1')
9
+
10
+    const doubleCount = computed(()=>count * 2)
11
+    function increment() {
12
+        count.value++
13
+    }
14
+    return { count, mqttstat, vertext, doubleCount, increment }
15
+})

+ 14 - 0
src/renderer/src/stores/savedata.js 查看文件

@@ -0,0 +1,14 @@
1
+import { ref, reactive, computed } from 'vue'
2
+import { defineStore } from 'pinia'
3
+
4
+export const useSaveStore = defineStore ('savedata', ()=> {
5
+    const saveData = reactive({
6
+        isSave: 'false',
7
+        userName: '',
8
+        passWord: ''
9
+    })
10
+
11
+    return { saveData }
12
+},{
13
+    persist: true,
14
+})

文件差異過大導致無法顯示
+ 3582 - 0
yarn.lock