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.
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.85fce10c.js +1 -0
- package/dist/js/app.85fce10c.js +1 -0
- package/dist/js/{chunk-vendors-legacy.884ef407.js → chunk-vendors-legacy.b57e1cce.js} +2 -2
- package/dist/js/{chunk-vendors.884ef407.js → chunk-vendors.b57e1cce.js} +2 -2
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +4 -4
- package/docs/global.html +610 -0
- package/docs/index.html +2 -2
- package/docs/{WWebPerm.mjs.html → server_WWebPerm.mjs.html} +84 -94
- package/docs/server_procCore.mjs.html +311 -0
- package/docs/src_fetchJson.mjs.html +113 -0
- package/package.json +18 -19
- package/server/WWebPerm.mjs +80 -90
- package/server/procCore.mjs +56 -4
- package/server/procLang.mjs +3 -3
- package/server/provideTabs.mjs +8 -9
- package/src/components/LayoutContentGrups.vue +5 -1
- package/src/components/LayoutContentPemis.vue +5 -1
- package/src/components/LayoutContentTargets.vue +5 -1
- package/src/components/LayoutContentUsers.vue +21 -8
- package/src/components/VeCgrups.vue +3 -3
- package/src/components/VeCpemis.vue +2 -2
- package/src/components/VeGrupBlngUsers.vue +2 -2
- package/src/components/VePemiBlngGrups.vue +2 -2
- package/src/fetchJson.mjs +41 -0
- package/src/getPerm.mjs +7 -7
- package/src/getPermUserInfor.mjs +7 -7
- package/src/main.js +1 -1
- package/src/perm.mjs +5 -4
- package/src/plugins/mUI.mjs +17 -0
- package/srv.mjs +8 -0
- package/tableTags-web-perm.json +1 -0
- package/dist/js/app-legacy.71fa8c3b.js +0 -1
- package/dist/js/app.71fa8c3b.js +0 -1
- package/tableTags.json +0 -1
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
|
|
114
114
|
</template>
|
|
115
115
|
|
|
116
|
-
<template v-if="
|
|
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="
|
|
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="
|
|
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="!
|
|
233
|
-
<input v-else-if="props.key==='isActive'" type="checkbox" :checked="props.value === 'y'" @click="toggleItemIsActiveById(props.row.id)" :disabled="!
|
|
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.
|
|
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.
|
|
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('
|
|
378
|
-
'mode': vo.$t('
|
|
379
|
-
'enable': vo.$t('
|
|
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
|
|
@@ -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('
|
|
416
|
-
'enable': vo.$t('
|
|
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('
|
|
416
|
-
'enable': vo.$t('
|
|
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
|
|
31
|
+
//get, 內建 fetch(不依賴 axios); 網路錯誤或非 2xx → cannotGetUserByUrl, 回應非 JSON → cannotGetUserDataByUrl(對齊 axios 時期語意)
|
|
32
|
+
let data = await fetchJson(url)
|
|
33
33
|
.catch((err) => {
|
|
34
|
-
errTemp = err
|
|
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
|
|
package/src/getPermUserInfor.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 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
|
|
35
|
+
//get, 內建 fetch(不依賴 axios); 網路錯誤或非 2xx → cannotGetUserByUrl, 回應非 JSON → cannotGetUserDataByUrl(對齊 axios 時期語意)
|
|
36
|
+
let data = await fetchJson(url)
|
|
37
37
|
.catch((err) => {
|
|
38
|
-
errTemp = err
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
pms =
|
|
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)
|
package/src/plugins/mUI.mjs
CHANGED
|
@@ -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"}
|