Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mr-julyedu
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
baiguangyao
mr-julyedu
Commits
c945923f
Commit
c945923f
authored
Aug 13, 2020
by
zhanghaozhe
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'search-result' into dev
# Conflicts: # public/api.js
parents
ba312042
40315e34
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
431 additions
and
285 deletions
+431
-285
.storybook/preview.js
+4
-2
public/api.js
+9
-10
src/common/address/address.stories.tsx
+43
-0
src/common/address/index.scss
+66
-0
src/common/address/index.tsx
+43
-11
src/components/search/search-result.js
+178
-186
src/components/search/search-result.scss
+88
-76
No files found.
.storybook/preview.js
View file @
c945923f
import
'./style.scss'
\ No newline at end of file
import
'./style.scss'
import
'../src/assets/font/iconfont.css'
import
'@csstools/normalize.css/normalize.css'
\ No newline at end of file
public/api.js
View file @
c945923f
var
API
=
{
'www'
:
'http://www-test.julyedu.com'
,
'home'
:
'http://fast-test.julyedu.com'
,
'search-api'
:
'https://search.julyedu.com'
,
'passport-api'
:
'http://passport-test.julyedu.com'
,
'base-api'
:
'http://api-test.julyedu.com'
,
'record'
:
'record.julyedu.com:8001'
,
'process-api'
:
'ws:process-test.julyedu.com:9502'
,
'm'
:
'http://m-test.julyedu.com'
,
//terminal
'ws'
:
'ws://47.93.119.175:8888/ws'
,
'www'
:
'http://www-test.julyedu.com'
,
'home'
:
'http://fast-test.julyedu.com'
,
'search-api'
:
'http://search-test.julyedu.com'
,
'passport-api'
:
'http://passport-test.julyedu.com'
,
'base-api'
:
'http://api-test.julyedu.com'
,
'record'
:
'record.julyedu.com:8001'
,
'process-api'
:
'ws:process-test.julyedu.com:9502'
,
'm'
:
'http://m-test.julyedu.com'
,
'ws'
:
'ws://47.93.119.175:8888/ws'
,
}
src/common/address/address.stories.tsx
0 → 100644
View file @
c945923f
import
React
from
'react'
import
Address
from
"./index"
;
import
{
text
,
boolean
,
withKnobs
}
from
'@storybook/addon-knobs'
import
{
action
}
from
'@storybook/addon-actions'
export
default
{
title
:
'address'
,
component
:
Address
,
decorators
:
[
withKnobs
]
}
let
visible
=
true
,
title
=
'收货地址'
,
subtitle
=
'获奖用户(以最终榜单为准)请及时填写收货信息'
,
address
=
'金域国际中心'
,
phone
=
'1331234123'
,
name
=
'某某某'
export
const
Default
=
()
=>
{
visible
=
boolean
(
'visible'
,
visible
)
name
=
text
(
'name'
,
name
)
phone
=
text
(
'phone'
,
phone
)
address
=
text
(
'address'
,
address
)
title
=
text
(
'title(optional)'
,
title
)
subtitle
=
text
(
'subtitle(optional)'
,
subtitle
)
return
<
Address
visible=
{
visible
}
subtitle=
{
subtitle
}
title=
{
title
}
name=
{
name
}
phone=
{
phone
}
address=
{
address
}
onClose=
{
action
(
'onClose'
)
}
validate=
{
()
=>
({
name
:
'姓名'
})
}
onError=
{
(
errors
)
=>
{
console
.
log
(
errors
);}
}
onSubmit=
{
(
values
,
formikHelpers
)
=>
{
console
.
log
(
formikHelpers
);
}
}
/>
}
\ No newline at end of file
src/common/address/index.scss
View file @
c945923f
.common-address-container
{
position
:
absolute
;
left
:
50%
;
top
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
width
:
300px
;
padding
:
20px
25px
;
background
:
#fff
;
border-radius
:
10px
;
box-sizing
:
border-box
;
text-align
:
center
;
.title
{
margin-bottom
:
8px
;
color
:
#525C65
;
font-size
:
16px
;
}
.subtitle
{
margin-bottom
:
15px
;
font-size
:
13px
;
color
:
#ED6A1D
;
text-align
:
left
;
}
input
{
width
:
250px
;
height
:
40px
;
line-height
:
40px
;
border
:
1px
solid
#DDD
;
font-size
:
13px
;
color
:
#999
;
outline
:
0
;
-webkit-appearance
:
none
;
&
:nth-child
(
n
+
2
)
{
margin-top
:
10px
;
}
&
:
:
placeholder
{
color
:
#999
;
}
}
.submit
{
margin-top
:
18px
;
width
:
121px
;
height
:
33px
;
background
:
#09f
;
border-radius
:
17px
;
font-size
:
15px
;
color
:
#fff
;
-webkit-appearance
:
none
;
outline
:
0
;
border
:
0
;
}
.close
{
position
:
absolute
;
left
:
50%
;
bottom
:
-56px
;
transform
:
translateX
(
-50%
);
font-size
:
26px
;
color
:
#fff
;
}
}
\ No newline at end of file
src/common/address/index.tsx
View file @
c945923f
import
React
,
{
Component
}
from
'react'
import
'./index.scss'
import
MaskCover
from
"../cover"
;
import
{
Formik
,
Form
,
Field
,
FormikHelpers
,
FormikErrors
}
from
"formik"
;
interface
PersonalInfo
{
name
:
string
...
...
@@ -9,20 +10,51 @@ interface PersonalInfo {
}
interface
Props
extends
PersonalInfo
{
subtitle
:
string
subtitle
?
:
string
title
?:
string
onSubmit
?:
()
=>
PersonalInfo
onClose
:
()
=>
void
visible
:
boolean
validate
:
()
=>
void
onSubmit
:
(
values
:
PersonalInfo
,
formikHelpers
:
FormikHelpers
<
PersonalInfo
>
)
=>
void
onError
:
(
errors
:
FormikErrors
<
PersonalInfo
>
)
=>
void
}
class
Address
extends
Component
<
Props
>
{
render
()
{
return
<
MaskCover
>
<
div
className=
"address-container"
>
<
div
className=
"title"
></
div
>
<
div
className=
"subtitle"
></
div
>
</
div
>
</
MaskCover
>
}
const
Address
:
React
.
FC
<
Props
>
=
({
name
,
phone
,
address
,
title
,
subtitle
,
visible
,
onClose
,
validate
,
onSubmit
,
onError
})
=>
{
return
(
visible
?
<
MaskCover
>
<
div
className=
"common-address-container"
>
<
div
className=
"title"
>
{
title
}
</
div
>
<
div
className=
"subtitle"
>
{
subtitle
}
</
div
>
<
Formik
initialValues=
{
{
name
,
phone
,
address
}
}
enableReinitialize=
{
true
}
onSubmit=
{
onSubmit
}
validate=
{
validate
}
>
{
(
props
)
=>
{
if
(
props
.
errors
)
{
onError
(
props
.
errors
)
}
return
<
Form
className=
{
'form'
}
>
<
Field
placeholder=
{
'姓名'
}
name=
'name'
></
Field
>
<
Field
placeholder=
{
'手机号'
}
name=
'phone'
></
Field
>
<
Field
placeholder=
{
'地址'
}
name=
'address'
></
Field
>
<
button
type=
{
'submit'
}
className=
{
'submit'
}
>
提交
</
button
>
</
Form
>
}
}
</
Formik
>
<
i
className=
{
'iconfont iconiconfront-2 close'
}
onClick=
{
onClose
}
></
i
>
</
div
>
</
MaskCover
>
:
null
)
}
Address
.
defaultProps
=
{
title
:
'收货信息'
,
subtitle
:
'获奖用户(以最终榜单为准)请及时填写收货信息'
}
export
default
Address
src/components/search/search-result.js
View file @
c945923f
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
import
SearchHeader
from
'./searchHead'
import
VList
from
'src/common/VList'
import
{
http
,
getParam
}
from
'src/utils'
...
...
@@ -6,199 +6,192 @@ import './search-result.scss'
import
Recommendation
from
'./recommendation'
import
throttle
from
'lodash/throttle'
const
ForwardRefSearchHead
=
React
.
forwardRef
((
props
,
ref
)
=>
{
return
<
SearchHeader
{...
props
}
forwardedRef
=
{
ref
}
/
>
return
<
SearchHeader
{...
props
}
forwardedRef
=
{
ref
}
/
>
})
const
Bottom
=
({
item
})
=>
{
return
(
<
div
className
=
'bottom'
>
<
span
className
=
'price'
>
¥
{
item
.
price1
}
<
/span
>
<
span
className
=
'stale-price'
>
¥
{
item
.
price0
}
<
/span
>
<
/div
>
)
const
Bottom
=
({
item
})
=>
{
return
(
<
div
className
=
"bottom"
>
<
span
className
=
"price"
>
¥
{
item
.
price1
}
<
/span
>
<
span
className
=
"stale-price"
>
¥
{
item
.
price0
}
<
/span
>
<
/div
>
)
}
class
SearchResult
extends
PureComponent
{
prevScrollY
=
0
searchHead
=
React
.
createRef
()
swipeUp
=
'up'
swipeDown
=
'down'
state
=
{
courseList
:
[],
value
:
decodeURIComponent
(
getParam
(
'word'
))
||
''
,
searchHistory
:
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
))
||
[],
fixedHeader
:
false
,
searchHeadStyle
:
{
top
:
0
},
swipeDirection
:
this
.
swipeUp
,
isHide
:
false
,
basicTop
:
0
}
componentDidMount
()
{
this
.
getCourses
(
getParam
(
'word'
))
document
.
addEventListener
(
'scroll'
,
this
.
handleScroll
)
}
componentWillUnmount
()
{
document
.
removeEventListener
(
'scroll'
,
this
.
handleScroll
)
}
getCourses
=
(
word
)
=>
{
http
.
get
(
`
${
API
[
'search-api'
]}
/search/
${
word
}
?type=course&page=1`
)
.
then
(
res
=>
{
const
data
=
res
.
data
if
(
data
.
errno
===
0
)
{
this
.
setState
({
courseList
:
data
.
data
.
info
[
'search_data'
].
course
});
}
})
prevScrollY
=
0
searchHead
=
React
.
createRef
()
swipeUp
=
'up'
swipeDown
=
'down'
state
=
{
courseList
:
[],
value
:
decodeURIComponent
(
getParam
(
'word'
))
||
''
,
searchHistory
:
JSON
.
parse
(
localStorage
.
getItem
(
'searchHistory'
))
||
[],
fixedHeader
:
false
,
searchHeadStyle
:
{
top
:
0
},
swipeDirection
:
this
.
swipeUp
,
isHide
:
false
,
basicTop
:
0
,
resultCount
:
0
,
}
componentDidMount
()
{
this
.
getCourses
(
getParam
(
'word'
))
document
.
addEventListener
(
'scroll'
,
this
.
handleScroll
)
}
componentWillUnmount
()
{
document
.
removeEventListener
(
'scroll'
,
this
.
handleScroll
)
}
getCourses
=
word
=>
{
http
.
get
(
`
${
API
[
'search-api'
]}
/search/
${
word
}
?type=v2-course&page=1`
).
then
(
res
=>
{
const
data
=
res
.
data
if
(
data
.
errno
===
0
)
{
this
.
setState
({
courseList
:
data
.
data
.
info
[
'search_data'
].
course
,
resultCount
:
data
.
data
.
info
[
'search_data'
].
num
,
})
}
})
}
handleClick
=
id
=>
{
this
.
props
.
history
.
push
(
`/detail?id=
${
id
}
`
)
}
handleSearch
=
()
=>
{
this
.
state
.
value
&&
this
.
getCourses
(
this
.
state
.
value
)
}
handleChange
=
value
=>
{
this
.
setState
({
value
})
}
toCourseDetail
=
id
=>
{
const
{
history
}
=
this
.
props
history
.
push
(
`/detail?id=
${
id
}
`
)
}
handleScroll
=
throttle
(()
=>
{
let
y
=
window
.
scrollY
<
0
?
0
:
window
.
scrollY
,
headY
=
this
.
searchHead
.
current
.
offsetTop
,
h
=
this
.
searchHead
.
current
.
offsetHeight
if
(
y
>
this
.
prevScrollY
)
{
this
.
setState
({
searchHeadStyle
:
{
top
:
`
${
-
h
}
px`
,
},
})
}
handleClick
=
id
=>
{
this
.
props
.
history
.
push
(
`/detail?id=
${
id
}
`
)
}
handleSearch
=
()
=>
{
this
.
state
.
value
&&
this
.
getCourses
(
this
.
state
.
value
)
if
(
y
<
this
.
prevScrollY
)
{
this
.
setState
({
searchHeadStyle
:
{
top
:
0
,
},
})
}
// if (y < this.prevScrollY) {
// if (this.state.swipeDirection === this.swipeDown) {
// y <= headY && this.state.searchHeadStyle.position !== 'fixed' &&
// this.setState({
// searchHeadStyle: {
// top: `0`,
// position: 'fixed'
// }
// })
// } else {
// this.setState({
// swipeDirection: this.swipeDown
// }, () => {
// if (this.state.swipeDirection === this.swipeDown) {
// let h = y > document.querySelector('body').offsetHeight? document.querySelector('body').offsetHeight: y;
// let h1 = this.searchHead.current.offsetHeight
// this.setState({
// searchHeadStyle: {
// // top: `${h > headY ? h - h1 : h}px`
// top: `${y}px`
// }
// })
// }
// })
// }
// } else {
// this.state.swipeDirection !== this.swipeUp &&
// this.setState({
// swipeDirection: this.swipeUp,
// searchHeadStyle: {
// position: 'absolute',
// top: `${y}px`
// }
// })
// }
this
.
prevScrollY
=
y
},
0
)
render
()
{
const
{
courseList
,
isHide
,
resultCount
}
=
this
.
state
handleChange
=
value
=>
{
this
.
setState
({
value
})
}
toCourseDetail
=
(
id
)
=>
{
const
{
history
}
=
this
.
props
;
history
.
push
(
`/detail?id=
${
id
}
`
)
}
handleScroll
=
throttle
(()
=>
{
let
y
=
window
.
scrollY
<
0
?
0
:
window
.
scrollY
,
headY
=
this
.
searchHead
.
current
.
offsetTop
,
h
=
this
.
searchHead
.
current
.
offsetHeight
;
if
(
y
>
this
.
prevScrollY
)
{
this
.
setState
({
searchHeadStyle
:
{
top
:
`
${
-
h
}
px`
}
});
}
if
(
y
<
this
.
prevScrollY
)
{
this
.
setState
({
searchHeadStyle
:
{
top
:
0
}
});
}
// if (y < this.prevScrollY) {
// if (this.state.swipeDirection === this.swipeDown) {
// y <= headY && this.state.searchHeadStyle.position !== 'fixed' &&
// this.setState({
// searchHeadStyle: {
// top: `0`,
// position: 'fixed'
// }
// })
// } else {
// this.setState({
// swipeDirection: this.swipeDown
// }, () => {
// if (this.state.swipeDirection === this.swipeDown) {
// let h = y > document.querySelector('body').offsetHeight? document.querySelector('body').offsetHeight: y;
// let h1 = this.searchHead.current.offsetHeight
// this.setState({
// searchHeadStyle: {
// // top: `${h > headY ? h - h1 : h}px`
// top: `${y}px`
// }
// })
// }
// })
// }
// } else {
// this.state.swipeDirection !== this.swipeUp &&
// this.setState({
// swipeDirection: this.swipeUp,
// searchHeadStyle: {
// position: 'absolute',
// top: `${y}px`
// }
// })
// }
this
.
prevScrollY
=
y
;
},
0
)
render
()
{
const
{
courseList
,
isHide
}
=
this
.
state
;
return
(
<
div
className
=
{
'search-result'
}
>
<
ForwardRefSearchHead
handleSearch
=
{
this
.
handleSearch
}
value
=
{
this
.
state
.
value
}
handleChange
=
{
this
.
handleChange
}
searchHistory
=
{
this
.
state
.
searchHistory
}
style
=
{
this
.
state
.
searchHeadStyle
}
ref
=
{
this
.
searchHead
}
isHide
=
{
isHide
}
return
(
<
div
className
=
{
'search-result'
}
>
<
ForwardRefSearchHead
handleSearch
=
{
this
.
handleSearch
}
value
=
{
this
.
state
.
value
}
handleChange
=
{
this
.
handleChange
}
searchHistory
=
{
this
.
state
.
searchHistory
}
style
=
{
this
.
state
.
searchHeadStyle
}
ref
=
{
this
.
searchHead
}
isHide
=
{
isHide
}
/
>
{
resultCount
>
0
&&
<
div
className
=
"result-count"
>
共
{
resultCount
}
个结果
<
/div>
}
{
courseList
&&
courseList
.
length
>
0
?
(
<
ul
>
{
courseList
.
map
((
item
,
index
)
=>
{
const
Info
=
(
<
div
className
=
"info"
>
<
p
className
=
"title text-overflow-2"
dangerouslySetInnerHTML
=
{{
__html
:
item
.
highlight
?.
course_title
?.
length
>
0
?
item
.
highlight
.
course_title
[
0
]
:
item
.
course_title
,
}}
><
/p
>
<
p
className
=
"des text-overflow-1"
>
{
item
.
simpledescription
}
<
/p
>
<
Bottom
item
=
{
item
}
/
>
<
/div
>
)
const
status
=
item
[
'bargain_num'
]
||
item
[
'groupon_num'
]
?
(
<
div
className
=
"status"
>
{
item
[
'bargain_num'
]
===
0
?
`砍价减
${
item
[
'groupon_num'
]}
元`
:
`拼团减
${
item
[
'bargain_num'
]}
元`
}
<
/div
>
)
:
null
return
(
<
VList
img
=
{
item
.
image_name
}
toDetail
=
{
this
.
toCourseDetail
}
key
=
{
index
}
info
=
{
Info
}
id
=
{
item
[
'course_id'
]}
status
=
{
status
}
/
>
{
courseList
&&
courseList
.
length
>
0
?
<
ul
>
{
courseList
.
map
(
item
=>
{
const
Info
=
(
<
div
className
=
"info"
>
<
p
className
=
'title text-overflow-2'
>
{
item
.
course_title
}
<
/p
>
<
p
className
=
'des text-overflow-1'
>
{
item
.
simpledescription
}
<
/p
>
<
Bottom
item
=
{
item
}
/
>
<
/div
>
)
const
status
=
(
(
item
[
'bargain_num'
]
||
item
[
'groupon_num'
])
?
<
div
className
=
'status'
>
{
item
[
'bargain_num'
]
===
0
?
`砍价减
${
item
[
'groupon_num'
]}
元`
:
`拼团减
${
item
[
'bargain_num'
]}
元`
}
<
/div
>
:
null
)
return
(
<
VList
img
=
{
item
.
image_name
}
toDetail
=
{
this
.
toCourseDetail
}
key
=
{
item
.
course_id
}
info
=
{
Info
}
id
=
{
item
[
'course_id'
]}
status
=
{
status
}
/
>
)
})
}
<
/ul
>
:
<
div
className
=
"empty"
>
<
img
src
=
{
require
(
'./image/ss_empty.png'
)}
alt
=
""
/>
抱歉,没有搜到相关内容!
<
/div
>
}
<
Recommendation
/>
<
/div
>
);
}
)
})}
<
/ul
>
)
:
(
<
div
className
=
"empty"
>
<
img
src
=
{
require
(
'./image/ss_empty.png'
)}
alt
=
""
/>
抱歉,没有搜到相关内容!
<
/div
>
)}
<
Recommendation
/>
<
/div
>
)
}
}
export
default
SearchResult
;
\ No newline at end of file
export
default
SearchResult
src/components/search/search-result.scss
View file @
c945923f
@import
"src/assets/css/variable"
;
@import
'src/assets/css/variable'
;
.search-result
{
padding-top
:
44px
;
padding-top
:
44px
;
.search-head
{
// position: absolute;
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
z-index
:
10
;
transition
:
top
0
.5
ease
;
.search-head
{
// position: absolute;
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
z-index
:
10
;
transition
:
top
0
.5
ease
;
&
.hide
{
top
:
-44px
;
}
&
.hide
{
top
:
-44px
;
}
}
ul
{
list-style
:
none
;
border-top
:
1px
solid
#ddd
;
}
.result-count
{
width
:
375px
;
height
:
30px
;
background
:
#f7f7fb
;
border-top
:
1px
solid
#ddd
;
color
:
#333
;
font-size
:
12px
;
text-align
:
center
;
line-height
:
30px
;
}
.v-list-item
{
.content
{
width
:
100%
;
}
}
ul
{
list-style
:
none
;
}
.info
{
width
:
50%
;
position
:
relative
;
display
:
block
;
.title
{
font-size
:
$font_16
;
color
:
#525C65
;
}
.v-list-item
{
.content
{
width
:
100%
;
}
}
.des
{
font-size
:
$font_14
;
color
:
#777
;
margin-top
:
10px
;
}
.info
{
width
:
50%
;
position
:
relative
;
display
:
block
;
.title
{
font-size
:
$font_16
;
color
:
#525c65
;
em
{
color
:
#09f
;
}
}
.price
{
color
:
$red
;
font-size
:
$font_16
;
margin-right
:
14
px
;
}
.des
{
font-size
:
$font_14
;
color
:
#777
;
margin-top
:
10
px
;
}
.stale-
price
{
text-decoration
:
line-through
;
color
:
$color_999
;
font-size
:
$font_12
;
}
.
price
{
color
:
$red
;
font-size
:
$font_16
;
margin-right
:
14px
;
}
.bottom
{
position
:
absolute
;
bottom
:
0
;
}
.stale-price
{
text-decoration
:
line-through
;
color
:
$color_999
;
font-size
:
$font_12
;
}
.empty
{
font-size
:
12px
;
color
:
#777
;
padding
:
50px
0
30px
0
;
text-align
:
center
;
background-color
:
#fff
;
border-top
:
1px
solid
#ddd
;
img
{
width
:
100px
;
height
:
81px
;
display
:
block
;
margin
:
0
auto
15px
auto
;
}
.bottom
{
position
:
absolute
;
bottom
:
0
;
}
}
.status
{
width
:
100%
;
position
:
absolute
;
bottom
:
-2px
;
left
:
0
;
height
:
24px
;
text-align
:
center
;
line-height
:
24px
;
font-size
:
13
px
;
color
:
$white
;
background-color
:
rgba
(
224
,
46
,
36
,
0
.6
)
;
border-radius
:
0
0
4px
4px
;
.empty
{
font-size
:
12px
;
color
:
#777
;
padding
:
50px
0
30px
0
;
text-align
:
center
;
background-color
:
#fff
;
border-top
:
1px
solid
#ddd
;
img
{
width
:
100
px
;
height
:
81px
;
display
:
block
;
margin
:
0
auto
15px
auto
;
}
}
\ No newline at end of file
}
.status
{
width
:
100%
;
position
:
absolute
;
bottom
:
-2px
;
left
:
0
;
height
:
24px
;
text-align
:
center
;
line-height
:
24px
;
font-size
:
13px
;
color
:
$white
;
background-color
:
rgba
(
224
,
46
,
36
,
0
.6
);
border-radius
:
0
0
4px
4px
;
}
}
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