n20-common-lib 3.0.98 → 3.0.99
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
|
@@ -313,38 +313,24 @@ export default {
|
|
|
313
313
|
return this.model
|
|
314
314
|
},
|
|
315
315
|
activeClass(item) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return this.model[item.value] !== null &&
|
|
321
|
-
this.model[item.value] !== undefined &&
|
|
322
|
-
this.model[item.value] !== ''
|
|
323
|
-
? this.prefixCls + '-active'
|
|
324
|
-
: ''
|
|
325
|
-
}
|
|
316
|
+
// 判断值是否有效(非空)
|
|
317
|
+
const hasValue = (val) => {
|
|
318
|
+
if (Array.isArray(val)) return val.length > 0
|
|
319
|
+
return val !== null && val !== undefined && val !== ''
|
|
326
320
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
case 'daterange':
|
|
339
|
-
case 'datetimerange':
|
|
340
|
-
case ' monthrange':
|
|
341
|
-
flag = this.model[item.startDate] && this.model[item.endDate] ? this.prefixCls + '-active' : ''
|
|
342
|
-
break
|
|
343
|
-
default:
|
|
344
|
-
flag = getFlag()
|
|
345
|
-
break
|
|
321
|
+
|
|
322
|
+
// 判断范围类型是否有效(起止值都存在)
|
|
323
|
+
const hasRange = (start, end) => hasValue(this.model[start]) && hasValue(this.model[end])
|
|
324
|
+
|
|
325
|
+
const rangeTypes = ['daterange', 'datetimerange', 'monthrange']
|
|
326
|
+
|
|
327
|
+
if (item.type === 'numberrange') {
|
|
328
|
+
return hasRange(item.startValue, item.endValue) ? this.prefixCls + '-active' : ''
|
|
329
|
+
}
|
|
330
|
+
if (rangeTypes.includes(item.type)) {
|
|
331
|
+
return hasRange(item.startDate, item.endDate) ? this.prefixCls + '-active' : ''
|
|
346
332
|
}
|
|
347
|
-
return
|
|
333
|
+
return hasValue(this.model[item.value]) ? this.prefixCls + '-active' : ''
|
|
348
334
|
},
|
|
349
335
|
handleClose(item) {
|
|
350
336
|
switch (item.type) {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
@mouseenter="hoveredReportId = report.id"
|
|
21
21
|
@mouseleave="hoveredReportId = null"
|
|
22
22
|
>
|
|
23
|
-
<i class="
|
|
23
|
+
<i :class="getChartTypeIcon(report.chartType)" class="report-icon"></i>
|
|
24
24
|
<span v-if="editingReportId !== report.id" class="report-name">{{ report.name }}</span>
|
|
25
25
|
<input
|
|
26
26
|
v-else
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
<!-- 内容头部工具栏 -->
|
|
71
71
|
<div class="main-toolbar">
|
|
72
72
|
<div class="toolbar-left">
|
|
73
|
-
<div
|
|
73
|
+
<div>
|
|
74
74
|
<h2 class="report-title">{{ currentReport.name }}</h2>
|
|
75
75
|
<div class="add-to-home-btn" @click="handleAddToHome">
|
|
76
76
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
@@ -195,13 +195,16 @@
|
|
|
195
195
|
<!-- 右侧维度配置 -->
|
|
196
196
|
<div class="pivot-sidebar-right" :class="{ collapsed: isRightSidebarCollapsed }">
|
|
197
197
|
<div class="sidebar-header">
|
|
198
|
-
<
|
|
198
|
+
<template v-if="!isRightSidebarCollapsed">
|
|
199
|
+
<i class="v3-icon-move-right sidebar-header-collapse" @click="handleToggleRightSidebar"></i>
|
|
200
|
+
<span class="sidebar-title">维度配置</span>
|
|
201
|
+
</template>
|
|
199
202
|
</div>
|
|
200
203
|
|
|
201
204
|
<div class="config-form">
|
|
202
205
|
<!-- 报表名称 -->
|
|
203
206
|
<div class="form-item">
|
|
204
|
-
<label class="form-label"
|
|
207
|
+
<label class="form-label">报表标题</label>
|
|
205
208
|
|
|
206
209
|
<div class="input-wrapper">
|
|
207
210
|
<input class="form-input" type="text" v-model="currentReport.name" placeholder="请输入报表名称" />
|
|
@@ -226,10 +229,7 @@
|
|
|
226
229
|
<label class="form-label">展示形式</label>
|
|
227
230
|
<div class="select-wrapper" @click="showChartTypeDropdown = !showChartTypeDropdown">
|
|
228
231
|
<div class="select-value">
|
|
229
|
-
<
|
|
230
|
-
<rect x="1" y="3" width="12" height="8" rx="1" stroke="currentColor" stroke-width="1.5" />
|
|
231
|
-
<path d="M3 8V11M7 6V11M11 9V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
232
|
-
</svg>
|
|
232
|
+
<i :class="getChartTypeIcon(currentReport.chartType)" class="report-icon"></i>
|
|
233
233
|
<span>{{ getChartTypeLabel(currentReport.chartType) }}</span>
|
|
234
234
|
</div>
|
|
235
235
|
<svg class="select-arrow" width="10" height="10" viewBox="0 0 10 10" fill="none">
|
|
@@ -255,6 +255,8 @@
|
|
|
255
255
|
</div>
|
|
256
256
|
</div>
|
|
257
257
|
|
|
258
|
+
<div class="form-divider"></div>
|
|
259
|
+
|
|
258
260
|
<!-- 分组维度 -->
|
|
259
261
|
<div class="form-item">
|
|
260
262
|
<label class="form-label">
|
|
@@ -309,6 +311,8 @@
|
|
|
309
311
|
</div>
|
|
310
312
|
</div>
|
|
311
313
|
|
|
314
|
+
<div class="form-divider"></div>
|
|
315
|
+
|
|
312
316
|
<!-- 计算指标 -->
|
|
313
317
|
<div class="form-item">
|
|
314
318
|
<div class="form-label-row">
|
|
@@ -897,6 +901,17 @@ export default {
|
|
|
897
901
|
return chart ? chart.label : '柱状图'
|
|
898
902
|
},
|
|
899
903
|
|
|
904
|
+
// 获取图表类型图标
|
|
905
|
+
getChartTypeIcon(type) {
|
|
906
|
+
const iconMap = {
|
|
907
|
+
bar: 'v3-icon-chart-histogram',
|
|
908
|
+
line: 'v3-icon-chart-line',
|
|
909
|
+
pie: 'v3-icon-chart-pie',
|
|
910
|
+
horizontalBar: 'v3-icon-histogram'
|
|
911
|
+
}
|
|
912
|
+
return iconMap[type] || 'v3-icon-chart-histogram'
|
|
913
|
+
},
|
|
914
|
+
|
|
900
915
|
// 获取聚合类型标签
|
|
901
916
|
getAggregateLabel(type) {
|
|
902
917
|
const agg = this.aggregateTypes.find((t) => t.value === type)
|
|
@@ -1113,11 +1128,14 @@ export default {
|
|
|
1113
1128
|
.el-dialog {
|
|
1114
1129
|
box-sizing: border-box;
|
|
1115
1130
|
margin-top: 3vh !important;
|
|
1116
|
-
max-height:
|
|
1131
|
+
max-height: 94vh;
|
|
1117
1132
|
display: flex;
|
|
1118
1133
|
flex-direction: column;
|
|
1119
1134
|
.el-dialog__body {
|
|
1120
1135
|
padding: 0;
|
|
1136
|
+
flex: 1;
|
|
1137
|
+
min-height: 0;
|
|
1138
|
+
overflow: hidden;
|
|
1121
1139
|
}
|
|
1122
1140
|
}
|
|
1123
1141
|
}
|
|
@@ -1125,7 +1143,6 @@ export default {
|
|
|
1125
1143
|
display: flex;
|
|
1126
1144
|
background: #fff;
|
|
1127
1145
|
height: 100%;
|
|
1128
|
-
min-height: 800px;
|
|
1129
1146
|
border-radius: 8px;
|
|
1130
1147
|
overflow: hidden;
|
|
1131
1148
|
font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
@@ -1447,6 +1464,8 @@ export default {
|
|
|
1447
1464
|
flex: 1;
|
|
1448
1465
|
padding: 16px;
|
|
1449
1466
|
overflow: hidden;
|
|
1467
|
+
border: 1px solid #e5e6eb;
|
|
1468
|
+
border-radius: 4px;
|
|
1450
1469
|
}
|
|
1451
1470
|
|
|
1452
1471
|
.chart-view,
|
|
@@ -1459,7 +1478,6 @@ export default {
|
|
|
1459
1478
|
.chart-container {
|
|
1460
1479
|
width: 100%;
|
|
1461
1480
|
height: 100%;
|
|
1462
|
-
min-height: 500px;
|
|
1463
1481
|
}
|
|
1464
1482
|
|
|
1465
1483
|
.table-container {
|
|
@@ -1577,14 +1595,34 @@ export default {
|
|
|
1577
1595
|
line-height: 22px; /* 157.143% */
|
|
1578
1596
|
}
|
|
1579
1597
|
|
|
1598
|
+
.pivot-sidebar-right .sidebar-header-collapse {
|
|
1599
|
+
font-size: 14px;
|
|
1600
|
+
color: #86909c;
|
|
1601
|
+
cursor: pointer;
|
|
1602
|
+
transition: color 0.2s;
|
|
1603
|
+
&:hover {
|
|
1604
|
+
color: #1d2129;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1580
1608
|
.config-form {
|
|
1581
1609
|
flex: 1;
|
|
1582
|
-
padding: 16px;
|
|
1610
|
+
padding: 0 16px;
|
|
1583
1611
|
overflow-y: auto;
|
|
1612
|
+
|
|
1613
|
+
.form-item:first-child {
|
|
1614
|
+
margin-top: 16px;
|
|
1615
|
+
}
|
|
1584
1616
|
}
|
|
1585
1617
|
|
|
1586
1618
|
.form-item {
|
|
1587
|
-
margin-bottom:
|
|
1619
|
+
margin-bottom: 16px;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.form-divider {
|
|
1623
|
+
height: 1px;
|
|
1624
|
+
background-color: #e5e6eb;
|
|
1625
|
+
margin: 16px 0;
|
|
1588
1626
|
}
|
|
1589
1627
|
|
|
1590
1628
|
.form-label {
|
|
@@ -1669,8 +1707,8 @@ export default {
|
|
|
1669
1707
|
position: relative;
|
|
1670
1708
|
display: flex;
|
|
1671
1709
|
align-items: center;
|
|
1672
|
-
|
|
1673
|
-
padding:
|
|
1710
|
+
gap: 4px;
|
|
1711
|
+
padding: 4px 12px;
|
|
1674
1712
|
background: #fff;
|
|
1675
1713
|
border: 1px solid #e5e6eb;
|
|
1676
1714
|
border-radius: 4px;
|
|
@@ -1689,6 +1727,17 @@ export default {
|
|
|
1689
1727
|
gap: 8px;
|
|
1690
1728
|
font-size: 14px;
|
|
1691
1729
|
color: #1d2129;
|
|
1730
|
+
.report-icon {
|
|
1731
|
+
width: 16px;
|
|
1732
|
+
height: 16px;
|
|
1733
|
+
font-size: 16px;
|
|
1734
|
+
line-height: 16px;
|
|
1735
|
+
display: inline-flex;
|
|
1736
|
+
align-items: center;
|
|
1737
|
+
justify-content: center;
|
|
1738
|
+
margin-right: 0;
|
|
1739
|
+
color: #007aff;
|
|
1740
|
+
}
|
|
1692
1741
|
}
|
|
1693
1742
|
|
|
1694
1743
|
.select-value .placeholder {
|
|
@@ -1696,7 +1745,7 @@ export default {
|
|
|
1696
1745
|
}
|
|
1697
1746
|
|
|
1698
1747
|
.select-arrow {
|
|
1699
|
-
color: #
|
|
1748
|
+
color: #4e5969;
|
|
1700
1749
|
transition: transform 0.2s;
|
|
1701
1750
|
}
|
|
1702
1751
|
|
|
@@ -95,10 +95,7 @@
|
|
|
95
95
|
<div slot="prefix">
|
|
96
96
|
<slot name="prefix"></slot>
|
|
97
97
|
</div>
|
|
98
|
-
<template
|
|
99
|
-
v-for="filter in slotFilters"
|
|
100
|
-
#[filter.slotName]="{ model }"
|
|
101
|
-
>
|
|
98
|
+
<template v-for="filter in slotFiltersWithValue" #[filter.slotName]="{ model }">
|
|
102
99
|
<slot
|
|
103
100
|
:name="filter.slotName"
|
|
104
101
|
:model="model"
|
|
@@ -106,6 +103,15 @@
|
|
|
106
103
|
:input="(val) => handleSlotInput(filter.value, val)"
|
|
107
104
|
></slot>
|
|
108
105
|
</template>
|
|
106
|
+
<!-- 无 value 的 slot(如 startDate/endDate 多值绑定),传递 searchValue 让父组件自行管理 -->
|
|
107
|
+
<template v-for="filter in slotFiltersWithoutValue" #[filter.slotName]="{ model }">
|
|
108
|
+
<slot
|
|
109
|
+
:name="filter.slotName"
|
|
110
|
+
:model="model"
|
|
111
|
+
:search-value="searchValue"
|
|
112
|
+
:update-search-value="updateSearchValue"
|
|
113
|
+
></slot>
|
|
114
|
+
</template>
|
|
109
115
|
<div slot="suffix">
|
|
110
116
|
<slot name="suffix"></slot>
|
|
111
117
|
</div>
|
|
@@ -146,10 +152,7 @@
|
|
|
146
152
|
:check-ids="form.keyIds"
|
|
147
153
|
@saveCheckData="saveCheckData"
|
|
148
154
|
>
|
|
149
|
-
<template
|
|
150
|
-
v-for="filter in slotFilters"
|
|
151
|
-
#[filter.slotName]="{ model }"
|
|
152
|
-
>
|
|
155
|
+
<template v-for="filter in slotFiltersWithValue" #[filter.slotName]="{ model }">
|
|
153
156
|
<slot
|
|
154
157
|
:name="filter.slotName"
|
|
155
158
|
:model="model"
|
|
@@ -157,6 +160,15 @@
|
|
|
157
160
|
:input="(val) => (searchForm[filter.value] = val)"
|
|
158
161
|
></slot>
|
|
159
162
|
</template>
|
|
163
|
+
<!-- 无 value 的 slot(如 startDate/endDate 多值绑定),传递 searchForm 让父组件自行管理 -->
|
|
164
|
+
<template v-for="filter in slotFiltersWithoutValue" #[filter.slotName]="{ model }">
|
|
165
|
+
<slot
|
|
166
|
+
:name="filter.slotName"
|
|
167
|
+
:model="model"
|
|
168
|
+
:search-value="searchForm"
|
|
169
|
+
:update-search-value="(updates) => Object.assign(searchForm, updates)"
|
|
170
|
+
></slot>
|
|
171
|
+
</template>
|
|
160
172
|
</cl-advanced-filter>
|
|
161
173
|
<advancedQuery
|
|
162
174
|
v-else
|
|
@@ -166,10 +178,7 @@
|
|
|
166
178
|
:filter-id="filterId"
|
|
167
179
|
:buss-id="bussId"
|
|
168
180
|
>
|
|
169
|
-
<template
|
|
170
|
-
v-for="filter in slotFilters"
|
|
171
|
-
#[filter.slotName]="{ model, value, input, disabled }"
|
|
172
|
-
>
|
|
181
|
+
<template v-for="filter in slotFiltersWithValue" #[filter.slotName]="{ model, value, input, disabled }">
|
|
173
182
|
<slot
|
|
174
183
|
:name="filter.slotName"
|
|
175
184
|
:model="model"
|
|
@@ -178,6 +187,16 @@
|
|
|
178
187
|
:disabled="disabled"
|
|
179
188
|
></slot>
|
|
180
189
|
</template>
|
|
190
|
+
<!-- 无 value 的 slot(如 startDate/endDate 多值绑定),传递 searchForm 让父组件自行管理 -->
|
|
191
|
+
<template v-for="filter in slotFiltersWithoutValue" #[filter.slotName]="{ model, value, input, disabled }">
|
|
192
|
+
<slot
|
|
193
|
+
:name="filter.slotName"
|
|
194
|
+
:model="model"
|
|
195
|
+
:search-value="searchForm"
|
|
196
|
+
:update-search-value="(updates) => Object.assign(searchForm, updates)"
|
|
197
|
+
:disabled="disabled"
|
|
198
|
+
></slot>
|
|
199
|
+
</template>
|
|
181
200
|
</advancedQuery>
|
|
182
201
|
</el-form>
|
|
183
202
|
<div slot="footer" class="flex-box flex-c flex-v page-button-shadow">
|
|
@@ -271,15 +290,22 @@ export default {
|
|
|
271
290
|
},
|
|
272
291
|
// 筛选出类型为 slot 且在父组件提供了对应作用域插槽的筛选项
|
|
273
292
|
slotFilters() {
|
|
274
|
-
return this.filterList.filter(
|
|
275
|
-
|
|
276
|
-
|
|
293
|
+
return this.filterList.filter((item) => item.type === 'slot' && this.$scopedSlots[item.slotName])
|
|
294
|
+
},
|
|
295
|
+
// 筛选出有 value 字段的 slot 类型筛选项(用于 searchValue 初始化和值传递)
|
|
296
|
+
slotFiltersWithValue() {
|
|
297
|
+
return this.slotFilters.filter((item) => item.value)
|
|
298
|
+
},
|
|
299
|
+
// 筛选出无 value 字段的 slot 类型筛选项(如 startDate/endDate 多值绑定)
|
|
300
|
+
slotFiltersWithoutValue() {
|
|
301
|
+
return this.slotFilters.filter((item) => !item.value)
|
|
277
302
|
},
|
|
278
303
|
getInitialSearchValue() {
|
|
279
304
|
const obj = {}
|
|
280
|
-
// 遍历 filterList,为每个 type 为 slot
|
|
305
|
+
// 遍历 filterList,为每个 type 为 slot 且有 value 字段的项初始化值
|
|
306
|
+
// 注意:跳过无 value 的 slot(如 startDate/endDate 多值绑定),这些由父组件在 slot 内部管理
|
|
281
307
|
this.filterList.forEach((item) => {
|
|
282
|
-
if (item.type === 'slot') {
|
|
308
|
+
if (item.type === 'slot' && item.value) {
|
|
283
309
|
obj[item.value] = undefined
|
|
284
310
|
}
|
|
285
311
|
})
|
|
@@ -340,8 +366,14 @@ export default {
|
|
|
340
366
|
this.searchValue = { ...this.getInitialSearchValue, ...this.initialValue }
|
|
341
367
|
},
|
|
342
368
|
methods: {
|
|
369
|
+
updateSearchValue(updates) {
|
|
370
|
+
console.log(updates, 'updates')
|
|
371
|
+
Object.assign(searchValue, updates)
|
|
372
|
+
},
|
|
343
373
|
// 处理 slot 类型字段的输入事件,同时更新 searchValue 和 initialValue
|
|
344
374
|
handleSlotInput(fieldName, val) {
|
|
375
|
+
// 防御性检查:如果 fieldName 无效,不执行任何操作
|
|
376
|
+
if (!fieldName) return
|
|
345
377
|
// 更新 searchValue
|
|
346
378
|
this.searchValue[fieldName] = val
|
|
347
379
|
// 同步更新 initialValue,确保双向绑定生效
|
|
@@ -410,12 +442,16 @@ export default {
|
|
|
410
442
|
})
|
|
411
443
|
this.searchValue = { ...this.getInitialSearchValue, ...preserved }
|
|
412
444
|
// 直接构建 payload,绕过 filterObj 避免 initialValue 重新注入
|
|
413
|
-
this.$emit(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
445
|
+
this.$emit(
|
|
446
|
+
'filter',
|
|
447
|
+
{
|
|
448
|
+
conditionGroups: this.conditionGroups,
|
|
449
|
+
searchValue: { ...this.searchValue },
|
|
450
|
+
viewId: this.selectItem ? this.selectItem.viewId : null,
|
|
451
|
+
viewType: this.selectItem ? this.selectItem.viewType : null
|
|
452
|
+
},
|
|
453
|
+
'clear'
|
|
454
|
+
)
|
|
419
455
|
},
|
|
420
456
|
isEnter() {
|
|
421
457
|
this.$rulesValidateForm('ruleValidate', (valid) => {
|