w-web-perm 1.0.78 → 1.0.80

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 (37) hide show
  1. package/dist/index.tmp +1 -1
  2. package/dist/js/app-legacy.85fce10c.js +1 -0
  3. package/dist/js/app.85fce10c.js +1 -0
  4. package/dist/js/{chunk-vendors-legacy.884ef407.js → chunk-vendors-legacy.b57e1cce.js} +2 -2
  5. package/dist/js/{chunk-vendors.884ef407.js → chunk-vendors.b57e1cce.js} +2 -2
  6. package/dist/w-web-perm.umd.js +2 -2
  7. package/dist/w-web-perm.umd.js.map +1 -1
  8. package/docs/WWebPerm.html +4 -4
  9. package/docs/global.html +610 -0
  10. package/docs/index.html +2 -2
  11. package/docs/{WWebPerm.mjs.html → server_WWebPerm.mjs.html} +84 -94
  12. package/docs/server_procCore.mjs.html +311 -0
  13. package/docs/src_fetchJson.mjs.html +113 -0
  14. package/package.json +18 -19
  15. package/server/WWebPerm.mjs +80 -90
  16. package/server/procCore.mjs +56 -4
  17. package/server/procLang.mjs +3 -3
  18. package/server/provideTabs.mjs +8 -9
  19. package/src/components/LayoutContentGrups.vue +5 -1
  20. package/src/components/LayoutContentPemis.vue +5 -1
  21. package/src/components/LayoutContentTargets.vue +5 -1
  22. package/src/components/LayoutContentUsers.vue +21 -8
  23. package/src/components/VeCgrups.vue +3 -3
  24. package/src/components/VeCpemis.vue +2 -2
  25. package/src/components/VeGrupBlngUsers.vue +2 -2
  26. package/src/components/VePemiBlngGrups.vue +2 -2
  27. package/src/fetchJson.mjs +41 -0
  28. package/src/getPerm.mjs +7 -7
  29. package/src/getPermUserInfor.mjs +7 -7
  30. package/src/main.js +1 -1
  31. package/src/perm.mjs +5 -4
  32. package/src/plugins/mUI.mjs +17 -0
  33. package/srv.mjs +8 -0
  34. package/tableTags-web-perm.json +1 -0
  35. package/dist/js/app-legacy.71fa8c3b.js +0 -1
  36. package/dist/js/app.71fa8c3b.js +0 -1
  37. package/tableTags.json +0 -1
@@ -113,7 +113,7 @@
113
113
 
114
114
  </template>
115
115
 
116
- <template v-if="isEditable">
116
+ <template v-if="isEditableIdentity">
117
117
 
118
118
  <WButtonCircle
119
119
  :paddingStyle="{v:6,h:6}"
@@ -133,7 +133,7 @@
133
133
 
134
134
  </template>
135
135
 
136
- <template v-if="isEditable && hasItemCheckOne">
136
+ <template v-if="isEditableIdentity && hasItemCheckOne">
137
137
 
138
138
  <WButtonCircle
139
139
  :paddingStyle="{v:6,h:6}"
@@ -153,7 +153,7 @@
153
153
 
154
154
  </template>
155
155
 
156
- <template v-if="isEditable && hasItemsCheck">
156
+ <template v-if="isEditableIdentity && hasItemsCheck">
157
157
 
158
158
  <WButtonCircle
159
159
  :paddingStyle="{v:6,h:6}"
@@ -229,8 +229,8 @@
229
229
  <button style="width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" @click="$dg.showVeCgrupsById(props.row.id)">{{ getCgrupsText(props.value) }}</button>
230
230
  </div>
231
231
  </template>
232
- <input v-else-if="props.key==='isAdmin'" type="checkbox" :checked="props.value === 'y'" @click="toggleItemIsAdminById(props.row.id)" :disabled="!isEditable" />
233
- <input v-else-if="props.key==='isActive'" type="checkbox" :checked="props.value === 'y'" @click="toggleItemIsActiveById(props.row.id)" :disabled="!isEditable" />
232
+ <input v-else-if="props.key==='isAdmin'" type="checkbox" :checked="props.value === 'y'" @click="toggleItemIsAdminById(props.row.id)" :disabled="!isEditableIdentity" />
233
+ <input v-else-if="props.key==='isActive'" type="checkbox" :checked="props.value === 'y'" @click="toggleItemIsActiveById(props.row.id)" :disabled="!isEditableIdentity" />
234
234
  <span v-else>{{ props.value }}</span>
235
235
  </template>
236
236
  </WAggridVue>
@@ -305,8 +305,10 @@ export default {
305
305
 
306
306
  firstLoading: true,
307
307
  firstSetting: true,
308
+ systemProcing: false, //程式端載入/重載清單資料期間為 true, 用於排除非使用者操作之 rowsChange (見 $ui.markDataReload)
308
309
  showIsEditable: false,
309
310
  isEditable: false,
311
+ modeEditUsers: '', //settings.modeEditUsers 原值: 'y' 全可編 / 'n' 全唯讀 / 'for:grups' 僅可編使用者所屬群組(身分欄與新增複製刪除鎖定)
310
312
  isModified: false,
311
313
 
312
314
  tabKeys: [
@@ -377,7 +379,8 @@ export default {
377
379
  let showModeEditUsers = get(vo, 'webInfor.showModeEditUsers', '')
378
380
  vo.showIsEditable = showModeEditUsers === 'y'
379
381
  let modeEditUsers = get(vo, 'webInfor.modeEditUsers', '')
380
- vo.isEditable = modeEditUsers === 'y'
382
+ vo.modeEditUsers = modeEditUsers
383
+ vo.isEditable = modeEditUsers === 'y' || modeEditUsers === 'for:grups' //for:grups 仍進編輯模式, 但身分欄位由 isEditableIdentity 鎖定
381
384
 
382
385
  vo.widthUsersName = get(vo, 'webInfor.widthUsersName', '')
383
386
  vo.widthUsersEmail = get(vo, 'webInfor.widthUsersEmail', '')
@@ -394,6 +397,13 @@ export default {
394
397
  },
395
398
  computed: {
396
399
 
400
+ //isEditableIdentity, 使用者身分(name/email/description/from/isAdmin/isActive/排序)與新增複製刪除是否可編;
401
+ //modeEditUsers='for:grups'(使用者來自外部單一登入系統之部署, 身分由該系統同步)時鎖定, 僅群組指派(VeCgrups)依 isEditable
402
+ isEditableIdentity: function() {
403
+ let vo = this
404
+ return vo.isEditable && vo.modeEditUsers !== 'for:grups'
405
+ },
406
+
397
407
  syncState: function() {
398
408
  let vo = this
399
409
  return get(vo, '$store.state.syncState')
@@ -664,7 +674,7 @@ export default {
664
674
  let kpCellEditable = {}
665
675
  let kpRowDrag = {}
666
676
  let kpHeadCheckBox = {}
667
- if (vo.isEditable) {
677
+ if (vo.isEditableIdentity) { //身分欄位編輯/拖曳排序/列勾選(供複製刪除)皆屬身分編修, for:grups 時鎖定
668
678
  kpCellEditable = {
669
679
  'name': true,
670
680
  'email': true,
@@ -746,7 +756,7 @@ export default {
746
756
  // console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
747
757
 
748
758
  //check
749
- if (!vo.syncState || vo.firstLoading || vo.firstSetting) {
759
+ if (!vo.syncState || vo.firstLoading || vo.firstSetting || vo.systemProcing) {
750
760
  return
751
761
  }
752
762
 
@@ -767,6 +777,9 @@ export default {
767
777
 
768
778
  }
769
779
 
780
+ //markDataReload, 重建 opt 屬程式端寫入, 其後非同步觸發之 rowsChange 不得視為使用者變更
781
+ vo.$ui.markDataReload(vo)
782
+
770
783
  //save
771
784
  vo.opt = opt
772
785
 
@@ -374,9 +374,9 @@ export default {
374
374
 
375
375
  //kpHead
376
376
  let kpHead = {
377
- 'name': vo.$t('pemiName'),
378
- 'mode': vo.$t('operGrupEnable'),
379
- 'enable': vo.$t('operMode'),
377
+ 'name': vo.$t('grupName'), //本對話框列出的是權限群組(items 由 vo.grups 產生)
378
+ 'mode': vo.$t('operGrupMode'),
379
+ 'enable': vo.$t('operEnable'),
380
380
  }
381
381
 
382
382
  //opt
@@ -375,8 +375,8 @@ export default {
375
375
  //kpHead
376
376
  let kpHead = {
377
377
  'name': vo.$t('pemiName'),
378
- 'mode': vo.$t('operPemiEnable'),
379
- 'enable': vo.$t('operMode'),
378
+ 'mode': vo.$t('operPemiMode'),
379
+ 'enable': vo.$t('operEnable'),
380
380
  }
381
381
 
382
382
  //opt
@@ -412,8 +412,8 @@ export default {
412
412
  let kpHead = {
413
413
  'name': vo.$t('userName'),
414
414
  'grupsNames': vo.$t('belongGrupsNames'),
415
- 'mode': vo.$t('operGrupEnable'),
416
- 'enable': vo.$t('operMode'),
415
+ 'mode': vo.$t('operGrupMode'),
416
+ 'enable': vo.$t('operEnable'),
417
417
  }
418
418
 
419
419
  //opt
@@ -412,8 +412,8 @@ export default {
412
412
  let kpHead = {
413
413
  'name': vo.$t('grupName'),
414
414
  'pemisNames': vo.$t('belongPemisNames'),
415
- 'mode': vo.$t('operPemiEnable'),
416
- 'enable': vo.$t('operMode'),
415
+ 'mode': vo.$t('operPemiMode'),
416
+ 'enable': vo.$t('operEnable'),
417
417
  }
418
418
 
419
419
  //opt
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 以瀏覽器與 node(18+) 內建之 fetch 取得 JSON 回應, 取代 axios(2026-09-07 起本套件不再依賴 axios)
3
+ *
4
+ * 與 axios 之語意對齊:
5
+ * - 網路錯誤或非 2xx: axios 會 reject, fetch 只在網路錯誤 reject, 故此處對 !res.ok 亦 reject, 統一為 { kind: 'request', status, message }
6
+ * - 回應非 JSON: axios 回原字串(呼叫端判 state 失敗), 此處 res.json() 失敗 reject { kind: 'parse', status, message }
7
+ * - 成功: resolve 解析後之 JSON(對應 axios 之 res.data)
8
+ * 注意: node 端 fetch(undici) 不讀 HTTP_PROXY / HTTPS_PROXY 環境變數(axios 會), 需走 proxy 之部署須自行以 opt.dispatcher 注入 undici Agent
9
+ *
10
+ * @param {String} url 輸入請求網址字串
11
+ * @param {Object} [opt={}] 輸入 fetch 選項物件(method / headers / body / signal / dispatcher 等原樣傳給 fetch), 預設 {}
12
+ * @returns {Promise} 回傳 Promise, resolve 為 JSON 物件, reject 為 { kind: 'request'|'parse', status, message }
13
+ */
14
+ async function fetchJson(url, opt = {}) {
15
+
16
+ //fetch
17
+ let res = null
18
+ try {
19
+ res = await fetch(url, opt)
20
+ }
21
+ catch (err) {
22
+ return Promise.reject({ kind: 'request', status: 0, message: String((err && err.message) || err) })
23
+ }
24
+
25
+ //check status
26
+ if (!res.ok) {
27
+ return Promise.reject({ kind: 'request', status: res.status, message: `HTTP ${res.status}` })
28
+ }
29
+
30
+ //json
31
+ try {
32
+ return await res.json()
33
+ }
34
+ catch (err) {
35
+ return Promise.reject({ kind: 'parse', status: res.status, message: String((err && err.message) || err) })
36
+ }
37
+
38
+ }
39
+
40
+
41
+ export default fetchJson
package/src/getPerm.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import axios from 'axios'
2
1
  import get from 'lodash-es/get.js'
3
2
  import isestr from 'wsemi/src/isestr.mjs'
4
3
  import iseobj from 'wsemi/src/iseobj.mjs'
5
4
  import isfun from 'wsemi/src/isfun.mjs'
6
5
  import ispm from 'wsemi/src/ispm.mjs'
6
+ import fetchJson from './fetchJson.mjs'
7
7
 
8
8
 
9
9
  async function getPerm(url, tokenTar, opt = {}) {
@@ -28,20 +28,20 @@ async function getPerm(url, tokenTar, opt = {}) {
28
28
  url = url.replaceAll('{token}', tokenTar)
29
29
  // console.log('getPerm: url', url)
30
30
 
31
- //get
32
- let res = await axios.get(url)
31
+ //get, 內建 fetch(不依賴 axios); 網路錯誤或非 2xx → cannotGetUserByUrl, 回應非 JSON → cannotGetUserDataByUrl(對齊 axios 時期語意)
32
+ let data = await fetchJson(url)
33
33
  .catch((err) => {
34
- errTemp = err.toString()
34
+ errTemp = err
35
35
  })
36
36
 
37
37
  //check
38
38
  if (errTemp !== null) {
39
+ if (get(errTemp, 'kind') === 'parse') {
40
+ return Promise.reject('cannotGetUserDataByUrl') //取得使用者資訊失敗
41
+ }
39
42
  return Promise.reject('cannotGetUserByUrl') //由SSO取得使用者資訊錯誤
40
43
  }
41
44
 
42
- //data
43
- let data = get(res, 'data')
44
-
45
45
  //state
46
46
  let state = get(data, 'state', '')
47
47
 
@@ -1,9 +1,9 @@
1
- import axios from 'axios'
2
1
  import get from 'lodash-es/get.js'
3
2
  import isestr from 'wsemi/src/isestr.mjs'
4
3
  import iseobj from 'wsemi/src/iseobj.mjs'
5
4
  import isfun from 'wsemi/src/isfun.mjs'
6
5
  import ispm from 'wsemi/src/ispm.mjs'
6
+ import fetchJson from './fetchJson.mjs'
7
7
 
8
8
 
9
9
  async function getPermUserInfor(url, tokenSelf, userIdTar, opt = {}) {
@@ -32,20 +32,20 @@ async function getPermUserInfor(url, tokenSelf, userIdTar, opt = {}) {
32
32
  url = url.replaceAll('{userId}', userIdTar)
33
33
  // console.log('getPermUserInfor: url', url)
34
34
 
35
- //get
36
- let res = await axios.get(url)
35
+ //get, 內建 fetch(不依賴 axios); 網路錯誤或非 2xx → cannotGetUserByUrl, 回應非 JSON → cannotGetUserDataByUrl(對齊 axios 時期語意)
36
+ let data = await fetchJson(url)
37
37
  .catch((err) => {
38
- errTemp = err.toString()
38
+ errTemp = err
39
39
  })
40
40
 
41
41
  //check
42
42
  if (errTemp !== null) {
43
+ if (get(errTemp, 'kind') === 'parse') {
44
+ return Promise.reject('cannotGetUserDataByUrl') //取得使用者資訊失敗
45
+ }
43
46
  return Promise.reject('cannotGetUserByUrl') //由SSO取得使用者資訊錯誤
44
47
  }
45
48
 
46
- //data
47
- let data = get(res, 'data')
48
-
49
49
  //state
50
50
  let state = get(data, 'state', '')
51
51
 
package/src/main.js CHANGED
@@ -37,7 +37,7 @@ Vue.directive('domresize', domResize())
37
37
  //WServHapiClient
38
38
  // let bFirstSync = false //不需要bFirstSync, 由getWebInfor結束代表第1次完成同步
39
39
  WServHapiClient({
40
- showLog: false,
40
+ useShowLog: false,
41
41
  url: window.location.origin + window.location.pathname,
42
42
  useWaitToken: true,
43
43
  apiName: 'api',
package/src/perm.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import axios from 'axios'
2
1
  import get from 'lodash-es/get.js'
3
2
  import each from 'lodash-es/each.js'
4
3
  import isestr from 'wsemi/src/isestr.mjs'
5
4
  import isfun from 'wsemi/src/isfun.mjs'
6
5
  import genPm from 'wsemi/src/genPm.mjs'
7
6
  import pmInvResolve from 'wsemi/src/pmInvResolve.mjs'
7
+ import fetchJson from './fetchJson.mjs'
8
8
 
9
9
 
10
10
  function perm() {
@@ -38,9 +38,10 @@ function perm() {
38
38
  //pm
39
39
  let pm = genPm()
40
40
 
41
- //get
42
- let pms = axios.get(url)
43
- pms = pmInvResolve(pms, { thenExtractData: true })
41
+ //get, 內建 fetch(不依賴 axios): fetchJson 直接 resolve JSON envelope, 故 pmInvResolve 不再需 thenExtractData(原為取 axios res.data)
42
+ //失敗時 permError / reject 收到 { kind: 'request'|'parse', status, message }(原為 AxiosError), 或後端 envelope 之 msg
43
+ let pms = fetchJson(url)
44
+ pms = pmInvResolve(pms)
44
45
  pms
45
46
  .then((res) => {
46
47
  // console.log('perm.getPerm then', res)
@@ -240,10 +240,27 @@ function getIcon(icon) {
240
240
  }
241
241
 
242
242
 
243
+ //markDataReload, 標記接下來由程式端資料載入/重載(初始載入、store 推播、isEditable/語系切換等 genOpt 重建 opt)所觸發之 rowsChange 非使用者變更
244
+ //why: WTable 之 rowsChange 於 Vue 更新 + aggrid 渲染後才非同步觸發, 早於此之 firstLoading/firstSetting 旗標於觸發當下可能已失效
245
+ //(儲存成功後伺服器推播更新列 → changeParams 重算 → genOpt 重建 opt → rowsChange 再把 isModified 設回 true, 儲存鈕殘留即為此例),
246
+ //故須於寫入資料當下標記, 待渲染完成後解除. 對齊 w-web-sso LayoutContentUsers.vue 之 markDataReload.
247
+ //用法: 元件 data() 宣告 systemProcing:false; genOpt 內於 vo.opt = opt 前呼叫 vo.$ui.markDataReload(vo); rowsChange 守門加 vo.systemProcing.
248
+ function markDataReload(vm) {
249
+ vm.systemProcing = true
250
+ vm.$nextTick(() => {
251
+ setTimeout(() => {
252
+ vm.systemProcing = false
253
+ }, 1)
254
+ })
255
+ }
256
+
257
+
243
258
  let mUI = {
244
259
 
245
260
  setVo,
246
261
 
262
+ markDataReload,
263
+
247
264
  updateConnState,
248
265
  updateLoading,
249
266
  // updateViewState,
package/srv.mjs CHANGED
@@ -47,6 +47,14 @@ let getUserByToken = async (token) => {
47
47
  isAdmin: 'y',
48
48
  }
49
49
  }
50
+ if (token === '{token-for-peter}') { //測試用: 在 perm users 表內、有效、但非系統管理者之瀏覽使用者(供 api 測試驗證 verifyClientUser 對資料通道亦生效)
51
+ return {
52
+ id: 'id-for-peter',
53
+ name: 'peter',
54
+ email: 'peter@example.com',
55
+ isAdmin: 'n',
56
+ }
57
+ }
50
58
  console.log('invalid token', token)
51
59
  return {}
52
60
  }
@@ -0,0 +1 @@
1
+ {"grups":"2026-09-12T23:25:31+08:00|zKEYPI","pemis":"2026-09-12T23:25:31+08:00|Q4YLum","targets":"2026-09-12T23:25:31+08:00|5JMz80","users":"2026-09-12T23:25:31+08:00|wz6Q7h"}