stellar-ui-plus 1.22.28 → 1.22.30

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.
@@ -164,6 +164,16 @@ const cmpSlotProps = computed(() => ({
164
164
  readonly: getDefaultData('readonly', false),
165
165
  }));
166
166
 
167
+ const cmpIconMargin = computed(() => {
168
+ // #ifdef APP-PLUS
169
+ return 2;
170
+ // #endif
171
+
172
+ // #ifndef APP-PLUS
173
+ return 0;
174
+ // #endif
175
+ });
176
+
167
177
  // 批处理更新相关
168
178
  const isBatchUpdating = ref(false);
169
179
  const pendingUpdate = ref(false);
@@ -257,7 +267,7 @@ const getDefaultData = <T,>(key: PropsKeyType, defaultValue: T): T => {
257
267
  <view class="icon" :style="[cmpIconStyle]">
258
268
  <slot name="icon" :slotProps="cmpSlotProps">
259
269
  <view class="input-icon" :style="[cmpInputStyle]">
260
- <ste-icon v-if="cmpChecked" :size="getDefaultData('iconSize', 36) * 0.8" code="&#xe67a;" :color="cmpDisabled ? '#bbbbbb' : '#fff'" bold />
270
+ <ste-icon v-if="cmpChecked" :size="getDefaultData('iconSize', 36) * 0.8" code="&#xe67a;" :color="cmpDisabled ? '#bbbbbb' : '#fff'" bold :marginBottom="cmpIconMargin" />
261
271
  </view>
262
272
  </slot>
263
273
  </view>
@@ -47,13 +47,21 @@
47
47
  </view>
48
48
  <view class="btn-group">
49
49
  <view class="btn-item primary" v-for="(item, k) in primaryBtn" :key="k">
50
- <ste-button width="100%" :mode="300" @click="handleBtnClick(item, 'primary')" :round="item.round" :background="mainColor">
51
- <text class="btn-text">{{ item.title }}</text>
50
+ <ste-button
51
+ width="100%"
52
+ :mode="300"
53
+ @click="handleBtnClick(item, 'primary')"
54
+ :round="item.round"
55
+ :background="item.style?.background ? item.style?.background : mainColor"
56
+ :border-color="item.style?.borderColor"
57
+ :color="item.style?.color"
58
+ >
59
+ <text class="btn-text" :style="{ fontSize: item.style?.fontSize }">{{ item.title }}</text>
52
60
  </ste-button>
53
61
  </view>
54
62
  <view class="secondary-box" :class="[{ two: secondaryBtn.length === 2, one: secondaryBtn.length === 1 }]">
55
- <view v-for="(item, k) in secondaryBtn" :key="k" class="btn-item secondary">
56
- <text class="btn-text">{{ item.title }}</text>
63
+ <view v-for="(item, k) in secondaryBtn" :key="k" class="btn-item secondary" @click="handleBtnClick(item, 'secondary')">
64
+ <text class="btn-text" :style="{ color: item.style?.color, fontSize: item.style?.fontSize }">{{ item.title }}</text>
57
65
  </view>
58
66
  </view>
59
67
  </view>
@@ -618,6 +618,58 @@
618
618
  </template>
619
619
  ```
620
620
 
621
+ #### 合并单元格
622
+
623
+ 当某列的数据为数组类型时,该列会自动触发合并单元格模式,将数组中的每个元素作为一行展示。这在需要展示一对多关系的数据时非常有用。
624
+
625
+ **功能特点:**
626
+ - 自动检测数组类型数据
627
+ - 自动合并显示为多行
628
+ - 支持边框样式
629
+ - 自动统一行高,保持视觉一致
630
+
631
+ ```html
632
+ <script lang="ts" setup>
633
+ import { ref } from 'vue';
634
+ const rows = ref([
635
+ {
636
+ project: '入店时的寒暄语',
637
+ desc: [
638
+ '打招呼的声音很小,听不到,没有看着顾客一方等。',
639
+ '店员看着顾客一方,可以听到响亮且有朝气的问候声。',
640
+ '「感觉普通」店员看着顾客一方有问候,但是声音不响亮无朝气。',
641
+ ],
642
+ score: [5, 11, 1],
643
+ sum: 0,
644
+ },
645
+ {
646
+ project: '收银机前的引导',
647
+ desc: [
648
+ '・POS收银机前排队等待的顾客≦3人(不含正在结账的顾客)。',
649
+ '其他店员没有注意到有3位以上的顾客在排队,或收银的店员不向其他收银台引导。'
650
+ ],
651
+ score: [5, 1],
652
+ sum: 0,
653
+ },
654
+ ]);
655
+ </script>
656
+ <template>
657
+ <ste-table :data="rows" border>
658
+ <template v-slot="{ row }">
659
+ <ste-table-column label="评价项目" prop="project"></ste-table-column>
660
+ <ste-table-column label="评价标准" prop="desc"></ste-table-column>
661
+ <ste-table-column label="分值" prop="score" width="150"></ste-table-column>
662
+ <ste-table-column label="得分" prop="sum" width="150"></ste-table-column>
663
+ </template>
664
+ </ste-table>
665
+ </template>
666
+ ```
667
+
668
+ 在上面的示例中:
669
+ - `desc` 和 `score` 列的值是数组,会自动合并显示为多行
670
+ - `project` 和 `sum` 列的值是普通类型,会正常显示单行
671
+ - 建议配合 `border` 属性使用,可以更清晰地展示单元格边界
672
+
621
673
  ---$
622
674
 
623
675
  ### API
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { computed, ref, type CSSProperties, type Ref } from 'vue';
2
+ import { computed, ref, watch, type CSSProperties, type Ref } from 'vue';
3
3
  import type { Obj } from '../../types';
4
4
  import propsData from './props';
5
5
  import utils from '../../utils/utils';
@@ -9,6 +9,7 @@ import { TABLE_KEY, SELECTION_COLOR_CONFIG, type TableProps } from '../ste-table
9
9
  import CheckBoxIcon from './checkbox-icon.vue';
10
10
  import RadioIcon from './radio-icon.vue';
11
11
  import TablePopover from './table-popover.vue';
12
+ import SubTable from './sub-table.vue';
12
13
 
13
14
  const componentName = `ste-table-column`;
14
15
  defineOptions({
@@ -26,9 +27,21 @@ const parentProps = Parent.parent?.props as TableProps;
26
27
 
27
28
  const row = ref<Obj>({});
28
29
  const selectionIconColor = ref<typeof SELECTION_COLOR_CONFIG>(parentProps.selectionIconColor);
30
+ const rowSpan = ref(false);
29
31
 
30
32
  defineExpose({ row });
31
33
 
34
+ // 监听 row 数据变化,判断是否需要合并单元格
35
+ watch(
36
+ () => row.value[props.prop],
37
+ val => {
38
+ if (Array.isArray(val)) {
39
+ rowSpan.value = true;
40
+ }
41
+ },
42
+ { immediate: true }
43
+ );
44
+
32
45
  const cmpRootStyle = computed(() => {
33
46
  let style: CSSProperties = {};
34
47
  if (props.width) {
@@ -69,6 +82,10 @@ const cmpRootClass = computed(() => {
69
82
  classArr.push('popover');
70
83
  }
71
84
 
85
+ if (rowSpan.value) {
86
+ classArr.push('row-span');
87
+ }
88
+
72
89
  const cellClassParam = {
73
90
  column: props,
74
91
  columnIndex: row.value.colIndex,
@@ -103,6 +120,10 @@ const cmpReadonlyCheck = computed(() => {
103
120
  return false;
104
121
  });
105
122
 
123
+ const cmpBorder = computed(() => {
124
+ return parentProps.border;
125
+ });
126
+
106
127
  function changeCheck() {
107
128
  if (!cmpDisableCheck.value && !cmpReadonlyCheck.value) {
108
129
  parent?.handleCheck(row.value);
@@ -149,7 +170,8 @@ function cellClick(event: any) {
149
170
  </template>
150
171
  <template v-else>
151
172
  <slot v-if="row[prop] || !$slots.empty">
152
- <view class="cell-box">
173
+ <sub-table :rows="row[prop]" v-if="rowSpan" :border="cmpBorder" />
174
+ <view class="cell-box" v-else>
153
175
  <template v-if="!parentProps.isPopover">
154
176
  {{ cellText() }}
155
177
  </template>
@@ -0,0 +1,117 @@
1
+ <script lang="ts" setup>
2
+ import { ref, computed, onMounted, getCurrentInstance, type ComponentPublicInstance, type CSSProperties } from 'vue';
3
+ import utils from '../../utils/utils';
4
+
5
+ defineOptions({
6
+ name: 'ste-sub-table',
7
+ options: {
8
+ virtualHost: true,
9
+ },
10
+ });
11
+
12
+ const props = defineProps({
13
+ rows: {
14
+ type: [Array, null],
15
+ default: () => [],
16
+ },
17
+ border: {
18
+ type: [Boolean, null],
19
+ default: false,
20
+ },
21
+ });
22
+
23
+ const instance = getCurrentInstance() as unknown as ComponentPublicInstance;
24
+
25
+ const rowEls = ref<UniApp.NodeInfo[]>([]);
26
+
27
+ onMounted(() => {
28
+ utils.querySelector('.sub-table-root .row', instance, true).then(rec => {
29
+ rowEls.value = rec;
30
+ });
31
+ });
32
+
33
+ const cmpRootClass = computed(() => {
34
+ let classArr: string[] = [];
35
+ if (props.border) {
36
+ classArr.push('border');
37
+ }
38
+ return classArr.join(' ');
39
+ });
40
+
41
+ const cmpRowStyle = computed(() => {
42
+ let style: CSSProperties = {};
43
+ if (rowEls.value.length > 0) {
44
+ let maxHeight = findMaxInRange(
45
+ 0.5,
46
+ rowEls.value.map(e => e.height || 0)
47
+ );
48
+ if (maxHeight > 0) {
49
+ style.flexBasis = maxHeight + 'px';
50
+ style.flexGrow = 0;
51
+ }
52
+ }
53
+ return style;
54
+ });
55
+
56
+ function findMaxInRange(tolerance: number, numbers: number[] = []) {
57
+ if (numbers.length === 0) return 0;
58
+
59
+ let max = Math.max(...numbers);
60
+ let min = Math.min(...numbers);
61
+
62
+ // 如果最大值和最小值的差值在浮动范围内,返回 0
63
+ if (max - min <= tolerance) {
64
+ return 0;
65
+ }
66
+
67
+ // 返回最大值
68
+ return max;
69
+ }
70
+ </script>
71
+
72
+ <template>
73
+ <view class="sub-table-root" :class="[cmpRootClass]">
74
+ <view class="row" v-for="(v, i) in rows" :key="i" :style="[cmpRowStyle]">
75
+ <view class="cell">
76
+ {{ v }}
77
+ </view>
78
+ </view>
79
+ </view>
80
+ </template>
81
+
82
+ <style lang="scss" scoped>
83
+ @import './var.scss';
84
+ .sub-table-root {
85
+ width: 100%;
86
+ height: 100%;
87
+ display: flex;
88
+ flex-direction: column;
89
+
90
+ &.border {
91
+ .row {
92
+ .cell {
93
+ border-bottom: $default-border;
94
+ }
95
+ }
96
+ }
97
+
98
+ .row {
99
+ width: 100%;
100
+ display: flex;
101
+ justify-content: space-between;
102
+ flex: 1;
103
+ .cell {
104
+ @import './common.scss';
105
+ @include cell;
106
+
107
+ border-bottom: none;
108
+ padding: 24rpx 32rpx;
109
+ }
110
+ &:nth-last-child(1) {
111
+ .cell {
112
+ border-bottom: none;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stellar-ui-plus",
3
- "version": "1.22.28",
3
+ "version": "1.22.30",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "license": "MIT",