Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mayi-mp-shop
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
程默
mayi-mp-shop
Commits
774db429
Commit
774db429
authored
Jan 27, 2021
by
李嘉林
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
小程序商品列表堆叠卡片样式
parent
3579d1b3
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
489 additions
and
7 deletions
+489
-7
static/nativeComponents/GoodsList/GoodsItem/index.wxml
+6
-6
static/nativeComponents/GoodsList/GoodsItem/index.wxss
+3
-0
static/nativeComponents/GoodsList/heapItem/index.js
+104
-0
static/nativeComponents/GoodsList/heapItem/index.json
+6
-0
static/nativeComponents/GoodsList/heapItem/index.wxml
+23
-0
static/nativeComponents/GoodsList/heapItem/index.wxss
+79
-0
static/nativeComponents/GoodsList/heapItem/utils/touchEvent.js
+261
-0
static/nativeComponents/GoodsList/index.json
+2
-1
static/nativeComponents/GoodsList/index.wxml
+5
-0
No files found.
static/nativeComponents/GoodsList/GoodsItem/index.wxml
View file @
774db429
<!-- 商品列表item -->
<view class="goodsItem {{datas.componentData.style==='rowList'?'goodsRowList':''}}" style="--proGap2:{{datas.componentData.proGap*2}}rpx;">
<view class="goodsItem {{datas.componentData.style==='rowList'?'goodsRowList':''}}
{{datas.componentData.style==='heap'?'goodsRowList goodsHeapItem':''}}
" style="--proGap2:{{datas.componentData.proGap*2}}rpx;">
<view class="goods-item-child {{datas.componentData['borderColorShow']?'goods-item-child-bd':''}}" style="border-radius:{{datas.componentData.borderRadius}}em;--border_color:{{datas.componentData['borderColor']}};box-shadow:{{datas.componentData['cardShadow']?'0rpx 0rpx 10rpx '+datas.componentData['cardShadowSize']*2+'rpx #ccc':''}};">
<!-- 商户入口-顶部 -->
<view class="merchantsEntrance flex" wx:if="{{datas.componentData['merchantsEntrance'] && datas.componentData['merchantsEntranceType'] == 0}}">
<view class="merchantsEntrance flex" wx:if="{{datas.componentData['merchantsEntrance'] && datas.componentData['merchantsEntranceType'] == 0
&& datas.componentData.style!='heap'
}}">
<view class="left flex" wx:if="{{items != null}}">
<view class="logo">
<image mode='widthFix' src="{{items.shopLogoUrl}}" />
...
...
@@ -18,7 +18,7 @@
style="padding:{{datas.componentData.imgSize}}"
>
<!-- 开启售卖 -->
<view class="saleWay" wx:if="{{items.saleWay==2 && items.getSaleTime>0}}">
<view class="saleWay" wx:if="{{items.saleWay==2 && items.getSaleTime>0
&& datas.componentData.style!='heap'
}}">
<view class="saleWayBg"></view>
<view class="saleTime">
<van-count-down
...
...
@@ -49,13 +49,13 @@
</view>
<!-- 商品主图 -->
<image
mode="{{datas.componentData.style==='rowList'?'aspectFit':'widthFix'}}"
mode="{{datas.componentData.style==='rowList'
|| datas.componentData.style==='heap'
?'aspectFit':'widthFix'}}"
class="productImgUrl"
wx:if="{{datas.componentData.goodsImgType == 1}}"
src="{{items.productImgUrl}}"
/>
<image
mode="{{datas.componentData.style==='rowList'?'aspectFit':'widthFix'}}"
mode="{{datas.componentData.style==='rowList'
|| datas.componentData.style==='heap'
?'aspectFit':'widthFix'}}"
class="productImgUrl"
wx:else
src="{{items.coverImage}}"
...
...
@@ -187,7 +187,7 @@
</block>
</view>
<!-- 商户入口二 -->
<view class="merchantsEntrance flex" wx:if="{{datas.componentData.merchantsEntrance&&datas.componentData.merchantsEntranceType==1}}">
<view class="merchantsEntrance flex" wx:if="{{datas.componentData.merchantsEntrance&&datas.componentData.merchantsEntranceType==1
&& datas.componentData.style!='heap'
}}">
<view class="left flex" wx:if="{{items!=null}}">
<view class="logo">
<image mode='widthFix' src="http://test-bucket-ant.oss-cn-shanghai.aliyuncs.com/product/ppefWK8BS6.png?x-oss-process=image/resize,limit_1,w_150,h_150" />
...
...
static/nativeComponents/GoodsList/GoodsItem/index.wxss
View file @
774db429
...
...
@@ -40,6 +40,9 @@ align-items: flex-start;
flex-direction: column;
justify-content: space-around;
}
.goodsHeapItem{
width: 100%;
}
.merchantsEntrance{
padding: 8rpx 20rpx;
...
...
static/nativeComponents/GoodsList/heapItem/index.js
0 → 100644
View file @
774db429
import
TouchEvent
from
"./utils/touchEvent"
;
const
componentOptions
=
{
// 组件选项
options
:
{
multipleSlots
:
true
},
behaviors
:
[],
properties
:
{
datas
:
{
type
:
Object
}
},
// 组件数据
data
:
{
isPageHidden
:
false
,
// 页面是否处于隐藏状态
isLoading
:
false
,
swiperCurIndex
:
0
,
slideClass
:
""
,
lockSwipe
:
false
},
// 数据监听器
observers
:
{},
// 组件生命周期
lifetimes
:
{
created
()
{
new
TouchEvent
(
this
,
"touchCard"
,
{
swipe
:
evt
=>
{
//在touch结束触发,evt.direction代表滑动的方向 ['Up','Right','Down','Left']
if
(
evt
.
direction
===
"Left"
)
this
.
next
(
evt
);
if
(
evt
.
direction
===
"Right"
)
this
.
prev
(
evt
);
}
});
},
attached
()
{}
},
// 组件方法
methods
:
{
next
(
e
)
{
if
(
this
.
data
.
lockSwipe
)
return
;
this
.
data
.
lockSwipe
=
true
;
if
(
-
this
.
data
.
swiperCurIndex
>=
this
.
data
.
datas
.
componentData
.
goodsList
.
length
-
1
)
{
return
(
this
.
data
.
lockSwipe
=
false
);
}
const
index
=
e
.
currentTarget
.
dataset
[
"index"
];
this
.
setData
(
{
[
"datas.componentData.goodsList["
+
index
+
"].slideClass"
]:
" ani-slide-up"
},
()
=>
{
this
.
setData
({
swiperCurIndex
:
--
this
.
data
.
swiperCurIndex
});
}
);
setTimeout
(()
=>
{
this
.
data
.
lockSwipe
=
false
;
this
.
setData
({
[
"datas.componentData.goodsList["
+
index
+
"].slideClass"
]:
""
});
},
590
);
},
prev
(
e
)
{
const
index
=
e
.
currentTarget
.
dataset
[
"index"
]
-
1
;
if
(
this
.
data
.
lockSwipe
||
index
<
0
)
return
;
this
.
data
.
lockSwipe
=
true
;
this
.
setData
({
[
"datas.componentData.goodsList["
+
index
+
"].slideClass"
]:
" ani-slide-down"
,
swiperCurIndex
:
++
this
.
data
.
swiperCurIndex
});
setTimeout
(()
=>
{
this
.
data
.
lockSwipe
=
false
;
this
.
setData
({
[
"datas.componentData.goodsList["
+
index
+
"].slideClass"
]:
""
});
},
590
);
}
},
definitionFilter
()
{},
// 页面生命周期
pageLifetimes
:
{
// 页面被展示
show
()
{
const
{
isPageHidden
}
=
this
.
data
;
// show事件发生前,页面不是处于隐藏状态时
if
(
!
isPageHidden
)
{
return
;
}
// 重新执行定时器等操作
},
// 页面被隐藏
hide
()
{
this
.
setData
({
isPageHidden
:
true
});
// 清除定时器等操作
},
// 页面尺寸变化时
resize
()
{}
}
};
Component
(
componentOptions
);
static/nativeComponents/GoodsList/heapItem/index.json
0 → 100644
View file @
774db429
{
"component"
:
true
,
"usingComponents"
:
{
"goods-item"
:
"../GoodsItem/index"
}
}
static/nativeComponents/GoodsList/heapItem/index.wxml
0 → 100644
View file @
774db429
<view class="container center">
<view class="card-swiper">
<view
wx:for="{{datas.componentData.goodsList}}"
wx:key="index"
data-index="{{index}}"
class="{{'card-swiper-item curdistance' + (swiperCurIndex + index) + (!!item.slideClass? item.slideClass: '')}}"
bindtap="next"
catchtouchstart="touchCard.start"
catchtouchmove="touchCard.move"
catchtouchend="touchCard.end"
catchtouchcancel="touchCard.cancel"
>
<goods-item
style="width:100%"
datas="{{datas}}"
items="{{datas.componentData.goodsList[index]}}"
indexs="{{index}}"
></goods-item>
</view>
</view>
</view>
static/nativeComponents/GoodsList/heapItem/index.wxss
0 → 100644
View file @
774db429
.container{
width: 100%;
height: 200rpx;
padding: 0 0 20rpx;
}
/**index.wxss**/
.card-swiper{
position: relative;
width: 100%;
height: 100%;
color: #666;
font-size: 28rpx;
}
.card-swiper-item{
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
width: 92%;
transition: all .3s ease-out;
z-index: 0;
display: none;
overflow: hidden;
}
.card-swiper .card-swiper-item.curdistance0{
z-index: 10;
display: flex;
}
.card-swiper .card-swiper-item.curdistance1{
display: flex;
z-index: 9;
transform: scale(.95);
opacity: .7;
left: 40rpx;
}
.card-swiper .card-swiper-item.curdistance2{
display: flex;
z-index: 8;
transform: scale(.9);
opacity: .4;
left: 80rpx;
}
.ani-slide-up{
display: flex;
animation: slideUp .6s ease-out;
z-index: 11;
/* transform: rotate(25deg); */
}
.ani-slide-down{
display: flex;
animation: slideUp .6s ease-in;
animation-direction: reverse;
}
@keyframes slideUp{
0% {
}
70% {
opacity: 1;
}
100% {
transform: translateX(-1000rpx);
opacity: 0;
-webkit-transform: translateX(-1000rpx);
-moz-transform: translateX(-1000rpx);
-ms-transform: translateX(-1000rpx);
-o-transform: translateX(-1000rpx);
}
}
\ No newline at end of file
static/nativeComponents/GoodsList/heapItem/utils/touchEvent.js
0 → 100644
View file @
774db429
var
defaultOption
=
{
touchStart
:
function
()
{
},
touchMove
:
function
()
{
},
touchEnd
:
function
()
{
},
touchCancel
:
function
()
{
},
multipointStart
:
function
()
{
},
multipointEnd
:
function
()
{
},
tap
:
function
()
{
},
doubleTap
:
function
()
{
},
longTap
:
function
()
{
},
singleTap
:
function
()
{
},
rotate
:
function
()
{
},
pinch
:
function
()
{
},
pressMove
:
function
()
{
},
swipe
:
function
()
{
}
}
export
default
class
YrobotTouch
{
constructor
(
pageOBJ
,
name
,
option
=
{})
{
this
.
preV
=
{
x
:
null
,
y
:
null
};
this
.
pinchStartLen
=
null
;
this
.
scale
=
1
;
this
.
isDoubleTap
=
false
;
this
.
delta
=
null
;
this
.
last
=
null
;
this
.
now
=
null
;
this
.
tapTimeout
=
null
;
this
.
singleTapTimeout
=
null
;
this
.
longTapTimeout
=
null
;
this
.
swipeTimeout
=
null
;
this
.
x1
=
this
.
x2
=
this
.
y1
=
this
.
y2
=
null
;
this
.
preTapPosition
=
{
x
:
null
,
y
:
null
};
this
.
lastZoom
=
1
;
this
.
tempZoom
=
1
;
try
{
if
(
this
.
_checkBeforeCreate
(
pageOBJ
,
name
))
{
this
.
_name
=
name
this
.
_option
=
{
...
defaultOption
,
...
option
}
pageOBJ
[
name
]
=
this
this
.
_bindFunc
(
pageOBJ
)
}
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
_checkBeforeCreate
(
pageOBJ
,
name
)
{
if
(
!
pageOBJ
||
!
name
)
{
throw
new
Error
(
'YrobotTouch实例化时,必须传入page对象和引用名'
)
return
false
}
if
(
pageOBJ
[
name
])
{
throw
new
Error
(
'YrobotTouch实例化error: '
+
name
+
' 已经存在page中'
)
return
false
}
return
true
}
_bindFunc
(
pageOBJ
)
{
let
funcNames
=
[
'start'
,
'move'
,
'end'
,
'cancel'
]
for
(
let
funcName
of
funcNames
)
pageOBJ
[
this
.
_name
+
'.'
+
funcName
]
=
this
[
funcName
].
bind
(
this
)
}
start
(
evt
)
{
if
(
!
evt
.
touches
)
return
;
this
.
now
=
Date
.
now
();
this
.
x1
=
evt
.
touches
[
0
].
pageX
==
null
?
evt
.
touches
[
0
].
x
:
evt
.
touches
[
0
].
pageX
;
this
.
y1
=
evt
.
touches
[
0
].
pageY
==
null
?
evt
.
touches
[
0
].
y
:
evt
.
touches
[
0
].
pageY
;
this
.
delta
=
this
.
now
-
(
this
.
last
||
this
.
now
);
this
.
_option
.
touchStart
(
evt
);
if
(
this
.
preTapPosition
.
x
!==
null
)
{
this
.
isDoubleTap
=
(
this
.
delta
>
0
&&
this
.
delta
<=
250
&&
Math
.
abs
(
this
.
preTapPosition
.
x
-
this
.
x1
)
<
30
&&
Math
.
abs
(
this
.
preTapPosition
.
y
-
this
.
y1
)
<
30
);
}
this
.
preTapPosition
.
x
=
this
.
x1
;
this
.
preTapPosition
.
y
=
this
.
y1
;
this
.
last
=
this
.
now
;
let
preV
=
this
.
preV
,
len
=
evt
.
touches
.
length
;
if
(
len
>
1
)
{
this
.
_cancelLongTap
();
this
.
_cancelSingleTap
();
let
otx
=
evt
.
touches
[
1
].
pageX
==
null
?
evt
.
touches
[
1
].
x
:
evt
.
touches
[
1
].
pageX
;
let
oty
=
evt
.
touches
[
1
].
pageY
==
null
?
evt
.
touches
[
1
].
y
:
evt
.
touches
[
1
].
pageY
;
let
v
=
{
x
:
otx
-
this
.
x1
,
y
:
oty
-
this
.
y1
};
preV
.
x
=
v
.
x
;
preV
.
y
=
v
.
y
;
this
.
pinchStartLen
=
getLen
(
preV
);
this
.
_option
.
multipointStart
(
evt
);
}
this
.
longTapTimeout
=
setTimeout
(
function
()
{
evt
.
type
=
"longTap"
;
this
.
_option
.
longTap
(
evt
);
}.
bind
(
this
),
750
);
}
move
(
evt
)
{
if
(
!
evt
.
touches
)
return
;
let
preV
=
this
.
preV
,
len
=
evt
.
touches
.
length
,
currentX
=
evt
.
touches
[
0
].
pageX
==
null
?
evt
.
touches
[
0
].
x
:
evt
.
touches
[
0
].
pageX
,
currentY
=
evt
.
touches
[
0
].
pageY
==
null
?
evt
.
touches
[
0
].
y
:
evt
.
touches
[
0
].
pageY
;
this
.
isDoubleTap
=
false
;
if
(
len
>
1
)
{
let
otx
=
evt
.
touches
[
1
].
pageX
==
null
?
evt
.
touches
[
1
].
x
:
evt
.
touches
[
1
].
pageX
;
let
oty
=
evt
.
touches
[
1
].
pageY
==
null
?
evt
.
touches
[
1
].
y
:
evt
.
touches
[
1
].
pageY
;
let
v
=
{
x
:
otx
-
currentX
,
y
:
oty
-
currentY
};
if
(
preV
.
x
!==
null
)
{
if
(
this
.
pinchStartLen
>
0
)
{
evt
.
singleZoom
=
getLen
(
v
)
/
this
.
pinchStartLen
;
evt
.
zoom
=
evt
.
singleZoom
*
this
.
lastZoom
;
this
.
tempZoom
=
evt
.
zoom
;
evt
.
type
=
"pinch"
;
this
.
_option
.
pinch
(
evt
);
}
evt
.
angle
=
getRotateAngle
(
v
,
preV
);
evt
.
type
=
"rotate"
;
this
.
_option
.
rotate
(
evt
);
}
preV
.
x
=
v
.
x
;
preV
.
y
=
v
.
y
;
}
else
{
if
(
this
.
x2
!==
null
)
{
evt
.
deltaX
=
currentX
-
this
.
x2
;
evt
.
deltaY
=
currentY
-
this
.
y2
;
}
else
{
evt
.
deltaX
=
0
;
evt
.
deltaY
=
0
;
}
this
.
_option
.
pressMove
(
evt
);
}
this
.
_option
.
touchMove
(
evt
);
this
.
_cancelLongTap
();
this
.
x2
=
currentX
;
this
.
y2
=
currentY
;
if
(
len
>
1
)
{
// evt.preventDefault();
}
}
end
(
evt
)
{
if
(
!
evt
.
changedTouches
)
return
;
this
.
_cancelLongTap
();
let
self
=
this
;
evt
.
direction
=
this
.
_swipeDirection
(
this
.
x1
,
this
.
x2
,
this
.
y1
,
this
.
y2
);
//在结束钩子都加入方向判断,但触发swipe瞬时必须位移大于30
if
(
evt
.
touches
.
length
<
2
)
{
this
.
lastZoom
=
this
.
tempZoom
;
this
.
_option
.
multipointEnd
(
evt
);
}
this
.
_option
.
touchEnd
(
evt
);
//swipe
if
((
this
.
x2
&&
Math
.
abs
(
this
.
x1
-
this
.
x2
)
>
30
)
||
(
this
.
y2
&&
Math
.
abs
(
this
.
y1
-
this
.
y2
)
>
30
))
{
// evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2);
this
.
swipeTimeout
=
setTimeout
(
function
()
{
evt
.
type
=
"swipe"
;
self
.
_option
.
swipe
(
evt
);
},
0
)
}
else
{
this
.
tapTimeout
=
setTimeout
(
function
()
{
evt
.
type
=
"tap"
;
self
.
_option
.
tap
(
evt
);
// trigger double tap immediately
if
(
self
.
isDoubleTap
)
{
evt
.
type
=
"doubleTap"
;
self
.
_option
.
doubleTap
(
evt
);
clearTimeout
(
self
.
singleTapTimeout
);
self
.
isDoubleTap
=
false
;
}
},
0
)
if
(
!
self
.
isDoubleTap
)
{
self
.
singleTapTimeout
=
setTimeout
(
function
()
{
self
.
_option
.
singleTap
(
evt
);
},
250
);
}
}
this
.
preV
.
x
=
0
;
this
.
preV
.
y
=
0
;
this
.
scale
=
1
;
this
.
pinchStartLen
=
null
;
this
.
x1
=
this
.
x2
=
this
.
y1
=
this
.
y2
=
null
;
}
cancel
(
evt
)
{
clearTimeout
(
this
.
singleTapTimeout
);
clearTimeout
(
this
.
tapTimeout
);
clearTimeout
(
this
.
longTapTimeout
);
clearTimeout
(
this
.
swipeTimeout
);
this
.
_option
.
touchCancel
(
evt
);
}
_cancelLongTap
()
{
clearTimeout
(
this
.
longTapTimeout
);
}
_cancelSingleTap
()
{
clearTimeout
(
this
.
singleTapTimeout
);
}
_swipeDirection
(
x1
,
x2
,
y1
,
y2
)
{
return
Math
.
abs
(
x1
-
x2
)
>=
Math
.
abs
(
y1
-
y2
)
?
(
x1
-
x2
>
0
?
'Left'
:
'Right'
)
:
(
y1
-
y2
>
0
?
'Up'
:
'Down'
)
}
destroy
()
{
if
(
this
.
singleTapTimeout
)
clearTimeout
(
this
.
singleTapTimeout
);
if
(
this
.
tapTimeout
)
clearTimeout
(
this
.
tapTimeout
);
if
(
this
.
longTapTimeout
)
clearTimeout
(
this
.
longTapTimeout
);
if
(
this
.
swipeTimeout
)
clearTimeout
(
this
.
swipeTimeout
);
this
.
_option
.
rotate
=
null
;
this
.
_option
.
touchStart
=
null
;
this
.
_option
.
multipointStart
=
null
;
this
.
_option
.
multipointEnd
=
null
;
this
.
_option
.
pinch
=
null
;
this
.
_option
.
swipe
=
null
;
this
.
_option
.
tap
=
null
;
this
.
_option
.
doubleTap
=
null
;
this
.
_option
.
longTap
=
null
;
this
.
_option
.
singleTap
=
null
;
this
.
_option
.
pressMove
=
null
;
this
.
_option
.
touchMove
=
null
;
this
.
_option
.
touchEnd
=
null
;
this
.
_option
.
touchCancel
=
null
;
this
.
preV
=
this
.
pinchStartLen
=
this
.
scale
=
this
.
isDoubleTap
=
this
.
delta
=
this
.
last
=
this
.
now
=
this
.
tapTimeout
=
this
.
singleTapTimeout
=
this
.
longTapTimeout
=
this
.
swipeTimeout
=
this
.
x1
=
this
.
x2
=
this
.
y1
=
this
.
y2
=
this
.
preTapPosition
=
this
.
rotate
=
this
.
touchStart
=
this
.
multipointStart
=
this
.
multipointEnd
=
this
.
pinch
=
this
.
swipe
=
this
.
tap
=
this
.
doubleTap
=
this
.
longTap
=
this
.
singleTap
=
this
.
pressMove
=
this
.
touchMove
=
this
.
touchEnd
=
this
.
touchCancel
=
null
;
return
null
;
}
}
function
getLen
(
v
)
{
return
Math
.
sqrt
(
v
.
x
*
v
.
x
+
v
.
y
*
v
.
y
);
}
function
dot
(
v1
,
v2
)
{
return
v1
.
x
*
v2
.
x
+
v1
.
y
*
v2
.
y
;
}
function
getAngle
(
v1
,
v2
)
{
let
mr
=
getLen
(
v1
)
*
getLen
(
v2
);
if
(
mr
===
0
)
return
0
;
let
r
=
dot
(
v1
,
v2
)
/
mr
;
if
(
r
>
1
)
r
=
1
;
return
Math
.
acos
(
r
);
}
function
cross
(
v1
,
v2
)
{
return
v1
.
x
*
v2
.
y
-
v2
.
x
*
v1
.
y
;
}
function
getRotateAngle
(
v1
,
v2
)
{
let
angle
=
getAngle
(
v1
,
v2
);
if
(
cross
(
v1
,
v2
)
>
0
)
{
angle
*=
-
1
;
}
return
angle
*
180
/
Math
.
PI
;
}
\ No newline at end of file
static/nativeComponents/GoodsList/index.json
View file @
774db429
...
...
@@ -2,6 +2,7 @@
"component"
:
true
,
"usingComponents"
:
{
"goods-item"
:
"./GoodsItem/index"
,
"waterfall-flow"
:
"../module/WaterfallFlow/index"
"waterfall-flow"
:
"../module/WaterfallFlow/index"
,
"heap-item"
:
"./heapItem/index"
}
}
static/nativeComponents/GoodsList/index.wxml
View file @
774db429
...
...
@@ -84,6 +84,11 @@
></goods-item>
</view>
</view>
<!-- 堆叠卡片 -->
<view class="goods-heap-Item" wx:if="{{datas.componentData.style==='heap'}}">
<heap-item datas="{{datas}}"></heap-item>
</view>
</view>
</view>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment