w-web-perm 1.1.12 → 1.1.13
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/README.md +158 -123
- package/dist/css/app.f09c7433.css +1 -0
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.8af87052.js +1 -0
- package/dist/js/app.8af87052.js +1 -0
- package/dist/js/{chunk-vendors.3e9e3ef1.js → chunk-vendors-legacy.ece5a361.js} +1 -1
- package/dist/js/{chunk-vendors-legacy.3e9e3ef1.js → chunk-vendors.ece5a361.js} +1 -1
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +84 -4
- package/docs/global.html +23023 -1125
- package/docs/index.html +2 -2
- package/docs/server_WWebPerm.mjs.html +232 -32
- package/docs/server_cascadeWrite.mjs.html +225 -0
- package/docs/server_dataChannel.mjs.html +2 -2
- package/docs/server_keyCheck.mjs.html +353 -0
- package/docs/server_lockSave.mjs.html +2 -2
- package/docs/server_maskLog.mjs.html +2 -2
- package/docs/server_procCore.mjs.html +180 -41
- package/docs/server_staLogs_resolveShellWorker.mjs.html +2 -2
- package/docs/server_syncKeep.mjs.html +311 -0
- package/docs/server_tools_authRefsAudit.mjs.html +247 -0
- package/docs/server_tools_authRefsClean.mjs.html +335 -0
- package/docs/server_tools_fromTools.mjs.html +797 -0
- package/docs/server_tools_isMain.mjs.html +103 -0
- package/docs/server_tools_nameDupReport.mjs.html +280 -0
- package/docs/server_validateRows.mjs.html +2 -2
- package/docs/server_wrapInjected.mjs.html +3 -3
- package/docs/src_fetchJson.mjs.html +2 -2
- package/docs/src_js_cvKpToArr.mjs.html +2 -2
- package/docs/src_js_cvKpToObj.mjs.html +44 -33
- package/docs/src_js_draftSync.mjs.html +142 -0
- package/docs/src_js_eachOwn.mjs.html +3 -3
- package/docs/src_js_editorRefRows.mjs.html +356 -0
- package/docs/src_js_externalRows.mjs.html +338 -0
- package/docs/src_js_externalUi.mjs.html +230 -0
- package/docs/src_js_findRefRows.mjs.html +107 -0
- package/docs/src_js_findTouchedDup.mjs.html +150 -0
- package/docs/src_js_fmtLossyMsg.mjs.html +2 -2
- package/docs/src_js_fromAudit.mjs.html +489 -0
- package/docs/src_js_getAddImpact.mjs.html +269 -0
- package/docs/src_js_getAuthWarn.mjs.html +463 -0
- package/docs/src_js_getCascadePreview.mjs.html +258 -0
- package/docs/src_js_getDelRefCount.mjs.html +210 -0
- package/docs/src_js_getEffMode.mjs.html +92 -0
- package/docs/src_js_getKpLossy.mjs.html +47 -52
- package/docs/src_js_getNameErrs.mjs.html +266 -0
- package/docs/src_js_getOwn.mjs.html +3 -3
- package/docs/src_js_getPropKey.mjs.html +109 -0
- package/docs/src_js_getRuleEnable.mjs.html +2 -2
- package/docs/src_js_isAuthMode.mjs.html +4 -4
- package/docs/src_js_joinNames.mjs.html +2 -2
- package/docs/src_js_legendFit.mjs.html +2 -2
- package/docs/src_js_pairRowsToSnapshot.mjs.html +156 -0
- package/docs/src_js_refAudit.mjs.html +359 -0
- package/docs/src_js_refCascade.mjs.html +572 -0
- package/docs/src_js_relDialogSend.mjs.html +199 -0
- package/docs/src_js_rowDigest.mjs.html +265 -0
- package/docs/src_plugins_dataReloadGuard.mjs.html +109 -0
- package/docs/src_plugins_reuseDeletedIds.mjs.html +2 -2
- package/docs/src_plugins_submitGuard.mjs.html +2 -2
- package/g_authRefsClean.mjs +79 -0
- package/g_fixFromType.mjs +92 -0
- package/g_initialData.mjs +3 -3
- package/g_purgeFrom.mjs +127 -0
- package/package.json +1 -1
- package/server/WWebPerm.mjs +230 -30
- package/server/cascadeWrite.mjs +153 -0
- package/server/keyCheck.mjs +281 -0
- package/server/procCore.mjs +178 -39
- package/server/procLang.mjs +270 -3
- package/server/syncKeep.mjs +239 -0
- package/server/tools/authRefsAudit.mjs +175 -0
- package/server/tools/authRefsClean.mjs +263 -0
- package/server/tools/fromTools.mjs +725 -0
- package/server/tools/isMain.mjs +31 -0
- package/server/tools/nameDupReport.mjs +208 -0
- package/server/wrapInjected.mjs +1 -1
- package/src/components/LayoutContentGrups.vue +396 -75
- package/src/components/LayoutContentPemis.vue +395 -75
- package/src/components/LayoutContentTargets.vue +261 -25
- package/src/components/LayoutContentUsers.vue +334 -97
- package/src/components/VeCgrups.vue +88 -129
- package/src/components/VeCpemis.vue +86 -129
- package/src/components/VeCrules.vue +15 -5
- package/src/components/VeGrupBlngUsers.vue +202 -89
- package/src/components/VePemiBlngGrups.vue +209 -99
- package/src/js/cvKpToObj.mjs +42 -31
- package/src/js/draftSync.mjs +70 -0
- package/src/js/eachOwn.mjs +1 -1
- package/src/js/editorRefRows.mjs +284 -0
- package/src/js/externalRows.mjs +266 -0
- package/src/js/externalUi.mjs +158 -0
- package/src/js/findRefRows.mjs +35 -0
- package/src/js/findTouchedDup.mjs +78 -0
- package/src/js/fromAudit.mjs +417 -0
- package/src/js/getAddImpact.mjs +197 -0
- package/src/js/getAuthWarn.mjs +391 -0
- package/src/js/getCascadePreview.mjs +186 -0
- package/src/js/getDelRefCount.mjs +138 -0
- package/src/js/getEffMode.mjs +20 -0
- package/src/js/getKpLossy.mjs +44 -49
- package/src/js/getNameErrs.mjs +194 -0
- package/src/js/getOwn.mjs +1 -1
- package/src/js/getPropKey.mjs +37 -0
- package/src/js/isAuthMode.mjs +2 -2
- package/src/js/pairRowsToSnapshot.mjs +84 -0
- package/src/js/refAudit.mjs +287 -0
- package/src/js/refCascade.mjs +500 -0
- package/src/js/relDialogSend.mjs +127 -0
- package/src/js/rowDigest.mjs +193 -0
- package/src/plugins/dataReloadGuard.mjs +37 -0
- package/src/plugins/mShare.mjs +144 -140
- package/src/plugins/mUI.mjs +3 -8
- package/src/schema/tables/users.mjs +152 -152
- package/dist/css/app.6918ec45.css +0 -1
- package/dist/js/app-legacy.7cb7e394.js +0 -1
- package/dist/js/app.7cb7e394.js +0 -1
- package/docs/src_js_getKeyOrderLost.mjs.html +0 -121
- package/docs/src_js_getKpWriteLost.mjs.html +0 -100
- package/docs/src_js_keepKeyOrder.mjs.html +0 -139
- package/src/js/getKeyOrderLost.mjs +0 -49
- package/src/js/getKpWriteLost.mjs +0 -28
- package/src/js/keepKeyOrder.mjs +0 -67
|
@@ -126,6 +126,17 @@
|
|
|
126
126
|
|
|
127
127
|
</div>
|
|
128
128
|
|
|
129
|
+
<!-- 資料已被更新提示(R-PRINCIPLE-00 原則 7): 開框後他人或外部同步改了使用者表, 本視窗之草稿與基準維持開框時之版本、不重建, 存檔將被伺服端拒收; 以 v-for 渲染(無提示時不渲染任何節點) -->
|
|
130
|
+
<div
|
|
131
|
+
v-for="ks in (staleNotice ? 1 : 0)"
|
|
132
|
+
:key="`stale-${ks}`"
|
|
133
|
+
data-fmid="grup-blng-users-banner-stale"
|
|
134
|
+
style="padding:5px 15px; border-top:1px solid #ddd; background:#FFE0B2; color:#444; font-size:0.8rem; overflow-wrap:anywhere;"
|
|
135
|
+
>
|
|
136
|
+
<img style="vertical-align:sub; width:16px; height:16px; margin-right:4px;" :src="$ui.getIcon('warning')" />
|
|
137
|
+
<span>{{ $t('staleDraftNoticeDialog') }}</span>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
129
140
|
</div>
|
|
130
141
|
|
|
131
142
|
<template
|
|
@@ -140,8 +151,25 @@
|
|
|
140
151
|
:opt="opt"
|
|
141
152
|
>
|
|
142
153
|
<template v-slot:cell-render="props">
|
|
154
|
+
<!-- 警告圖示(R-PRINCIPLE-00 原則 3「提示」): 該列使用者之 cgrups 有衝突時(warnText, genItems 由 getRefWarns 取得)於儲存格最左顯示; 以外層 flex 容器與 RelationChips 並排,
|
|
155
|
+
RelationChips 根元素寬=儲存格寬−圖示寬, 其量測(v-domresize 監聽根元素尺寸)與「+K」溢出計算(可用寬取自根元素)不變; 無警告者不渲染外層容器與圖示, 與修前同一節點 -->
|
|
156
|
+
<div
|
|
157
|
+
style="display:flex; align-items:center; height:100%;"
|
|
158
|
+
v-if="props.key==='grupsNames' && props.row.warnText"
|
|
159
|
+
>
|
|
160
|
+
<span style="flex:0 0 auto; display:flex; align-items:center;" :title="props.row.warnText"><img style="vertical-align:sub; width:16px; height:16px; margin-right:4px;" :src="$ui.getIcon('warning')" /></span>
|
|
161
|
+
<RelationChips
|
|
162
|
+
style="flex:1 1 auto;"
|
|
163
|
+
:items="props.row.grups"
|
|
164
|
+
:editable="isEditable"
|
|
165
|
+
:popupTitle="$t('belongGrupsNames')"
|
|
166
|
+
:tooltipAll="$t('chipsShowAll')"
|
|
167
|
+
:labelContent="'modeSelect'"
|
|
168
|
+
@input="(item)=>{showVeGrupBlngUsersToggleItemModeByName(props.row.name, item, props.row.id)}"
|
|
169
|
+
></RelationChips>
|
|
170
|
+
</div>
|
|
143
171
|
<RelationChips
|
|
144
|
-
v-if="props.key==='grupsNames'"
|
|
172
|
+
v-else-if="props.key==='grupsNames'"
|
|
145
173
|
:items="props.row.grups"
|
|
146
174
|
:editable="isEditable"
|
|
147
175
|
:popupTitle="$t('belongGrupsNames')"
|
|
@@ -181,7 +209,6 @@ import size from 'lodash-es/size.js'
|
|
|
181
209
|
import filter from 'lodash-es/filter.js'
|
|
182
210
|
import join from 'lodash-es/join.js'
|
|
183
211
|
import sortBy from 'lodash-es/sortBy.js'
|
|
184
|
-
import isEqual from 'lodash-es/isEqual.js'
|
|
185
212
|
import cloneDeep from 'lodash-es/cloneDeep.js'
|
|
186
213
|
import genPm from 'wsemi/src/genPm.mjs'
|
|
187
214
|
import haskey from 'wsemi/src/haskey.mjs'
|
|
@@ -192,8 +219,13 @@ import eachOwn from '../js/eachOwn.mjs'
|
|
|
192
219
|
import getOwn from '../js/getOwn.mjs'
|
|
193
220
|
import cvKpToObj from '../js/cvKpToObj.mjs'
|
|
194
221
|
import getKpLossy from '../js/getKpLossy.mjs'
|
|
195
|
-
import getKpWriteLost from '../js/getKpWriteLost.mjs'
|
|
196
222
|
import fmtLossyMsg from '../js/fmtLossyMsg.mjs'
|
|
223
|
+
import pairRowsToSnapshot from '../js/pairRowsToSnapshot.mjs'
|
|
224
|
+
import { makeBaseline } from '../js/rowDigest.mjs'
|
|
225
|
+
import { isDraftStale } from '../js/draftSync.mjs'
|
|
226
|
+
import { makeRowStates, getChangedIdx, buildRowsSend } from '../js/relDialogSend.mjs'
|
|
227
|
+
import { getRefState, getRawModeMap, putRefEntry } from '../js/editorRefRows.mjs'
|
|
228
|
+
import { getRefWarns, getRefWarnText } from '../js/getAuthWarn.mjs'
|
|
197
229
|
import WDialog from 'w-component-vue/src/components/WDialog.vue'
|
|
198
230
|
import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
|
|
199
231
|
import WAggridVue from 'w-aggrid-vue/src/components/WAggridVue.vue'
|
|
@@ -233,9 +265,16 @@ export default {
|
|
|
233
265
|
firstSetting: true,
|
|
234
266
|
isEditable: false,
|
|
235
267
|
isModified: false,
|
|
268
|
+
ownSaving: false, //本視窗自己之存檔已送出(成功後視窗隨即關閉): 自己之寫入所致之推播(users 表更新)不是「他人更新」, watch 不得據以提示; 每次 show() 歸零, 存檔失敗時歸零
|
|
236
269
|
|
|
237
270
|
grup: null,
|
|
238
271
|
|
|
272
|
+
//baseline, rowStates: 開框(show)時綁定之草稿基準(F-26, R-BASELINE-01), 之後 store 推播變新不更新, 每次 show() 重新綁定;
|
|
273
|
+
//baseline 為草稿所依據之那份使用者列之逐列內容指紋, 存檔隨 updateUsers 第 2 參送伺服端; rowStates 為開框時各列之歸屬狀態, 存檔時判定使用者有無變更該列
|
|
274
|
+
baseline: null,
|
|
275
|
+
rowStates: null,
|
|
276
|
+
staleNotice: false, //有未存編輯時收到推播、且使用者表已異於本視窗載入時之基準(R-PRINCIPLE-00 原則 7): true 時顯示「資料已被更新」提示; 每次 show() 歸零
|
|
277
|
+
|
|
239
278
|
items: [],
|
|
240
279
|
itemsCheck: [],
|
|
241
280
|
opt: null,
|
|
@@ -265,6 +304,17 @@ export default {
|
|
|
265
304
|
}
|
|
266
305
|
|
|
267
306
|
},
|
|
307
|
+
watch: {
|
|
308
|
+
//store 之使用者表被推播更新(整表以新陣列取代): 本視窗不重建(草稿與基準維持開框時之版本, 見 show()). 此刻有未存編輯者, 若表已異於載入時之基準(任一列內容不同或列增刪; 僅 order/伺服器管理欄不同不算)
|
|
309
|
+
//即顯示「資料已被更新」提示, 存檔時由伺服端以表雜湊拒收(saveStale); 無未存編輯者維持原行為(靜默, 存檔時才由伺服端拒收). 唯讀檢視無存檔, 不提示
|
|
310
|
+
users: function(rows) {
|
|
311
|
+
let vo = this
|
|
312
|
+
if (!vo.bShow || !vo.isEditable || !vo.isModified || vo.ownSaving || !Array.isArray(vo.baseline)) {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
vo.staleNotice = isDraftStale({ keys: vo.$ds.users.keys, rows, baseline: vo.baseline })
|
|
316
|
+
},
|
|
317
|
+
},
|
|
268
318
|
computed: {
|
|
269
319
|
|
|
270
320
|
syncState: function() {
|
|
@@ -550,6 +600,23 @@ export default {
|
|
|
550
600
|
return rows
|
|
551
601
|
},
|
|
552
602
|
|
|
603
|
+
getDisplayRows: function() {
|
|
604
|
+
let vo = this
|
|
605
|
+
|
|
606
|
+
//cmp
|
|
607
|
+
let cmp = get(vo, '$refs.rftable')
|
|
608
|
+
// console.log('cmp', cmp)
|
|
609
|
+
|
|
610
|
+
//getDisplayData 只回傳顯示中之欄位(name/enable/grupsNames), 不含 id; 批次勾選須以 id 定位列(使用者 name 允許重複), 故另以 ag-grid api 取篩選排序後之完整列(含 id)
|
|
611
|
+
let api = cmp.getApi()
|
|
612
|
+
let rows = []
|
|
613
|
+
api.forEachNodeAfterFilterAndSort((node) => {
|
|
614
|
+
rows.push(node.data)
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
return rows
|
|
618
|
+
},
|
|
619
|
+
|
|
553
620
|
fltKpGrup: function(kp) {
|
|
554
621
|
// console.log('fltKpGrup', kp)
|
|
555
622
|
|
|
@@ -581,8 +648,12 @@ export default {
|
|
|
581
648
|
//items
|
|
582
649
|
let items = map(users, (u) => {
|
|
583
650
|
|
|
584
|
-
//kpGrup
|
|
585
|
-
let kpGrup =
|
|
651
|
+
//kpGrup, 空字串(新增列之 funNew 預設值)與壞 JSON 視為空容器, 同 VeCgrups/清單頁/伺服端(R-REL-PARSE-01: 任一列解碼失敗不得使對話框打不開)
|
|
652
|
+
let kpGrup = Object.create(null)
|
|
653
|
+
try {
|
|
654
|
+
kpGrup = cvKpToObj(JSON5.parse(u.cgrups), { hasMode: true })
|
|
655
|
+
}
|
|
656
|
+
catch (err) {}
|
|
586
657
|
// console.log(u.name, 'kpGrup', kpGrup)
|
|
587
658
|
|
|
588
659
|
//fltKpGrup
|
|
@@ -596,16 +667,12 @@ export default {
|
|
|
596
667
|
eachOwn(kpGrup, (v, k) => {
|
|
597
668
|
// console.log(k, 'v', v)
|
|
598
669
|
|
|
599
|
-
//
|
|
600
|
-
let
|
|
601
|
-
if (
|
|
670
|
+
//st, 此為使用者cgrups內設定, 可為y或n, 僅提取y, 再判斷此使用者是否有使用此grup; 與伺服端同解讀(getRefState: 純量值 { "G1": "y" } 為啟用 AND, 啟用者 mode 缺/非法為 AND)
|
|
671
|
+
let st = getRefState(v)
|
|
672
|
+
if (st.enable !== 'y') {
|
|
602
673
|
return true //跳出換下一個
|
|
603
674
|
}
|
|
604
|
-
// console.log(k, '
|
|
605
|
-
|
|
606
|
-
//_mode
|
|
607
|
-
let _mode = get(v, 'mode', '')
|
|
608
|
-
// console.log(k, '_mode', _mode)
|
|
675
|
+
// console.log(k, 'st', st)
|
|
609
676
|
|
|
610
677
|
//b, 使用者是否擁有指定權限群組
|
|
611
678
|
let b = k === grup.name
|
|
@@ -614,14 +681,15 @@ export default {
|
|
|
614
681
|
//mode, isActive
|
|
615
682
|
if (b) {
|
|
616
683
|
enable = 'y'
|
|
617
|
-
mode =
|
|
684
|
+
mode = st.mode //使用使用者給予cgrups內指定權限群組之合併權限模式
|
|
618
685
|
// console.log('使用使用者給予cgrups內指定權限群組之合併權限模式 grup', grup)
|
|
619
686
|
}
|
|
620
687
|
|
|
621
|
-
//push
|
|
688
|
+
//push, 僅供 RelationChips 顯示(name/mode/enable); 存檔不讀本陣列(doSave 以 putRefEntry 自快照字串只改本群組一項)
|
|
622
689
|
grups.push({
|
|
623
690
|
name: k,
|
|
624
|
-
|
|
691
|
+
mode: st.mode,
|
|
692
|
+
isActive: 'y',
|
|
625
693
|
enable: b ? 'y' : 'n', //給render使用
|
|
626
694
|
})
|
|
627
695
|
|
|
@@ -633,6 +701,11 @@ export default {
|
|
|
633
701
|
grupsNames = join(grupsNames, ' ')
|
|
634
702
|
// console.log(u.name, 'grupsNames', grupsNames)
|
|
635
703
|
|
|
704
|
+
//warnText: 該使用者 cgrups 之衝突提示(R-PRINCIPLE-00 原則 3; 偵測同伺服端判準, getRefWarns 以該列字串), 「所屬權限群組名稱」儲存格最左之警告圖示與 tooltip, 無衝突為 ''
|
|
705
|
+
// revRows 只回寫 grupsNames/grups 不回寫本欄(其複本字串經 fltKpGrup 已去掉懸空項), 故警告反映開啟時之已儲存資料; 存檔只改本群組一項, 其餘衝突項於存檔後仍在
|
|
706
|
+
let resWarn = getRefWarns(u.cgrups, vo.grups)
|
|
707
|
+
let warnText = resWarn.hasWarn ? getRefWarnText(resWarn, vo.$t, 'warnKindGrup') : ''
|
|
708
|
+
|
|
636
709
|
return {
|
|
637
710
|
id: u.id, //列識別: 使用者 name 允許重複(僅前端警告、後端不擋), 切換處理須以 id 找列, 不可以 name(否則同名者會改到第一位)
|
|
638
711
|
name: u.name,
|
|
@@ -641,6 +714,7 @@ export default {
|
|
|
641
714
|
enable,
|
|
642
715
|
// kpGrup,
|
|
643
716
|
grups,
|
|
717
|
+
warnText,
|
|
644
718
|
}
|
|
645
719
|
})
|
|
646
720
|
// console.log(u.name, 'items', items)
|
|
@@ -665,11 +739,20 @@ export default {
|
|
|
665
739
|
let rows = get(vo, 'opt.rows', [])
|
|
666
740
|
// console.log('rows', rows)
|
|
667
741
|
|
|
742
|
+
//以列身分(id)配對: 對話框之列 ↔ 目前 store 中同一位使用者(R-REL-PAIR-01; 不得以列索引配對——store 於對話框開啟期間可被更新〔他人增刪、改 order〕而錯位)
|
|
743
|
+
//krs, usersRev: 對話框有列且 store 仍存在之使用者(usersRev[k] 即第 krs[k] 列所配到者; 配對不到者不更新其顯示, 存檔時由 doSave 整次拒存)
|
|
744
|
+
let { krs, matched: usersRev } = pairRowsToSnapshot(rows, users)
|
|
745
|
+
|
|
668
746
|
//users
|
|
669
|
-
users = map(
|
|
747
|
+
users = map(usersRev, (u, ku) => {
|
|
748
|
+
let row = get(rows, krs[ku], {})
|
|
670
749
|
|
|
671
|
-
//kpGrup
|
|
672
|
-
let kpGrup =
|
|
750
|
+
//kpGrup, 空字串與壞 JSON 視為空容器(R-REL-PARSE-01)
|
|
751
|
+
let kpGrup = Object.create(null)
|
|
752
|
+
try {
|
|
753
|
+
kpGrup = cvKpToObj(JSON5.parse(u.cgrups), { hasMode: true })
|
|
754
|
+
}
|
|
755
|
+
catch (err) {}
|
|
673
756
|
// console.log(u.name, 'kpGrup', kpGrup)
|
|
674
757
|
|
|
675
758
|
//fltKpGrup
|
|
@@ -677,7 +760,7 @@ export default {
|
|
|
677
760
|
// console.log(u.name, 'kpGrup(fltKpGrup)', kpGrup)
|
|
678
761
|
|
|
679
762
|
//mode
|
|
680
|
-
let mode = get(
|
|
763
|
+
let mode = get(row, 'mode', '')
|
|
681
764
|
if (mode !== 'OR' && mode !== 'AND') {
|
|
682
765
|
console.log(`非預期: mode應該皆已指定不應觸發`, mode)
|
|
683
766
|
mode = 'OR'
|
|
@@ -685,7 +768,7 @@ export default {
|
|
|
685
768
|
// console.log(u.name, 'mode', mode)
|
|
686
769
|
|
|
687
770
|
//enable
|
|
688
|
-
let enable = get(
|
|
771
|
+
let enable = get(row, 'enable', '')
|
|
689
772
|
if (enable !== 'y' && enable !== 'n') {
|
|
690
773
|
console.log(`非預期: enable應該皆已指定不應觸發`, enable)
|
|
691
774
|
enable = 'n'
|
|
@@ -718,8 +801,8 @@ export default {
|
|
|
718
801
|
let items = vo.genItems(grup, users)
|
|
719
802
|
// console.log('items', items)
|
|
720
803
|
|
|
721
|
-
//update grupsNames, grups
|
|
722
|
-
each(items, (r,
|
|
804
|
+
//update grupsNames, grups, items 與 krs 同序(item k 對應對話框第 krs[k] 列)
|
|
805
|
+
each(items, (r, k) => {
|
|
723
806
|
// name: u.name,
|
|
724
807
|
// grupsNames,
|
|
725
808
|
// mode,
|
|
@@ -727,6 +810,7 @@ export default {
|
|
|
727
810
|
// grups,
|
|
728
811
|
|
|
729
812
|
//set
|
|
813
|
+
let kr = krs[k]
|
|
730
814
|
set(vo, `opt.rows[${kr}].grupsNames`, r.grupsNames)
|
|
731
815
|
set(vo, `opt.rows[${kr}].grups`, r.grups)
|
|
732
816
|
// console.log('vo.opt.rows[kr]', cloneDeep(vo.opt.rows[kr]))
|
|
@@ -842,21 +926,21 @@ export default {
|
|
|
842
926
|
let rows = get(vo, 'opt.rows', [])
|
|
843
927
|
// console.log('rows', rows)
|
|
844
928
|
|
|
845
|
-
//kpRow
|
|
929
|
+
//kpRow, 以使用者 id 為鍵(使用者 name 允許重複, 以 name 會使同名者互相錯位; 單列切換早已以 id, 見 showVeGrupBlngUsersToggleItemEnableByName)
|
|
846
930
|
let kpRow = Object.create(null)
|
|
847
931
|
each(rows, (r, kr) => {
|
|
848
|
-
kpRow[r.
|
|
932
|
+
kpRow[r.id] = kr
|
|
849
933
|
})
|
|
850
934
|
|
|
851
|
-
//rowsEff
|
|
852
|
-
let rowsEff = vo.
|
|
935
|
+
//rowsEff, 篩選排序後之完整列(含 id)
|
|
936
|
+
let rowsEff = vo.getDisplayRows()
|
|
853
937
|
// console.log('rowsEff', rowsEff)
|
|
854
938
|
|
|
855
939
|
//toggle
|
|
856
940
|
each(rowsEff, (r) => {
|
|
857
941
|
|
|
858
942
|
//kr
|
|
859
|
-
let kr = kpRow[r.
|
|
943
|
+
let kr = kpRow[r.id]
|
|
860
944
|
|
|
861
945
|
//set
|
|
862
946
|
set(vo, `opt.rows[${kr}].enable`, 'y')
|
|
@@ -881,21 +965,21 @@ export default {
|
|
|
881
965
|
let rows = get(vo, 'opt.rows', [])
|
|
882
966
|
// console.log('rows', rows)
|
|
883
967
|
|
|
884
|
-
//kpRow
|
|
968
|
+
//kpRow, 以使用者 id 為鍵(使用者 name 允許重複, 以 name 會使同名者互相錯位; 單列切換早已以 id, 見 showVeGrupBlngUsersToggleItemEnableByName)
|
|
885
969
|
let kpRow = Object.create(null)
|
|
886
970
|
each(rows, (r, kr) => {
|
|
887
|
-
kpRow[r.
|
|
971
|
+
kpRow[r.id] = kr
|
|
888
972
|
})
|
|
889
973
|
|
|
890
|
-
//rowsEff
|
|
891
|
-
let rowsEff = vo.
|
|
974
|
+
//rowsEff, 篩選排序後之完整列(含 id)
|
|
975
|
+
let rowsEff = vo.getDisplayRows()
|
|
892
976
|
// console.log('rowsEff', rowsEff)
|
|
893
977
|
|
|
894
978
|
//toggle
|
|
895
979
|
each(rowsEff, (r) => {
|
|
896
980
|
|
|
897
981
|
//kr
|
|
898
|
-
let kr = kpRow[r.
|
|
982
|
+
let kr = kpRow[r.id]
|
|
899
983
|
|
|
900
984
|
//set
|
|
901
985
|
set(vo, `opt.rows[${kr}].enable`, 'n')
|
|
@@ -920,21 +1004,21 @@ export default {
|
|
|
920
1004
|
let rows = get(vo, 'opt.rows', [])
|
|
921
1005
|
// console.log('rows', rows)
|
|
922
1006
|
|
|
923
|
-
//kpRow
|
|
1007
|
+
//kpRow, 以使用者 id 為鍵(使用者 name 允許重複, 以 name 會使同名者互相錯位; 單列切換早已以 id, 見 showVeGrupBlngUsersToggleItemEnableByName)
|
|
924
1008
|
let kpRow = Object.create(null)
|
|
925
1009
|
each(rows, (r, kr) => {
|
|
926
|
-
kpRow[r.
|
|
1010
|
+
kpRow[r.id] = kr
|
|
927
1011
|
})
|
|
928
1012
|
|
|
929
|
-
//rowsEff
|
|
930
|
-
let rowsEff = vo.
|
|
1013
|
+
//rowsEff, 篩選排序後之完整列(含 id)
|
|
1014
|
+
let rowsEff = vo.getDisplayRows()
|
|
931
1015
|
// console.log('rowsEff', rowsEff)
|
|
932
1016
|
|
|
933
1017
|
//toggle
|
|
934
1018
|
each(rowsEff, (r) => {
|
|
935
1019
|
|
|
936
1020
|
//kr
|
|
937
|
-
let kr = kpRow[r.
|
|
1021
|
+
let kr = kpRow[r.id]
|
|
938
1022
|
|
|
939
1023
|
//enable
|
|
940
1024
|
let _enable = get(r, 'enable', 'n')
|
|
@@ -954,16 +1038,19 @@ export default {
|
|
|
954
1038
|
|
|
955
1039
|
},
|
|
956
1040
|
|
|
957
|
-
saveUsers: async function(rows) {
|
|
958
|
-
// console.log('method saveUsers', rows)
|
|
1041
|
+
saveUsers: async function(rows, baseline) {
|
|
1042
|
+
// console.log('method saveUsers', rows, baseline)
|
|
959
1043
|
|
|
960
1044
|
let vo = this
|
|
961
1045
|
|
|
962
1046
|
//3) updateUsers, 各自 catch + okX 旗標短路 (canonical 五段結構; 空清單檢查已上移至 doSave core())
|
|
1047
|
+
//第 2 參 { baseline }: 開框時綁定之草稿基準(由 doSave 於流程開頭取得並傳入, 不於此讀 vo.baseline——存檔進行中若再次 show() 會換成新草稿之基準); 伺服端比對「本次將寫入之列」之庫內版本, 草稿之後被改過即整次拒收 saveStale(失敗路徑沿用下方 catch 顯示語系文字)
|
|
963
1048
|
let okSave = false
|
|
964
|
-
|
|
1049
|
+
vo.ownSaving = true //自己之寫入所致之推播不提示(成功路徑維持至視窗關閉; show() 歸零)
|
|
1050
|
+
await vo.$fapi.updateUsers(rows, { baseline })
|
|
965
1051
|
.then(() => { okSave = true })
|
|
966
1052
|
.catch(async (err) => {
|
|
1053
|
+
vo.ownSaving = false //失敗(含 saveStale 被拒): 視窗仍開著, 恢復監看
|
|
967
1054
|
vo.$ui.updateLoading(false) //showCheckYes 前關 loading(modal 阻斷期間避免 loading 疊在底下)
|
|
968
1055
|
await vo.$dg.showCheckYes(`${vo.$t('userSaveUsersFail')}: ${vo.$tErr(err)}`)
|
|
969
1056
|
})
|
|
@@ -1005,6 +1092,10 @@ export default {
|
|
|
1005
1092
|
|
|
1006
1093
|
let vo = this
|
|
1007
1094
|
|
|
1095
|
+
//pmDlg: 本次存檔流程所屬之對話框 promise(外審 sol R2): 存檔進行中(如 await delay)若再次 show() 開啟另一群組, vo.pm 會被換成新視窗之 promise,
|
|
1096
|
+
//舊流程完成時只能 resolve 自己的 promise、且不得關閉新視窗(見下方 .then)
|
|
1097
|
+
let pmDlg = vo.pm
|
|
1098
|
+
|
|
1008
1099
|
async function core(unlockBtn) {
|
|
1009
1100
|
|
|
1010
1101
|
//1) 事先檢測 (所有同步檢查在開 loading 之前, 空清單不應先閃 loading); 訊息框前以 unlockBtn() 釋放儲存鈕之 promiseUnlock 鎖:
|
|
@@ -1017,14 +1108,39 @@ export default {
|
|
|
1017
1108
|
return
|
|
1018
1109
|
}
|
|
1019
1110
|
|
|
1111
|
+
//草稿基準與開框時各列之歸屬狀態(F-26, R-BASELINE-01): 於流程開頭(任何 await 之前)取得並以區域變數持有——存檔進行中若再次 show() 會把 vo.baseline/vo.rowStates 換成新草稿之值, 舊草稿之列不可配新草稿之基準
|
|
1112
|
+
let baseline = cloneDeep(vo.baseline)
|
|
1113
|
+
let rowStates = vo.rowStates
|
|
1114
|
+
|
|
1115
|
+
//grupName: 被編輯之群組名稱, 同樣於流程開頭(任何 await 之前)取得——等待後才讀 vo.grup.name 會讀到新開啟之實體, 把舊草稿之歸屬寫成指派新實體(外審 sol R2)
|
|
1116
|
+
let grupName = get(vo, 'grup.name', '')
|
|
1117
|
+
|
|
1020
1118
|
//1b) 無法無損表示之授權資料(R-AUTH-LOSSY-01, 主控複審 R4): 該列使用者之 cgrups 含物件形式無法無損表示之格(getKpLossy)者, 本對話框不得改寫其字串——
|
|
1021
1119
|
//本對話框所管之歸屬/模式有變更 → 整次儲存拒絕並點名(原子, 其餘列亦不寫); 未變更 → 該列字串逐字元不動(見下方 kpLossyIdx)
|
|
1022
1120
|
//使用者列之快照只取這一次: 有損檢查之值即下方寫入之值(外審 sol#2: 兩次讀取之間 store 可能被伺服器推送更新, 檢查與寫入看到不同資料)
|
|
1023
1121
|
let users = cloneDeep(vo.users)
|
|
1122
|
+
|
|
1123
|
+
//1a) 以列身分(id)配對快照(R-REL-PAIR-01, 主控 run02 F-24): 修前以開框時的列索引取快照列, store 於對話框開啟期間被更新(他人增刪、改 order)時關聯寫到另一位使用者;
|
|
1124
|
+
//對話框有列所對應的使用者已被刪除者 → 整次拒存(原子, 不寫任何列)並提示重新整理, 不得退而配到其他列
|
|
1125
|
+
let { pairs, missing } = pairRowsToSnapshot(rows, users)
|
|
1126
|
+
if (size(missing) > 0) {
|
|
1127
|
+
unlockBtn()
|
|
1128
|
+
await vo.$dg.showCheckYes(fmtLossyMsg(vo.$t('relationRowsStale'), map(missing, 'name')))
|
|
1129
|
+
return false
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
//1a2) 使用者於本次編輯相對「開框時」有變更之列(F-26, S4; 外審 B.1): 對話框之列是開框時之草稿值, 他人於開框後改過而使用者未碰之列, 不得以草稿值改寫(覆蓋他人之變更);
|
|
1133
|
+
//故只有 kpChanged 之列才對存檔當下快照之字串套用變更(下方 putRefEntry), 其餘列(含開框後新增而不在對話框內者)一律原樣帶回存檔當下 store 之版本(逐字不動), 亦不納入下方有損檢查
|
|
1134
|
+
let kpChanged = getChangedIdx(rows, rowStates)
|
|
1135
|
+
|
|
1024
1136
|
let kpLossyIdx = Object.create(null)
|
|
1137
|
+
let kpRawModes = Object.create(null) //列索引 → 該列快照字串之限制項原始 mode(getRawModeMap), 供下方 putRefEntry 寫回未改之限制項
|
|
1025
1138
|
let lossyNames = []
|
|
1026
1139
|
each(rows, (r, kr) => {
|
|
1027
|
-
|
|
1140
|
+
if (kpChanged[kr] !== true) {
|
|
1141
|
+
return true //使用者未變更: 原樣帶回, 不檢查有損亦不需 rawModeMap
|
|
1142
|
+
}
|
|
1143
|
+
let u = pairs[kr]
|
|
1028
1144
|
let kpRaw = null
|
|
1029
1145
|
try {
|
|
1030
1146
|
kpRaw = JSON5.parse(get(u, 'cgrups', ''))
|
|
@@ -1032,11 +1148,12 @@ export default {
|
|
|
1032
1148
|
catch (err) {
|
|
1033
1149
|
return true //無法解析者交由下方原流程處理
|
|
1034
1150
|
}
|
|
1151
|
+
kpRawModes[kr] = getRawModeMap(kpRaw)
|
|
1035
1152
|
if (size(getKpLossy(kpRaw)) === 0) {
|
|
1036
1153
|
return true //跳出換下一個
|
|
1037
1154
|
}
|
|
1038
1155
|
kpLossyIdx[kr] = true
|
|
1039
|
-
let p = getOwn(cvKpToObj(kpRaw, { hasMode: true }),
|
|
1156
|
+
let p = getOwn(cvKpToObj(kpRaw, { hasMode: true }), grupName, null)
|
|
1040
1157
|
let was = get(p, 'isActive', '') === 'y'
|
|
1041
1158
|
let wasMode = was ? get(p, 'mode', '') : ''
|
|
1042
1159
|
let now = get(r, 'enable', '') === 'y'
|
|
@@ -1059,63 +1176,46 @@ export default {
|
|
|
1059
1176
|
|
|
1060
1177
|
//users: 沿用 1b 之快照(見上)
|
|
1061
1178
|
|
|
1062
|
-
//
|
|
1063
|
-
let
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
//u
|
|
1067
|
-
let u = get(users, kr, {})
|
|
1179
|
+
//kpNewCgrups: 列索引 → 該列新 cgrups 字串; 只收「使用者有變更(kpChanged)、非有損、且 putRefEntry 判定本項確有變更」者, 其餘一律不收(由 buildRowsSend 原樣帶回)
|
|
1180
|
+
let kpNewCgrups = Object.create(null)
|
|
1181
|
+
each(rows, (r, kr) => {
|
|
1068
1182
|
|
|
1069
|
-
|
|
1070
|
-
if (kpLossyIdx[kr] === true) {
|
|
1071
|
-
return
|
|
1183
|
+
//使用者未變更之列(kpChanged)與有損列(kpLossyIdx): 歸屬未變更, 字串逐字元不動
|
|
1184
|
+
if (kpChanged[kr] !== true || kpLossyIdx[kr] === true) {
|
|
1185
|
+
return true //跳出換下一個
|
|
1072
1186
|
}
|
|
1073
1187
|
|
|
1188
|
+
//u, 以列身分配對之快照列(見 1a)
|
|
1189
|
+
let u = pairs[kr]
|
|
1190
|
+
|
|
1074
1191
|
//cgrups
|
|
1075
1192
|
let cgrups = get(u, 'cgrups', '')
|
|
1076
1193
|
|
|
1077
|
-
//kpGrup
|
|
1078
|
-
let kpGrup =
|
|
1194
|
+
//kpGrup, 空字串與壞 JSON 視為空容器(R-REL-PARSE-01)
|
|
1195
|
+
let kpGrup = Object.create(null)
|
|
1196
|
+
try {
|
|
1197
|
+
kpGrup = cvKpToObj(JSON5.parse(cgrups), { hasMode: true })
|
|
1198
|
+
}
|
|
1199
|
+
catch (err) {}
|
|
1079
1200
|
// console.log('kpGrup', kpGrup)
|
|
1080
1201
|
|
|
1081
|
-
//
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1202
|
+
//kpGrupNew: 只改本群組對應之一項(依列之 enable/mode), 其餘引用項(含懸空、名稱 ''、mode 缺/非法〔已解碼為 AND〕、停用項、純量值)原樣保留, 授權效果不變(putRefEntry; R-PRINCIPLE-00 c/e/f)——
|
|
1203
|
+
//修前以列上之 grups(genItems 經 fltKpGrup 只剩查得之群組、且只取啟用者)重組並以 isEqual 判定改寫, 啟用中之懸空 AND 引用被丟棄即放寬(例: { G1: OR, GX〔已刪〕: AND } 原授權為空, 未改 G1 存檔後得 G1 之授權)
|
|
1204
|
+
//本項未變更 → 回 false, 字串逐字元不動(含其他項為陣列形式/含停用項者, 修前會正規化改寫)
|
|
1205
|
+
//其餘項中之限制項(mode 缺或非法之啟用引用)寫回原始 mode(kpRawModes[kr]), 不寫成可當基底之合法 AND(否則本群組為該層唯一合法項而被取消時多給, R-PRINCIPLE-00 e)
|
|
1085
1206
|
let kpGrupNew = Object.create(null)
|
|
1086
|
-
if (
|
|
1087
|
-
each(grups, (g) => {
|
|
1088
|
-
if (g.isActive === 'y') {
|
|
1089
|
-
kpGrupNew[g.name] = {
|
|
1090
|
-
mode: g.mode,
|
|
1091
|
-
isActive: g.isActive,
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
})
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
//check
|
|
1098
|
-
if (!isEqual(kpGrup, kpGrupNew)) {
|
|
1099
|
-
|
|
1100
|
-
//欲寫出之鍵序被 JS 屬性序改變而使有效 AND 項易位(如新啟用整數式名稱之 AND 群組) → 授權會改變, 不寫出(外審 minimax#2)
|
|
1101
|
-
if (size(getKpWriteLost(kpGrup, kpGrupNew)) > 0) { //預期序由 kpGrup(解碼後之原資料)與 kpGrupNew 於函數內導出——不可用本處已套用 JS 屬性序之物件序(外審 minimax 第二輪#1)
|
|
1102
|
-
lostRows.push(get(u, 'name', ''))
|
|
1103
|
-
return u
|
|
1104
|
-
}
|
|
1207
|
+
if (putRefEntry(kpGrupNew, kpGrup, grupName, get(r, 'enable', ''), get(r, 'mode', ''), kpRawModes[kr])) {
|
|
1105
1208
|
|
|
1106
|
-
//cgrups
|
|
1107
|
-
|
|
1209
|
+
//cgrups(鍵序無關授權, R-AUTH-MERGE-00 第 4 條: 不需保留原鍵序, 亦無整數式名稱易位之拒存)
|
|
1210
|
+
kpNewCgrups[kr] = JSON.stringify(kpGrupNew)
|
|
1108
1211
|
|
|
1109
1212
|
}
|
|
1110
1213
|
|
|
1111
|
-
return u
|
|
1112
1214
|
})
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}
|
|
1118
|
-
console.log('usersNew', usersNew)
|
|
1215
|
+
|
|
1216
|
+
//usersNew: 送出之整表=存檔當下 store 快照(users)之全部列、依其 order 序(F-26 c0, S4): 修前只送「對話框之列」, 開框後他人/外部同步新增之列被伺服端整表 diff 刪除;
|
|
1217
|
+
//只有 kpNewCgrups 之列換其 cgrups 一欄, 其餘列(使用者未變更者、有損列、開框後新增而不在對話框內者)與快照同一物件、逐字不動(帶回存檔當下 store 版本以免誤刪/誤改他人之列; 但載入基準仍是開框時之版本, 伺服端表級比對〔R-BASELINE-01, run11〕會因開框後他人之任何變更而整次拒收 saveStale)
|
|
1218
|
+
let usersNew = buildRowsSend(users, pairs, kpNewCgrups, 'cgrups')
|
|
1119
1219
|
|
|
1120
1220
|
// //check
|
|
1121
1221
|
// if (size(usersNew) === 0) {
|
|
@@ -1123,7 +1223,7 @@ export default {
|
|
|
1123
1223
|
// }
|
|
1124
1224
|
|
|
1125
1225
|
//saveUsers, 失敗時回 false: 失敗提示已由 saveUsers 顯示, 此處不關閉對話框(保留未存變更供重按儲存)
|
|
1126
|
-
let okSave = await vo.saveUsers(usersNew)
|
|
1226
|
+
let okSave = await vo.saveUsers(usersNew, baseline)
|
|
1127
1227
|
if (!okSave) {
|
|
1128
1228
|
return false
|
|
1129
1229
|
}
|
|
@@ -1142,10 +1242,12 @@ export default {
|
|
|
1142
1242
|
}
|
|
1143
1243
|
|
|
1144
1244
|
//resolve
|
|
1145
|
-
|
|
1245
|
+
pmDlg.resolve(rows)
|
|
1146
1246
|
|
|
1147
1247
|
//hide
|
|
1148
|
-
vo.
|
|
1248
|
+
if (vo.pm === pmDlg) { //期間若已 show() 開啟另一視窗, 舊流程不得關閉新視窗
|
|
1249
|
+
vo.bShow = false
|
|
1250
|
+
}
|
|
1149
1251
|
|
|
1150
1252
|
})
|
|
1151
1253
|
.catch((err) => {
|
|
@@ -1191,6 +1293,8 @@ export default {
|
|
|
1191
1293
|
|
|
1192
1294
|
//default
|
|
1193
1295
|
vo.isModified = false
|
|
1296
|
+
vo.staleNotice = false
|
|
1297
|
+
vo.ownSaving = false
|
|
1194
1298
|
|
|
1195
1299
|
//isEditable, grup
|
|
1196
1300
|
let isEditable = get(msg, 'isEditable', false)
|
|
@@ -1202,13 +1306,22 @@ export default {
|
|
|
1202
1306
|
//users
|
|
1203
1307
|
let users = cloneDeep(vo.users)
|
|
1204
1308
|
|
|
1309
|
+
//baseline(F-26, R-BASELINE-01; 外審 B.1): 於生成 items 之同一處, 以 items 所依據之那份使用者列(users)計算並綁定為本次草稿之載入基準; 之後 store 推播變新不得更新它
|
|
1310
|
+
//(存檔當下才由最新 store 取基準, 證明的是「store 新」而非「待存之編輯內容新」), 每次 show() 重新綁定. 欄位口徑與伺服端同一(vo.$ds.users.keys 經 rowDigest.makeBaseline)
|
|
1311
|
+
let baseline = makeBaseline(users, vo.$ds.users.keys)
|
|
1312
|
+
|
|
1205
1313
|
//items
|
|
1206
1314
|
let items = vo.genItems(grup, users)
|
|
1207
1315
|
|
|
1316
|
+
//rowStates: 開框時各列之歸屬狀態(須在任何切換操作前由 items 記下; opt.rows 與 items 為同一份陣列, 切換會就地改 enable/mode), 供存檔時判定使用者有無變更該列
|
|
1317
|
+
let rowStates = makeRowStates(items)
|
|
1318
|
+
|
|
1208
1319
|
//save
|
|
1209
1320
|
vo.isEditable = isEditable
|
|
1210
1321
|
vo.grup = grup
|
|
1211
1322
|
vo.items = items
|
|
1323
|
+
vo.baseline = baseline
|
|
1324
|
+
vo.rowStates = rowStates
|
|
1212
1325
|
|
|
1213
1326
|
//genOpt
|
|
1214
1327
|
vo.genOpt()
|