Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
ydl-group-course
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
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
黄志
ydl-group-course
Commits
e2ac3edf
Commit
e2ac3edf
authored
Feb 24, 2023
by
huangzhi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 已购和首页
parent
1842bd1a
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
261 additions
and
438 deletions
+261
-438
.eslintrc.js
.eslintrc.js
+3
-0
config.js
src/config.js
+5
-4
manifest.json
src/manifest.json
+1
-1
Empty.vue
src/pages/home/Empty.vue
+26
-0
home.vue
src/pages/home/home.vue
+204
-408
my.vue
src/pages/my/my.vue
+22
-25
No files found.
.eslintrc.js
View file @
e2ac3edf
...
...
@@ -7,6 +7,9 @@ module.exports = {
sourceType
:
'module'
,
allowImportExportEverywhere
:
true
,
},
globals
:
{
uni
:
'readonly'
,
},
// 此项指定环境的全局变量,下面的配置指定为浏览器环境
env
:
{
browser
:
true
,
...
...
src/config.js
View file @
e2ac3edf
...
...
@@ -6,10 +6,11 @@ let h2Prefix
let
ydlH5Prefix
if
(
isDevelopment
)
{
// hostPrefix = 'https://testnewm.ydl.com'
// apiPrefix = 'https://testapi.ydl.com'
hostPrefix
=
'https://newm-test.ydl.com'
// 下云
apiPrefix
=
'https://api-test.ydl.com'
// 下云
hostPrefix
=
'https://testnewm.ydl.com'
// hostPrefix = 'http://192.168.211.138'
apiPrefix
=
'https://testapi.ydl.com'
// hostPrefix = 'https://newm-test.ydl.com' // 下云
// apiPrefix = 'https://api-test.ydl.com' // 下云
h2Prefix
=
'https://h2.yidianling.com'
ydlH5Prefix
=
'https://testh5.ydl.com'
}
else
{
...
...
src/manifest.json
View file @
e2ac3edf
...
...
@@ -54,7 +54,7 @@
"quickapp"
:
{},
/*
快应用特有相关
*/
"mp-weixin"
:
{
"appid"
:
""
,
"appid"
:
"
wx1106c32a36d87d49
"
,
"setting"
:
{
"urlCheck"
:
false
,
"es6"
:
false
,
...
...
src/pages/home/Empty.vue
0 → 100644
View file @
e2ac3edf
<
template
>
<div
class=
"empty"
>
<img
src=
"//static.ydlcdn.com/m/images/m/course/rectangle.svg"
/>
<p>
{{
text
||
'暂无数据'
}}
</p>
</div>
</
template
>
<
script
>
export
default
{
name
:
'Empty'
,
props
:
[
'text'
],
}
</
script
>
<
style
lang=
"less"
scoped
>
.empty
{
text-align
:
center
;
p
{
margin-top
:
12px
;
color
:
#999
;
font-size
:
13px
;
text-align
:
center
;
}
}
</
style
>
\ No newline at end of file
src/pages/home/home.vue
View file @
e2ac3edf
<
template
>
<view
class=
"container home"
>
<!-- 筛选条件 -->
<screen-box
:url-params=
"urlParams"
:allow-home-scroll
.
sync=
"allowHomeScroll"
@
screenDoctor=
"onScreenDoctor"
></screen-box>
<!-- 专家列表 -->
<scroll-view
v-if=
"doctorArr.length"
class=
"consult-list box-c"
:scroll-y=
"allowHomeScroll"
:refresher-enabled=
"true"
:refresher-triggered=
"scrollBottomRefresher"
:refresher-threshold=
"100"
@
refresherrefresh=
"onRefresh"
@
scrolltolower=
"handleScrollBottom"
>
<block
v-for=
"item in doctorArr"
:key=
"item.uid"
>
<doctor-item
:data-item=
"item"
:item=
"item"
:is-login=
"isLogin"
:current-doctor=
"currentDoctor.confidedId"
:play-status=
"playStatus"
@
tap=
"toDetail(item)"
@
player=
"playAudio"
/>
</block>
<view
class=
"load-more"
>
{{
!
paging
.
hasMore
?
'没有更多了~'
:
'加载中...'
}}
</view>
</scroll-view>
<view
v-else
class=
"no-data"
>
<view
v-if=
"isLoaded"
>
<image
mode=
"aspectFill"
class=
"icon-find"
src=
"https://static.ydlcdn.com/weixin/image/home/no_data.png"
/>
<view
class=
"c-666"
>
未搜索到对应专家,请查询其他条件试试
</view>
</view>
<view
v-else
>
加载中...
</view>
</view>
<view
v-if=
"playStatus
"
class=
"player
"
class=
"buy-list
"
@
scroll=
"handleScroll($event)
"
>
<view
class=
"player-content"
>
<template
v-if=
"initFinish"
>
<template
v-if=
"courseList.length > 0"
>
<view
class=
"close-player"
@
click=
"closePlayer"
v-for=
"(course, index) in courseList"
:key=
"index"
class=
"item-wrap"
@
click=
"onPageToOrderDetail(course)"
>
<view
class=
"item-header"
>
<text
class=
"item-header-left"
>
<text
class=
"item-header-title"
>
订单号:
</text>
<text
class=
"item-header-num"
>
{{
course
.
orderId
}}
</text>
<img
class=
"player-icon"
src=
"https://static.ydlcdn.com/mini/mini_confide/confide_icon_close.png"
alt=
""
/>
</view>
<view
class=
"player-info"
>
<image
class=
"player-cover"
mode=
"aspectFill"
:src=
"currentDoctor.confidedIcon"
class=
"item-header-img"
src=
"//static.ydlcdn.com/m/images/m/course/ico_kcml_lock@3x.png"
/>
<view>
<view
class=
"player-title"
>
壹点倾诉,心灵寄语
</view>
<view
class=
"player-user"
>
<text
class=
"player-user-name"
>
{{
currentDoctor
.
confidedName
}}
</text>
</text>
<text
v-show=
"playStartTime && playEndTime"
class=
"player-user-time"
:class=
"
{
'item-header-right': true,
'item-header-right-blue': index === 0,
}"
>
{{
playStartTime
}}
/
{{
playEndTime
}}
拼团中
</text>
</view>
<view
class=
"item-content"
>
<view
class=
"item-content-img"
>
<img
src=
"//pic.ydlcdn.com/K4RBkeK5zx.jpg"
/>
</view>
<view
class=
"item-content-info"
>
<view
class=
"item-content-info-row-first"
>
<text
class=
"row-first-title"
>
12天重塑你的安全感,让恋爱不再患得患失
</text>
<text
class=
"row-first-money"
>
¥299
</text>
</view>
<view
class=
"item-content-info-row-second"
>
<text
class=
"row-second-time"
>
{{
course
.
paymentTime
?
dayjs
(
course
.
paymentTime
).
format
(
'YYYY-MM-DD HH:mm:ss'
)
:
''
}}
</text>
<text
class=
"row-second-money"
>
实付款:
<text
class=
"row-second-money-num"
>
¥199
</text>
</text>
</view>
<view
class=
"pause-player"
@
click=
"pausePlayer"
>
<img
class=
"player-icon"
:src=
"
isPausePlay
? 'https://static.ydlcdn.com/mini/mini_confide/confide_icon_play.png'
: 'https://static.ydlcdn.com/mini/mini_confide/confide_icon_pause.png'
"
alt=
""
/>
</view>
</view>
<view
class=
"item-footer"
>
<text
class=
"item-footer-btn"
type=
"default"
@
click=
"handleCourseItemClick($event, course)"
>
查看商品
</text>
</view>
</view>
</
template
>
<empty
v-else
></empty>
</template>
</view>
</template>
<
script
>
/* eslint-disable no-undef */
import
ScreenBox
from
'@/components/expert-screen'
import
DoctorItem
from
'@/components/expert-item'
import
{
setTrackData
}
from
'@/utils/util'
const
innerAudioContext
=
uni
.
createInnerAudioContext
()
import
Empty
from
'./Empty.vue'
import
{
throttle
}
from
'lodash'
import
dayjs
from
'dayjs'
import
{
hostPrefix
}
from
'@/config.js'
// import { Toast } from 'vant'
// 页面跳转
const
navTo
=
url
=>
{
const
newUrl
=
`
${
hostPrefix
}
/h5-course/
${
url
}
`
uni
.
navigateTo
({
url
:
`/pages/web/web?loadUrl=
${
encodeURIComponent
(
newUrl
)}
`
,
})
}
export
default
{
name
:
'ExpertPage'
,
name
:
'BuyList'
,
components
:
{
ScreenBox
,
DoctorItem
,
},
data
()
{
return
{
allowHomeScroll
:
true
,
scrollBottomRefresher
:
false
,
fromOpenId
:
''
,
// 从分享进入时所携带分享人的openid
isLoaded
:
false
,
isFixed
:
true
,
statistics
:
null
,
doctorArr
:
[],
userInfo
:
{},
// 用户信息
paging
:
{
hasMore
:
true
,
extras
:
null
,
},
preScreen
:
{},
// 筛选信息
isLogin
:
false
,
urlParams
:
{},
currentDoctor
:
{},
playStatus
:
false
,
isPausePlay
:
false
,
playStartTime
:
''
,
playEndTime
:
''
,
}
},
onLoad
(
options
)
{
// 进入小程序判断是否携带参数
if
(
options
&&
options
.
searchWord
)
{
this
.
urlParams
=
options
this
.
preScreen
.
keywords
=
options
.
searchWord
}
this
.
init
()
// 咨询师列表访问埋点
setTrackData
({
events
:
[
{
event_id
:
'page_visit'
,
event_custom_properties
:
{
part
:
'listener_list_page '
,
position
:
''
,
element
:
''
,
},
},
],
})
},
onPullDownRefresh
()
{
this
.
onRefresh
()
},
methods
:
{
// 下拉刷新
onRefresh
()
{
// 开启下拉刷新
this
.
scrollBottomRefresher
=
true
this
.
paging
.
extras
=
null
this
.
init
()
setTimeout
(()
=>
{
// 停止下拉刷新
uni
.
stopPullDownRefresh
()
this
.
scrollBottomRefresher
=
false
},
1000
)
},
handleScrollBottom
()
{
// 滚动到底部加载更多数据
if
(
!
this
.
paging
.
hasMore
)
return
this
.
getData
()
},
init
()
{
this
.
getData
(
true
)
const
accessToken
=
uni
.
getStorageSync
(
'accessToken'
)
this
.
isLogin
=
!!
accessToken
},
onLoginEvent
(
e
)
{
if
(
e
.
detail
.
accessToken
)
{
this
.
isLogin
=
true
}
},
onScreenDoctor
(
e
=
{})
{
// 筛选组件回调
this
.
preScreen
=
e
this
.
paging
=
{
hasMore
:
true
,
}
uni
.
pageScrollTo
({
scrollTop
:
0
,
})
this
.
getData
(
true
)
},
async
getData
(
isReset
)
{
let
{
doctorArr
}
=
this
// dmp接口入参配置
const
res
=
await
this
.
$request
.
get
(
'smart-rank/v1/search-adapter/listeners'
,
{
params
:
{
...
this
.
preScreen
,
extras
:
this
.
paging
?
this
.
paging
.
extras
||
null
:
null
,
// 分页参数,将上一页返回的 extras 传到这里,没有上一页则不传
},
})
.
finally
(()
=>
(
this
.
loading
=
false
))
const
{
body
}
=
res
if
(
isReset
)
{
doctorArr
=
[]
}
this
.
doctorArr
=
doctorArr
.
concat
(
body
)
// 接口返回数据并且长度大于每页条数,可滚动加载
this
.
paging
.
hasMore
=
!!
(
Array
.
isArray
(
body
)
&&
body
.
length
>
9
)
this
.
paging
.
extras
=
res
.
extras
this
.
isLoaded
=
true
Empty
,
},
// 跳转详情页
toDetail
(
e
)
{
if
(
!
e
.
confidedId
)
{
return
uni
.
showToast
(
'专家数据错误'
)
}
uni
.
navigateTo
({
url
:
`/pages/confide/confide?listenerId=
${
e
.
confidedId
}
`
,
})
props
:
{
keywords
:
{
type
:
String
,
default
:
''
,
},
onShareAppMessage
()
{
const
openId
=
this
.
$store
.
state
.
user
.
openId
setTrackData
({
events
:
[
{
event_id
:
'common_click'
,
event_custom_properties
:
{
part
:
'listener_list_page'
,
position
:
'top_column'
,
element
:
'share_friends'
,
// 是否展示学习进度
showProgress
:
{
type
:
Boolean
,
default
:
false
,
},
},
],
})
data
()
{
return
{
title
:
'倾诉师推荐'
,
path
:
`/pages/home/home?fromOpenId=
${
openId
}${
this
.
preScreen
.
keywords
?
'&searchWord='
+
this
.
preScreen
.
keywords
:
''
}
`
,
dayjs
,
pageNo
:
1
,
pageSize
:
10
,
moreStatus
:
'nomore'
,
courseList
:
[{},
{}],
// todo 应该是 false
initFinish
:
true
,
}
},
onShareTimeline
()
{
setTrackData
({
events
:
[
{
event_id
:
'common_click'
,
event_custom_properties
:
{
part
:
'listener_list_page'
,
position
:
'top_column'
,
element
:
'share_moments'
,
watch
:
{
keywords
:
{
handler
()
{
// todo
// this.refresh();
},
immediate
:
true
,
},
],
})
return
{
title
:
'倾诉师推荐-壹点灵心理'
,
query
:
{
fromOpenId
:
this
.
$store
.
state
.
user
.
openId
,
searchWord
:
this
.
preScreen
.
keywords
,
},
}
methods
:
{
// 查看订单详情 todo
onPageToOrderDetail
({
id
=
111
})
{
navTo
(
`my/orderDetail/
${
id
}
`
)
},
// 格式化音频时长
formatTime
:
function
(
s
)
{
let
t
=
''
s
=
Math
.
floor
(
s
)
if
(
s
>
-
1
)
{
const
min
=
Math
.
floor
(
s
/
60
)
%
60
const
sec
=
s
%
60
if
(
min
<
10
)
{
t
+=
'0'
}
t
+=
min
+
':'
if
(
sec
<
10
)
{
t
+=
'0'
}
t
+=
sec
}
return
t
// 课程卡片点击,跳转课程详情 todo
handleCourseItemClick
(
e
,
{
id
=
111
})
{
e
.
stopPropagation
()
navTo
(
`detail/
${
id
}
`
)
},
closePlayer
()
{
this
.
playStatus
=
false
innerAudioContext
.
stop
()
// 刷新列表,提供给父组件搜索过滤调用
refresh
()
{
this
.
initFinish
=
false
this
.
moreStatus
=
'nomore'
this
.
courseList
=
[]
this
.
pageNo
=
1
this
.
getCourseList
()
},
pausePlayer
()
{
if
(
!
this
.
isPausePlay
)
{
innerAudioContext
.
pause
()
}
else
{
innerAudioContext
.
play
()
// 获取课程列表
async
getCourseList
()
{
try
{
// todo
// this.moreStatus === 'loading';
// Toast.loading('加载中...');
// const {total, list} = await this.$http.post('/course/v1/orders/pay/query', {
// pageNo: this.pageNo,
// pageSize: this.pageSize,
// title: this.keywords || '',
// });
// Toast.clear();
// this.courseList = [...this.courseList, ...list];
// this.courseList.map(item => {
// item.salesPrice = item.money;
// item.originPrice = 0;
// })
// this.initFinish = true;
// this.moreStatus = this.courseList.length
<
total
?
"more"
:
"nomore"
;
}
catch
(
e
)
{
console
.
log
(
e
)
}
this
.
isPausePlay
=
!
this
.
isPausePlay
},
// 播放音频
playAudio
(
item
)
{
var
audioUrl
=
item
.
confideVoice
this
.
currentDoctor
=
item
if
(
item
.
isPlayer
)
{
this
.
playStatus
=
false
this
.
isPausePlay
=
false
innerAudioContext
.
stop
()
return
// 上拉加载下一页
handleScroll
:
throttle
(
function
(
e
)
{
const
{
scrollHeight
,
offsetHeight
,
scrollTop
}
=
e
.
target
const
height
=
scrollHeight
-
offsetHeight
-
scrollTop
const
scrollThreshold
=
100
if
(
height
<=
scrollThreshold
)
{
if
(
this
.
moreStatus
===
'more'
)
{
this
.
pageNo
=
this
.
pageNo
+
1
this
.
getCourseList
()
}
this
.
playStatus
=
true
this
.
isPausePlay
=
false
innerAudioContext
.
stop
()
innerAudioContext
.
autoplay
=
false
innerAudioContext
.
src
=
audioUrl
innerAudioContext
.
play
()
innerAudioContext
.
onPlay
(()
=>
{
console
.
log
(
'开始播放'
,
innerAudioContext
.
duration
)
setTimeout
(()
=>
{
// 设置音频总时长
this
.
playEndTime
=
this
.
formatTime
(
Math
.
ceil
(
innerAudioContext
.
duration
))
},
300
)
})
innerAudioContext
.
onTimeUpdate
(()
=>
{
// 音频跟新时若总时长未获取则重新获取
if
(
!
this
.
playEndTime
)
{
this
.
playEndTime
=
this
.
formatTime
(
Math
.
ceil
(
innerAudioContext
.
duration
))
}
console
.
log
(
'更新播放'
,
innerAudioContext
.
duration
)
// 跟新音频播放时长
this
.
playStartTime
=
this
.
formatTime
(
Math
.
ceil
(
innerAudioContext
.
currentTime
))
})
innerAudioContext
.
onError
(
res
=>
{
console
.
log
(
res
.
errMsg
,
'播放出错'
,
res
.
errCode
)
this
.
playStatus
=
false
})
innerAudioContext
.
onPause
(()
=>
{
console
.
log
(
'播放暂停'
)
})
innerAudioContext
.
onEnded
(()
=>
{
console
.
log
(
'播放结束'
)
this
.
playStatus
=
false
this
.
isPausePlay
=
false
this
.
playStartTime
=
'00:00'
})
},
300
,
{
leading
:
true
},
),
},
}
</
script
>
<
style
lang=
"less"
scoped
>
.home
{
position
:
relative
;
background
:
#fff
;
padding
:
94px
0
0
0
;
.buy-list
{
background
:
#f8f8f8
;
height
:
100%
;
}
.consult-list
{
width
:
100%
;
height
:
100%
;
}
.icon-find
{
width
:
100%
;
}
.no-data
,
.load-more
{
text-align
:
center
;
padding
:
20px
;
}
.player
{
position
:
fixed
;
width
:
100%
;
bottom
:
60px
;
left
:
0
;
padding
:
16px
;
overflow
:
auto
;
-webkit-overflow-scrolling
:
touch
;
overscroll-behavior-y
:
none
;
&-content
{
width
:
100%
;
color
:
#fff
;
background
:
rgba
(
0
,
0
,
0
,
0.8
);
border-radius
:
8px
;
padding
:
8px
20px
8px
8px
;
.item-wrap
{
background
:
#fff
;
//
border-bottom
:
10px
solid
rgb
(
249
,
249
,
249
);
margin
:
0
10px
;
margin-top
:
10px
;
border-radius
:
12px
;
padding
:
12px
12px
16px
12px
;
.item-header
{
display
:
flex
;
align-items
:
center
;
.player-cover
{
width
:
40px
;
height
:
40px
;
margin-right
:
12px
;
border-radius
:
4px
;
justify-content
:
space-between
;
border-bottom
:
1px
solid
rgb
(
249
,
249
,
249
);
padding-bottom
:
8px
;
.item-header-img
{
height
:
15px
;
vertical-align
:
middle
;
}
.item-header-right-blue
{
color
:
#1698ff
;
}
}
.player-info
{
width
:
100%
;
.item-content
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
/* height: 65px; */
margin-top
:
8px
;
.item-content-img
{
img
{
height
:
57px
;
width
:
46px
;
}
.player-title
{
font-size
:
16px
;
line-height
:
20px
;
margin-bottom
:
4px
;
}
.player-user
{
.item-content-info
{
flex
:
1
;
padding-left
:
8px
;
.item-content-info-row-first
{
display
:
flex
;
justify-content
:
space-between
;
font-size
:
14px
;
font-weight
:
500
;
.row-first-title
{
max-width
:
175px
;
}
}
.item-content-info-row-second
{
display
:
flex
;
justify-content
:
space-between
;
/* line-height: 100%; */
align-items
:
center
;
margin-top
:
1px
;
.row-second-time
{
font-size
:
12px
;
line-height
:
16px
;
color
:
rgba
(
255
,
255
,
255
,
0.6
);
&-name
{
::after
{
content
:
''
;
border-right
:
0.5px
solid
rgba
(
255
,
255
,
255
,
0.9
);
height
:
8px
;
padding-right
:
8px
;
margin-right
:
8px
;
color
:
#999999
;
.row-second-money
{
.row-second-money-num
{
font-weight
:
500
;
}
}
}
.close-player
{
margin-right
:
8px
;
.player-icon
{
width
:
32px
;
height
:
32px
;
vertical-align
:
middle
;
}
}
.player-icon
{
width
:
16px
;
height
:
16px
;
vertical-align
:
middle
;
}
.item-footer
{
display
:
flex
;
flex-direction
:
row-reverse
;
margin-top
:
17px
;
button
{
height
:
30px
;
width
:
68px
;
padding
:
0
;
border-radius
:
5px
;
font-size
:
13px
;
}
}
}
}
</
style
>
src/pages/my/my.vue
View file @
e2ac3edf
...
...
@@ -20,12 +20,14 @@
src=
"https://static.ydlcdn.com/mini/mini_consult/pic_default_avatar.png"
@
click=
"handleLoginClick"
/>
<text
v-if=
"isLogin"
class=
"user-name"
>
<view
v-if=
"isLogin"
>
<text
class=
"user-name-phone"
>
{{
userInfo
.
nickName
}}
</text>
<text
class=
"user-name-phone"
>
{{
userPhoneTxt
}}
</text>
</view>
<view
v-else
@
click=
"handleLoginClick"
...
...
@@ -70,7 +72,7 @@
</template>
<
script
>
import
{
hostPrefix
,
h2Prefix
}
from
'@/config'
import
{
hostPrefix
}
from
'@/config'
import
{
setTrackData
}
from
'@/utils/util'
export
default
{
...
...
@@ -79,26 +81,6 @@ export default {
return
{
menus
:
[
{
icon
:
'/static/icon_order.png'
,
label
:
'我的订单'
,
callback
:
()
=>
{
this
.
handleSendMenuTrackData
(
'我的订单'
)
this
.
navigateToOrder
()
},
},
{
icon
:
'/static/icon_help_center.png'
,
label
:
'帮助中心'
,
callback
:
()
=>
{
this
.
handleSendMenuTrackData
(
'帮助中心'
)
uni
.
navigateTo
({
url
:
`/pages/web/web?title=帮助中心&loadUrl=
${
encodeURIComponent
(
`
${
h2Prefix
}
/help/list/12`
,
)}
`
,
})
},
},
{
icon
:
'/static/icon_custom_service.png'
,
label
:
'联系客服'
,
callback
:
()
=>
{
...
...
@@ -142,6 +124,13 @@ export default {
isLogin
()
{
return
this
.
$store
.
getters
.
isLogin
},
// 混淆手机号码
userPhoneTxt
()
{
if
(
this
.
userInfo
.
phone
)
{
return
String
(
this
.
userInfo
.
phone
).
replace
(
/
(
.
{3})(
.
{4})(
.+$
)
/
,
'$1****$3'
)
}
return
''
},
},
mounted
()
{
// 页面访问埋点
...
...
@@ -243,6 +232,14 @@ export default {
font-size
:
18px
;
margin-bottom
:
4px
;
}
.user-name-phone
{
margin-left
:
16px
;
color
:
#000000
;
font-weight
:
400
;
font-size
:
15px
;
margin-bottom
:
4px
;
display
:
block
;
}
.login-tips
{
margin-left
:
16px
;
...
...
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