Skip to content

Commit 8f5447c

Browse files
feat: Popover 组件 Taro 小程序适配 (#1882)
* fix: 修复 ImagePreview 在Taro编译成H5后报错的问题 * fix: 地址关闭时, Close 事件触发两次问题解决 * feat: 组件DatePicker 添加双向绑定 * docs: 组件Picker文档修改 * feat: 组件Picker与DatePicker新增属性safe-area-inset-bottom * feat: imagepreview * fix: 组件imagepreview点击视频遮罩关闭(#1729) * fix: 解决 Picker 在微信小程序中无法使用问题 (#1774) * fix: 修改 Picker 组件 v-model 失效问题 * fix: 组件NoticeBar修改height之后,垂直轮播会卡顿 * fix: 删除Datepicker Demo演示多余内容 * fix: 组件Picker在JD小程序上适配 * fix: 组件Address京东小程序适配 * feat: 京东小程序适配 * fix: 删除空格 * feat: 删除console * fix: 京东小程序imagepreview适配 * fix: 修复 imagepreview 动态设置 initNo 显示不正确问题 * fix: 组件 InfiniteLoading 某些情况下会错误触发下拉刷新#1819 * fix: 删除pullrefresh * feat: 组件 imagepreview瘦身 * feat: 组件Picker 瘦身 * fix: address线上问题修改 * fix: 完善imagepreview * feat: 公共函数提取 * feat: 函数式改用 createComponent * fix: 组件 Imagepreview与Address兼容性修改 * feat: 组件 popover 小程序适配 * feat: 单元测试 * fix: 文件回撤 * feat: 组件popover添加单元测试 * feat: 组件 popup 修改
1 parent e27e346 commit 8f5447c

File tree

10 files changed

+443
-277
lines changed

10 files changed

+443
-277
lines changed

src/packages/__VUE/address/index.taro.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
<!-- 请选择 -->
3535
<view class="custom-address" v-if="privateType == 'custom'">
36-
<view class="region-tab" ref="tabRegion">
36+
<view class="nut-address-region-tab" ref="tabRegion">
3737
<view
3838
:class="{ 'tab-item': true, active: index == tabIndex, [tabName[index]]: true }"
3939
v-for="(item, key, index) in selectedRegion"
@@ -72,7 +72,7 @@
7272

7373
<!-- 请选择 -->
7474
<view class="custom-address" v-else-if="privateType == 'custom2'">
75-
<view class="region-tab" ref="tabRegion">
75+
<view class="nut-address-region-tab" ref="tabRegion">
7676
<view
7777
:class="{ 'tab-item': true, active: index == tabIndex, [tabName[index]]: true }"
7878
v-for="(item, key, index) in selectedRegion"

src/packages/__VUE/imagepreview/index.taro.vue

+26-75
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<template>
2-
<nut-popup pop-class="nut-imagepreview-custom-pop" v-model:visible="showPop" @click="onClose" style="width: 100%">
3-
<!-- @click.stop="closeOnImg" -->
2+
<nut-popup pop-class="nut-imagepreview-custom-pop" v-model:visible="showPop" @closed="onClose">
43
<view class="nut-imagepreview" @touchstart.capture="onTouchStart">
54
<nut-swiper
65
v-if="showPop"
@@ -9,32 +8,28 @@
98
:loop="isLoop"
109
:is-preventDefault="false"
1110
direction="horizontal"
12-
@change="slideChangeEnd"
13-
:init-page="initPage"
11+
@change="setActive"
12+
:init-page="initNo"
1413
:pagination-visible="paginationVisible"
1514
:pagination-color="paginationColor"
1615
>
17-
<nut-swiper-item v-for="(item, index) in images" :key="index">
16+
<nut-swiper-item v-for="(item, index) in images" :key="index" @click="onClose">
1817
<image mode="aspectFit" :src="item.src" class="nut-imagepreview-taro-img" v-if="ENV != ENV_TYPE.WEB" />
1918
<img :src="item.src" mode="aspectFit" class="nut-imagepreview-img" v-else />
2019
</nut-swiper-item>
2120
</nut-swiper>
2221
</view>
2322

24-
<view class="nut-imagepreview-index" v-if="showIndex"> {{ active }} / {{ images.length }} </view>
25-
<view class="nut-imagepreview-close-icon" @click="handleCloseIcon" :style="styles" v-if="closeable"
23+
<view class="nut-imagepreview-index" v-if="showIndex"> {{ active + 1 }} / {{ images.length }} </view>
24+
<view class="nut-imagepreview-close-icon" @click="onClose" :style="styles" v-if="closeable"
2625
><nut-icon :name="closeIcon" v-bind="$attrs" color="#ffffff"></nut-icon
2726
></view>
2827
</nut-popup>
2928
</template>
3029
<script lang="ts">
3130
import { toRefs, reactive, watch, onMounted, computed, CSSProperties, PropType } from 'vue';
3231
import { createComponent } from '@/packages/utils/create';
33-
import Popup from '../popup/index.taro.vue';
34-
import Swiper from '../swiper/index.taro.vue';
35-
import SwiperItem from '../swiperitem/index.taro.vue';
36-
import Icon from '../icon/index.taro.vue';
37-
import { isPromise } from '@/packages/utils/util';
32+
import { funInterceptor, Interceptor } from '@/packages/utils/util';
3833
import { ImageInterface } from './types';
3934
import Taro from '@tarojs/taro';
4035
const { create } = createComponent('imagepreview');
@@ -55,7 +50,7 @@ export default create({
5550
},
5651
initNo: {
5752
type: Number,
58-
default: 1
53+
default: 0
5954
},
6055
paginationVisible: {
6156
type: Boolean,
@@ -85,29 +80,19 @@ export default create({
8580
type: String,
8681
default: 'top-right' // top-right top-left
8782
},
88-
beforeClose: Function,
83+
beforeClose: Function as PropType<Interceptor>,
8984
isLoop: {
9085
type: Boolean,
9186
default: true
9287
}
9388
},
9489
emits: ['close', 'change'],
95-
components: {
96-
[Popup.name]: Popup,
97-
// [Video.name]: Video,
98-
[Swiper.name]: Swiper,
99-
[SwiperItem.name]: SwiperItem,
100-
[Icon.name]: Icon
101-
},
90+
components: {},
10291
10392
setup(props, { emit }) {
10493
const state = reactive({
10594
showPop: false,
106-
active: 1,
107-
source: {
108-
src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
109-
type: 'video/mp4'
110-
},
95+
active: 0,
11196
options: {
11297
muted: true,
11398
controls: true
@@ -135,9 +120,12 @@ export default create({
135120
return style;
136121
});
137122
138-
const slideChangeEnd = function (page: number) {
139-
state.active = page + 1;
140-
emit('change', state.active);
123+
// 设置当前选中第几个
124+
const setActive = (active: number) => {
125+
if (active !== state.active) {
126+
state.active = active;
127+
emit('change', state.active);
128+
}
141129
};
142130
143131
const closeOnImg = () => {
@@ -148,35 +136,21 @@ export default create({
148136
};
149137
150138
const onClose = () => {
151-
if (props.beforeClose) {
152-
const returnVal = props.beforeClose.apply(null, state.active);
153-
154-
if (isPromise(returnVal)) {
155-
returnVal.then((value) => {
156-
if (value) {
157-
closeDone();
158-
}
159-
});
160-
} else if (returnVal) {
161-
closeDone();
162-
}
163-
} else {
164-
closeDone();
165-
}
139+
funInterceptor(props.beforeClose, {
140+
args: [state.active],
141+
done: () => closeDone()
142+
});
166143
};
167144
// 执行关闭
168145
const closeDone = () => {
169146
state.showPop = false;
170147
state.store.scale = 1;
171148
scaleNow();
172-
// state.active = 1;
173149
emit('close');
174150
};
175151
176152
// 计算两个点的距离
177153
const getDistance = (first: { x: number; y: number }, second: { x: number; y: number }) => {
178-
// 计算两个点起始时刻的距离和终止时刻的距离,终止时刻距离变大了则放大,变小了则缩小
179-
// 放大 k 倍则 scale 也 扩大 k 倍
180154
return Math.hypot(Math.abs(second.x - first.x), Math.abs(second.y - first.y));
181155
};
182156
@@ -187,8 +161,6 @@ export default create({
187161
};
188162
189163
const onTouchStart = (event: TouchEvent) => {
190-
// console.log('start');
191-
// 如果已经放大,双击应变回原尺寸;如果是原尺寸,双击应放大
192164
const curTouchTime = new Date().getTime();
193165
if (curTouchTime - state.lastTouchEndTime < 300) {
194166
const store = state.store;
@@ -204,13 +176,10 @@ export default create({
204176
var events = touches[0];
205177
var events2 = touches[1];
206178
207-
// event.preventDefault();
208-
209179
const store = state.store;
210180
store.moveable = true;
211181
212182
if (events2) {
213-
// 如果开始两指操作,记录初始时刻两指间的距离
214183
store.oriDistance = getDistance(
215184
{
216185
x: events.pageX,
@@ -222,7 +191,7 @@ export default create({
222191
}
223192
);
224193
}
225-
// 取到开始两指操作时的放大(缩小比例),store.scale 存储的是当前的放缩比(相对于标准大小 scale 为 1 的情况的放大缩小比)
194+
226195
store.originScale = store.scale || 1;
227196
};
228197
@@ -265,7 +234,6 @@ export default create({
265234
};
266235
267236
const onTouchEnd = () => {
268-
// console.log('end');
269237
state.lastTouchEndTime = new Date().getTime();
270238
const store = state.store;
271239
store.moveable = false;
@@ -293,42 +261,25 @@ export default create({
293261
watch(
294262
() => props.initNo,
295263
(val) => {
296-
if (val != state.active) {
297-
state.active = val;
298-
}
264+
if (val != state.active) setActive(val);
299265
}
300266
);
301267
302-
const initPage = computed(() => {
303-
const maxNo = props.images.length;
304-
const _initPage = props.initNo > maxNo ? maxNo - 1 : props.initNo - 1;
305-
return _initPage >= 0 ? _initPage : 0;
306-
});
307-
308-
// 点击关闭按钮
309-
const handleCloseIcon = () => {
310-
onClose();
311-
};
312-
313268
onMounted(() => {
314-
// 初始化页码
315-
state.active = props.initNo;
316-
state.showPop = props.show;
269+
setActive(props.initNo);
317270
});
318271
319272
return {
320273
...toRefs(state),
321-
initPage,
322-
slideChangeEnd,
274+
setActive,
323275
onClose,
324276
closeOnImg,
325277
onTouchStart,
326278
onTouchMove,
327279
onTouchEnd,
328280
getDistance,
329281
scaleNow,
330-
styles,
331-
handleCloseIcon
282+
styles
332283
};
333284
}
334285
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { config, mount } from '@vue/test-utils';
2+
import Popover from '../index.vue';
3+
import NutPupup from '../../popup/index.vue';
4+
import NutOverlay from '../../overlay/index.vue';
5+
import NutIcon from '../../icon/index.vue';
6+
import { nextTick, reactive } from 'vue';
7+
8+
beforeAll(() => {
9+
config.global.components = {
10+
NutIcon,
11+
NutPupup,
12+
NutOverlay
13+
};
14+
});
15+
16+
afterAll(() => {
17+
config.global.components = {};
18+
});
19+
20+
const iconItemList = [{ name: 'option1' }, { name: 'option2' }, { name: 'option3' }];
21+
22+
const listDisabled = reactive([
23+
{ name: 'option1', disabled: true },
24+
{ name: 'option2', disabled: true },
25+
{ name: 'option3' }
26+
]);
27+
28+
test('first render', async () => {
29+
const wrapper = mount(Popover, {
30+
props: {
31+
visible: true,
32+
list: iconItemList,
33+
teleportDisable: false
34+
}
35+
});
36+
await nextTick();
37+
expect(wrapper.find('.nut-popover-menu-item').exists()).toBeTruthy();
38+
});
39+
40+
test('Props theme dark', async () => {
41+
const wrapper = mount(Popover, {
42+
props: {
43+
visible: true,
44+
list: iconItemList,
45+
teleportDisable: false,
46+
theme: 'dark'
47+
}
48+
});
49+
await nextTick();
50+
expect(wrapper.find('.nut-popover--dark').exists()).toBeTruthy();
51+
});
52+
53+
test('should not emit select event when the action is disabled', async () => {
54+
const wrapper = mount(Popover, {
55+
props: {
56+
visible: true,
57+
list: listDisabled,
58+
teleportDisable: false
59+
}
60+
});
61+
await nextTick();
62+
expect(wrapper.findAll('.nut-popover-menu-disabled').length).toEqual(2);
63+
64+
wrapper.find('.nut-popover-menu-item').trigger('click');
65+
expect(wrapper.emitted('choose')).toBeFalsy();
66+
});
67+
68+
test('should close popover when clicking the action', async () => {
69+
const wrapper = mount(Popover, {
70+
props: {
71+
visible: true,
72+
list: iconItemList,
73+
teleportDisable: false
74+
}
75+
});
76+
await nextTick();
77+
78+
await wrapper.find('.nut-popover-menu-item').trigger('click');
79+
expect(wrapper.emitted('update:visible')![0]).toEqual([false]);
80+
81+
await wrapper.setProps({ closeOnClickAction: false });
82+
await wrapper.find('.nut-popover-menu-item').trigger('click');
83+
expect(wrapper.emitted('update:visible')).toHaveLength(1);
84+
});
85+
86+
test('Set Props Position', async () => {
87+
const wrapper = mount(Popover, {
88+
props: {
89+
visible: true,
90+
list: iconItemList,
91+
teleportDisable: false,
92+
location: 'top-start'
93+
}
94+
});
95+
await nextTick();
96+
expect(wrapper.find('.nut-popover-arrow--top-start').exists()).toBeTruthy();
97+
});

src/packages/__VUE/popover/index.scss

+18
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@
125125
border-bottom: none;
126126
}
127127

128+
.nut-popover-item-img {
129+
vertical-align: top;
130+
}
131+
128132
.nut-popover-menu-item-name {
129133
margin-right: 12px;
130134
margin-left: 8px;
@@ -135,6 +139,10 @@
135139
color: $popover-disable-color;
136140
cursor: not-allowed;
137141
}
142+
143+
&.nut-popover-menu-taroitem {
144+
display: flex;
145+
}
138146
}
139147

140148
&--top {
@@ -263,3 +271,13 @@
263271
.nut-popover-leave-active {
264272
transition-timing-function: ease-in;
265273
}
274+
275+
.nut-popover-content-bg {
276+
position: fixed;
277+
height: 100%;
278+
width: 100%;
279+
top: 0;
280+
left: 0;
281+
background: transparent;
282+
z-index: 1999;
283+
}

0 commit comments

Comments
 (0)