w-web-perm 1.0.48 → 1.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.github/workflows/ci-test.yml +2 -2
  2. package/_gh-pages-deploy/.nojekyll +0 -0
  3. package/_gh-pages-deploy/WWebPerm.html +938 -0
  4. package/_gh-pages-deploy/WWebPerm.mjs.html +1228 -0
  5. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  6. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  7. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  8. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  9. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  10. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  11. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  12. package/_gh-pages-deploy/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  13. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  14. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  15. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  16. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  17. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  18. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  19. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  20. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  21. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  22. package/_gh-pages-deploy/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  23. package/_gh-pages-deploy/index.html +84 -0
  24. package/_gh-pages-deploy/scripts/collapse.js +39 -0
  25. package/_gh-pages-deploy/scripts/commonNav.js +28 -0
  26. package/_gh-pages-deploy/scripts/linenumber.js +25 -0
  27. package/_gh-pages-deploy/scripts/nav.js +12 -0
  28. package/_gh-pages-deploy/scripts/polyfill.js +4 -0
  29. package/_gh-pages-deploy/scripts/prettify/Apache-License-2.0.txt +202 -0
  30. package/_gh-pages-deploy/scripts/prettify/lang-css.js +2 -0
  31. package/_gh-pages-deploy/scripts/prettify/prettify.js +28 -0
  32. package/_gh-pages-deploy/scripts/search.js +99 -0
  33. package/_gh-pages-deploy/styles/jsdoc.css +776 -0
  34. package/_gh-pages-deploy/styles/prettify.css +80 -0
  35. package/dist/css/app.d8b16216.css +1 -0
  36. package/dist/css/{chunk-vendors.f149e124.css → chunk-vendors.2d3ef2ba.css} +1 -1
  37. package/dist/index.tmp +2 -2
  38. package/dist/js/app-legacy.fb48310a.js +1 -0
  39. package/dist/js/app.fb48310a.js +1 -0
  40. package/dist/js/chunk-vendors-legacy.8fdd4435.js +21 -0
  41. package/dist/js/chunk-vendors.8fdd4435.js +21 -0
  42. package/dist/w-web-perm.umd.js +2 -2
  43. package/dist/w-web-perm.umd.js.map +1 -1
  44. package/docs/WWebPerm.html +2 -2
  45. package/docs/WWebPerm.mjs.html +138 -199
  46. package/docs/index.html +1 -1
  47. package/package.json +15 -11
  48. package/public/index.html +2 -2
  49. package/server/WWebPerm.mjs +137 -198
  50. package/server/procCore.mjs +169 -0
  51. package/server/procLang.mjs +197 -0
  52. package/server/procStaInfor.mjs +118 -0
  53. package/server/provideTabs.mjs +1 -1
  54. package/server/srLog.mjs +30 -0
  55. package/server/staLogs/staEvent.callWorker.mjs +46 -0
  56. package/server/staLogs/staEvent.mjs +116 -0
  57. package/server/staLogs/staEvent.shellWorker.mjs +19 -0
  58. package/server/staLogs/staEventTable.mjs +100 -0
  59. package/server/staLogs/test_staEvent.mjs +22 -0
  60. package/settings.json +40 -0
  61. package/src/components/Common/CheckYes.vue +18 -1
  62. package/src/components/Layout.vue +1 -1
  63. package/src/components/LayoutContent.vue +13 -1
  64. package/src/components/LayoutContentGrups.vue +32 -23
  65. package/src/components/LayoutContentPemis.vue +32 -23
  66. package/src/components/LayoutContentStaInfor.vue +536 -0
  67. package/src/components/LayoutContentTargets.vue +32 -23
  68. package/src/components/LayoutContentUsers.vue +32 -23
  69. package/src/components/LayoutState.vue +5 -0
  70. package/src/components/VeCgrups.vue +12 -3
  71. package/src/components/VeCpemis.vue +12 -3
  72. package/src/components/VeCrules.vue +12 -3
  73. package/src/components/VeGrupBlngUsers.vue +18 -6
  74. package/src/components/VePemiBlngGrups.vue +18 -6
  75. package/src/getPerm.mjs +7 -17
  76. package/src/getPermUserInfor.mjs +8 -18
  77. package/src/main.js +1 -0
  78. package/src/plugins/mShare.mjs +8 -13
  79. package/src/plugins/mUI.mjs +11 -0
  80. package/srv.mjs +76 -101
  81. package/tableTags.json +1 -1
  82. package/db/wperm/grups/data.mdb +0 -0
  83. package/db/wperm/grups/lock.mdb +0 -0
  84. package/db/wperm/pemis/data.mdb +0 -0
  85. package/db/wperm/pemis/lock.mdb +0 -0
  86. package/db/wperm/targets/data.mdb +0 -0
  87. package/db/wperm/targets/lock.mdb +0 -0
  88. package/db/wperm/users/data.mdb +0 -0
  89. package/db/wperm/users/lock.mdb +0 -0
  90. package/dist/css/app.30e81bfe.css +0 -1
  91. package/dist/js/app-legacy.3324392e.js +0 -1
  92. package/dist/js/app.3324392e.js +0 -1
  93. package/dist/js/chunk-vendors-legacy.e8703728.js +0 -21
  94. package/dist/js/chunk-vendors.e8703728.js +0 -21
  95. package/test/getUserRules.test.mjs +0 -899
@@ -0,0 +1,19 @@
1
+ import { parentPort } from 'worker_threads'
2
+ import staEvent from './staEvent.mjs'
3
+
4
+
5
+ parentPort.on('message', async (param) => {
6
+ try {
7
+ let r = await staEvent(param.timeLength, param.timeInterval, param.opt)
8
+ parentPort.postMessage({
9
+ mode: 'done',
10
+ payload: r,
11
+ })
12
+ }
13
+ catch (err) {
14
+ parentPort.postMessage({
15
+ mode: 'error',
16
+ payload: err,
17
+ })
18
+ }
19
+ })
@@ -0,0 +1,100 @@
1
+ import ot from 'dayjs'
2
+ import get from 'lodash-es/get.js'
3
+ import each from 'lodash-es/each.js'
4
+ import genPm from 'wsemi/src/genPm.mjs'
5
+ import isestr from 'wsemi/src/isestr.mjs'
6
+ import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
7
+ import fsBuildReadStreamText from 'wsemi/src/fsBuildReadStreamText.mjs'
8
+
9
+
10
+ //統計各 event 事件於近 1日/8hr/4hr/1hr 各窗內之發生數量(鏡像 staEvent.mjs 的 NDJSON 讀取方式)。
11
+ //巢狀窗:1hr⊂4hr⊂8hr⊂24hr, 各窗各自累計該窗內出現總數。
12
+ //回傳: [{ event, last1Day, last8Hour, last4Hour, last1Hour }, ...], 依 last1Day 由大到小排序。
13
+ async function staEventTable(opt = {}) {
14
+
15
+ //fdLog
16
+ let fdLog = get(opt, 'fdLog')
17
+ if (!isestr(fdLog)) {
18
+ fdLog = './_logs'
19
+ }
20
+
21
+ //now
22
+ let now = ot()
23
+
24
+ //kp, 各 event 名累計各窗計數
25
+ let kp = {}
26
+
27
+ //vpfs
28
+ let vpfs = fsTreeFolder(fdLog)
29
+
30
+ //逐檔逐行讀取
31
+ for (let vpf of vpfs) {
32
+
33
+ //fp
34
+ let fp = vpf.path
35
+
36
+ //ev
37
+ let ev = fsBuildReadStreamText(fp)
38
+
39
+ //line
40
+ ev.on('line', (line) => {
41
+ try {
42
+ let v = JSON.parse(line)
43
+ let event = v.event
44
+ if (!isestr(event)) {
45
+ return
46
+ }
47
+
48
+ //dh, 距今幾小時(用 dayjs 比較)
49
+ let dh = now.diff(ot(v.time), 'hour')
50
+ if (dh < 0 || dh > 24) {
51
+ return //超過 1日窗或未來時間略過
52
+ }
53
+
54
+ //初始化
55
+ if (!kp[event]) {
56
+ kp[event] = { last1Day: 0, last8Hour: 0, last4Hour: 0, last1Hour: 0 }
57
+ }
58
+
59
+ //巢狀窗各自累計(1hr⊂4hr⊂8hr⊂24hr)
60
+ if (dh <= 24) {
61
+ kp[event].last1Day += 1
62
+ }
63
+ if (dh <= 8) {
64
+ kp[event].last8Hour += 1
65
+ }
66
+ if (dh <= 4) {
67
+ kp[event].last4Hour += 1
68
+ }
69
+ if (dh <= 1) {
70
+ kp[event].last1Hour += 1
71
+ }
72
+ }
73
+ catch (err) {}
74
+ })
75
+
76
+ //await close
77
+ let pm = genPm()
78
+ ev.on('close', () => {
79
+ pm.resolve()
80
+ })
81
+ await pm
82
+
83
+ }
84
+
85
+ //rs
86
+ let rs = []
87
+ each(kp, (vv, event) => {
88
+ rs.push({
89
+ event,
90
+ ...vv,
91
+ })
92
+ })
93
+
94
+ //依 last1Day 由大到小排序
95
+ rs.sort((a, b) => b.last1Day - a.last1Day)
96
+
97
+ return rs
98
+ }
99
+
100
+ export default staEventTable
@@ -0,0 +1,22 @@
1
+ // import staEvent from './staEvent.mjs' //一般版
2
+ import staEvent from './staEvent.callWorker.mjs' //worker 版
3
+
4
+ async function main() {
5
+ let rs = await staEvent(7, 'hr')
6
+ console.log(JSON.stringify(rs, null, 2))
7
+ }
8
+ main()
9
+ // [
10
+ // {
11
+ // "time": "2026-06-21T12",
12
+ // "data": {
13
+ // "count": 50,
14
+ // "verifyConn-success": 42,
15
+ // "updatePemis-success": 5,
16
+ // "api/getPerm-success": 3
17
+ // }
18
+ // },
19
+ // ...
20
+ // ]
21
+
22
+ //node server/staLogs/test_staEvent.mjs
package/settings.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+
3
+ serverPort: 11006,
4
+ subfolder: '', //mperm
5
+ urlRedirect: 'https://www.google.com/', //本機測試時得先編譯, 再瀏覽: http://localhost:11006/
6
+
7
+ showLanguage: 'y', //'n',
8
+ language: 'eng', //'eng', 'cht'
9
+
10
+ showModeEditTargets: 'y', //'n',
11
+ showModeEditPemis: 'y',
12
+ showModeEditGrups: 'y',
13
+ showModeEditUsers: 'y',
14
+ modeEditTargets: 'y', //'n',
15
+ modeEditPemis: 'y',
16
+ modeEditGrups: 'y',
17
+ modeEditUsers: 'y',
18
+
19
+ webName: {
20
+ eng: 'Permission Service',
21
+ cht: '權限管理系統',
22
+ },
23
+ webDescription: {
24
+ eng: 'A web service package for user permissions and management targets.',
25
+ cht: '基於簡易分層架構來給予與設定使用者所需之權限及群組管理功能',
26
+ },
27
+ webLogo: 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjQ4IiB3aWR0aD0iNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTQ4IDBIMHY0OGg0OFYwWiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuMDEiLz48cGF0aCBkPSJNMzcuODU2IDIwdjhNMjcuNDY0IDM4bDMuNDY0LTIgMy40NjQtMk0yMC41MzYgMzhsLTMuNDY1LTItMy40NjQtMk0xMC4xNDQgMjB2OE0xMy42MDcgMTRsMy40NjUtMiAzLjQ2NC0yTTI3LjQ2NCAxMGwzLjQ2NCAyIDMuNDY0IDIiIHN0cm9rZT0iI0ZGOTgwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNMjQgNDRhNCA0IDAgMSAwIDAtOCA0IDQgMCAwIDAgMCA4Wk0yNCAxMmE0IDQgMCAxIDAgMC04IDQgNCAwIDAgMCAwIDhaTTI0IDI4YTQgNCAwIDEgMCAwLTggNCA0IDAgMCAwIDAgOFpNMzggMjBhNCA0IDAgMSAwIDAtOCA0IDQgMCAwIDAgMCA4Wk0zOCAzNmE0IDQgMCAxIDAgMC04IDQgNCAwIDAgMCAwIDhaTTEwIDIwYTQgNCAwIDEgMCAwLTggNCA0IDAgMCAwIDAgOFpNMTAgMzZhNCA0IDAgMSAwIDAtOCA0IDQgMCAwIDAgMCA4WiIgZmlsbD0iI0ZGRTBCMiIgc3Ryb2tlPSIjRkY5ODAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPg==',
28
+
29
+ // kpLangExt: {
30
+ // mmTargets: {
31
+ // eng: `Current features list`,
32
+ // cht: `現有功能清單`,
33
+ // },
34
+ // mmTargetsMsg: {
35
+ // eng: `Features generally refer to entities such as pages, data, etc., and can also include features or services.`,
36
+ // cht: `現有功能泛指實體的頁面、數據等,亦可為的功能或服務。`,
37
+ // },
38
+ // },
39
+
40
+ }
@@ -3,6 +3,8 @@
3
3
  :show.sync="bShow"
4
4
  :title="$t('systemMessage')"
5
5
  :content="content"
6
+ :contentIcon="contentIcon"
7
+ :contentIconColor="contentIconColor"
6
8
  :_contentColor="'white'"
7
9
  :_contentIconColor="'white'"
8
10
  :_contentBackgroundColor="'orange lighten-5'"
@@ -15,6 +17,7 @@
15
17
 
16
18
  <script>
17
19
  import Vue from 'vue'
20
+ import { mdiAlert, mdiCheckCircle } from '@mdi/js'
18
21
  import genPm from 'wsemi/src/genPm.mjs'
19
22
  import WConfirm from 'w-component-vue/src/components/WConfirm.vue'
20
23
 
@@ -32,6 +35,10 @@ export default {
32
35
 
33
36
  content: '',
34
37
 
38
+ //content icon(依 type 切換:success=綠勾,其餘=驚嘆號)
39
+ contentIcon: mdiAlert,
40
+ contentIconColor: '#D81B60',
41
+
35
42
  }
36
43
  },
37
44
  mounted: function() {
@@ -47,7 +54,7 @@ export default {
47
54
  },
48
55
  methods: {
49
56
 
50
- show: function (content) {
57
+ show: function (content, opt = {}) {
51
58
  //console.log('methods show', content)
52
59
 
53
60
  let vo = this
@@ -58,6 +65,16 @@ export default {
58
65
  //save
59
66
  vo.content = content
60
67
 
68
+ //content icon:type='success' 顯示綠勾,其餘維持驚嘆號
69
+ if (opt && opt.type === 'success') {
70
+ vo.contentIcon = mdiCheckCircle
71
+ vo.contentIconColor = '#2E7D32'
72
+ }
73
+ else {
74
+ vo.contentIcon = mdiAlert
75
+ vo.contentIconColor = '#D81B60'
76
+ }
77
+
61
78
  //show
62
79
  vo.bShow = true
63
80
 
@@ -2,7 +2,7 @@
2
2
  <div style="height:100svh; background:#f5f5f5;">
3
3
 
4
4
  <!-- menu top, 因窄版導致名稱換行故須使用overflow-y:hidden -->
5
- <div :style="`height:${heightToolbar}px; overflow-y:hidden; padding:0px 10px; background:#fff; border-bottom:1px solid #ccc; display:flex; align-items:center;`">
5
+ <div data-fmid="app-topbar" :style="`height:${heightToolbar}px; box-sizing:border-box; overflow-y:hidden; padding:0px 10px; background:#fff; border-bottom:1px solid #ccc; display:flex; align-items:center;`">
6
6
 
7
7
  <div style="padding-left:5px; white-space:nowrap">
8
8
  <div style="display:flex; align-items:center;">
@@ -89,6 +89,11 @@
89
89
  v-if="menuKey==='mmUsers'"
90
90
  ></LayoutContentUsers>
91
91
 
92
+ <LayoutContentStaInfor
93
+ :drawer="drawer"
94
+ v-if="menuKey==='mmStaInfor'"
95
+ ></LayoutContentStaInfor>
96
+
92
97
  </template>
93
98
 
94
99
  <div
@@ -129,7 +134,7 @@
129
134
  </template>
130
135
 
131
136
  <script>
132
- import { mdiArrowLeftBoldHexagonOutline, mdiArrowRightBoldHexagonOutline, mdiGamepadCircle, mdiStackOverflow, mdiSelectGroup, mdiAccountGroupOutline } from '@mdi/js/mdi.js'
137
+ import { mdiArrowLeftBoldHexagonOutline, mdiArrowRightBoldHexagonOutline, mdiGamepadCircle, mdiStackOverflow, mdiSelectGroup, mdiAccountGroupOutline, mdiChartBoxOutline } from '@mdi/js/mdi.js'
133
138
  import get from 'lodash-es/get.js'
134
139
  import find from 'lodash-es/find.js'
135
140
  import WDrawer from 'w-component-vue/src/components/WDrawer.vue'
@@ -139,6 +144,7 @@ import LayoutContentTargets from './LayoutContentTargets.vue'
139
144
  import LayoutContentPemis from './LayoutContentPemis.vue'
140
145
  import LayoutContentGrups from './LayoutContentGrups.vue'
141
146
  import LayoutContentUsers from './LayoutContentUsers.vue'
147
+ import LayoutContentStaInfor from './LayoutContentStaInfor.vue'
142
148
 
143
149
 
144
150
  export default {
@@ -150,6 +156,7 @@ export default {
150
156
  LayoutContentPemis,
151
157
  LayoutContentGrups,
152
158
  LayoutContentUsers,
159
+ LayoutContentStaInfor,
153
160
  },
154
161
  props: {
155
162
  },
@@ -200,6 +207,11 @@ export default {
200
207
  text: vo.$t('mmUsers'),
201
208
  icon: mdiAccountGroupOutline,
202
209
  },
210
+ {
211
+ key: 'mmStaInfor',
212
+ text: vo.$t('mmStaInfor'),
213
+ icon: mdiChartBoxOutline,
214
+ },
203
215
  ]
204
216
  return ms
205
217
  },
@@ -39,6 +39,7 @@
39
39
 
40
40
  <!-- 功能區 -->
41
41
  <div
42
+ data-fmid="grups-toolbar"
42
43
  style="padding:5px; border-top:1px solid #ddd; display:flex; align-items:center;"
43
44
  _v-if="showIsEditable || isEditable"
44
45
  >
@@ -185,6 +186,7 @@
185
186
  :iconColor="'#eee'"
186
187
  :iconColorHover="'#fff'"
187
188
  :shadow="false"
189
+ :promiseUnlock="true"
188
190
  @click="saveGrups"
189
191
  ></WButtonCircle>
190
192
 
@@ -1177,20 +1179,28 @@ export default {
1177
1179
 
1178
1180
  },
1179
1181
 
1180
- saveGrups: function() {
1181
- // console.log('method saveGrups')
1182
+ saveGrups: function(msg) {
1182
1183
 
1183
1184
  let vo = this
1184
1185
 
1185
- async function core() {
1186
- let errTemp = null
1186
+ //第一行立刻釋放按鈕視覺鎖
1187
+ msg.pm.resolve()
1187
1188
 
1188
- //show loading
1189
- vo.$ui.updateLoading(true)
1189
+ //fire-and-forget, 不 await
1190
+ vo.doSaveGrups()
1190
1191
 
1191
- //check
1192
+ },
1193
+
1194
+ doSaveGrups: function() {
1195
+ // console.log('method doSaveGrups')
1196
+
1197
+ let vo = this
1198
+
1199
+ async function core() {
1200
+
1201
+ //1) 同步檢測 (在開 loading 之前)
1192
1202
  if (isestr(vo.isError)) {
1193
- vo.$alert(`${vo.isError}`, { type: 'error' })
1203
+ await vo.$dg.showCheckYes(`${vo.isError}`)
1194
1204
  return
1195
1205
  }
1196
1206
 
@@ -1199,35 +1209,34 @@ export default {
1199
1209
 
1200
1210
  //check
1201
1211
  if (size(rows) === 0) {
1202
- vo.$alert(`${vo.$t('grupAddEmpty')}`, { type: 'error' })
1212
+ await vo.$dg.showCheckYes(`${vo.$t('grupAddEmpty')}`)
1203
1213
  return
1204
1214
  }
1205
1215
 
1206
- //updateGrups
1216
+ //2) 確定打 API 才開 loading
1217
+ vo.$ui.updateLoading(true)
1218
+
1219
+ //3) updateGrups, 各自 catch + 旗標短路
1220
+ let okSave = false
1207
1221
  await vo.$fapi.updateGrups(rows)
1208
- .catch((err) => {
1209
- errTemp = err
1222
+ .then(() => { okSave = true })
1223
+ .catch(async (err) => {
1224
+ vo.$ui.updateLoading(false) //showCheckYes 前關 loading(modal 阻斷期間避免 loading 疊在底下)
1225
+ await vo.$dg.showCheckYes(`${vo.$t('grupSaveGrupsFail')}: ${vo.$tErr(err)}`)
1210
1226
  })
1211
-
1212
- //check
1213
- if (errTemp !== null) {
1214
- vo.$alert(`${vo.$t('grupSaveGrupsFail')}: ${errTemp}`, { type: 'error' })
1215
- return
1216
- }
1227
+ if (!okSave) return
1217
1228
 
1218
1229
  //isModified
1219
1230
  vo.isModified = false
1220
1231
 
1221
- //alert
1222
- vo.$alert(vo.$t('grupSaveGrupsSuccess'))
1232
+ //alert(showCheckYes 前關 loading,避免 modal 等待期間 loading 疊在底下)
1233
+ vo.$ui.updateLoading(false)
1234
+ await vo.$dg.showCheckYes(vo.$t('grupSaveGrupsSuccess'), { type: 'success' })
1223
1235
 
1224
1236
  }
1225
1237
 
1226
1238
  //core
1227
1239
  core()
1228
- // .then((res) => {
1229
- // console.log('then', res)
1230
- // })
1231
1240
  .catch((err) => {
1232
1241
  console.log('catch', err)
1233
1242
  vo.$alert(vo.$t('anUnexpectedErrorOccurred'), { type: 'error' })
@@ -39,6 +39,7 @@
39
39
 
40
40
  <!-- 功能區 -->
41
41
  <div
42
+ data-fmid="pemis-toolbar"
42
43
  style="padding:5px; border-top:1px solid #ddd; display:flex; align-items:center;"
43
44
  _v-if="showIsEditable || isEditable"
44
45
  >
@@ -185,6 +186,7 @@
185
186
  :iconColor="'#eee'"
186
187
  :iconColorHover="'#fff'"
187
188
  :shadow="false"
189
+ :promiseUnlock="true"
188
190
  @click="savePemis"
189
191
  ></WButtonCircle>
190
192
 
@@ -1179,20 +1181,28 @@ export default {
1179
1181
 
1180
1182
  },
1181
1183
 
1182
- savePemis: function() {
1183
- // console.log('method savePemis')
1184
+ savePemis: function(msg) {
1184
1185
 
1185
1186
  let vo = this
1186
1187
 
1187
- async function core() {
1188
- let errTemp = null
1188
+ //第一行立刻釋放按鈕視覺鎖
1189
+ msg.pm.resolve()
1189
1190
 
1190
- //show loading
1191
- vo.$ui.updateLoading(true)
1191
+ //fire-and-forget, 不 await
1192
+ vo.doSavePemis()
1192
1193
 
1193
- //check
1194
+ },
1195
+
1196
+ doSavePemis: function() {
1197
+ // console.log('method doSavePemis')
1198
+
1199
+ let vo = this
1200
+
1201
+ async function core() {
1202
+
1203
+ //1) 同步檢測 (在開 loading 之前)
1194
1204
  if (isestr(vo.isError)) {
1195
- vo.$alert(`${vo.isError}`, { type: 'error' })
1205
+ await vo.$dg.showCheckYes(`${vo.isError}`)
1196
1206
  return
1197
1207
  }
1198
1208
 
@@ -1201,35 +1211,34 @@ export default {
1201
1211
 
1202
1212
  //check
1203
1213
  if (size(rows) === 0) {
1204
- vo.$alert(`${vo.$t('pemiAddEmpty')}`, { type: 'error' })
1214
+ await vo.$dg.showCheckYes(`${vo.$t('pemiAddEmpty')}`)
1205
1215
  return
1206
1216
  }
1207
1217
 
1208
- //updatePemis
1218
+ //2) 確定打 API 才開 loading
1219
+ vo.$ui.updateLoading(true)
1220
+
1221
+ //3) updatePemis, 各自 catch + 旗標短路
1222
+ let okSave = false
1209
1223
  await vo.$fapi.updatePemis(rows)
1210
- .catch((err) => {
1211
- errTemp = err
1224
+ .then(() => { okSave = true })
1225
+ .catch(async (err) => {
1226
+ vo.$ui.updateLoading(false) //showCheckYes 前關 loading(modal 阻斷期間避免 loading 疊在底下)
1227
+ await vo.$dg.showCheckYes(`${vo.$t('pemiSavePemisFail')}: ${vo.$tErr(err)}`)
1212
1228
  })
1213
-
1214
- //check
1215
- if (errTemp !== null) {
1216
- vo.$alert(`${vo.$t('pemiSavePemisFail')}: ${errTemp}`, { type: 'error' })
1217
- return
1218
- }
1229
+ if (!okSave) return
1219
1230
 
1220
1231
  //isModified
1221
1232
  vo.isModified = false
1222
1233
 
1223
- //alert
1224
- vo.$alert(vo.$t('pemiSavePemisSuccess'))
1234
+ //alert(showCheckYes 前關 loading,避免 modal 等待期間 loading 疊在底下)
1235
+ vo.$ui.updateLoading(false)
1236
+ await vo.$dg.showCheckYes(vo.$t('pemiSavePemisSuccess'), { type: 'success' })
1225
1237
 
1226
1238
  }
1227
1239
 
1228
1240
  //core
1229
1241
  core()
1230
- // .then((res) => {
1231
- // console.log('then', res)
1232
- // })
1233
1242
  .catch((err) => {
1234
1243
  console.log('catch', err)
1235
1244
  vo.$alert(vo.$t('anUnexpectedErrorOccurred'), { type: 'error' })