@x-edu/live-player 0.0.10 → 0.0.12
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.
- package/README.md +41 -1
- package/dist/XEduLivePlayer.common.js +49384 -25752
- package/dist/XEduLivePlayerPre.common.js +49364 -25732
- package/package.json +1 -1
- package/src/App.jsx +3 -1
- package/src/component/AliPlayer/index.jsx +7 -4
- package/src/component/Avatar/index.jsx +1 -3
- package/src/component/Icon/index.jsx +16 -0
- package/src/component/Icon/index.module.less +3 -0
- package/src/component/Pagination/LocalPagination.jsx +27 -0
- package/src/component/Pagination/RemotePagination.jsx +32 -0
- package/src/component/Pagination/index.jsx +31 -0
- package/src/component/Pagination/index.module.less +110 -0
- package/src/config/request/live-activity.js +1 -1
- package/src/demo/Detail.jsx +11 -0
- package/src/demo/List.jsx +62 -0
- package/src/demo/index.jsx +17 -0
- package/src/detail/LiveVideo/index.jsx +1 -1
- package/src/index.js +2 -7
- package/src/list/Empty/img/empty.png +0 -0
- package/src/list/Empty/index.jsx +20 -0
- package/src/list/Empty/index.module.less +23 -0
- package/src/list/ListItem/Action/index.jsx +98 -0
- package/src/list/ListItem/Action/index.module.less +22 -0
- package/src/list/ListItem/img/live.png +0 -0
- package/src/list/ListItem/img/play.svg +16 -0
- package/src/list/ListItem/index.jsx +95 -0
- package/src/list/ListItem/index.module.less +123 -0
- package/src/list/index.jsx +150 -0
- package/src/list/index.module.less +36 -0
- package/src/service/live.js +57 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
import Avatar from '@/component/Avatar'
|
|
5
|
+
import Icon from '@/component/Icon'
|
|
6
|
+
import Action from './Action'
|
|
7
|
+
import DefaultCover from './img/live.png'
|
|
8
|
+
import PlayImg from './img/play.svg'
|
|
9
|
+
import style from './index.module.less'
|
|
10
|
+
|
|
11
|
+
const TagName = {
|
|
12
|
+
0: '预告',
|
|
13
|
+
1: '直播中',
|
|
14
|
+
2: '回放'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function ListItem({
|
|
18
|
+
data,
|
|
19
|
+
subscription,
|
|
20
|
+
onDetailClick,
|
|
21
|
+
handleLogin,
|
|
22
|
+
isLogin,
|
|
23
|
+
onSubscribe
|
|
24
|
+
}) {
|
|
25
|
+
const {
|
|
26
|
+
live_id: liveId,
|
|
27
|
+
live_name: liveName,
|
|
28
|
+
begin_time: beginTime,
|
|
29
|
+
end_time: endTime,
|
|
30
|
+
cover_url: coverUrl,
|
|
31
|
+
status
|
|
32
|
+
} = data
|
|
33
|
+
|
|
34
|
+
const handleDetailClick = async () => {
|
|
35
|
+
if (data.login && !isLogin) {
|
|
36
|
+
handleLogin(data)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if (onDetailClick) {
|
|
40
|
+
onDetailClick(data)
|
|
41
|
+
} else {
|
|
42
|
+
// 默认跳转中小学
|
|
43
|
+
window.open(`https://basic.smartedu.cn/publicLive/${liveId}}`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
className={style.container}
|
|
50
|
+
onClick={handleDetailClick}
|
|
51
|
+
>
|
|
52
|
+
<div className={style.left}>
|
|
53
|
+
<Avatar
|
|
54
|
+
src={coverUrl || DefaultCover}
|
|
55
|
+
errorImg={DefaultCover}
|
|
56
|
+
className={style.avatar}
|
|
57
|
+
/>
|
|
58
|
+
<div className={classNames(style.tag, style[`tag${status}`])}>
|
|
59
|
+
{status === 1 ? <img src={PlayImg} alt="" /> : null}
|
|
60
|
+
{TagName[status]}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<div className={style.right}>
|
|
64
|
+
<div className={style['right-top']}>
|
|
65
|
+
<div className={style.title}>{liveName}</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div className={style['right-bottom']}>
|
|
68
|
+
<div className={style.time}>
|
|
69
|
+
<Icon type="web_icon_shijian_fill" className={style['time-icon']} />
|
|
70
|
+
{status === 2 ? (
|
|
71
|
+
<>
|
|
72
|
+
{`${dayjs(beginTime).format('YYYY/MM/DD HH:mm')}-${dayjs(endTime).format('HH:mm')}`}
|
|
73
|
+
</>
|
|
74
|
+
) : (
|
|
75
|
+
<>
|
|
76
|
+
{dayjs(beginTime).format('YYYY/MM/DD HH:mm')}
|
|
77
|
+
开始
|
|
78
|
+
</>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
<div>
|
|
82
|
+
<Action
|
|
83
|
+
data={data}
|
|
84
|
+
subscription={subscription}
|
|
85
|
+
onActionDetailClick={handleDetailClick}
|
|
86
|
+
handleLogin={handleLogin}
|
|
87
|
+
isLogin={isLogin}
|
|
88
|
+
onSubscribe={onSubscribe}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
padding: 24px 0;
|
|
3
|
+
margin: 0 24px;
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: flex-start;
|
|
6
|
+
border-top: 1px solid #eee;
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
|
|
9
|
+
&:first-child {
|
|
10
|
+
border-top: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.left {
|
|
14
|
+
margin-right: 24px;
|
|
15
|
+
border-radius: 8px;
|
|
16
|
+
position: relative;
|
|
17
|
+
|
|
18
|
+
.avatar {
|
|
19
|
+
width: 204px;
|
|
20
|
+
height: 120px;
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
object-fit: cover;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.tag {
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0px;
|
|
28
|
+
border-radius: 4px 0px;
|
|
29
|
+
font-size: 12px;
|
|
30
|
+
line-height: 12px;
|
|
31
|
+
height: 20px;
|
|
32
|
+
padding: 0 8px;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
color: #fff;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.tag0 { // 预告
|
|
39
|
+
background-color: #FF7826;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.tag1 { // 直播中
|
|
43
|
+
background-color: #1E62EC;
|
|
44
|
+
padding: 0 5px;
|
|
45
|
+
|
|
46
|
+
img {
|
|
47
|
+
transform: rotate(180deg);
|
|
48
|
+
height: 10px;
|
|
49
|
+
width: 10px;
|
|
50
|
+
margin-right: 5px;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.tag2 { // 回放
|
|
55
|
+
background-color: rgba(0, 0, 0, 0.60);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.right {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
justify-content: space-between;
|
|
63
|
+
width: calc(100% - 228px);
|
|
64
|
+
|
|
65
|
+
&-top {
|
|
66
|
+
.title {
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
text-overflow: ellipsis;
|
|
69
|
+
display: -webkit-box;
|
|
70
|
+
-webkit-box-orient: vertical;
|
|
71
|
+
word-wrap: break-word;
|
|
72
|
+
line-height: 28px;
|
|
73
|
+
-webkit-line-clamp: 2;
|
|
74
|
+
font-size: 20px;
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
color: #333;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&-bottom {
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: space-between;
|
|
83
|
+
align-items: center;
|
|
84
|
+
|
|
85
|
+
.time {
|
|
86
|
+
font-size: 14px;
|
|
87
|
+
color: #999;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
&-icon {
|
|
91
|
+
width: 16px;
|
|
92
|
+
height: 16px;
|
|
93
|
+
color: #ccc;
|
|
94
|
+
margin-right: 4px;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.container:hover {
|
|
102
|
+
.title {
|
|
103
|
+
color: #1E62EC;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.action {
|
|
108
|
+
font-size: 14px;
|
|
109
|
+
line-height: 22px;
|
|
110
|
+
color: #1E62EC;
|
|
111
|
+
border-radius: 144.889px;
|
|
112
|
+
border: 1px solid #1E62EC;
|
|
113
|
+
width: 88px;
|
|
114
|
+
height: 32px;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.action-subscribe {
|
|
121
|
+
background: #1E62EC;
|
|
122
|
+
color: #fff;
|
|
123
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { Tabs } from 'fish'
|
|
4
|
+
import { OpenLiveSearchFront, getSubscription } from '@/service/live'
|
|
5
|
+
import { setUC } from '@/util/auth/func'
|
|
6
|
+
import config from '@/config/env'
|
|
7
|
+
import Loading from '@/component/status/Loading'
|
|
8
|
+
import Empty from './Empty'
|
|
9
|
+
import Pagination from '@/component/Pagination'
|
|
10
|
+
import ListItem from './ListItem'
|
|
11
|
+
import style from './index.module.less'
|
|
12
|
+
|
|
13
|
+
const PageSize = 7
|
|
14
|
+
|
|
15
|
+
const StatusEnum = [
|
|
16
|
+
{
|
|
17
|
+
label: '全部',
|
|
18
|
+
value: 'all'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: '直播中',
|
|
22
|
+
value: 1
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: '预告',
|
|
26
|
+
value: 0
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: '回放',
|
|
30
|
+
value: 2
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
export default function PublicLiveList({
|
|
35
|
+
scopeType,
|
|
36
|
+
scopeId,
|
|
37
|
+
containerClassName,
|
|
38
|
+
uc,
|
|
39
|
+
loginInfo,
|
|
40
|
+
sdpAppId: propSdpAppId,
|
|
41
|
+
onDetailClick,
|
|
42
|
+
handleLogin = () => {},
|
|
43
|
+
onSubscribe = () => {}
|
|
44
|
+
}) {
|
|
45
|
+
setUC(uc, loginInfo?.userInfo)
|
|
46
|
+
if (propSdpAppId) {
|
|
47
|
+
config.app.appid = propSdpAppId
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const [list, setList] = useState([])
|
|
51
|
+
const [total, setTotal] = useState(0)
|
|
52
|
+
const [loading, setLoading] = useState(true)
|
|
53
|
+
const [page, setPage] = useState(1)
|
|
54
|
+
const [status, setStatus] = useState('all')
|
|
55
|
+
const [subscription, setSubscription] = useState([])
|
|
56
|
+
const [isLogin, setIsLogin] = useState(false)
|
|
57
|
+
|
|
58
|
+
const handleTabChange = (value) => {
|
|
59
|
+
setStatus(value)
|
|
60
|
+
setPage(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handlePageChange = (value) => {
|
|
64
|
+
setPage(value)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 分页获取直播列表
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
async function getList() {
|
|
70
|
+
setLoading(true)
|
|
71
|
+
const data = await OpenLiveSearchFront({
|
|
72
|
+
status,
|
|
73
|
+
scopeType,
|
|
74
|
+
scopeId,
|
|
75
|
+
offset: (page - 1) * PageSize,
|
|
76
|
+
limit: PageSize
|
|
77
|
+
})
|
|
78
|
+
const { items, count } = data
|
|
79
|
+
if (uc) {
|
|
80
|
+
const curIsLogin = await uc.isLogin()
|
|
81
|
+
if (curIsLogin) {
|
|
82
|
+
const curSubscription = await getSubscription()
|
|
83
|
+
setSubscription(curSubscription)
|
|
84
|
+
setIsLogin(true)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
setList(items)
|
|
88
|
+
setTotal(count)
|
|
89
|
+
setLoading(false)
|
|
90
|
+
}
|
|
91
|
+
getList()
|
|
92
|
+
}, [page, status])
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
className={classNames(
|
|
97
|
+
containerClassName,
|
|
98
|
+
style.container
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
<Tabs
|
|
102
|
+
accessKey={status}
|
|
103
|
+
onChange={handleTabChange}
|
|
104
|
+
>
|
|
105
|
+
{StatusEnum.map((item) => (
|
|
106
|
+
<Tabs.TabPane
|
|
107
|
+
key={item.value}
|
|
108
|
+
tab={item.label}
|
|
109
|
+
>
|
|
110
|
+
<Loading
|
|
111
|
+
loading={loading}
|
|
112
|
+
>
|
|
113
|
+
<div className={style.list}>
|
|
114
|
+
{loading ? null : (
|
|
115
|
+
<>
|
|
116
|
+
{!loading && list.length ? (
|
|
117
|
+
<>
|
|
118
|
+
{list.map((listItem) => (
|
|
119
|
+
<ListItem
|
|
120
|
+
data={listItem}
|
|
121
|
+
key={listItem.live_id}
|
|
122
|
+
subscription={subscription}
|
|
123
|
+
onDetailClick={onDetailClick}
|
|
124
|
+
handleLogin={handleLogin}
|
|
125
|
+
isLogin={isLogin}
|
|
126
|
+
onSubscribe={onSubscribe}
|
|
127
|
+
/>
|
|
128
|
+
))}
|
|
129
|
+
<Pagination
|
|
130
|
+
total={total}
|
|
131
|
+
pageSize={PageSize}
|
|
132
|
+
current={page}
|
|
133
|
+
onChange={handlePageChange}
|
|
134
|
+
/>
|
|
135
|
+
</>
|
|
136
|
+
) : (
|
|
137
|
+
<Empty
|
|
138
|
+
tip="暂无内容"
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</Loading>
|
|
145
|
+
</Tabs.TabPane>
|
|
146
|
+
))}
|
|
147
|
+
</Tabs>
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
max-width: 1300px;
|
|
3
|
+
margin: 0 auto;
|
|
4
|
+
padding: 24px 24px 0px 24px;
|
|
5
|
+
width: 956px;
|
|
6
|
+
|
|
7
|
+
:global {
|
|
8
|
+
.fish-tabs-nav {
|
|
9
|
+
margin-bottom: 24px;
|
|
10
|
+
}
|
|
11
|
+
.fish-tabs-nav::before {
|
|
12
|
+
display: none;
|
|
13
|
+
}
|
|
14
|
+
.fish-tabs-ink-bar {
|
|
15
|
+
display: none;
|
|
16
|
+
}
|
|
17
|
+
.fish-tabs-tab + .fish-tabs-tab {
|
|
18
|
+
margin-left: 0px;
|
|
19
|
+
}
|
|
20
|
+
.fish-tabs-tab {
|
|
21
|
+
padding: 9px 20px;
|
|
22
|
+
}
|
|
23
|
+
.fish-tabs-tab-btn {
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
line-height: 14px;
|
|
26
|
+
color: #666
|
|
27
|
+
}
|
|
28
|
+
.fish-tabs-tab-active {
|
|
29
|
+
border-radius: 16px;
|
|
30
|
+
background: var(--f-3-f-7-ff, #F3F7FF);
|
|
31
|
+
.fish-tabs-tab-btn {
|
|
32
|
+
color: #1E62EC;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/service/live.js
CHANGED
|
@@ -234,3 +234,60 @@ export async function getLiveInfo(ids) {
|
|
|
234
234
|
return result.map((res) => res.data)
|
|
235
235
|
// return result?.data || {}
|
|
236
236
|
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 查询直播列表-前台
|
|
240
|
+
*/
|
|
241
|
+
export async function OpenLiveSearchFront({
|
|
242
|
+
status,
|
|
243
|
+
scopeType,
|
|
244
|
+
scopeId,
|
|
245
|
+
offset,
|
|
246
|
+
limit
|
|
247
|
+
}) {
|
|
248
|
+
try {
|
|
249
|
+
const url = '/v1/open_class_lives/search_front'
|
|
250
|
+
const response = await proxyApi.get(url, {
|
|
251
|
+
params: {
|
|
252
|
+
status: status === 'all' ? undefined : status,
|
|
253
|
+
scope_type: scopeType,
|
|
254
|
+
scope_id: scopeId,
|
|
255
|
+
offset,
|
|
256
|
+
limit
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
return response?.data || {
|
|
260
|
+
count: 0,
|
|
261
|
+
items: []
|
|
262
|
+
}
|
|
263
|
+
} catch (_) {
|
|
264
|
+
return {
|
|
265
|
+
count: 0,
|
|
266
|
+
items: []
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 获取预约直播
|
|
273
|
+
*/
|
|
274
|
+
export async function getSubscription() {
|
|
275
|
+
try {
|
|
276
|
+
const url = '/v1/open_class_lives/actions/get_subscription'
|
|
277
|
+
const response = await liveActivityAPI.get(url)
|
|
278
|
+
return response.data
|
|
279
|
+
} catch (_) {
|
|
280
|
+
return []
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 预约直播
|
|
286
|
+
*/
|
|
287
|
+
export async function openLiveSubscribe({
|
|
288
|
+
liveId
|
|
289
|
+
}) {
|
|
290
|
+
const url = `/v1/open_class_lives/${liveId}/actions/subscribe`
|
|
291
|
+
const response = await liveActivityAPI.post(url)
|
|
292
|
+
return response?.data
|
|
293
|
+
}
|