@kaiyinchem/ky-uniui 1.0.3

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.
@@ -0,0 +1,348 @@
1
+ <template>
2
+ <view :style="{ height: full ? '100%' : 'auto'}" class="wrap">
3
+ <swiper
4
+ class="swiper"
5
+ :style="{ height: full ? '100%' : height + 'rpx'}"
6
+ :autoplay="autoplay"
7
+ :interval="interval"
8
+ :duration="duration"
9
+ :circular="loop"
10
+ :previous-margin="previousMargin + 'rpx'"
11
+ :next-margin="nextMargin + 'rpx'"
12
+ @change="change"
13
+ @touchmove.prevent=""
14
+ >
15
+ <swiper-item v-for="(item,index) in list" :key="index" @click="onItemClick(item, index)">
16
+ <view
17
+ v-if="list && list.length>0"
18
+ :class="[!crown ? '' : current==index ? 'crown-active':'crown']"
19
+ class="item"
20
+ >
21
+ <template v-if="!slots">
22
+ <image
23
+ v-if="item.type !== 'video'"
24
+ class="item-img"
25
+ :class="[imgShadow?'imgShadow':'']"
26
+ :src="item[imgKey] || '/static/img/pro_0.jpg'"
27
+ :style="{ borderRadius: imgRadius + 'px',width:imgWidth}"
28
+ mode="aspectFill"
29
+ >
30
+ </image>
31
+ <view
32
+ v-else
33
+ :style="{ borderRadius: imgRadius + 'px',width:imgWidth}"
34
+ :class="[imgShadow?'imgShadow':'']"
35
+ class="item-img"
36
+ >
37
+ <view v-if="videoCtx.status !== 'play'" class="video-cover">
38
+ <image
39
+ class="video-img"
40
+ :src="item.poster"
41
+ mode="aspectFill"
42
+ >
43
+ </image>
44
+ <text class="video-btn iconfont" @click="onVideoClick(index)">&#xe64b;</text>
45
+ </view>
46
+ <video
47
+ v-if="videoCtx.status === 'play'"
48
+ class="video-comp"
49
+ :id="'video_' + index"
50
+ :src="item[imgKey]"
51
+ :enable-progress-gesture="false"
52
+ :autoplay="true"
53
+ @touchmove="onVideoTouchMove"
54
+ @play="onVideoPlay"
55
+ @pause="onVideoPause"
56
+ @ended="onVideoEnded"
57
+ @error="onVideoError"
58
+ >
59
+ </video>
60
+ </view>
61
+ </template>
62
+ <slot v-else :data='item'></slot>
63
+ </view>
64
+ </swiper-item>
65
+ </swiper>
66
+ <view class="dots flex" :style="{bottom: bottom + 'rpx'}" v-if="dots">
67
+ <view class="dot" :class="[current == i ? 'curr-dot' : '']" v-for="(d,i) in list" :key='i'></view>
68
+ </view>
69
+ </view>
70
+ </template>
71
+
72
+ <script>
73
+ export default {
74
+ props: {
75
+ list: {
76
+ type: Array,
77
+ default: () => []
78
+ },
79
+ // 轮播图片key
80
+ imgKey: {
81
+ type: String,
82
+ required: true
83
+ },
84
+ // 高度
85
+ height: {
86
+ type: Number,
87
+ default: 300
88
+ },
89
+ // 图片圆角
90
+ imgRadius: {
91
+ type: Number,
92
+ default: 0
93
+ },
94
+ // 图片阴影
95
+ imgShadow: {
96
+ type: Boolean,
97
+ default: false
98
+ },
99
+ // 前边距
100
+ previousMargin: {
101
+ type: Number,
102
+ default: 0
103
+ },
104
+ // 后边距
105
+ nextMargin: {
106
+ type: Number,
107
+ default: 0
108
+ },
109
+ // 图片宽度
110
+ imgWidth: {
111
+ type: String,
112
+ default: '100%'
113
+ },
114
+ // 是否循环
115
+ loop: {
116
+ type: Boolean,
117
+ default: true
118
+ },
119
+ // 自动播放
120
+ autoplay: {
121
+ type: Boolean,
122
+ default: true
123
+ },
124
+ // 播放时间间隔
125
+ interval: {
126
+ type: Number,
127
+ default: 3000
128
+ },
129
+ // 滑动速度
130
+ duration: {
131
+ type: Number,
132
+ default: 500
133
+ },
134
+ // 显示指示点
135
+ dots: {
136
+ type: Boolean,
137
+ default: true
138
+ },
139
+ // 轮播点下边距
140
+ bottom: {
141
+ type: Number,
142
+ default: 24
143
+ },
144
+ // 卡片特效
145
+ crown: {
146
+ type: Boolean,
147
+ default: false
148
+ },
149
+ // 全屏
150
+ full: {
151
+ type: Boolean,
152
+ default: false
153
+ },
154
+ },
155
+ data() {
156
+ return {
157
+ current: 0,
158
+ slots: false,
159
+ videoCtx: { status: '' }, // 视频对象
160
+ };
161
+ },
162
+ watch: {
163
+ // 判断异步数据源,是否使用插槽自定义样式
164
+ list: {
165
+ handler(val) {
166
+ if (val.length > 0 && this.$slots.default) {
167
+ this.slots = true
168
+ }
169
+ // this.videoCtx = []
170
+ // this.$nextTick(function() {
171
+ // val.forEach((e, i) => {
172
+ // if (e.type === 'video') {
173
+ // const videoEle = uni.createVideoContext(`video_${i}`, this)
174
+ // videoEle.index = i
175
+ // videoEle.status = ''
176
+ // this.videoCtx.push(videoEle)
177
+ // }
178
+ // })
179
+ // })
180
+ },
181
+ immediate: true,
182
+ }
183
+ },
184
+ computed: {
185
+ // currVideoCtx() {
186
+ // return this.videoCtx.find(e => e.index === this.current)
187
+ // }
188
+ },
189
+ mounted() {
190
+ },
191
+ methods: {
192
+ change(event) {
193
+ let current = event.detail.current
194
+ this.current = current
195
+ this.$emit('change', this.list[current])
196
+
197
+ // 切换的时候把所有视频都暂停
198
+ if (this.videoCtx.id) {
199
+ this.videoCtx.pause()
200
+ // this.videoCtx.forEach((e) => {
201
+ // if (current !== e.index) {
202
+ // console.log('切换暂停')
203
+ // e.pause()
204
+ // }
205
+ // })
206
+ }
207
+ },
208
+
209
+ onItemClick(el, index) {
210
+ if (this.full) {
211
+ if (index === this.list.length - 1) {
212
+ this.$emit('selected', el)
213
+ }
214
+ return
215
+ }
216
+ this.$emit('selected', el)
217
+ },
218
+
219
+ onVideoClick(i) {
220
+ this.setVideoStatus('play')
221
+ const videoEle = uni.createVideoContext(`video_${i}`, this)
222
+ videoEle.index = i
223
+ videoEle.status = 'play'
224
+ this.videoCtx = videoEle
225
+ this.videoCtx.play()
226
+ console.log('点击了', this.videoCtx.status)
227
+ },
228
+
229
+ setVideoStatus(status) {
230
+ this.videoCtx.status = status
231
+ },
232
+
233
+ onVideoPlay() {
234
+ this.setVideoStatus('play')
235
+ },
236
+
237
+ onVideoPause() {
238
+ this.setVideoStatus('pause')
239
+ console.log('pause')
240
+ },
241
+
242
+ onVideoEnded() {
243
+ this.setVideoStatus('ended')
244
+ this.videoCtx.exitFullScreen()
245
+ console.log('ended')
246
+ },
247
+
248
+ onVideoError() {
249
+ this.$toast('播放失败!')
250
+ this.setVideoStatus('error')
251
+ },
252
+
253
+ /**
254
+ * 视频滑动监听
255
+ * @description 因为video组件层级高,禁止了手势进度,所以没办法进行切换swiper
256
+ * @version 2021-09-28 zzc
257
+ */
258
+ onVideoTouchMove(e) {
259
+ console.log(e)
260
+ }
261
+ }
262
+ }
263
+ </script>
264
+
265
+ <style lang="scss" scoped>
266
+ .video-comp {
267
+ width: 100%;
268
+ height: 100%;
269
+ }
270
+ .video-cover {
271
+ position: relative;
272
+ height: 100%;
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ &:after {
277
+ content: '';
278
+ display: block;
279
+ width: 100%;
280
+ height: 100%;
281
+ position: absolute;
282
+ left: 0;
283
+ top: 0;
284
+ background: rgba(0,0,0,0.1);
285
+ }
286
+ .video-img {
287
+ width: 100%;
288
+ height: 100%;
289
+ }
290
+ .video-btn {
291
+ position: absolute;
292
+ z-index: 1;
293
+ color: var(--color-dark);
294
+ font-size: 150rpx;
295
+ }
296
+ }
297
+ .wrap {
298
+ position: relative;
299
+
300
+ .crown {
301
+ transform: scale(0.93, 0.9);
302
+ }
303
+
304
+ .item {
305
+ height: 100%;
306
+ transition: 1.2s;
307
+ }
308
+
309
+ .item-img {
310
+ width: 100%;
311
+ height: 100%;
312
+ }
313
+
314
+ .imgShadow {
315
+ height: calc(100% - 10px);
316
+ margin-bottom: 10px;
317
+ box-shadow: 0 6px 6px rgba(0, 0, 0, .15);
318
+ }
319
+
320
+ .crown-active {
321
+ transform: scale(1);
322
+ }
323
+
324
+ .dots {
325
+ display: flex;
326
+ position: absolute;
327
+ left: 50%;
328
+ transform: translateX(-50%);
329
+
330
+ .dot {
331
+ width: 10rpx;
332
+ height: 10rpx;
333
+ border-radius: 50%;
334
+ background-color: #D6D6D6;
335
+ margin-right: 10rpx;
336
+ transition: all 0.3s;
337
+ }
338
+
339
+ .curr-dot {
340
+ height: 10rpx;
341
+ width: 30rpx;
342
+ border-radius: 5rpx;
343
+ background-color: #fff;
344
+ }
345
+ }
346
+
347
+ }
348
+ </style>
@@ -0,0 +1,159 @@
1
+ <!--莉美易 switch组件-->
2
+ <template>
3
+ <view class="lmy-switch">
4
+ <text v-if="label" :style="{ color }" class="ls-label">{{ label }}</text>
5
+ <view :style="{ 'background-color': checked ? actColor : inactColor }" class="ls-parent" @click="onSwitchClick">
6
+ <view :class="{ active: checked }" class="ls-pointer"></view>
7
+ </view>
8
+ </view>
9
+ </template>
10
+
11
+ <script>
12
+ const isDark = uni.getSystemInfoSync().theme === 'dark'
13
+
14
+ export default {
15
+ emits: ['update:value', 'change'],
16
+ props: {
17
+ /**
18
+ * @param { String } label 提示文字
19
+ */
20
+ label: {
21
+ type: String,
22
+ default: ''
23
+ },
24
+ /**
25
+ * @param { String } color 文本颜色
26
+ */
27
+ color: {
28
+ type: String,
29
+ default: '#666666'
30
+ },
31
+ /**
32
+ * @param { String } activeColor 激活的背景色
33
+ */
34
+ activeColor: {
35
+ type: String,
36
+ default: ''
37
+ },
38
+ /**
39
+ * @param { String } inactiveColor 未激活的背景色
40
+ */
41
+ inactiveColor: {
42
+ type: String,
43
+ default: ''
44
+ },
45
+ /**
46
+ * @param { Boolean, Number, String } value v-model 目前仅支持布尔值
47
+ */
48
+ value: {
49
+ type: [Boolean, Number, String],
50
+ default: false
51
+ },
52
+ /**
53
+ * @param { Boolean, Number, String } inactiveValue 关闭的时候的值,值不是布尔值的时候使用的
54
+ */
55
+ inactiveValue: {
56
+ type: [Boolean, Number, String],
57
+ default: false
58
+ },
59
+ /**
60
+ * @param { Boolean, Number, String } activeValue 打开的时候的值
61
+ */
62
+ activeValue: {
63
+ type: [Boolean, Number, String],
64
+ default: true
65
+ }
66
+ },
67
+ computed: {
68
+ inactColor() {
69
+ if (this.inactiveColor) {
70
+ return this.inactiveColor
71
+ }
72
+ return this.isDark ? '#666666' : '#dddddd'
73
+ },
74
+ actColor() {
75
+ if (this.activeColor) {
76
+ return this.activeColor
77
+ }
78
+ return this.isDark ? '#067272' : '#00B2A9'
79
+ }
80
+ },
81
+ data() {
82
+ return {
83
+ actVal: false,
84
+ checked: false,
85
+ isDark
86
+ }
87
+ },
88
+ mounted() {
89
+ this.setValue(this.value !== this.activeValue, true)
90
+ uni.onThemeChange(res => {
91
+ this.isDark = res.theme === 'dark'
92
+ })
93
+ },
94
+ destroyed() {
95
+ uni.offThemeChange()
96
+ },
97
+ methods: {
98
+ onSwitchClick() {
99
+ this.setValue(this.checked)
100
+ this.$emit('update:value', this.actVal)
101
+ this.$emit('change', this.actVal)
102
+ },
103
+ setValue(flag, isInit) {
104
+ const inact = typeof this.inactiveValue !== 'undefined'
105
+ const act = typeof this.activeValue !== 'undefined'
106
+ if (inact || act) {
107
+ this.actVal = !this.checked ? this.activeValue : this.inactiveValue
108
+ this.checked = !flag
109
+ } else {
110
+ if (isInit) {
111
+ this.actVal = !!this.value
112
+ this.checked = !!this.value
113
+ } else {
114
+ this.actVal = !this.value
115
+ this.checked = !this.checked
116
+ }
117
+ }
118
+ },
119
+ }
120
+ }
121
+ </script>
122
+
123
+ <style scoped lang="scss">
124
+ .lmy-switch {
125
+ display: flex;
126
+ align-items: center;
127
+ }
128
+ .ls-label {
129
+ display: inline-block;
130
+ margin-right: 12rpx;
131
+ color: var(--color-gray);
132
+ font-size: 24rpx;
133
+ }
134
+ .ls-parent {
135
+ width: 80rpx;
136
+ height: 40rpx;
137
+ background-color: var(--bg-gray);
138
+ border-radius: 40rpx;
139
+ position: relative;
140
+ display: flex;
141
+ align-items: center;
142
+ transition: all 0.3s;
143
+ .ls-pointer {
144
+ position: absolute;
145
+ width: 25rpx;
146
+ height: 25rpx;
147
+ border-radius: 50%;
148
+ background: var(--bg-white);
149
+ top: 0;
150
+ bottom: 0;
151
+ margin: auto;
152
+ left: 10rpx;
153
+ transition: left 0.3s;
154
+ &.active {
155
+ left: 50rpx;
156
+ }
157
+ }
158
+ }
159
+ </style>
@@ -0,0 +1,214 @@
1
+ <template>
2
+ <scroll-view
3
+ :scroll-x="scrollX"
4
+ :class="{
5
+ fixed: position === 'fixed',
6
+ sticky: position === 'sticky',
7
+ iphonex: $safeBottom > 0 && position === 'fixed',
8
+ icon: hasIcon,
9
+ scroll: scrollX
10
+ }"
11
+ class="tab-scroll"
12
+ >
13
+ <view id="tab-parent" class="tab-components">
14
+ <view class="tab-box">
15
+ <view
16
+ v-for="(tab, i) in menu"
17
+ :style="{ color: activeColor && i === tabIndex ? activeColor : color }"
18
+ :key="i"
19
+ :class="{ active: i === tabIndex, icon: tab.icon }"
20
+ :id="'tab_'+i"
21
+ class="tab-item"
22
+ @click="onTabClick(tab, i)"
23
+ >
24
+ <text v-if="tab.icon" class="tab-icon iconfont">{{ tab.icon }}</text>
25
+ <text class="tab-text">{{ tab[label] }}</text>
26
+ </view>
27
+ </view>
28
+ <!-- <view
29
+ v-if="tabItemWidth > 0 && !hasIcon"
30
+ :style="{ left: tabItemOffsetLeft + 'px', width: tabItemWidth + 'px', 'background-color': activeColor }"
31
+ class="tab-slider"
32
+ >
33
+ </view> -->
34
+ </view>
35
+ </scroll-view>
36
+ </template>
37
+
38
+ <script>
39
+ export default {
40
+ emits: ['update:value', 'change'],
41
+ props: {
42
+ // 显示字体图标传入icon: '\'
43
+ menu: {
44
+ type: Array,
45
+ default: () => [],
46
+ required: true
47
+ },
48
+ index: {
49
+ type: Number,
50
+ default: 0
51
+ },
52
+ activeColor: {
53
+ type: String,
54
+ },
55
+ color: {
56
+ type: String,
57
+ },
58
+
59
+ // fixed, sticky
60
+ position: {
61
+ type: String,
62
+ default: 'relative'
63
+ },
64
+
65
+ scrollX: {
66
+ type: Boolean,
67
+ default: false,
68
+ },
69
+ value: {
70
+ type: [ String, Number, Boolean ],
71
+ default: undefined
72
+ },
73
+ label: {
74
+ type: String,
75
+ default: 'name',
76
+ },
77
+ val: {
78
+ type: String,
79
+ default: 'id',
80
+ },
81
+ },
82
+ data() {
83
+ return {
84
+ tabItemOffsetLeft: 0,
85
+ tabItemWidth: 0,
86
+ tabIndex: 0,
87
+ }
88
+ },
89
+ watch: {
90
+ value(v) {
91
+ this.tabIndex = v
92
+ }
93
+ },
94
+ computed: {
95
+ hasIcon() {
96
+ return this.menu.find(e => e.icon)
97
+ },
98
+ $safeBottom() {
99
+ return uni.getSystemInfoSync().safeAreaInsets.bottom
100
+ }
101
+ },
102
+ mounted() {
103
+ if (!this.hasIcon) {
104
+ this.onTabClick(this.menu[this.index], this.index)
105
+ } else {
106
+ this.tabIndex = typeof this.value !== 'undefined' ? this.value : this.index
107
+ }
108
+ },
109
+ methods: {
110
+ onTabClick(item, index) {
111
+ if (this.hasIcon) {
112
+ this.tabIndex = index
113
+ this.$emit('change', index)
114
+ this.$emit('update:value', index)
115
+ return
116
+ }
117
+ const query = uni.createSelectorQuery().in(this)
118
+ const parent = query.select('#tab-parent')
119
+ // vue3版本在真机上不能同时获取两个元素
120
+ parent.boundingClientRect(data => {
121
+ // console.log(data)
122
+ this.tabItemWidth = data.width / (this.menu.length * 2)
123
+ this.tabItemOffsetLeft = index * this.tabItemWidth * 2 + (data.left || (this.tabItemWidth/2.75))
124
+ if (this.tabIndex === index) {
125
+ return
126
+ }
127
+ this.tabIndex = index
128
+ this.$emit('change', index)
129
+ this.$emit('update:value', index)
130
+ }).exec()
131
+ }
132
+ }
133
+ }
134
+ </script>
135
+
136
+ <style scoped lang="scss">
137
+ .tab-scroll {
138
+ background: var(--bg-white);
139
+ width: auto;
140
+ &.scroll {
141
+ padding: 0 24rpx;
142
+ .tab-components {
143
+ white-space: nowrap;
144
+ .tab-box {
145
+ justify-content: flex-start;
146
+ position: relative;
147
+ }
148
+ }
149
+ }
150
+ &.icon {
151
+ padding: 12rpx 24rpx;
152
+ }
153
+ &.fixed {
154
+ position: fixed;
155
+ z-index: 9;
156
+ left: 0;
157
+ right: 0;
158
+ bottom: 0;
159
+ &.iphonex {
160
+ padding-bottom: 50rpx;
161
+ }
162
+ }
163
+ &.sticky {
164
+ position: sticky;
165
+ z-index: 999;
166
+ top: 0;
167
+ }
168
+ }
169
+ .tab-components {
170
+ position: relative;
171
+ z-index: 1;
172
+ .tab-box {
173
+ display: flex;
174
+ justify-content: space-around;
175
+ .tab-item {
176
+ margin: 0 12rpx;
177
+ padding: 12rpx 0;
178
+ transition: all 0.3s;
179
+ color: var(--color-gray);
180
+ border-bottom: 2px solid rgba(255,255,255,0);
181
+ box-sizing: border-box;
182
+ &.active {
183
+ font-weight: bold;
184
+ color: var(--color-primary);
185
+ border-color: var(--color-primary);
186
+ }
187
+ &.icon {
188
+ font-weight: normal;
189
+ .tab-text {
190
+ font-size: 22rpx;
191
+ }
192
+ }
193
+ .tab-icon, .tab-text {
194
+ display: block;
195
+ text-align: center;
196
+ color: inherit;
197
+ }
198
+ .tab-icon {
199
+ font-size: 48rpx;
200
+ }
201
+ }
202
+ }
203
+ .tab-slider {
204
+ position: absolute;
205
+ background-color: var(--color-primary);
206
+ height: 6rpx;
207
+ border-radius: 12rpx;
208
+ width: 50rpx;
209
+ left: 15%;
210
+ bottom: 0;
211
+ transition: all 0.5s;
212
+ }
213
+ }
214
+ </style>