w-web-perm 1.0.47 → 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 (96) 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 +48 -24
  65. package/src/components/LayoutContentPemis.vue +48 -24
  66. package/src/components/LayoutContentStaInfor.vue +536 -0
  67. package/src/components/LayoutContentTargets.vue +48 -24
  68. package/src/components/LayoutContentUsers.vue +48 -24
  69. package/src/components/LayoutState.vue +5 -0
  70. package/src/components/VeCgrups.vue +62 -20
  71. package/src/components/VeCpemis.vue +62 -20
  72. package/src/components/VeCrules.vue +58 -16
  73. package/src/components/VeGrupBlngUsers.vue +77 -32
  74. package/src/components/VePemiBlngGrups.vue +77 -32
  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 +15 -7
  80. package/src/store/mutations.mjs +1 -1
  81. package/srv.mjs +76 -101
  82. package/tableTags.json +1 -1
  83. package/db/wperm/grups/data.mdb +0 -0
  84. package/db/wperm/grups/lock.mdb +0 -0
  85. package/db/wperm/pemis/data.mdb +0 -0
  86. package/db/wperm/pemis/lock.mdb +0 -0
  87. package/db/wperm/targets/data.mdb +0 -0
  88. package/db/wperm/targets/lock.mdb +0 -0
  89. package/db/wperm/users/data.mdb +0 -0
  90. package/db/wperm/users/lock.mdb +0 -0
  91. package/dist/css/app.30e81bfe.css +0 -1
  92. package/dist/js/app-legacy.f1277a07.js +0 -1
  93. package/dist/js/app.f1277a07.js +0 -1
  94. package/dist/js/chunk-vendors-legacy.e8703728.js +0 -21
  95. package/dist/js/chunk-vendors.e8703728.js +0 -21
  96. 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
 
@@ -369,12 +371,22 @@ export default {
369
371
  vo.widthGrupsName = get(vo, 'webInfor.widthGrupsName', '')
370
372
  vo.widthGrupsDescription = get(vo, 'webInfor.widthGrupsDescription', '')
371
373
 
372
- vo.firstSetting = false
374
+ //會觸發數據變更再導致opt變更導致觸發rowsChange等事件, 故得要延遲, 供組件偵測初始設定數據初始化之用
375
+ setTimeout(() => {
376
+ vo.firstSetting = false
377
+ // console.log('firstSetting', vo.firstSetting)
378
+ }, 1)
379
+
373
380
  }
374
381
 
375
382
  },
376
383
  computed: {
377
384
 
385
+ syncState: function() {
386
+ let vo = this
387
+ return get(vo, '$store.state.syncState')
388
+ },
389
+
378
390
  webInfor: function() {
379
391
  let wi = get(this, `$store.state.webInfor`)
380
392
  return wi
@@ -739,6 +751,11 @@ export default {
739
751
  // console.log('rowsChange', rs)
740
752
  // console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
741
753
 
754
+ //check
755
+ if (!vo.syncState || vo.firstLoading || vo.firstSetting) {
756
+ return
757
+ }
758
+
742
759
  //isModified
743
760
  vo.isModified = true
744
761
 
@@ -1162,20 +1179,28 @@ export default {
1162
1179
 
1163
1180
  },
1164
1181
 
1165
- saveGrups: function() {
1166
- // console.log('method saveGrups')
1182
+ saveGrups: function(msg) {
1167
1183
 
1168
1184
  let vo = this
1169
1185
 
1170
- async function core() {
1171
- let errTemp = null
1186
+ //第一行立刻釋放按鈕視覺鎖
1187
+ msg.pm.resolve()
1172
1188
 
1173
- //show loading
1174
- vo.$ui.updateLoading(true)
1189
+ //fire-and-forget, 不 await
1190
+ vo.doSaveGrups()
1175
1191
 
1176
- //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 之前)
1177
1202
  if (isestr(vo.isError)) {
1178
- vo.$alert(`${vo.isError}`, { type: 'error' })
1203
+ await vo.$dg.showCheckYes(`${vo.isError}`)
1179
1204
  return
1180
1205
  }
1181
1206
 
@@ -1184,35 +1209,34 @@ export default {
1184
1209
 
1185
1210
  //check
1186
1211
  if (size(rows) === 0) {
1187
- vo.$alert(`${vo.$t('grupAddEmpty')}`, { type: 'error' })
1212
+ await vo.$dg.showCheckYes(`${vo.$t('grupAddEmpty')}`)
1188
1213
  return
1189
1214
  }
1190
1215
 
1191
- //updateGrups
1216
+ //2) 確定打 API 才開 loading
1217
+ vo.$ui.updateLoading(true)
1218
+
1219
+ //3) updateGrups, 各自 catch + 旗標短路
1220
+ let okSave = false
1192
1221
  await vo.$fapi.updateGrups(rows)
1193
- .catch((err) => {
1194
- 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)}`)
1195
1226
  })
1196
-
1197
- //check
1198
- if (errTemp !== null) {
1199
- vo.$alert(`${vo.$t('grupSaveGrupsFail')}: ${errTemp}`, { type: 'error' })
1200
- return
1201
- }
1227
+ if (!okSave) return
1202
1228
 
1203
1229
  //isModified
1204
1230
  vo.isModified = false
1205
1231
 
1206
- //alert
1207
- 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' })
1208
1235
 
1209
1236
  }
1210
1237
 
1211
1238
  //core
1212
1239
  core()
1213
- // .then((res) => {
1214
- // console.log('then', res)
1215
- // })
1216
1240
  .catch((err) => {
1217
1241
  console.log('catch', err)
1218
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
 
@@ -369,12 +371,22 @@ export default {
369
371
  vo.widthPemisName = get(vo, 'webInfor.widthPemisName', '')
370
372
  vo.widthPemisDescription = get(vo, 'webInfor.widthPemisDescription', '')
371
373
 
372
- vo.firstSetting = false
374
+ //會觸發數據變更再導致opt變更導致觸發rowsChange等事件, 故得要延遲, 供組件偵測初始設定數據初始化之用
375
+ setTimeout(() => {
376
+ vo.firstSetting = false
377
+ // console.log('firstSetting', vo.firstSetting)
378
+ }, 1)
379
+
373
380
  }
374
381
 
375
382
  },
376
383
  computed: {
377
384
 
385
+ syncState: function() {
386
+ let vo = this
387
+ return get(vo, '$store.state.syncState')
388
+ },
389
+
378
390
  webInfor: function() {
379
391
  let wi = get(this, `$store.state.webInfor`)
380
392
  return wi
@@ -739,6 +751,11 @@ export default {
739
751
  // console.log('rowsChange', rs)
740
752
  // console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
741
753
 
754
+ //check
755
+ if (!vo.syncState || vo.firstLoading || vo.firstSetting) {
756
+ return
757
+ }
758
+
742
759
  //isModified
743
760
  vo.isModified = true
744
761
 
@@ -1164,20 +1181,28 @@ export default {
1164
1181
 
1165
1182
  },
1166
1183
 
1167
- savePemis: function() {
1168
- // console.log('method savePemis')
1184
+ savePemis: function(msg) {
1169
1185
 
1170
1186
  let vo = this
1171
1187
 
1172
- async function core() {
1173
- let errTemp = null
1188
+ //第一行立刻釋放按鈕視覺鎖
1189
+ msg.pm.resolve()
1174
1190
 
1175
- //show loading
1176
- vo.$ui.updateLoading(true)
1191
+ //fire-and-forget, 不 await
1192
+ vo.doSavePemis()
1177
1193
 
1178
- //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 之前)
1179
1204
  if (isestr(vo.isError)) {
1180
- vo.$alert(`${vo.isError}`, { type: 'error' })
1205
+ await vo.$dg.showCheckYes(`${vo.isError}`)
1181
1206
  return
1182
1207
  }
1183
1208
 
@@ -1186,35 +1211,34 @@ export default {
1186
1211
 
1187
1212
  //check
1188
1213
  if (size(rows) === 0) {
1189
- vo.$alert(`${vo.$t('pemiAddEmpty')}`, { type: 'error' })
1214
+ await vo.$dg.showCheckYes(`${vo.$t('pemiAddEmpty')}`)
1190
1215
  return
1191
1216
  }
1192
1217
 
1193
- //updatePemis
1218
+ //2) 確定打 API 才開 loading
1219
+ vo.$ui.updateLoading(true)
1220
+
1221
+ //3) updatePemis, 各自 catch + 旗標短路
1222
+ let okSave = false
1194
1223
  await vo.$fapi.updatePemis(rows)
1195
- .catch((err) => {
1196
- 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)}`)
1197
1228
  })
1198
-
1199
- //check
1200
- if (errTemp !== null) {
1201
- vo.$alert(`${vo.$t('pemiSavePemisFail')}: ${errTemp}`, { type: 'error' })
1202
- return
1203
- }
1229
+ if (!okSave) return
1204
1230
 
1205
1231
  //isModified
1206
1232
  vo.isModified = false
1207
1233
 
1208
- //alert
1209
- 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' })
1210
1237
 
1211
1238
  }
1212
1239
 
1213
1240
  //core
1214
1241
  core()
1215
- // .then((res) => {
1216
- // console.log('then', res)
1217
- // })
1218
1242
  .catch((err) => {
1219
1243
  console.log('catch', err)
1220
1244
  vo.$alert(vo.$t('anUnexpectedErrorOccurred'), { type: 'error' })