stellar-ui-plus 1.22.26 → 1.22.28

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.
@@ -25,6 +25,7 @@
25
25
  | `monthCount` | 渲染的月份个数 | `number` | `12` | - | - |
26
26
  | `weekendColor` | 周末颜色,默认跟随主题色 | `string` | `` | - | - |
27
27
  | `signs` | 日历标签 | `{[key:string]:SignType}` | `{}` | - | - |
28
+ | `showScrollbar` | 是否显示滚动条 | `boolean` | `true` | - | `1.22.27` |
28
29
 
29
30
 
30
31
  #### Events
@@ -1,29 +1,30 @@
1
- import type { PropType } from 'vue'
2
- import type { DateType, SignType } from './date'
1
+ import type { PropType } from 'vue';
2
+ import type { DateType, SignType } from './date';
3
3
 
4
4
  export default {
5
- title: { type: String, default: () => '日期选择' },
6
- showTitle: { type: Boolean, default: () => true },
7
- list: { type: Array as PropType<DateType[]>, default: () => [] },
8
- mode: { type: String, default: () => 'single' },
9
- startText: { type: String, default: () => '开始' },
10
- endText: { type: String, default: () => '结束' },
11
- color: { type: String, default: () => '' },
12
- minDate: { type: [String, Number, Date], default: () => 0 },
13
- maxDate: { type: [String, Number, Date], default: () => 0 },
14
- maxCount: { type: [Number, String], default: () => 0 },
15
- formatter: { type: String, default: () => 'YYYY-MM-DD' },
16
- showMark: { type: Boolean, default: () => true },
17
- readonly: { type: Boolean, default: () => false },
18
- maxRange: { type: Number, default: () => null },
19
- rangePrompt: { type: String, default: () => null },
20
- showRangePrompt: { type: Boolean, default: () => true },
21
- allowSameDay: { type: Boolean, default: () => false },
22
- showConfirm: { type: Boolean, default: () => true },
23
- width: { type: [Number, String], default: () => '100%' },
24
- height: { type: [Number, String], default: () => '100%' },
25
- signs: { type: Object as PropType<{ [key: string]: SignType }>, default: () => ({}) },
26
- defaultDate: { type: [String, Number, Date], default: () => 0 },
27
- monthCount: { type: Number, default: () => 12 },
28
- weekendColor: { type: String, default: () => '' },
29
- }
5
+ title: { type: String, default: () => '日期选择' },
6
+ showTitle: { type: Boolean, default: () => true },
7
+ list: { type: Array as PropType<DateType[]>, default: () => [] },
8
+ mode: { type: String, default: () => 'single' },
9
+ startText: { type: String, default: () => '开始' },
10
+ endText: { type: String, default: () => '结束' },
11
+ color: { type: String, default: () => '' },
12
+ minDate: { type: [String, Number, Date], default: () => 0 },
13
+ maxDate: { type: [String, Number, Date], default: () => 0 },
14
+ maxCount: { type: [Number, String], default: () => 0 },
15
+ formatter: { type: String, default: () => 'YYYY-MM-DD' },
16
+ showMark: { type: Boolean, default: () => true },
17
+ readonly: { type: Boolean, default: () => false },
18
+ maxRange: { type: Number, default: () => null },
19
+ rangePrompt: { type: String, default: () => null },
20
+ showRangePrompt: { type: Boolean, default: () => true },
21
+ allowSameDay: { type: Boolean, default: () => false },
22
+ showConfirm: { type: Boolean, default: () => true },
23
+ width: { type: [Number, String], default: () => '100%' },
24
+ height: { type: [Number, String], default: () => '100%' },
25
+ signs: { type: Object as PropType<{ [key: string]: SignType }>, default: () => ({}) },
26
+ defaultDate: { type: [String, Number, Date], default: () => 0 },
27
+ monthCount: { type: Number, default: () => 12 },
28
+ weekendColor: { type: String, default: () => '' },
29
+ showScrollbar: { type: Boolean, default: () => true },
30
+ };
@@ -160,6 +160,13 @@
160
160
  "type": "{[key:string]:SignType}",
161
161
  "default": "{}"
162
162
  },
163
+ {
164
+ "name": "showScrollbar",
165
+ "description": "是否显示滚动条",
166
+ "type": "boolean",
167
+ "default": "true",
168
+ "version": "1.22.27"
169
+ },
163
170
  {
164
171
  "name": "[event]select",
165
172
  "description": "选择日期时触发",
@@ -198,4 +205,4 @@
198
205
  ]
199
206
  }
200
207
  ]
201
- }
208
+ }
@@ -215,7 +215,15 @@ const onScroll = (e: any) => {
215
215
  </view>
216
216
  </view>
217
217
  </view>
218
- <scroll-view class="date-content" :class="{ 'show-confirm': cmpShowConfirm, 'show-title': showTitle }" scroll-y :scroll-top="contentScrollTop" @scroll="onScroll">
218
+ <scroll-view
219
+ class="date-content"
220
+ :class="{ 'show-confirm': cmpShowConfirm, 'show-title': showTitle }"
221
+ scroll-y
222
+ :scroll-top="contentScrollTop"
223
+ @scroll="onScroll"
224
+ :show-scrollbar="showScrollbar"
225
+ enhanced
226
+ >
219
227
  <view class="month-item" v-for="m in cmpDates.monthDatas" :key="m.key" :id="`month-${m.key}`">
220
228
  <view class="month-bg" v-if="showMark">
221
229
  {{ m.month }}
@@ -304,7 +312,6 @@ const onScroll = (e: any) => {
304
312
 
305
313
  .date-content {
306
314
  width: 100%;
307
- overflow-y: auto;
308
315
  height: calc(100% - var(--font-size-80, 80rpx));
309
316
  padding-bottom: 12rpx;
310
317
 
@@ -4,6 +4,8 @@
4
4
 
5
5
  ---$
6
6
 
7
+ - 由于性能不如原生组件,若数据量较多时推荐使用虚拟列表的形式渲染或者直接使用原生组件
8
+
7
9
  ### 代码演示
8
10
 
9
11
  #### 基础用法
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { useSlots, computed, ref, nextTick, markRaw, type CSSProperties } from 'vue';
2
+ import { useSlots, computed, ref, nextTick, onMounted, onUnmounted, type CSSProperties } from 'vue';
3
3
  import { useColorStore } from '../../store/color';
4
4
  let { getColor } = useColorStore();
5
5
  import utils from '../../utils/utils';
@@ -19,147 +19,150 @@ const props = defineProps(propsData);
19
19
  const emits = defineEmits<CheckboxEmits>();
20
20
  const slots = useSlots();
21
21
 
22
- // 静态配置使用 markRaw 避免不必要的响应式
23
- const staticConfig = markRaw({
24
- defaultIconSizeMultiplier: 0.8,
25
- borderWidth: 2,
26
- });
27
-
28
- const Parent = useInject<{ props: Required<CheckboxGroupProps>; updateValue: (value: any[]) => void }>(CHECKBOX_KEY);
22
+ const Parent = useInject<{
23
+ props: Required<CheckboxGroupProps>;
24
+ updateValue: (value: any[]) => void;
25
+ registerChild: () => number;
26
+ unregisterChild: () => void;
27
+ }>(CHECKBOX_KEY);
29
28
  const parentProps = computed(() => Parent?.parent?.props);
30
29
 
31
- // 基础计算属性
32
- const cmpReadonly = computed(() => getDefaultData('readonly', false));
33
- const cmpShape = computed(() => getDefaultData('shape', 'circle'));
34
- const cmpIconSize = computed(() => getDefaultData('iconSize', 36));
35
- const cmpCheckedColor = computed(() => getDefaultData('checkedColor', getColor().steThemeColor));
36
- const cmpTextPosition = computed(() => getDefaultData('textPosition', 'right'));
37
- const cmpTextSize = computed(() => getDefaultData('textSize', 28));
38
- const cmpTextInactiveColor = computed(() => getDefaultData('textInactiveColor', '#000000'));
39
- const cmpTextActiveColor = computed(() => getDefaultData('textActiveColor', '#000000'));
40
- const cmpTextDisabled = computed(() => getDefaultData('textDisabled', false));
41
- const cmpMarginLeft = computed(() => getDefaultData('marginLeft', '0'));
42
- const cmpMarginRight = computed(() => getDefaultData('marginRight', '0'));
43
- const cmpColumnGap = computed(() => getDefaultData('columnGap', '16'));
30
+ // 记录当前组件在 group 中的索引
31
+ const childIndex = ref<number>(-1);
44
32
 
45
- const cmpSlotProps = computed(() => ({
46
- checked: cmpChecked.value,
47
- disabled: cmpDisabled.value,
48
- readonly: cmpReadonly.value,
49
- }));
33
+ onMounted(() => {
34
+ if (Parent?.parent?.registerChild) {
35
+ childIndex.value = Parent.parent.registerChild();
36
+ }
37
+ });
50
38
 
51
- // 拆分计算属性依赖 - 静态样式
52
- const baseRootStyle = computed(() => ({
53
- fontSize: `var(--font-size-${cmpTextSize.value},${utils.formatPx(cmpTextSize.value)})`,
54
- marginLeft: utils.formatPx(cmpMarginLeft.value),
55
- marginRight: utils.formatPx(cmpMarginRight.value),
56
- }));
39
+ onUnmounted(() => {
40
+ if (Parent?.parent?.unregisterChild) {
41
+ Parent.parent.unregisterChild();
42
+ }
43
+ });
57
44
 
58
- // 拆分计算属性依赖 - 动态样式
59
- const dynamicRootStyle = computed(() => ({
60
- color: cmpChecked.value ? cmpTextActiveColor.value : cmpTextInactiveColor.value,
61
- flexDirection: cmpTextPosition.value == 'right' ? 'row' : 'row-reverse',
62
- }));
45
+ // 🚀 优化: 缓存 themeColor,避免每次调用 getColor()
46
+ const themeColor = getColor().steThemeColor;
63
47
 
64
- // 拆分计算属性依赖 - 交互样式
65
- const interactiveRootStyle = computed(() => {
66
- const style: CSSProperties = {};
48
+ // 强制更新选中状态
49
+ let num = ref(1);
67
50
 
68
- // #ifdef H5
69
- if (cmpDisabled.value || cmpReadonly.value) {
70
- style['cursor'] = 'not-allowed';
71
- } else if (cmpTextDisabled.value) {
72
- style['cursor'] = 'default';
73
- } else {
74
- style['cursor'] = 'pointer';
75
- }
76
- // #endif
51
+ // 🚀 优化: 只保留必要的 computed
52
+ const cmpChecked = computed(() => {
53
+ let v = num.value && parentProps.value ? parentProps.value.modelValue.includes(props.name) : props.modelValue;
54
+ return v;
55
+ });
77
56
 
78
- if (cmpTextDisabled.value) {
79
- style['pointerEvents'] = 'none';
57
+ const cmpDisabled = computed(() => {
58
+ let disabled = getDefaultData('disabled', false);
59
+ // 限制最大可选数
60
+ if (parentProps.value && parentProps.value.max) {
61
+ if (!cmpChecked.value && parentProps.value.modelValue.length >= parentProps.value.max) {
62
+ disabled = true;
63
+ }
80
64
  }
81
-
82
- return style;
65
+ return disabled;
83
66
  });
84
67
 
85
- // 合并所有根样式
86
- const cmpRootStyle = computed(() => ({
87
- ...baseRootStyle.value,
88
- ...dynamicRootStyle.value,
89
- ...interactiveRootStyle.value,
90
- }));
91
-
92
- // 图标样式(相对简单,保持原样)
93
- const cmpIconStyle = computed(() => ({
94
- marginRight: utils.formatPx(cmpColumnGap.value),
95
- }));
68
+ // 🚀 优化: 合并所有样式计算,直接调用 getDefaultData
69
+ const cmpRootStyle = computed(() => {
70
+ const textSize = getDefaultData('textSize', 28);
71
+ const textPosition = getDefaultData('textPosition', 'right');
72
+ const readonly = getDefaultData('readonly', false);
73
+ const textDisabled = getDefaultData('textDisabled', false);
96
74
 
97
- // 拆分输入框样式 - 基础样式
98
- const baseInputStyle = computed(() => ({
99
- borderRadius: cmpShape.value == 'circle' ? '50%' : '0',
100
- width: `var(--font-size-${cmpIconSize.value},${utils.formatPx(cmpIconSize.value)})`,
101
- height: `var(--font-size-${cmpIconSize.value},${utils.formatPx(cmpIconSize.value)})`,
102
- lineHeight: `var(--font-size-${cmpIconSize.value},${utils.formatPx(cmpIconSize.value)})`,
103
- columnGap: slots.default ? undefined : 0,
104
- }));
75
+ let marginLeft = getDefaultData('marginLeft', '0');
76
+ let marginRight = getDefaultData('marginRight', '0');
105
77
 
106
- // 拆分输入框样式 - 动态样式
107
- const dynamicInputStyle = computed(() => ({
108
- border: `${utils.formatPx(staticConfig.borderWidth)} solid ${cmpChecked.value ? cmpCheckedColor.value : '#BBBBBB'}`,
109
- background: cmpChecked.value ? cmpCheckedColor.value : '#FFFFFF',
110
- }));
78
+ // 如果在 checkbox-group 中,并且不是第一个元素,自动应用间距
79
+ if (parentProps.value && childIndex.value > 0 && marginLeft === '0') {
80
+ const direction = parentProps.value.direction || 'column';
111
81
 
112
- // 拆分输入框样式 - 禁用状态样式
113
- const disabledInputStyle = computed(() => {
114
- if (!cmpDisabled.value) return {};
82
+ if (direction === 'row') {
83
+ // 横向排列时,设置左间距 16rpx
84
+ marginLeft = '16';
85
+ }
86
+ }
115
87
 
116
- return {
117
- background: '#eeeeee',
118
- borderColor: '#bbbbbb',
88
+ const style: CSSProperties = {
89
+ fontSize: `var(--font-size-${textSize},${utils.formatPx(textSize)})`,
90
+ color: cmpChecked.value ? getDefaultData('textActiveColor', '#000000') : getDefaultData('textInactiveColor', '#000000'),
91
+ flexDirection: textPosition === 'right' ? 'row' : 'row-reverse',
92
+ marginLeft: utils.formatPx(marginLeft),
93
+ marginRight: utils.formatPx(marginRight),
119
94
  };
120
- });
121
95
 
122
- // 拆分输入框样式 - 交互样式
123
- const interactiveInputStyle = computed(() => {
124
- const style: CSSProperties = {};
96
+ // 纵向排列时,设置上间距 16rpx
97
+ if (parentProps.value && childIndex.value > 0) {
98
+ const direction = parentProps.value.direction || 'column';
99
+ if (direction === 'column') {
100
+ style.marginTop = '16rpx';
101
+ }
102
+ }
125
103
 
126
104
  // #ifdef H5
127
- if (cmpDisabled.value || cmpReadonly.value) {
105
+ if (cmpDisabled.value || readonly) {
128
106
  style['cursor'] = 'not-allowed';
107
+ } else if (textDisabled) {
108
+ style['cursor'] = 'default';
129
109
  } else {
130
110
  style['cursor'] = 'pointer';
131
111
  }
132
112
  // #endif
133
113
 
114
+ if (textDisabled) {
115
+ style['pointerEvents'] = 'none';
116
+ }
117
+
134
118
  return style;
135
119
  });
136
120
 
137
- // 合并所有输入框样式
138
- const cmpInputStyle = computed(() => ({
139
- ...baseInputStyle.value,
140
- ...dynamicInputStyle.value,
141
- ...disabledInputStyle.value,
142
- ...interactiveInputStyle.value,
143
- }));
144
-
145
- const cmpChecked = computed(() => {
146
- let v = num.value && parentProps.value ? parentProps.value.modelValue.includes(props.name) : props.modelValue;
147
- return v;
121
+ const cmpIconStyle = computed(() => {
122
+ return {
123
+ marginRight: utils.formatPx(getDefaultData('columnGap', '16')),
124
+ } as CSSProperties;
148
125
  });
149
126
 
150
- const cmpDisabled = computed(() => {
151
- let disabled = getDefaultData('disabled', false);
152
- // 限制最大可选数
153
- if (parentProps.value && parentProps.value.max) {
154
- if (!cmpChecked.value && parentProps.value.modelValue.length >= parentProps.value.max) {
155
- disabled = true;
156
- }
127
+ const cmpInputStyle = computed(() => {
128
+ const shape = getDefaultData('shape', 'circle');
129
+ const iconSize = getDefaultData('iconSize', 36);
130
+ const checkedColor = getDefaultData('checkedColor', themeColor);
131
+ const readonly = getDefaultData('readonly', false);
132
+ const checked = cmpChecked.value;
133
+
134
+ const style: CSSProperties = {
135
+ borderRadius: shape === 'circle' ? '50%' : '0',
136
+ border: `${utils.formatPx(2)} solid ${checked ? checkedColor : '#BBBBBB'}`,
137
+ background: checked ? checkedColor : '#FFFFFF',
138
+ width: `var(--font-size-${iconSize},${utils.formatPx(iconSize)})`,
139
+ height: `var(--font-size-${iconSize},${utils.formatPx(iconSize)})`,
140
+ lineHeight: `var(--font-size-${iconSize},${utils.formatPx(iconSize)})`,
141
+ };
142
+
143
+ // #ifdef H5
144
+ style['cursor'] = cmpDisabled.value || readonly ? 'not-allowed' : 'pointer';
145
+ // #endif
146
+
147
+ if (cmpDisabled.value) {
148
+ style['background'] = '#eeeeee';
149
+ style['borderColor'] = '#bbbbbb';
157
150
  }
158
- return disabled;
151
+
152
+ // 在没有使用插槽内容时去掉边距
153
+ if (!slots.default) {
154
+ style['columnGap'] = '0';
155
+ }
156
+
157
+ return style;
159
158
  });
160
159
 
161
- // 强制更新选中状态
162
- let num = ref(1);
160
+ // 🚀 优化: slotProps 也直接计算
161
+ const cmpSlotProps = computed(() => ({
162
+ checked: cmpChecked.value,
163
+ disabled: cmpDisabled.value,
164
+ readonly: getDefaultData('readonly', false),
165
+ }));
163
166
 
164
167
  // 批处理更新相关
165
168
  const isBatchUpdating = ref(false);
@@ -195,7 +198,9 @@ const batchedUpdate = async (updateFn: () => Promise<void>) => {
195
198
 
196
199
  // 实际的点击处理逻辑
197
200
  const handleClick = async () => {
198
- if (cmpDisabled.value || cmpReadonly.value) {
201
+ const readonly = getDefaultData('readonly', false);
202
+
203
+ if (cmpDisabled.value || readonly) {
199
204
  return;
200
205
  }
201
206
 
@@ -240,20 +245,11 @@ const click = () => {
240
245
  batchedUpdate(handleClick);
241
246
  };
242
247
 
243
- type PropsKeyTypee = keyof typeof props;
244
- function getDefaultData(key: PropsKeyTypee, value: any) {
245
- try {
246
- if (Object.prototype.hasOwnProperty.call(props, key)) {
247
- if (props[key]) {
248
- return props[key];
249
- } else {
250
- return value;
251
- }
252
- }
253
- } catch (e) {
254
- return value;
255
- }
256
- }
248
+ type PropsKeyType = keyof typeof props;
249
+ const getDefaultData = <T,>(key: PropsKeyType, defaultValue: T): T => {
250
+ const value = props[key];
251
+ return value !== undefined && value !== '' ? (value as T) : defaultValue;
252
+ };
257
253
  </script>
258
254
 
259
255
  <template>
@@ -261,7 +257,7 @@ function getDefaultData(key: PropsKeyTypee, value: any) {
261
257
  <view class="icon" :style="[cmpIconStyle]">
262
258
  <slot name="icon" :slotProps="cmpSlotProps">
263
259
  <view class="input-icon" :style="[cmpInputStyle]">
264
- <ste-icon v-if="cmpChecked && cmpIconSize" :size="cmpIconSize * staticConfig.defaultIconSizeMultiplier" code="&#xe67a;" :color="cmpDisabled ? '#bbbbbb' : '#fff'" bold />
260
+ <ste-icon v-if="cmpChecked" :size="getDefaultData('iconSize', 36) * 0.8" code="&#xe67a;" :color="cmpDisabled ? '#bbbbbb' : '#fff'" bold />
265
261
  </view>
266
262
  </slot>
267
263
  </view>
@@ -1,12 +1,26 @@
1
1
  <script lang="ts" setup>
2
- import { computed, type CSSProperties } from 'vue';
2
+ import { computed, ref, type CSSProperties } from 'vue';
3
3
  import propsData, { checkboxGroupEmits } from './props';
4
4
  import { useProvide } from '../../utils/mixin';
5
5
  import { CHECKBOX_KEY } from '../ste-checkbox/props';
6
6
  const props = defineProps(propsData);
7
7
  const emits = defineEmits(checkboxGroupEmits);
8
8
 
9
- useProvide(CHECKBOX_KEY, 'ste-checkbox')({ props, updateValue });
9
+ const childrenCount = ref(0);
10
+
11
+ // 注册子组件,返回索引
12
+ function registerChild() {
13
+ const index = childrenCount.value;
14
+ childrenCount.value++;
15
+ return index;
16
+ }
17
+
18
+ // 卸载时减少计数
19
+ function unregisterChild() {
20
+ childrenCount.value--;
21
+ }
22
+
23
+ useProvide(CHECKBOX_KEY, 'ste-checkbox')({ props, updateValue, registerChild, unregisterChild });
10
24
 
11
25
  const cmpRootStyle = computed(() => {
12
26
  return {
@@ -21,14 +35,13 @@ function updateValue(value: any[]) {
21
35
  </script>
22
36
 
23
37
  <template>
24
- <view class="ste-checkbox-group-root" :style="[cmpRootStyle]">
38
+ <view class="ste-checkbox-group-root" :style="[cmpRootStyle]" :class="[direction]">
25
39
  <slot></slot>
26
40
  </view>
27
41
  </template>
28
42
 
29
43
  <style lang="scss" scoped>
30
44
  .ste-checkbox-group-root {
31
- display: grid;
32
- row-gap: 16rpx;
45
+ display: flex;
33
46
  }
34
47
  </style>
@@ -129,17 +129,50 @@
129
129
  ```html
130
130
  <template>
131
131
  <ste-input placeholder="请输入内容" confirmType="next" rootClass="root-my-input" shape="line">
132
- <view slot="prefix" style="margin-right: 28rpx">
133
- <ste-icon code="&#xe68c;" size="28" />
134
- <text>文本</text>
135
- </view>
136
- <view slot="suffix">
137
- <ste-icon code="&#xe672;" size="28" />
138
- </view>
132
+ <template v-slot:prefix>
133
+ <view style="margin-right: 28rpx">
134
+ <ste-icon code="&#xe68c;" size="28" />
135
+ <text>文本</text>
136
+ </view>
137
+ </template>
138
+ <template v-slot:suffix>
139
+ <view>
140
+ <ste-icon code="&#xe672;" size="28" />
141
+ </view>
142
+ </template>
139
143
  </ste-input>
140
144
  </template>
141
145
  ```
142
146
 
147
+ #### 前后插槽-验证码
148
+
149
+ ```html
150
+ <template>
151
+ <ste-input placeholder="请输入验证码" confirmType="next" rootClass="root-my-input" shape="line">
152
+ <template v-slot:suffix>
153
+ <view>
154
+ <ste-button :mode="100" :round="false" @click="getCode" :disabled="count > 0">{{ count <= 0 ? '获取验证码' : count + '秒后获取' }}</ste-button>
155
+ </view>
156
+ </template>
157
+ </ste-input>
158
+ </template>
159
+ <script lang="ts" setup>
160
+ import { ref } from 'vue';
161
+ const count = ref(0);
162
+ let codeTimer: any;
163
+
164
+ function getCode() {
165
+ count.value = 20;
166
+ codeTimer = setInterval(() => {
167
+ if (count.value <= 0) {
168
+ clearInterval(codeTimer);
169
+ }
170
+ count.value--;
171
+ }, 1000);
172
+ }
173
+ </script>
174
+ ```
175
+
143
176
  #### 前置过滤
144
177
 
145
178
  ```html
@@ -55,7 +55,8 @@
55
55
  // letter-spacing: 10rpx;
56
56
 
57
57
  &.textarea {
58
- height: 160rpx;
58
+ min-height: 160rpx;
59
+ height: 100%;
59
60
  }
60
61
  }
61
62
 
@@ -169,6 +169,7 @@ function inputClick() {
169
169
  :placeholder-class="placeholderClass"
170
170
  :confirm-type="confirmType"
171
171
  :cursor-spacing="cursorSpacing"
172
+ :maxlength="maxlength"
172
173
  @input="onInput"
173
174
  @focus="onFocus"
174
175
  @blur="onBlur"
@@ -197,6 +198,7 @@ function inputClick() {
197
198
  :placeholder-style="placeholderStyle"
198
199
  :placeholder-class="placeholderClass"
199
200
  :confirm-type="confirmType"
201
+ :maxlength="maxlength"
200
202
  @input="onInput"
201
203
  @focus="onFocus"
202
204
  @blur="onBlur"
@@ -16,6 +16,10 @@ const loginInfoProps = {
16
16
  loginSrc: { type: String, default: '' },
17
17
  /** 未登录标题 */
18
18
  loginTitle: { type: String, default: '' },
19
+ showTitleIcon: {
20
+ type: Boolean as PropType<boolean>,
21
+ default: false,
22
+ },
19
23
  };
20
24
 
21
25
  export const loginInfoEmits = {
@@ -13,7 +13,7 @@
13
13
  <text>{{ subTitle }}</text>
14
14
  </view>
15
15
  </view>
16
- <view class="user-info user-info-login" @click="loginTitleClick" v-else>
16
+ <view class="user-info user-info-login" @click="loginTitleClick" v-if="loginStatus == 0 || showTitleIcon">
17
17
  <view class="name">
18
18
  <text class="name-text">{{ loginTitle }}</text>
19
19
  <ste-icon code="&#xe674;" size="28" color="#000" marginLeft="8" :fontFamily="fontFamily" />
@@ -76,13 +76,11 @@ const loginTitleClick = () => {
76
76
  margin-right: 24rpx;
77
77
  }
78
78
  .user-info-login {
79
- justify-content: center;
79
+ // justify-content: center;
80
+ flex: 0 !important;
80
81
  }
81
82
  .user-info {
82
83
  flex: 1;
83
- display: flex;
84
- flex-direction: column;
85
- justify-content: space-between;
86
84
 
87
85
  .name {
88
86
  display: flex;
@@ -200,6 +200,7 @@ function handleClick() {
200
200
  display: flex;
201
201
  align-items: center;
202
202
  overflow-x: hidden;
203
+ overflow-y: hidden;
203
204
  justify-content: flex-start;
204
205
  flex: 1;
205
206
  height: 100%;
@@ -4,63 +4,9 @@
4
4
 
5
5
  ---$
6
6
 
7
- ### 代码演示
8
-
9
- 后面的演示代码中涉及到的变量和方法都使用该代码
7
+ - 由于性能不如原生组件,若数据量较多时推荐使用虚拟列表的形式渲染或者直接使用原生组件
10
8
 
11
- ```html
12
- <script lang="ts" setup>
13
- import { reactive } from 'vue';
14
- const val = reactive({
15
- value1: 'a',
16
- value2: 'a',
17
- value3: 'a',
18
- value4: 'a',
19
- value5: 'a',
20
- value6: 'a',
21
- value7: 'a',
22
- value8: '',
23
- value9: 'a',
24
- value10: 'a',
25
- value11: '',
26
- value12: '',
27
- value13: '',
28
- value14: '',
29
- value15: '',
30
- });
31
- function click1(value: any, suspend: () => void, next: () => void) {
32
- uni.showToast({
33
- icon: 'none',
34
- title: `点击:${value} 复选框的值`,
35
- });
36
- suspend(); // 阻止操作
37
-
38
- setTimeout(() => {
39
- next(); // 异步操作后,执行操作
40
- }, 1500);
41
- }
42
- function click2(value: any, suspend: () => void, _next: any, stop: () => void) {
43
- uni.showToast({
44
- icon: 'none',
45
- title: `点击:${value} 复选框的值`,
46
- });
47
- suspend(); // 阻止操作
48
- setTimeout(() => {
49
- // 异步操作后,停止操作
50
- stop();
51
- }, 200);
52
- }
53
-
54
- function change(value: any) {
55
- setTimeout(() => {
56
- uni.showToast({
57
- icon: 'none',
58
- title: `改变:${value} 复选框的值`,
59
- });
60
- }, 1000);
61
- }
62
- </script>
63
- ```
9
+ ### 代码演示
64
10
 
65
11
  #### 基础用法
66
12
 
@@ -62,13 +62,17 @@
62
62
  position: relative;
63
63
 
64
64
  .ste-step-line {
65
- background: var(---line-color);
65
+ // background: var(---line-color);
66
66
  display: inline-block;
67
67
  position: absolute;
68
68
  height: 2rpx;
69
69
  left: 50%;
70
70
  right: -50%;
71
71
  top: 20rpx;
72
+ border-top-width: 2rpx;
73
+ border-top-color: var(---line-color);
74
+ border-top-style: solid;
75
+ box-sizing: border-box;
72
76
  }
73
77
  .ste-step-icon.is-icon {
74
78
  border-radius: 50%;
@@ -27,6 +27,7 @@ const stepIndex = ref(0);
27
27
  const cmpDirection = computed(() => parentProps.direction);
28
28
  const cmpDot = computed(() => parentProps.dot);
29
29
  const cmpReverse = computed(() => parentProps.reverse);
30
+ const cmpLineStyle = computed(() => parentProps.lineStyle);
30
31
 
31
32
  watch(
32
33
  () => parent?.internalChildren,
@@ -90,18 +91,17 @@ function clickStep() {
90
91
  <template>
91
92
  <view class="ste-step" :class="[`ste-step-${cmpDirection}`, cmpReverse ? 'reverse' : '']" :style="[cmpRootStyle]">
92
93
  <view class="ste-step-head" :class="cmpDot ? 'head-is-dot' : ''">
93
- <view class="ste-step-line" v-if="stepIndex < childrenLen"></view>
94
+ <view class="ste-step-line" v-if="stepIndex < childrenLen" :style="{ borderTopStyle: cmpLineStyle }"></view>
94
95
  <view class="ste-step-icon" :class="[!cmpDot ? ($slots.icon || icon ? '' : cmpStatusObj.icon ? 'is-icon' : 'is-text') : 'is-dot']" @click="clickStep">
95
- <template v-if="$slots.icon">
96
- <slot name="icon"></slot>
97
- </template>
98
- <template v-else-if="cmpDot"></template>
99
- <template v-else-if="cmpStatusObj.icon">
100
- <ste-icon class="ste-step-icon-inner" :code="cmpStatusObj.icon" :size="icon ? 40 : 20" :color="cmpStatusObj.color"></ste-icon>
101
- </template>
102
- <template v-else>
103
- <view class="ste-step-inner">{{ stepIndex }}</view>
104
- </template>
96
+ <slot name="icon">
97
+ <template v-if="cmpDot"></template>
98
+ <template v-else-if="cmpStatusObj.icon">
99
+ <ste-icon class="ste-step-icon-inner" :code="cmpStatusObj.icon" :size="icon ? 40 : 20" :color="cmpStatusObj.color"></ste-icon>
100
+ </template>
101
+ <template v-else>
102
+ <view class="ste-step-inner">{{ stepIndex }}</view>
103
+ </template>
104
+ </slot>
105
105
  </view>
106
106
  </view>
107
107
  <view class="ste-step-content" :class="[`ste-step-content-${parentProps.direction}`]">
@@ -112,8 +112,7 @@ function clickStep() {
112
112
  <slot name="title"></slot>
113
113
  </view>
114
114
  <view class="ste-step-desc" v-if="description || $slots.description">
115
- <span v-if="!$slots.description" v-html="description"></span>
116
- <slot name="description"></slot>
115
+ <slot name="description"><span v-html="description"></span></slot>
117
116
  </view>
118
117
  </view>
119
118
  </view>
@@ -4,7 +4,8 @@
4
4
  | `active` | 步骤进度 | `number` | `0` | - | - |
5
5
  | `direction` | 步骤条方向 | `boolean` | `row` | `row`:横向<br/>`column`:竖向 | - |
6
6
  | `dot` | 点状步骤条 | `boolean` | `false` | - | - |
7
- | `reverse` | 方向反转 | `boolean` | `false` | - | - |
7
+ | `reverse` | 方向反转 | `boolean` | `false` | - | `1.22.27` |
8
+ | `lineStyle` | 线条样式(支持border-style全部属性) | `string` | `solid` | - | `1.22.27` |
8
9
 
9
10
 
10
11
  #### Events
@@ -1,19 +1,20 @@
1
- import type { ExtractPropTypes, PropType } from 'vue'
1
+ import type { ExtractPropTypes, PropType } from 'vue';
2
2
 
3
- type directionType = 'row' | 'column'
3
+ type directionType = 'row' | 'column';
4
4
 
5
- export const STEPS_KEY = Symbol('ste-steps')
5
+ export const STEPS_KEY = Symbol('ste-steps');
6
6
  const stepsProps = {
7
- active: { type: Number, default: 0 },
8
- direction: { type: String as PropType<directionType>, default: 'row' },
9
- dot: { type: Boolean, default: false },
10
- reverse: { type: Boolean, default: false },
11
- }
7
+ active: { type: Number, default: 0 },
8
+ direction: { type: String as PropType<directionType>, default: 'row' },
9
+ dot: { type: Boolean, default: false },
10
+ reverse: { type: Boolean, default: false },
11
+ lineStyle: { type: String, default: 'solid' },
12
+ };
12
13
 
13
- export type StepsProps = ExtractPropTypes<typeof stepsProps>
14
+ export type StepsProps = ExtractPropTypes<typeof stepsProps>;
14
15
 
15
16
  export const stepsEmits = {
16
- 'click-step': (index: number) => typeof index === 'number',
17
- }
17
+ 'click-step': (index: number) => typeof index === 'number',
18
+ };
18
19
 
19
- export default stepsProps
20
+ export default stepsProps;
@@ -35,7 +35,15 @@
35
35
  "name": "reverse",
36
36
  "description": "方向反转",
37
37
  "type": "boolean",
38
- "default": "false"
38
+ "default": "false",
39
+ "version": "1.22.27"
40
+ },
41
+ {
42
+ "name": "lineStyle",
43
+ "description": "线条样式(支持border-style全部属性)",
44
+ "type": "string",
45
+ "default": "solid",
46
+ "version": "1.22.27"
39
47
  },
40
48
  {
41
49
  "name": "[event]clickStep",
@@ -42,7 +42,18 @@ useInject(SWIPER_KEY, { setTransform, setLinearScale });
42
42
  .ste-swiper-item-root {
43
43
  width: 100%;
44
44
  height: 100%;
45
- /* 添加此行以确保在循环播放时不会出现闪烁 */
45
+ /* 启用硬件加速优化 */
46
46
  will-change: transform;
47
+ /* 隐藏背面,避免翻转时闪烁 */
48
+ backface-visibility: hidden;
49
+ /* CSS containment 优化 */
50
+ contain: layout style paint;
51
+ /* 抗锯齿 */
52
+ -webkit-font-smoothing: antialiased;
53
+ -moz-osx-font-smoothing: grayscale;
54
+ /* 优化图像渲染 */
55
+ image-rendering: -webkit-optimize-contrast;
56
+ /* 确保子元素也使用硬件加速 */
57
+ transform-style: flat;
47
58
  }
48
59
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stellar-ui-plus",
3
- "version": "1.22.26",
3
+ "version": "1.22.28",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "license": "MIT",