n20-common-lib 3.3.17 → 3.3.19
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/package.json
CHANGED
|
@@ -34,6 +34,10 @@ export default {
|
|
|
34
34
|
type: Boolean,
|
|
35
35
|
default: false
|
|
36
36
|
},
|
|
37
|
+
selectAnyLevel: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
default: false
|
|
40
|
+
},
|
|
37
41
|
isStreet: {
|
|
38
42
|
type: Boolean,
|
|
39
43
|
default: false
|
|
@@ -56,20 +60,29 @@ export default {
|
|
|
56
60
|
areaTree: []
|
|
57
61
|
}
|
|
58
62
|
},
|
|
63
|
+
watch: {
|
|
64
|
+
selectAnyLevel() {
|
|
65
|
+
// 开关运行中切换时复用对应缓存,否则按新模式重新请求地区树。
|
|
66
|
+
if (window[this.cacheKey]) {
|
|
67
|
+
this.areaTree = window[this.cacheKey]
|
|
68
|
+
} else {
|
|
69
|
+
this.getAreaTree()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
59
73
|
computed: {
|
|
60
|
-
isChinaUrban() {
|
|
61
|
-
return this.isUrban && this.countryId === 'CHN'
|
|
62
|
-
},
|
|
63
74
|
propsAs() {
|
|
64
75
|
let _prop = {
|
|
65
76
|
value: 'regionNo',
|
|
66
77
|
label: 'regionName',
|
|
67
78
|
children: 'children',
|
|
68
79
|
expandTrigger: 'hover',
|
|
69
|
-
|
|
70
|
-
checkStrictly: this.isChinaUrban || !this.isUrban
|
|
80
|
+
checkStrictly: !this.isUrban
|
|
71
81
|
}
|
|
72
|
-
|
|
82
|
+
const props = Object.assign(_prop, this.props)
|
|
83
|
+
// 任意级模式必须允许非叶子节点被选中,避免调用方覆盖后出现不可选层级。
|
|
84
|
+
if (this.selectAnyLevel) props.checkStrictly = true
|
|
85
|
+
return props
|
|
73
86
|
},
|
|
74
87
|
areaKey() {
|
|
75
88
|
return this.countryId === 'CHN' ? '_china_area_tree_' : `_${this.countryId}_area_tree_`
|
|
@@ -81,8 +94,9 @@ export default {
|
|
|
81
94
|
return this.countryId === 'CHN' ? '_china_street_tree_' : `_${this.countryId}_street_tree_`
|
|
82
95
|
},
|
|
83
96
|
cacheKey() {
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
const modeKey = this.isUrban ? this.urbanKey : this.isStreet ? this.streetKey : this.areaKey
|
|
98
|
+
// 两种模式的 disabled 标记及树深度不同,不能共享已处理的选项树。
|
|
99
|
+
return this.selectAnyLevel ? `${modeKey}_select_any_level_` : modeKey
|
|
86
100
|
}
|
|
87
101
|
},
|
|
88
102
|
mounted() {
|
|
@@ -94,25 +108,28 @@ export default {
|
|
|
94
108
|
},
|
|
95
109
|
methods: {
|
|
96
110
|
getAreaTree() {
|
|
111
|
+
const cacheKey = this.cacheKey
|
|
112
|
+
const isUrban = this.isUrban
|
|
113
|
+
const isStreet = this.isStreet
|
|
114
|
+
const selectAnyLevel = this.selectAnyLevel
|
|
97
115
|
this.$axios
|
|
98
116
|
.get(
|
|
99
117
|
`/bems/1.0/area?t=${Math.random()}`,
|
|
100
|
-
|
|
118
|
+
// 任意级模式需要请求下级地区;默认模式保持原先的请求范围。
|
|
119
|
+
{ countryId: this.countryId, isEnable: '1', resultType: 2, queryLeave4: isStreet || selectAnyLevel },
|
|
101
120
|
{ loading: false, noMsg: true }
|
|
102
121
|
)
|
|
103
122
|
.then(({ data }) => {
|
|
104
123
|
if (data) {
|
|
105
124
|
let result = JSON.parse(JSON.stringify(data))
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.areaTree = window[this.areaKey]
|
|
115
|
-
}
|
|
125
|
+
// 旧城市模式仍在二级截断;任意级模式统一保留接口返回的完整树。
|
|
126
|
+
window[cacheKey] = selectAnyLevel
|
|
127
|
+
? treeFn(result, true)
|
|
128
|
+
: isUrban
|
|
129
|
+
? isUrbanFn(result)
|
|
130
|
+
: treeFn(result)
|
|
131
|
+
// 若请求期间切换了模式,不用旧请求覆盖当前显示。
|
|
132
|
+
if (cacheKey === this.cacheKey) this.areaTree = window[cacheKey]
|
|
116
133
|
}
|
|
117
134
|
})
|
|
118
135
|
},
|
|
@@ -122,36 +139,28 @@ export default {
|
|
|
122
139
|
}
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
const treeFn = (data) => {
|
|
142
|
+
const treeFn = (data, selectAnyLevel = false) => {
|
|
126
143
|
return data?.map((res) => {
|
|
127
|
-
|
|
144
|
+
// 默认仍只开放原先可选的层级;开关开启时保留接口已有的禁用状态。
|
|
145
|
+
if (!selectAnyLevel && !['3', '4', '5'].includes(res.regionLevel) && res.hasChildren) {
|
|
128
146
|
res.disabled = true
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
if (!res.children?.length) {
|
|
132
150
|
res.children = null
|
|
133
151
|
} else {
|
|
134
|
-
res.children = treeFn(res.children)
|
|
152
|
+
res.children = treeFn(res.children, selectAnyLevel)
|
|
135
153
|
}
|
|
136
154
|
return res
|
|
137
155
|
})
|
|
138
156
|
}
|
|
139
157
|
|
|
140
|
-
const isUrbanFn = (data
|
|
158
|
+
const isUrbanFn = (data) => {
|
|
141
159
|
return data?.map((res) => {
|
|
142
|
-
|
|
143
|
-
// 直辖市只保留一级节点,直接返回接口原有的两位 regionNo,不伪造下级编码。
|
|
144
|
-
const isMunicipality =
|
|
145
|
-
isChinaUrban && regionLevel === '1' && ['11', '12', '31', '50'].includes(String(res.regionNo))
|
|
146
|
-
if (isChinaUrban && regionLevel === '1' && !isMunicipality) {
|
|
147
|
-
// 普通省份仅用于展开;checkStrictly 模式下禁用父节点不影响子节点选择。
|
|
148
|
-
res.disabled = true
|
|
149
|
-
}
|
|
150
|
-
if (isMunicipality || (!isChinaUrban && regionLevel === '2') || !res.children?.length) {
|
|
160
|
+
if (res.regionLevel === '2' || !res.children?.length) {
|
|
151
161
|
res.children = null
|
|
152
162
|
} else {
|
|
153
|
-
|
|
154
|
-
res.children = isUrbanFn(res.children, isChinaUrban)
|
|
163
|
+
res.children = isUrbanFn(res.children)
|
|
155
164
|
}
|
|
156
165
|
return res
|
|
157
166
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-table-column v-if="!bigData" v-bind="
|
|
2
|
+
<el-table-column v-if="!bigData" v-bind="resolvedColumnAttrs">
|
|
3
3
|
<OperateBtns slot-scope="{ row, $index }" :btn-list="btnList" :row="row" @command="(c) => $emit(c, row, $index)" />
|
|
4
4
|
</el-table-column>
|
|
5
|
-
<ux-table-column v-else :field="$attrs.prop" :title="$attrs.label" v-bind="
|
|
5
|
+
<ux-table-column v-else :field="$attrs.prop" :title="$attrs.label" v-bind="resolvedColumnAttrs">
|
|
6
6
|
<OperateBtns slot-scope="{ row, $index }" :btn-list="btnList" :row="row" @command="(c) => $emit(c, row, $index)" />
|
|
7
7
|
</ux-table-column>
|
|
8
8
|
</template>
|
|
@@ -10,17 +10,143 @@
|
|
|
10
10
|
<script>
|
|
11
11
|
// import { $lc } from '../../utils/i18n/index'
|
|
12
12
|
import OperateBtns from './OperateBtns.vue'
|
|
13
|
+
|
|
14
|
+
const DEFAULT_OPERATE_COLUMN_MIN_WIDTH = 80
|
|
15
|
+
|
|
13
16
|
export default {
|
|
14
17
|
name: 'TableOperateColumn',
|
|
15
18
|
components: {
|
|
16
19
|
OperateBtns
|
|
17
20
|
},
|
|
18
|
-
inject: {
|
|
21
|
+
inject: {
|
|
22
|
+
bigData: { default: false },
|
|
23
|
+
n20TableContext: { default: null }
|
|
24
|
+
},
|
|
19
25
|
props: {
|
|
20
26
|
btnList: {
|
|
21
27
|
type: Array,
|
|
22
28
|
default: () => []
|
|
23
29
|
}
|
|
30
|
+
},
|
|
31
|
+
computed: {
|
|
32
|
+
resolvedColumnAttrs() {
|
|
33
|
+
const attrs = { ...this.$attrs }
|
|
34
|
+
const hasWidth =
|
|
35
|
+
Object.prototype.hasOwnProperty.call(attrs, 'width') &&
|
|
36
|
+
attrs.width !== null &&
|
|
37
|
+
attrs.width !== undefined &&
|
|
38
|
+
attrs.width !== ''
|
|
39
|
+
if (hasWidth) {
|
|
40
|
+
return attrs
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const minWidthKey = ['minWidth', 'min-width'].find((key) => {
|
|
44
|
+
const value = attrs[key]
|
|
45
|
+
return Object.prototype.hasOwnProperty.call(attrs, key) && value !== null && value !== undefined && value !== ''
|
|
46
|
+
})
|
|
47
|
+
const calculatedWidth = this.calcOperateColumnWidth()
|
|
48
|
+
if (minWidthKey) {
|
|
49
|
+
const minWidth = this.parsePixelWidth(attrs[minWidthKey])
|
|
50
|
+
if (minWidth !== null) {
|
|
51
|
+
attrs[minWidthKey] = Math.max(calculatedWidth, minWidth)
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// 避免 Element/UX Table 将大量剩余空间分配给操作列。
|
|
55
|
+
attrs.width = calculatedWidth
|
|
56
|
+
}
|
|
57
|
+
return attrs
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
methods: {
|
|
61
|
+
measureOperateText(text) {
|
|
62
|
+
const normalizedText = text === undefined || text === null ? '' : String(text)
|
|
63
|
+
if (!normalizedText) return 0
|
|
64
|
+
|
|
65
|
+
const fallbackWidth = Array.from(normalizedText).reduce(
|
|
66
|
+
(width, char) => width + (/[⺀-鿿]/.test(char) ? 14 : 8),
|
|
67
|
+
0
|
|
68
|
+
)
|
|
69
|
+
if (typeof document === 'undefined') return fallbackWidth
|
|
70
|
+
|
|
71
|
+
const canvas = document.createElement('canvas')
|
|
72
|
+
const context = canvas.getContext('2d')
|
|
73
|
+
if (!context) return fallbackWidth
|
|
74
|
+
|
|
75
|
+
context.font = '14px PingFang SC, Microsoft YaHei, sans-serif'
|
|
76
|
+
return Math.max(context.measureText(normalizedText).width, fallbackWidth)
|
|
77
|
+
},
|
|
78
|
+
parsePixelWidth(value) {
|
|
79
|
+
if (typeof value === 'number') {
|
|
80
|
+
return Number.isFinite(value) && value >= 0 ? value : null
|
|
81
|
+
}
|
|
82
|
+
if (typeof value !== 'string') return null
|
|
83
|
+
|
|
84
|
+
const normalizedValue = value.trim()
|
|
85
|
+
if (!/^\d+(\.\d+)?(px)?$/.test(normalizedValue)) return null
|
|
86
|
+
const parsedValue = Number.parseFloat(normalizedValue)
|
|
87
|
+
return Number.isFinite(parsedValue) ? parsedValue : null
|
|
88
|
+
},
|
|
89
|
+
calcOperateColumnWidth() {
|
|
90
|
+
const CELL_HORIZONTAL_PADDING = this.bigData ? 20 : 16
|
|
91
|
+
// OperateBtns 使用行内元素,除 6px margin 外还会产生约 4px 的模板空白。
|
|
92
|
+
const BUTTON_GAP = 10
|
|
93
|
+
const CHILDREN_ARROW_WIDTH = 20
|
|
94
|
+
const MORE_BUTTON_WIDTH = 16
|
|
95
|
+
const buttons = (this.btnList || []).filter((button) => button && button.isHas !== false)
|
|
96
|
+
const getButtonWidth = (button) => {
|
|
97
|
+
let width = this.measureOperateText(button.label)
|
|
98
|
+
if (button.children && button.children.length > 0) {
|
|
99
|
+
width += CHILDREN_ARROW_WIDTH
|
|
100
|
+
}
|
|
101
|
+
return width
|
|
102
|
+
}
|
|
103
|
+
const buttonWidths = buttons.map(getButtonWidth)
|
|
104
|
+
|
|
105
|
+
if (buttonWidths.length === 0) {
|
|
106
|
+
return DEFAULT_OPERATE_COLUMN_MIN_WIDTH
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const calcLayoutWidth = (inlineWidths, hasMoreButton = false) => {
|
|
110
|
+
const itemCount = inlineWidths.length + (hasMoreButton ? 1 : 0)
|
|
111
|
+
const contentWidth = inlineWidths.reduce((total, width) => total + width, 0)
|
|
112
|
+
const moreButtonWidth = hasMoreButton ? MORE_BUTTON_WIDTH : 0
|
|
113
|
+
const gapWidth = Math.max(itemCount - 1, 0) * BUTTON_GAP
|
|
114
|
+
return contentWidth + moreButtonWidth + gapWidth + CELL_HORIZONTAL_PADDING
|
|
115
|
+
}
|
|
116
|
+
const rows = this.n20TableContext?.getData?.() || []
|
|
117
|
+
if (rows.length > 0) {
|
|
118
|
+
const maxRowWidth = rows.reduce((maxWidth, row) => {
|
|
119
|
+
const visibleButtonWidths = buttons
|
|
120
|
+
.filter((button) => this.hasButton(button.isHas, row))
|
|
121
|
+
.map(getButtonWidth)
|
|
122
|
+
const rowWidth = visibleButtonWidths.length > 3
|
|
123
|
+
? calcLayoutWidth(visibleButtonWidths.slice(0, 2), true)
|
|
124
|
+
: calcLayoutWidth(visibleButtonWidths)
|
|
125
|
+
return Math.max(maxWidth, rowWidth)
|
|
126
|
+
}, 0)
|
|
127
|
+
return Math.max(DEFAULT_OPERATE_COLUMN_MIN_WIDTH, Math.ceil(maxRowWidth))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sortedButtonWidths = [...buttonWidths].sort((a, b) => b - a)
|
|
131
|
+
const directButtonsWidth = calcLayoutWidth(sortedButtonWidths.slice(0, 3))
|
|
132
|
+
const collapsedButtonsWidth =
|
|
133
|
+
sortedButtonWidths.length > 3 ? calcLayoutWidth(sortedButtonWidths.slice(0, 2), true) : 0
|
|
134
|
+
|
|
135
|
+
return Math.max(
|
|
136
|
+
DEFAULT_OPERATE_COLUMN_MIN_WIDTH,
|
|
137
|
+
Math.ceil(directButtonsWidth),
|
|
138
|
+
Math.ceil(collapsedButtonsWidth)
|
|
139
|
+
)
|
|
140
|
+
},
|
|
141
|
+
hasButton(isHas, row) {
|
|
142
|
+
if (isHas === undefined || isHas === null) return true
|
|
143
|
+
if (typeof isHas === 'boolean') return isHas
|
|
144
|
+
if (typeof isHas === 'function') return isHas(row)
|
|
145
|
+
if (typeof isHas === 'string' || Array.isArray(isHas)) {
|
|
146
|
+
return typeof this.$has === 'function' ? this.$has(isHas) : true
|
|
147
|
+
}
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
24
150
|
}
|
|
25
151
|
}
|
|
26
152
|
</script>
|