w-web-perm 1.0.83 → 1.0.85
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/css/{chunk-vendors.034f7a87.css → chunk-vendors.18034562.css} +1 -1
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.aaaa3681.js +1 -0
- package/dist/js/app.aaaa3681.js +1 -0
- package/dist/js/{chunk-vendors-legacy.d0596078.js → chunk-vendors-legacy.7b6ae0b0.js} +3 -3
- package/dist/js/{chunk-vendors.d0596078.js → chunk-vendors.7b6ae0b0.js} +3 -3
- package/dist/w-web-perm.umd.js +2 -2
- package/dist/w-web-perm.umd.js.map +1 -1
- package/docs/WWebPerm.html +1 -1
- package/docs/global.html +1 -1
- package/docs/index.html +1 -1
- package/docs/server_WWebPerm.mjs.html +1 -1
- package/docs/server_procCore.mjs.html +1 -1
- package/docs/src_fetchJson.mjs.html +1 -1
- package/package.json +2 -2
- package/server/procLang.mjs +12 -4
- package/src/components/LayoutContentGrups.vue +6 -6
- package/src/components/LayoutContentPemis.vue +6 -6
- package/src/components/LayoutContentTargets.vue +2 -2
- package/src/components/LayoutContentUsers.vue +5 -3
- package/src/components/ModeSelectChip.vue +182 -0
- package/src/components/RelationChip.vue +114 -0
- package/src/components/RelationChips.vue +375 -0
- package/src/components/VeCgrups.vue +23 -20
- package/src/components/VeCpemis.vue +21 -20
- package/src/components/VeCrules.vue +8 -1
- package/src/components/VeGrupBlngUsers.vue +40 -51
- package/src/components/VePemiBlngGrups.vue +27 -41
- package/tableTags-web-perm.json +1 -1
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/README.md +58 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/server_procLang.mjs.diff +31 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_App.vue.diff +19 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_LayoutContentGrups.vue.diff +40 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_LayoutContentPemis.vue.diff +40 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_ModeSelectChip.vue.new.diff +124 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_RelationChip.vue.new.diff +116 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_RelationChips.vue.new.diff +152 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_VeCgrups.vue.diff +76 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_VeCpemis.vue.diff +76 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_VeCrules.vue.diff +14 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_VeGrupBlngUsers.vue.diff +121 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/diff/src_components_VePemiBlngGrups.vue.diff +121 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/server/procLang.mjs +1049 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/App.vue +204 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/LayoutContentGrups.vue +1260 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/LayoutContentPemis.vue +1262 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/ModeSelectChip.vue +121 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/RelationChip.vue +113 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/RelationChips.vue +149 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/VeCgrups.vue +943 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/VeCpemis.vue +943 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/VeCrules.vue +848 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/VeGrupBlngUsers.vue +1157 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231/src/components/VePemiBlngGrups.vue +1157 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/A-/347/256/241/347/220/206/346/254/212/351/231/220/344/270/273/350/241/250.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/A-/347/256/241/347/220/206/346/254/212/351/231/220/347/276/244/347/265/204/344/270/273/350/241/250.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B1-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/260/215/350/251/261/346/241/206.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B2-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-hover/346/234/254/351/240/205chip.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B3-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/261/225/351/226/213/346/234/254/351/240/205chip/346/270/205/345/226/256.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B4-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/346/234/254/351/240/205chip/345/267/262/347/202/272AND.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B5-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/261/225/351/226/213/345/205/250/351/203/250/346/265/256/345/261/244.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/B6-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/213/276/351/201/270/345/276/214/346/234/254/351/240/205chip/345/234/250/351/246/226/344/275/215.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/C1-/347/267/250/350/274/257/344/275/277/347/224/250/346/254/212/351/231/220-/345/260/215/350/251/261/346/241/206.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/C2-/347/267/250/350/274/257/344/275/277/347/224/250/346/254/212/351/231/220-hover/345/267/262/345/213/276/351/201/270/345/210/227/346/216/247/345/210/266/351/240/205.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/C3-/347/267/250/350/274/257/344/275/277/347/224/250/346/254/212/351/231/220-/345/261/225/351/226/213/346/270/205/345/226/256.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/D1-/347/267/250/350/274/257/346/211/200/345/261/254/344/275/277/347/224/250/350/200/205-/345/260/215/350/251/261/346/241/206.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/E1-/347/267/250/350/274/257/344/275/277/347/224/250/346/254/212/351/231/220/347/276/244/347/265/204-hover.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/E2-/347/267/250/350/274/257/344/275/277/347/224/250/346/254/212/351/231/220/347/276/244/347/265/204-/345/261/225/351/226/213/346/270/205/345/226/256.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/F1-/345/261/225/347/244/272/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/224/257/350/256/200/346/205/213.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/F2-/345/261/225/347/244/272/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/261/225/351/226/213/345/205/250/351/203/250.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/G1-zoom-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/211/215/345/205/255/345/210/227.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/G2-zoom-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-hover/346/234/254/351/240/205chip.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/G3-zoom-/347/267/250/350/274/257/346/211/200/345/261/254/346/254/212/351/231/220/347/276/244/347/265/204-/345/261/225/351/226/213/346/270/205/345/226/256.png +0 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/346/210/252/345/234/226/verify.json +390 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/350/205/263/346/234/254/build.log +93 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/350/205/263/346/234/254/lint.log +23 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/350/205/263/346/234/254/zz_perm_probe.mjs +321 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/350/205/263/346/234/254/zz_perm_zoom.mjs +173 -0
- package/w-web-perm/351/232/250/351/231/204/350/263/207/346/226/231//351/251/227/350/255/211/350/205/263/346/234/254/zz_wwp_verify.mjs +330 -0
- package//345/273/272/350/255/260w-aggrid-vue/350/252/277/346/225/264.md +193 -0
- package//345/273/272/350/255/260w-component-vue/350/210/207wsemi/350/252/277/346/225/264.md +76 -0
- package//345/273/272/350/255/260w-web-perm/350/252/277/346/225/264.md +244 -0
- package//346/226/275/345/267/245/345/226/256_/345/273/272/350/255/260w-web-perm/350/252/277/346/225/264.md +166 -0
- package/dist/index.html +0 -4
- package/dist/js/app-legacy.04fa72c9.js +0 -1
- package/dist/js/app.04fa72c9.js +0 -1
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="root"
|
|
4
|
+
:style="`position:relative; display:flex; align-items:center; gap:${gap}px; height:100%; min-width:0; overflow:hidden; white-space:nowrap;`"
|
|
5
|
+
>
|
|
6
|
+
|
|
7
|
+
<!-- 儲存格內之標籤: 本項永遠第 1 顆(唯一可編輯者, 永不被收合); 只渲染放得下的前 nShow 顆 -->
|
|
8
|
+
<!-- 用 v-if 不用 v-show: RelationChip 根元素之 style 含 display:inline-flex, 父層任何無關之重繪(如 hoverAll、vw 變動)會使 Vue 2 之 style 模組
|
|
9
|
+
把 display 寫回 inline-flex, 而 v-show 值未變不會重套 display:none, 被收合之標籤即重新顯示(2026-09-17 實測: 收合後再縮窄一級, 2 顆與「+1」同時可見);
|
|
10
|
+
寬度已於量測時快取, 卸載不影響計算 -->
|
|
11
|
+
<template v-for="(item, kitem) in itemsSorted">
|
|
12
|
+
<RelationChip
|
|
13
|
+
:key="`c-${item.name}`"
|
|
14
|
+
:ref="`chip${kitem}`"
|
|
15
|
+
:style="useChipStyle(kitem)"
|
|
16
|
+
:name="item.name"
|
|
17
|
+
:mode="item.mode"
|
|
18
|
+
:isCurrent="item.enable==='y'"
|
|
19
|
+
:editable="editable"
|
|
20
|
+
:labelContent="labelContent"
|
|
21
|
+
@input="(v)=>{$emit('input',v)}"
|
|
22
|
+
v-if="measuring || kitem < nShow"
|
|
23
|
+
></RelationChip>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<!-- 溢出指示「+K」: 只在確有標籤放不下時出現, K 為看不到的數量, 接在最後一顆可見標籤之後(對標 antd Select maxTagCount=responsive、MUI Autocomplete limitTags) -->
|
|
27
|
+
<!-- line-height:0: 切斷儲存格 27px 行高之繼承, 否則 WPopup 內之 WTooltip 觸發層會自成 27px 行框, 使指示偏下並溢出儲存格被裁(實測上 6.03/下 −1.03) -->
|
|
28
|
+
<WPopup
|
|
29
|
+
style="flex:0 0 auto; line-height:0;"
|
|
30
|
+
:displayType="'block'"
|
|
31
|
+
:isolated="true"
|
|
32
|
+
:placement="'bottom-start'"
|
|
33
|
+
:paddingStyle="{v:8,h:10}"
|
|
34
|
+
:minWidth="usePopupMinWidth"
|
|
35
|
+
:maxWidth="usePopupMaxWidth"
|
|
36
|
+
:labelContent="labelContentAll"
|
|
37
|
+
v-if="!measuring && nHidden > 0"
|
|
38
|
+
>
|
|
39
|
+
<template v-slot:trigger>
|
|
40
|
+
<div
|
|
41
|
+
:style="useIndicatorStyle"
|
|
42
|
+
:title="tooltipAll"
|
|
43
|
+
@mouseenter="hoverAll=true"
|
|
44
|
+
@mouseleave="hoverAll=false"
|
|
45
|
+
>
|
|
46
|
+
+{{ nHidden }}
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
<template v-slot:content>
|
|
50
|
+
<div style="font-size:12px; color:#666; padding-bottom:6px;">
|
|
51
|
+
{{ usePopupTitle }}
|
|
52
|
+
</div>
|
|
53
|
+
<!-- 列出全部標籤, 本項排第 1 顆且與儲存格內同樣可改模式(展開狀態即操作狀態, 不是唯讀預覽); 最大高 50vh 超過即於浮層內捲動 -->
|
|
54
|
+
<div :style="`display:flex; flex-wrap:wrap; gap:4px 6px; max-height:50vh; max-width:${usePopupMaxWidth}px; overflow:auto;`">
|
|
55
|
+
<RelationChip
|
|
56
|
+
v-for="item in itemsSorted"
|
|
57
|
+
:key="`p-${item.name}`"
|
|
58
|
+
:name="item.name"
|
|
59
|
+
:mode="item.mode"
|
|
60
|
+
:isCurrent="item.enable==='y'"
|
|
61
|
+
:editable="editable"
|
|
62
|
+
:nameMaxWidth="520"
|
|
63
|
+
:labelContent="labelContent"
|
|
64
|
+
@input="(v)=>{$emit('input',v)}"
|
|
65
|
+
></RelationChip>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
68
|
+
</WPopup>
|
|
69
|
+
|
|
70
|
+
<!-- 量測用: 預留「+{n−1}」位數之指示寬度, 使收合顆數不因 K 之位數變化而來回跳動; 不可見、不佔版面 -->
|
|
71
|
+
<div
|
|
72
|
+
ref="indicatorMeasure"
|
|
73
|
+
:style="`${useIndicatorStyle} position:absolute; left:-9999px; top:0; visibility:hidden; pointer-events:none;`"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
>
|
|
76
|
+
+{{ Math.max(n - 1, 1) }}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|
|
81
|
+
|
|
82
|
+
<script>
|
|
83
|
+
import get from 'lodash-es/get.js'
|
|
84
|
+
import size from 'lodash-es/size.js'
|
|
85
|
+
import filter from 'lodash-es/filter.js'
|
|
86
|
+
import sum from 'lodash-es/sum.js'
|
|
87
|
+
import WPopup from 'w-component-vue/src/components/WPopup.vue'
|
|
88
|
+
import RelationChip from './RelationChip.vue'
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
//關聯標籤列(供 VeGrupBlngUsers 之「所屬權限群組名稱」欄與 VePemiBlngGrups 之「所屬權限名稱」欄)
|
|
92
|
+
// - items: genItems 產生之 grups/pemis 陣列, 各項含 name、mode、enable('y' 為本項)
|
|
93
|
+
// - 顯示順序: enable='y'(本項)永遠第 1 顆, 其餘依原順序; 只改顯示, 不改儲存之 cgrups/cpemis 字串(權限合併結果與順序無關, 見 mShare.mjs 之 mergeRules)
|
|
94
|
+
// - 溢出(2026-09-17 重設計, 取代「標籤數 ≥ 2 即於最左出現⋯N」):
|
|
95
|
+
// 依實測寬度決定顯示前幾顆; 全部放得下即不出現任何指示; 放不下時於最後一顆可見標籤之後出現「+K」(K=看不到的數量),
|
|
96
|
+
// 點開浮層列出全部標籤。舊版以數量代替溢出, 實測權限頁 4 列標籤總寬 165~192px、儲存格可用 295px, 全部放得下卻 4 列都出鈕。
|
|
97
|
+
// 至少顯示第 1 顆(本項); 連第 1 顆都放不下時由其名稱段收縮讓位(省略號), 模式段與「+K」維持完整
|
|
98
|
+
// - 量測: 標籤自然寬於資料變動時量一次並快取(量測期間暫時全部顯示且不收縮); 欄寬變動只以快取重算顆數, 不重繪
|
|
99
|
+
// - 浮層內本項標籤與儲存格內同樣可改模式(舊版浮層一律唯讀, 展開後反而不能改); 改動後由呼叫端 revRows 重繪列,
|
|
100
|
+
// 本元件隨儲存格重建而卸載、浮層隨之關閉——該列唯一可改之項已改完, 關閉符合預期
|
|
101
|
+
// - 垂直置中: 根元素 flex 置中且 height:100%(其百分比高度之包含塊為 .ag-cell), 標籤與指示於 27px 儲存格內上下各 2.5
|
|
102
|
+
// - 尺寸監聽用原生 ResizeObserver, 不用元件庫之 v-domresize(wsemi domDetect): 後者每實例一個 20ms setInterval 輪詢 offsetWidth,
|
|
103
|
+
// 且比較基準為「上一次取樣」, 連續 ≤1px 之變化永不觸發(2026-09-17 讀碼定案並實測: 視窗逐 1px 縮窄時表格欄寬始終不重算),
|
|
104
|
+
// 每列一實例時輪詢成本隨可見列數累積; ResizeObserver 為事件驅動、任何寬度變化皆觸發。根元素寬由儲存格決定(overflow:hidden),
|
|
105
|
+
// 子元素增減不改變根寬, 無 ResizeObserver 迴圈之虞; 回呼以 requestAnimationFrame 合併
|
|
106
|
+
export default {
|
|
107
|
+
components: {
|
|
108
|
+
WPopup,
|
|
109
|
+
RelationChip,
|
|
110
|
+
},
|
|
111
|
+
props: {
|
|
112
|
+
items: {
|
|
113
|
+
type: Array,
|
|
114
|
+
default: () => [],
|
|
115
|
+
},
|
|
116
|
+
editable: {
|
|
117
|
+
type: Boolean,
|
|
118
|
+
default: false,
|
|
119
|
+
},
|
|
120
|
+
popupTitle: { //浮層標題所用之欄名(如「所屬權限名稱」), 與數量合成 chipsPopupTitle
|
|
121
|
+
type: String,
|
|
122
|
+
default: '',
|
|
123
|
+
},
|
|
124
|
+
tooltipAll: {
|
|
125
|
+
type: String,
|
|
126
|
+
default: '',
|
|
127
|
+
},
|
|
128
|
+
labelContent: { //本項標籤之模式下拉浮層之 wtlp 屬性值
|
|
129
|
+
type: String,
|
|
130
|
+
default: 'modeSelect',
|
|
131
|
+
},
|
|
132
|
+
labelContentAll: { //展開全部之浮層之 wtlp 屬性值
|
|
133
|
+
type: String,
|
|
134
|
+
default: 'relationChipsAll',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
data: function() {
|
|
138
|
+
return {
|
|
139
|
+
gap: 4, //相鄰標籤與指示之間距, 以根元素 CSS gap 實作(未顯示者 display:none 不產生間距), 與 relayout 之計算模型一致
|
|
140
|
+
measuring: true,
|
|
141
|
+
measured: false,
|
|
142
|
+
chipWidths: [],
|
|
143
|
+
indicatorWidth: 0,
|
|
144
|
+
nShow: 0,
|
|
145
|
+
hoverAll: false,
|
|
146
|
+
vw: typeof window !== 'undefined' ? window.innerWidth : 1440, //視窗寬, 供浮層寬度上限(窄視窗不超出)
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
mounted: function() {
|
|
150
|
+
let vo = this
|
|
151
|
+
vo.measure()
|
|
152
|
+
|
|
153
|
+
//ResizeObserver: 根元素寬度變化(儲存格欄寬改變、視窗變動致表格重算欄寬)即重算
|
|
154
|
+
if (typeof window !== 'undefined' && window.ResizeObserver) {
|
|
155
|
+
vo.ro = new window.ResizeObserver(() => {
|
|
156
|
+
if (vo.rafId) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
vo.rafId = window.requestAnimationFrame(() => {
|
|
160
|
+
vo.rafId = null
|
|
161
|
+
vo.relayout()
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
vo.ro.observe(vo.$refs.root)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
},
|
|
168
|
+
beforeDestroy: function() {
|
|
169
|
+
let vo = this
|
|
170
|
+
if (vo.ro) {
|
|
171
|
+
vo.ro.disconnect()
|
|
172
|
+
vo.ro = null
|
|
173
|
+
}
|
|
174
|
+
if (vo.rafId) {
|
|
175
|
+
window.cancelAnimationFrame(vo.rafId)
|
|
176
|
+
vo.rafId = null
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
watch: {
|
|
180
|
+
items: {
|
|
181
|
+
handler: function() {
|
|
182
|
+
let vo = this
|
|
183
|
+
vo.measure()
|
|
184
|
+
},
|
|
185
|
+
deep: true,
|
|
186
|
+
},
|
|
187
|
+
nHidden: function(v) {
|
|
188
|
+
let vo = this
|
|
189
|
+
//指示被移除時重設 hover 態: 游標停在指示上時拉寬使其消失, 被移除之節點不保證收到 mouseleave, 否則下次出現會殘留 hover 色
|
|
190
|
+
if (v === 0) {
|
|
191
|
+
vo.hoverAll = false
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
computed: {
|
|
196
|
+
|
|
197
|
+
n: function() {
|
|
198
|
+
let vo = this
|
|
199
|
+
return size(vo.items)
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
nHidden: function() {
|
|
203
|
+
let vo = this
|
|
204
|
+
return Math.max(vo.n - vo.nShow, 0)
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
itemsSorted: function() {
|
|
208
|
+
let vo = this
|
|
209
|
+
let a = filter(vo.items, (v) => get(v, 'enable', '') === 'y')
|
|
210
|
+
let b = filter(vo.items, (v) => get(v, 'enable', '') !== 'y')
|
|
211
|
+
return [...a, ...b]
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
usePopupTitle: function() {
|
|
215
|
+
let vo = this
|
|
216
|
+
let t = vo.$t('chipsPopupTitle')
|
|
217
|
+
t = t.replace('{title}', vo.popupTitle)
|
|
218
|
+
t = t.replace('{n}', vo.n)
|
|
219
|
+
return t
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
//浮層寬度: 內容寬(不含 paddingStyle 左右各 10)不超過「視窗寬 − 內距 20 − 左右各留 16」, 使 320 寬視窗開啟時浮層仍完整在視窗內;
|
|
223
|
+
// 寬視窗時維持原設計之最小 320、最大 540(元件庫 minWidth/maxWidth 不含 padding, 見 WTooltip.vue 之 prop 說明)
|
|
224
|
+
usePopupMaxWidth: function() {
|
|
225
|
+
let vo = this
|
|
226
|
+
return Math.max(120, Math.min(540, vo.vw - 52))
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
usePopupMinWidth: function() {
|
|
230
|
+
let vo = this
|
|
231
|
+
return Math.min(320, vo.usePopupMaxWidth)
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
useIndicatorStyle: function() {
|
|
235
|
+
let vo = this
|
|
236
|
+
let h = vo.hoverAll
|
|
237
|
+
return [
|
|
238
|
+
'display:inline-flex;',
|
|
239
|
+
'align-items:center;',
|
|
240
|
+
'box-sizing:border-box;',
|
|
241
|
+
'height:22px;',
|
|
242
|
+
'padding:0 6px;',
|
|
243
|
+
`border:1px solid ${h ? '#888' : '#aaa'};`,
|
|
244
|
+
'border-radius:4px;',
|
|
245
|
+
`background:${h ? '#f5f5f5' : '#fff'};`,
|
|
246
|
+
`color:${h ? '#333' : '#555'};`,
|
|
247
|
+
'font-size:12px;',
|
|
248
|
+
'line-height:20px;',
|
|
249
|
+
'white-space:nowrap;',
|
|
250
|
+
'cursor:pointer;',
|
|
251
|
+
'user-select:none;',
|
|
252
|
+
'transition:background-color 0.3s, border-color 0.3s, color 0.3s;',
|
|
253
|
+
].join(' ')
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
},
|
|
257
|
+
methods: {
|
|
258
|
+
|
|
259
|
+
useChipStyle: function(kitem) {
|
|
260
|
+
let vo = this
|
|
261
|
+
//量測期間一律不收縮以取自然寬; 平時只有第 1 顆可收縮(連它都放不下時由名稱段讓位)
|
|
262
|
+
let flex = (!vo.measuring && kitem === 0) ? '0 1 auto' : '0 0 auto'
|
|
263
|
+
return `flex:${flex}; min-width:0;`
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
//縮放係數: 視覺寬(getBoundingClientRect, 受 transform 影響) ÷ 版面寬(offsetWidth, 不受 transform 影響)
|
|
267
|
+
// 對話框開啟時有縮放動畫(實測量到 0.2 倍寬), 以此係數把視覺寬還原為版面寬, 取得次像素精度而不受縮放污染
|
|
268
|
+
getScale: function() {
|
|
269
|
+
let vo = this
|
|
270
|
+
let el = vo.$refs.root
|
|
271
|
+
let lw = el ? el.offsetWidth : 0
|
|
272
|
+
if (lw <= 0) {
|
|
273
|
+
return 0
|
|
274
|
+
}
|
|
275
|
+
let s = el.getBoundingClientRect().width / lw
|
|
276
|
+
return s > 0 ? s : 0
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
measure: function() {
|
|
280
|
+
let vo = this
|
|
281
|
+
|
|
282
|
+
//暫時全部顯示且不收縮, 下一幀量自然寬
|
|
283
|
+
vo.measuring = true
|
|
284
|
+
vo.$nextTick(() => {
|
|
285
|
+
|
|
286
|
+
//chipWidths, 逐索引取 ref(Vue 2 之 v-for 同名 ref 陣列不保證與資料順序一致, 故每顆各給 chip{k})
|
|
287
|
+
// 寬度 = getBoundingClientRect().width ÷ 縮放係數: 不可直接用 getBoundingClientRect——對話框開啟有縮放動畫,
|
|
288
|
+
// 動畫中量得縮放後之寬(2026-09-17 實測 21/15.7, 實為 105.2/78.7 之 0.2 倍), 會誤判「全部放得下」而永不出現「+K」;
|
|
289
|
+
// 亦不直接用 offsetWidth——其為整數, 需逐顆補 +1 保守值, 誤差隨顆數累積而提早收合(2026-09-17 審計查出)
|
|
290
|
+
let scale = vo.getScale()
|
|
291
|
+
let wOf = (el) => (scale > 0 && el && el.offsetWidth > 0) ? el.getBoundingClientRect().width / scale : 0
|
|
292
|
+
let ws = []
|
|
293
|
+
for (let k = 0; k < vo.n; k++) {
|
|
294
|
+
let c = get(vo, ['$refs', `chip${k}`, 0], null)
|
|
295
|
+
ws.push(wOf(c && c.$el))
|
|
296
|
+
}
|
|
297
|
+
vo.chipWidths = ws
|
|
298
|
+
|
|
299
|
+
//indicatorWidth
|
|
300
|
+
vo.indicatorWidth = wOf(vo.$refs.indicatorMeasure)
|
|
301
|
+
|
|
302
|
+
//尚未排版(元素未顯示時寬為 0)者視為未量測, 待下次尺寸變動再量
|
|
303
|
+
vo.measured = ws.length > 0 && ws.every((w) => w > 0) && vo.indicatorWidth > 0
|
|
304
|
+
|
|
305
|
+
//relayout
|
|
306
|
+
vo.measuring = false
|
|
307
|
+
if (!vo.measured) {
|
|
308
|
+
//量不到(元素尚未顯示): 先全部顯示避免儲存格空白, 不在此重試(否則元素持續不可見時會每幀互相呼叫), 待元素顯示使根寬改變、ResizeObserver 觸發 relayout 時再量
|
|
309
|
+
vo.nShow = vo.n
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
vo.relayout()
|
|
313
|
+
|
|
314
|
+
})
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
relayout: function() {
|
|
318
|
+
let vo = this
|
|
319
|
+
|
|
320
|
+
//check
|
|
321
|
+
if (vo.measuring) {
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
let n = vo.n
|
|
325
|
+
if (n === 0) {
|
|
326
|
+
vo.nShow = 0
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
//尚未量得有效寬度(掛載時元素未顯示)者, 趁此次尺寸變動重量, 量完會再呼叫本函式
|
|
331
|
+
if (!vo.measured) {
|
|
332
|
+
vo.measure()
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
//vw, 供浮層寬度上限
|
|
337
|
+
vo.vw = window.innerWidth
|
|
338
|
+
|
|
339
|
+
//avail, 與標籤寬同一計算模型(視覺寬 ÷ 縮放係數)
|
|
340
|
+
let scale = vo.getScale()
|
|
341
|
+
if (scale <= 0) {
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
let avail = vo.$refs.root.getBoundingClientRect().width / scale
|
|
345
|
+
let eps = 0.01 //浮點誤差
|
|
346
|
+
|
|
347
|
+
//全部放得下: 各顆寬 + 顆間間距(CSS gap 只在相鄰已顯示者之間, 最後一顆之後無間距)
|
|
348
|
+
let ws = vo.chipWidths
|
|
349
|
+
let all = sum(ws) + vo.gap * (n - 1)
|
|
350
|
+
if (all <= avail + eps) {
|
|
351
|
+
vo.nShow = n
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//放不下: 前 k 顆 + k 個間距(k 顆之間 k−1 個, 加最後一顆與指示之間 1 個) + 指示寬, 取最大 k; 至少 1 顆(本項永不收合)
|
|
356
|
+
let used = 0
|
|
357
|
+
let k = 0
|
|
358
|
+
for (let i = 0; i < n; i++) {
|
|
359
|
+
let need = used + ws[i] + vo.gap + vo.indicatorWidth
|
|
360
|
+
if (need > avail + eps) {
|
|
361
|
+
break
|
|
362
|
+
}
|
|
363
|
+
used += ws[i] + vo.gap
|
|
364
|
+
k = i + 1
|
|
365
|
+
}
|
|
366
|
+
vo.nShow = Math.max(k, 1)
|
|
367
|
+
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
</script>
|
|
373
|
+
|
|
374
|
+
<style scoped>
|
|
375
|
+
</style>
|
|
@@ -128,19 +128,18 @@
|
|
|
128
128
|
>
|
|
129
129
|
<template v-slot:cell-render="props">
|
|
130
130
|
<span v-if="props.key==='name'">{{ props.value }}</span>
|
|
131
|
-
|
|
131
|
+
<!-- flex 置中容器: 22px 控制項以行內排版放入 27px 儲存格會偏下(上 4.77/下 0.23), 包一層 height:100% 之 flex 容器即上下各 2.5(同 w-aggrid-vue 範例 AppSlotCellRenderAlign.vue) -->
|
|
132
|
+
<div
|
|
133
|
+
style="display:flex; align-items:center; height:100%;"
|
|
132
134
|
v-else-if="props.key==='mode'"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:labelContent="'modeSelect'"
|
|
142
|
-
@input="(item)=>{showVeCgrupsToggleItemModeByName(props.row.name, item)}"
|
|
143
|
-
></WTextSelect>
|
|
135
|
+
>
|
|
136
|
+
<ModeSelectChip
|
|
137
|
+
:value="props.row.mode"
|
|
138
|
+
:editable="isEditable && props.row.enable === 'y'"
|
|
139
|
+
:labelContent="'modeSelect'"
|
|
140
|
+
@input="(item)=>{showVeCgrupsToggleItemModeByName(props.row.name, item)}"
|
|
141
|
+
></ModeSelectChip>
|
|
142
|
+
</div>
|
|
144
143
|
<input v-else-if="props.key==='enable'" type="checkbox" :checked="props.value === 'y'" @click="showVeCgrupsToggleItemEnableByName(props.row.name)" :disabled="!isEditable" />
|
|
145
144
|
<span v-else>{{ props.value }}</span>
|
|
146
145
|
</template>
|
|
@@ -180,7 +179,7 @@ import isestr from 'wsemi/src/isestr.mjs'
|
|
|
180
179
|
import WDialog from 'w-component-vue/src/components/WDialog.vue'
|
|
181
180
|
import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
|
|
182
181
|
import WAggridVue from 'w-aggrid-vue/src/components/WAggridVue.vue'
|
|
183
|
-
import
|
|
182
|
+
import ModeSelectChip from './ModeSelectChip.vue'
|
|
184
183
|
|
|
185
184
|
|
|
186
185
|
export default {
|
|
@@ -188,7 +187,7 @@ export default {
|
|
|
188
187
|
WDialog,
|
|
189
188
|
WButtonCircle,
|
|
190
189
|
WAggridVue,
|
|
191
|
-
|
|
190
|
+
ModeSelectChip,
|
|
192
191
|
},
|
|
193
192
|
props: {
|
|
194
193
|
},
|
|
@@ -217,10 +216,6 @@ export default {
|
|
|
217
216
|
isEditable: false,
|
|
218
217
|
isModified: false,
|
|
219
218
|
|
|
220
|
-
//mode 欄之自製下拉(WTextSelect, 取代原生 select 以免彈出清單之 highlight 色由 OS 決定)
|
|
221
|
-
modeItems: ['OR', 'AND'],
|
|
222
|
-
modeSelectWidth: 72, //= 最長項 'AND' 文字實寬 + 左右內距 10×2 + 展開箭頭 18 + 邊框 1×2, 實測後定值(見 CLAUDE_experience.md)
|
|
223
|
-
|
|
224
219
|
user: null,
|
|
225
220
|
|
|
226
221
|
items: [],
|
|
@@ -405,12 +400,16 @@ export default {
|
|
|
405
400
|
defHeadFilter: true,
|
|
406
401
|
defCellAlignH: 'left',
|
|
407
402
|
kpCellEditable: {
|
|
408
|
-
|
|
403
|
+
//name 一律唯讀: 本對話框之列固定為「全部既有權限群組」(genItems 產生, 無新增/刪除列),
|
|
404
|
+
// 就地改名無任何正當用途; 且 doSave 以列上之 name 當鍵寫出 cgrups(kpGrup[name]={mode,isActive}),
|
|
405
|
+
// 改名即寫出對不到任何群組之鍵 → 關聯靜默失效、DB 留下垃圾鍵, 屬「儲存成功但實況不符」。
|
|
406
|
+
// 2026-09-16 雙獨立審計提出、經追 doSave 控制流證實(全域 §4 規則 6)。
|
|
407
|
+
'name': false,
|
|
409
408
|
'mode': false,
|
|
410
409
|
'enable': false,
|
|
411
410
|
},
|
|
412
411
|
kpHeadWidth: {
|
|
413
|
-
'mode':
|
|
412
|
+
'mode': 90, //ModeSelectChip 寬 56 + 儲存格內距 11×2 = 78, 留餘裕
|
|
414
413
|
'enable': 100,
|
|
415
414
|
},
|
|
416
415
|
// kpRowDrag: {
|
|
@@ -427,6 +426,10 @@ export default {
|
|
|
427
426
|
kpHeadCheckBox: {
|
|
428
427
|
'name': true,
|
|
429
428
|
},
|
|
429
|
+
kpHeadFocusHighlight: { //兩欄之儲存格內皆有自己的控制項(模式下拉、勾選框), 焦點框徒增干擾故關閉; 控制項取得焦點時之焦點框由 w-aggrid-vue 2.0.87 之 :focus-within 規則一併擋下
|
|
430
|
+
'mode': false,
|
|
431
|
+
'enable': false,
|
|
432
|
+
},
|
|
430
433
|
rowsChange: (rs) => {
|
|
431
434
|
// console.log('rowsChange', rs)
|
|
432
435
|
// console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
|
|
@@ -128,19 +128,18 @@
|
|
|
128
128
|
>
|
|
129
129
|
<template v-slot:cell-render="props">
|
|
130
130
|
<span v-if="props.key==='name'">{{ props.value }}</span>
|
|
131
|
-
|
|
131
|
+
<!-- flex 置中容器: 理由同 VeCgrups 該處註解 -->
|
|
132
|
+
<div
|
|
133
|
+
style="display:flex; align-items:center; height:100%;"
|
|
132
134
|
v-else-if="props.key==='mode'"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:labelContent="'modeSelect'"
|
|
142
|
-
@input="(item)=>{showVeCpemisToggleItemModeByName(props.row.name, item)}"
|
|
143
|
-
></WTextSelect>
|
|
135
|
+
>
|
|
136
|
+
<ModeSelectChip
|
|
137
|
+
:value="props.row.mode"
|
|
138
|
+
:editable="isEditable && props.row.enable === 'y'"
|
|
139
|
+
:labelContent="'modeSelect'"
|
|
140
|
+
@input="(item)=>{showVeCpemisToggleItemModeByName(props.row.name, item)}"
|
|
141
|
+
></ModeSelectChip>
|
|
142
|
+
</div>
|
|
144
143
|
<input v-else-if="props.key==='enable'" type="checkbox" :checked="props.value === 'y'" @click="showVeCpemisToggleItemEnableByName(props.row.name)" :disabled="!isEditable" />
|
|
145
144
|
<span v-else>{{ props.value }}</span>
|
|
146
145
|
</template>
|
|
@@ -180,7 +179,7 @@ import isestr from 'wsemi/src/isestr.mjs'
|
|
|
180
179
|
import WDialog from 'w-component-vue/src/components/WDialog.vue'
|
|
181
180
|
import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
|
|
182
181
|
import WAggridVue from 'w-aggrid-vue/src/components/WAggridVue.vue'
|
|
183
|
-
import
|
|
182
|
+
import ModeSelectChip from './ModeSelectChip.vue'
|
|
184
183
|
|
|
185
184
|
|
|
186
185
|
export default {
|
|
@@ -188,7 +187,7 @@ export default {
|
|
|
188
187
|
WDialog,
|
|
189
188
|
WButtonCircle,
|
|
190
189
|
WAggridVue,
|
|
191
|
-
|
|
190
|
+
ModeSelectChip,
|
|
192
191
|
},
|
|
193
192
|
props: {
|
|
194
193
|
},
|
|
@@ -217,10 +216,6 @@ export default {
|
|
|
217
216
|
isEditable: false,
|
|
218
217
|
isModified: false,
|
|
219
218
|
|
|
220
|
-
//mode 欄之自製下拉(WTextSelect, 取代原生 select 以免彈出清單之 highlight 色由 OS 決定)
|
|
221
|
-
modeItems: ['OR', 'AND'],
|
|
222
|
-
modeSelectWidth: 72, //= 最長項 'AND' 文字實寬 + 左右內距 10×2 + 展開箭頭 18 + 邊框 1×2, 實測後定值(見 CLAUDE_experience.md)
|
|
223
|
-
|
|
224
219
|
grup: null,
|
|
225
220
|
|
|
226
221
|
items: [],
|
|
@@ -405,12 +400,14 @@ export default {
|
|
|
405
400
|
defHeadFilter: true,
|
|
406
401
|
defCellAlignH: 'left',
|
|
407
402
|
kpCellEditable: {
|
|
408
|
-
|
|
403
|
+
//name 一律唯讀: 理由同 VeCgrups——列固定為全部既有權限, 無新增/刪除列; doSave 以列上之 name 當鍵寫出 cpemis,
|
|
404
|
+
// 改名即寫出對不到任何權限之鍵 → 關聯靜默失效、DB 留下垃圾鍵。
|
|
405
|
+
'name': false,
|
|
409
406
|
'mode': false,
|
|
410
407
|
'enable': false,
|
|
411
408
|
},
|
|
412
409
|
kpHeadWidth: {
|
|
413
|
-
'mode':
|
|
410
|
+
'mode': 90, //ModeSelectChip 寬 56 + 儲存格內距 11×2 = 78, 留餘裕
|
|
414
411
|
'enable': 100,
|
|
415
412
|
},
|
|
416
413
|
// kpRowDrag: {
|
|
@@ -427,6 +424,10 @@ export default {
|
|
|
427
424
|
kpHeadCheckBox: {
|
|
428
425
|
'name': true,
|
|
429
426
|
},
|
|
427
|
+
kpHeadFocusHighlight: { //兩欄之儲存格內皆有自己的控制項(模式下拉、勾選框), 焦點框徒增干擾故關閉; 控制項取得焦點時之焦點框由 w-aggrid-vue 2.0.87 之 :focus-within 規則一併擋下
|
|
428
|
+
'mode': false,
|
|
429
|
+
'enable': false,
|
|
430
|
+
},
|
|
430
431
|
rowsChange: (rs) => {
|
|
431
432
|
// console.log('rowsChange', rs)
|
|
432
433
|
// console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
|
|
@@ -383,7 +383,11 @@ export default {
|
|
|
383
383
|
defHeadFilter: true,
|
|
384
384
|
defCellAlignH: 'left',
|
|
385
385
|
kpCellEditable: {
|
|
386
|
-
|
|
386
|
+
//id 一律唯讀: 列固定為「全部既有管控對象」(由 $store.state.targets 映出, 無新增/刪除列),
|
|
387
|
+
// 就地改 id 無任何正當用途; 且 doSave 以列上之 id 當鍵寫出 crules(kpRule[id]=enable),
|
|
388
|
+
// 改 id 即寫出對不到任何 target 之鍵 → 規則靜默失效、DB 留下垃圾鍵。
|
|
389
|
+
// 與 VeCgrups/VeCpemis/VeGrupBlngUsers/VePemiBlngGrups 同類(§2.1 第 4 條 對稱性: 兄弟元素逐一比對)。
|
|
390
|
+
'id': false,
|
|
387
391
|
'enable': false,
|
|
388
392
|
},
|
|
389
393
|
kpHeadWidth: {
|
|
@@ -402,6 +406,9 @@ export default {
|
|
|
402
406
|
kpHeadCheckBox: {
|
|
403
407
|
'id': true,
|
|
404
408
|
},
|
|
409
|
+
kpHeadFocusHighlight: { //本欄儲存格內有自己的控制項(勾選框), 焦點框徒增干擾故關閉, 與其餘關聯對話框一致
|
|
410
|
+
'enable': false,
|
|
411
|
+
},
|
|
405
412
|
rowsChange: (rs) => {
|
|
406
413
|
// console.log('rowsChange', rs)
|
|
407
414
|
// console.log('rowsChange cloneDeep(vo.opt.rows)', cloneDeep(vo.opt.rows))
|