stellar-ui-plus 1.25.6 → 1.25.8
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/components/ste-app-update/method.ts +189 -20
- package/components/ste-app-update/ste-app-update.vue +160 -15
- package/components/ste-calendar/date.ts +10 -4
- package/components/ste-calendar/ste-calendar.vue +85 -12
- package/components/ste-calendar/useData.ts +1 -1
- package/components/ste-dropdown-menu/README.md +186 -1
- package/components/ste-dropdown-menu/ste-dropdown-menu.scss +8 -1
- package/components/ste-popup/ATTRIBUTES.md +3 -3
- package/components/ste-popup/ste-popup.easycom.json +138 -140
- package/components/ste-popup/ste-popup.vue +19 -23
- package/components/ste-radio/ATTRIBUTES.md +2 -2
- package/components/ste-radio/ste-radio.easycom.json +4 -4
- package/components/ste-select/README.md +0 -6
- package/components/ste-select-order/README.md +61 -0
- package/components/ste-select-order/config.json +5 -0
- package/components/ste-select-order/props.ts +30 -0
- package/components/ste-select-order/ste-select-order.easycom.json +73 -0
- package/components/ste-select-order/ste-select-order.vue +276 -0
- package/components/ste-table/ste-table.vue +7 -37
- package/package.json +2 -3
- package/utils/System.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { watch, onMounted, computed, nextTick, onUnmounted } from 'vue';
|
|
2
|
+
import { watch, onMounted, computed, nextTick, onUnmounted, ref, getCurrentInstance } from 'vue';
|
|
3
3
|
import { useColorStore } from '../../store/color';
|
|
4
4
|
let { getColor } = useColorStore();
|
|
5
5
|
import { formatDate, type DateType, type WeekType } from './date';
|
|
@@ -75,8 +75,81 @@ const clearViewTimer = () => {
|
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
// 根节点引用,用于可见性监听
|
|
79
|
+
const rootRef = ref<any>(null);
|
|
80
|
+
// 是否已可见(在视口内)
|
|
81
|
+
let isVisible = false;
|
|
82
|
+
// 待执行的滚动目标,组件不可见时暂存
|
|
83
|
+
let pendingScrollTop: number | null = null;
|
|
84
|
+
// 在 setup 顶层获取实例,供小程序端观察器使用
|
|
85
|
+
const instance = getCurrentInstance();
|
|
86
|
+
|
|
87
|
+
const doScroll = (top: number, _viewMonth: string) => {
|
|
88
|
+
if (scrollTop.value === top) {
|
|
89
|
+
viewMonth.value = _viewMonth;
|
|
90
|
+
initing.value = false;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
contentScrollTop.value = scrollTop.value;
|
|
94
|
+
nextTick(() => {
|
|
95
|
+
contentScrollTop.value = top;
|
|
96
|
+
scrollTop.value = top;
|
|
97
|
+
viewMonth.value = _viewMonth;
|
|
98
|
+
initing.value = false;
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let observer: any = null;
|
|
103
|
+
const startObserver = () => {
|
|
104
|
+
// #ifdef H5
|
|
105
|
+
if (typeof IntersectionObserver !== 'undefined' && rootRef.value) {
|
|
106
|
+
// uni-app H5 中 <view> 的 ref 直接是 DOM 元素
|
|
107
|
+
const el = rootRef.value.$el ?? rootRef.value;
|
|
108
|
+
observer = new IntersectionObserver(entries => {
|
|
109
|
+
const visible = entries[0]?.isIntersecting;
|
|
110
|
+
if (visible && !isVisible) {
|
|
111
|
+
isVisible = true;
|
|
112
|
+
if (pendingScrollTop !== null) {
|
|
113
|
+
const top = pendingScrollTop;
|
|
114
|
+
const _viewMonth = viewDate.value.format('YYYY-MM');
|
|
115
|
+
pendingScrollTop = null;
|
|
116
|
+
nextTick(() => doScroll(top, _viewMonth));
|
|
117
|
+
}
|
|
118
|
+
} else if (!visible) {
|
|
119
|
+
isVisible = false;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
observer.observe(el);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// #endif
|
|
126
|
+
// 小程序端使用 uni.createIntersectionObserver
|
|
127
|
+
try {
|
|
128
|
+
observer = uni.createIntersectionObserver(instance).relativeToViewport();
|
|
129
|
+
observer.observe('.ste-calendar-root', (res: any) => {
|
|
130
|
+
const visible = res.intersectionRatio > 0;
|
|
131
|
+
if (visible && !isVisible) {
|
|
132
|
+
isVisible = true;
|
|
133
|
+
if (pendingScrollTop !== null) {
|
|
134
|
+
const top = pendingScrollTop;
|
|
135
|
+
const _viewMonth = viewDate.value.format('YYYY-MM');
|
|
136
|
+
pendingScrollTop = null;
|
|
137
|
+
nextTick(() => doScroll(top, _viewMonth));
|
|
138
|
+
}
|
|
139
|
+
} else if (!visible) {
|
|
140
|
+
isVisible = false;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
} catch (_) {}
|
|
144
|
+
};
|
|
145
|
+
|
|
78
146
|
onUnmounted(() => {
|
|
79
147
|
clearViewTimer();
|
|
148
|
+
if (observer) {
|
|
149
|
+
observer.disconnect?.();
|
|
150
|
+
observer.unobserve?.();
|
|
151
|
+
observer = null;
|
|
152
|
+
}
|
|
80
153
|
});
|
|
81
154
|
|
|
82
155
|
const showMonth = (date?: DateType) => {
|
|
@@ -84,6 +157,7 @@ const showMonth = (date?: DateType) => {
|
|
|
84
157
|
if (newDate.format('YYYY-MM-DD') !== viewDate.value.format('YYYY-MM-DD')) {
|
|
85
158
|
viewDate.value = newDate;
|
|
86
159
|
}
|
|
160
|
+
initing.value = true;
|
|
87
161
|
clearViewTimer();
|
|
88
162
|
viewTimer = setTimeout(() => {
|
|
89
163
|
if (props.minDate && props.maxDate) {
|
|
@@ -99,18 +173,17 @@ const showMonth = (date?: DateType) => {
|
|
|
99
173
|
}
|
|
100
174
|
const _viewMonth = viewDate.value.format('YYYY-MM');
|
|
101
175
|
const tops = cmpMonthTops.value;
|
|
102
|
-
const top = tops[_viewMonth]?.top
|
|
103
|
-
if (top === undefined
|
|
176
|
+
const top = tops[_viewMonth]?.top;
|
|
177
|
+
if (top === undefined) {
|
|
104
178
|
initing.value = false;
|
|
105
179
|
return;
|
|
106
180
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
181
|
+
// 组件不可见时,暂存目标位置,等可见后再执行
|
|
182
|
+
if (!isVisible) {
|
|
183
|
+
pendingScrollTop = top;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
doScroll(top, _viewMonth);
|
|
114
187
|
}, VIEW_MONTH_DELAY);
|
|
115
188
|
};
|
|
116
189
|
|
|
@@ -208,7 +281,6 @@ watch(
|
|
|
208
281
|
() => props.defaultDate,
|
|
209
282
|
v => {
|
|
210
283
|
viewDate.value = v ? utils.dayjs(v) : utils.dayjs();
|
|
211
|
-
showMonth();
|
|
212
284
|
},
|
|
213
285
|
{ immediate: true }
|
|
214
286
|
);
|
|
@@ -217,6 +289,7 @@ const confirm = () => {
|
|
|
217
289
|
emits('confirm', dataList.value);
|
|
218
290
|
};
|
|
219
291
|
onMounted(() => {
|
|
292
|
+
startObserver();
|
|
220
293
|
showMonth();
|
|
221
294
|
});
|
|
222
295
|
|
|
@@ -241,7 +314,7 @@ const onScroll = (e: any) => {
|
|
|
241
314
|
};
|
|
242
315
|
</script>
|
|
243
316
|
<template>
|
|
244
|
-
<view class="ste-calendar-root" :style="[cmpRootStyle, { opacity: initing ? 0 : 1 }]">
|
|
317
|
+
<view ref="rootRef" class="ste-calendar-root" :style="[cmpRootStyle, { opacity: initing ? 0 : 1 }]">
|
|
245
318
|
<view v-if="showTitle" class="calendar-title">{{ title }}</view>
|
|
246
319
|
<view class="week-head">
|
|
247
320
|
<view class="week-row">
|
|
@@ -3,7 +3,7 @@ import utils from '../../utils/utils'
|
|
|
3
3
|
import type { Dayjs } from '../../types'
|
|
4
4
|
|
|
5
5
|
export default function useData() {
|
|
6
|
-
const initing = ref(
|
|
6
|
+
const initing = ref(true)
|
|
7
7
|
const setIniting = (val: boolean) => initing.value = val
|
|
8
8
|
const startDate = ref<number | string | null>(null)
|
|
9
9
|
const setStartDate = (val: number | string | null) => startDate.value = val
|
|
@@ -177,10 +177,195 @@
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
.action-box {
|
|
180
|
-
padding: 0 40rpx;
|
|
180
|
+
padding: 0 40rpx 20rpx 40rpx;
|
|
181
181
|
display: flex;
|
|
182
182
|
justify-content: space-between;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
</style>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 单据类型筛选
|
|
190
|
+
|
|
191
|
+
此示例展示了如何实现一个单据类型筛选菜单,包含左侧分类选择、右侧输入框和子选项选择。
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<script lang="ts" setup>
|
|
195
|
+
import type { RefDropdownMenu } from 'stellar-ui-plus/types/refComponents';
|
|
196
|
+
import { computed, reactive, ref } from 'vue';
|
|
197
|
+
|
|
198
|
+
const orderMenus = [
|
|
199
|
+
{
|
|
200
|
+
label: '单据类型1',
|
|
201
|
+
value: '1',
|
|
202
|
+
children: [
|
|
203
|
+
{ label: '类型1A', value: '1-1' },
|
|
204
|
+
{ label: '类型1B', value: '1-2' },
|
|
205
|
+
{ label: '类型1C', value: '1-3' },
|
|
206
|
+
{ label: '类型1D', value: '1-4' },
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
label: '单据类型2',
|
|
211
|
+
value: '2',
|
|
212
|
+
children: [
|
|
213
|
+
{ label: '类型2A', value: '2-1' },
|
|
214
|
+
{ label: '类型2B', value: '2-2' },
|
|
215
|
+
{ label: '类型2C', value: '2-3' },
|
|
216
|
+
{ label: '类型2D', value: '2-4' },
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
const orderData = reactive({
|
|
222
|
+
parent: '1',
|
|
223
|
+
type: '1-1',
|
|
224
|
+
code: '',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const orderChildren = computed(() => orderMenus.find(item => item.value == orderData.parent)?.children || []);
|
|
228
|
+
|
|
229
|
+
const setOrder = (key: keyof typeof orderData, v: string) => {
|
|
230
|
+
orderData[key] = v;
|
|
231
|
+
if (key == 'parent') {
|
|
232
|
+
const children = orderMenus.find(item => item.value == v)?.children || [];
|
|
233
|
+
orderData.type = children[0]?.value || '';
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const orderMenuRef = ref<RefDropdownMenu>();
|
|
238
|
+
function confirmOrderMenu() {
|
|
239
|
+
uni.showToast({
|
|
240
|
+
title: `确认订单 parent:${orderData.parent}, type:${orderData.type}, code:${orderData.code}`,
|
|
241
|
+
icon: 'none',
|
|
242
|
+
});
|
|
243
|
+
orderMenuRef.value?.close();
|
|
244
|
+
}
|
|
245
|
+
</script>
|
|
246
|
+
<template>
|
|
247
|
+
<view class="menu-item">
|
|
248
|
+
<view>
|
|
249
|
+
<ste-dropdown-menu value="2" title="单据类型" ref="orderMenuRef">
|
|
250
|
+
<view class="order-menu-box">
|
|
251
|
+
<view class="content-box">
|
|
252
|
+
<view class="content-left">
|
|
253
|
+
<view v-for="(m, i) in orderMenus" :key="i" class="left-menu-item" :class="orderData.parent == m.value ? 'active' : ''" @click="setOrder('parent', m.value)">
|
|
254
|
+
{{ m.label }}
|
|
255
|
+
</view>
|
|
256
|
+
</view>
|
|
257
|
+
<view class="content-right">
|
|
258
|
+
<view class="right-codes">
|
|
259
|
+
<view class="content-title">订单尾号</view>
|
|
260
|
+
<ste-input style="width: 356rpx" placeholder="请输入订单最后5位" v-model="orderData.code" background="#f5f5f5"></ste-input>
|
|
261
|
+
</view>
|
|
262
|
+
<view class="right-types">
|
|
263
|
+
<view class="content-title">单据类型</view>
|
|
264
|
+
<view class="right-menus">
|
|
265
|
+
<view v-for="(m, i) in orderChildren" :key="i" class="right-menu-item" :class="orderData.type == m.value ? 'active' : ''" @click="setOrder('type', m.value)">
|
|
266
|
+
{{ m.label }}
|
|
267
|
+
</view>
|
|
268
|
+
</view>
|
|
269
|
+
</view>
|
|
270
|
+
</view>
|
|
271
|
+
</view>
|
|
272
|
+
<view class="action-box">
|
|
273
|
+
<ste-button width="320" background="rgba(0,0,0,0)" borderColor="#0090FF" color="#0090FF" @click="confirmOrderMenu">重置</ste-button>
|
|
274
|
+
<ste-button width="320" @click="confirmOrderMenu">确认</ste-button>
|
|
275
|
+
</view>
|
|
276
|
+
</view>
|
|
277
|
+
</ste-dropdown-menu>
|
|
278
|
+
</view>
|
|
279
|
+
</view>
|
|
280
|
+
</template>
|
|
281
|
+
<style lang="scss">
|
|
282
|
+
.menu-item {
|
|
283
|
+
display: flex;
|
|
284
|
+
padding: 0 20rpx;
|
|
285
|
+
width: 100%;
|
|
286
|
+
box-shadow: 0 0 10px #ddd;
|
|
287
|
+
> view {
|
|
288
|
+
flex: 1;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
justify-content: center;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.order-menu-box {
|
|
295
|
+
background-color: #fff;
|
|
296
|
+
padding-top: 24rpx;
|
|
297
|
+
border-top: solid 4rpx #f5f5f5;
|
|
183
298
|
|
|
299
|
+
.content-box {
|
|
300
|
+
width: 100%;
|
|
301
|
+
display: flex;
|
|
302
|
+
margin-bottom: 56rpx;
|
|
303
|
+
font-size: 28rpx;
|
|
304
|
+
|
|
305
|
+
.content-left {
|
|
306
|
+
width: 150rpx;
|
|
307
|
+
background-color: #f9f9f9;
|
|
308
|
+
|
|
309
|
+
.left-menu-item {
|
|
310
|
+
height: 90rpx;
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: center;
|
|
314
|
+
font-size: 24rpx;
|
|
315
|
+
color: #a4a4a4;
|
|
316
|
+
|
|
317
|
+
&.active {
|
|
318
|
+
background-color: #fff;
|
|
319
|
+
color: #0090ff;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.content-right {
|
|
325
|
+
flex: 1;
|
|
326
|
+
margin-left: 26rpx;
|
|
327
|
+
margin-right: 18rpx;
|
|
328
|
+
background-color: #fff;
|
|
329
|
+
|
|
330
|
+
.content-title {
|
|
331
|
+
height: 90rpx;
|
|
332
|
+
display: flex;
|
|
333
|
+
align-items: center;
|
|
334
|
+
font-size: 24rpx;
|
|
335
|
+
color: #1c1f23;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.right-types {
|
|
339
|
+
.right-menus {
|
|
340
|
+
display: flex;
|
|
341
|
+
flex-wrap: wrap;
|
|
342
|
+
align-items: center;
|
|
343
|
+
gap: 20rpx;
|
|
344
|
+
|
|
345
|
+
.right-menu-item {
|
|
346
|
+
width: 162rpx;
|
|
347
|
+
height: 64rpx;
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
justify-content: center;
|
|
351
|
+
background: #f9f9f9;
|
|
352
|
+
border-radius: 8rpx;
|
|
353
|
+
|
|
354
|
+
&.active {
|
|
355
|
+
background-color: #0090ff;
|
|
356
|
+
font-weight: bold;
|
|
357
|
+
color: #fff;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.action-box {
|
|
366
|
+
padding: 0 40rpx;
|
|
367
|
+
display: flex;
|
|
368
|
+
justify-content: space-between;
|
|
184
369
|
padding-bottom: 20rpx;
|
|
185
370
|
}
|
|
186
371
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
.ste-dropdown-menu-root {
|
|
2
2
|
.menu-box {
|
|
3
3
|
padding: 20rpx 0;
|
|
4
|
+
|
|
4
5
|
.menu-title-icon {
|
|
5
6
|
display: inline-flex;
|
|
6
7
|
transition: all var(--duration) linear;
|
|
7
8
|
}
|
|
9
|
+
|
|
8
10
|
.title {
|
|
9
11
|
font-size: var(--font-size-24, 24rpx);
|
|
10
12
|
margin-right: 16rpx;
|
|
11
13
|
}
|
|
14
|
+
|
|
12
15
|
display: flex;
|
|
13
16
|
align-items: center;
|
|
14
17
|
}
|
|
@@ -59,6 +62,7 @@
|
|
|
59
62
|
.menu-title-icon {
|
|
60
63
|
transform: rotate(180deg);
|
|
61
64
|
}
|
|
65
|
+
|
|
62
66
|
.dropdown-content {
|
|
63
67
|
opacity: 1;
|
|
64
68
|
z-index: var(--menu-z-index);
|
|
@@ -73,6 +77,7 @@
|
|
|
73
77
|
.menu-title-icon {
|
|
74
78
|
transform: rotate(180deg);
|
|
75
79
|
}
|
|
80
|
+
|
|
76
81
|
.dropdown-content {
|
|
77
82
|
display: flex;
|
|
78
83
|
flex-direction: column;
|
|
@@ -82,10 +87,12 @@
|
|
|
82
87
|
transform: translateY(100%);
|
|
83
88
|
}
|
|
84
89
|
}
|
|
90
|
+
|
|
85
91
|
&.open {
|
|
86
92
|
.menu-title-icon {
|
|
87
93
|
transform: rotate(0);
|
|
88
94
|
}
|
|
95
|
+
|
|
89
96
|
.menu-item-content {
|
|
90
97
|
transform: translateY(0);
|
|
91
98
|
}
|
|
@@ -96,4 +103,4 @@
|
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
|
-
}
|
|
106
|
+
}
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
### Events
|
|
22
22
|
| 事件名 | 说明 | 事件参数 | 支持版本 |
|
|
23
23
|
| ----- | ----- | ------- | -------- |
|
|
24
|
-
| `close` |
|
|
25
|
-
| `open` | 弹窗打开动画执行完毕事件 |
|
|
26
|
-
| `
|
|
24
|
+
| `close` | 弹窗关闭前触发,可用于异步拦截关闭 | `suspend`:开启等待的回调函数<br/>`next`:执行后续关闭操作的回调函数<br/>`stop`:阻止后续关闭操作的回调函数 | - |
|
|
25
|
+
| `open-after` | 弹窗打开动画执行完毕事件 | - | - |
|
|
26
|
+
| `clickMask` | 点击遮罩时触发 | - | - |
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
### Slots
|