Compare commits
137 Commits
Author | SHA1 | Date |
---|---|---|
一杯沧海 | 88f1eb3720 | 10 months ago |
一杯沧海 | 07fed1a1e5 | 1 year ago |
一杯沧海 | e3ced26435 | 1 year ago |
一杯沧海 | 624d7cb183 | 1 year ago |
一杯沧海 | c421e30b71 | 1 year ago |
一杯沧海 | 163af32973 | 1 year ago |
一杯沧海 | 1df2db15b0 | 1 year ago |
一杯沧海 | 9178e7ba47 | 1 year ago |
一杯沧海 | 7666062ea0 | 1 year ago |
一杯沧海 | 3844bbc0ca | 1 year ago |
一杯沧海 | b33c7170ea | 1 year ago |
一杯沧海 | 2a29617017 | 1 year ago |
king | 84fbd98fa9 | 1 year ago |
king | c2b9a4812f | 1 year ago |
king | acd6b352b5 | 1 year ago |
king | a43a0fabc2 | 1 year ago |
king | 9d9506c66e | 1 year ago |
king | ee5741a752 | 1 year ago |
king | 5e6375cd4f | 1 year ago |
king | 9f17e4c886 | 1 year ago |
king | aeb6e1bbc5 | 1 year ago |
king | 3a777d0b57 | 1 year ago |
king | cb88f49e85 | 1 year ago |
king | aa4e006b00 | 1 year ago |
king | 358c3ee95c | 1 year ago |
king | 50bc02a178 | 1 year ago |
king | d52902def3 | 1 year ago |
king | bc0682351e | 1 year ago |
king | 380b7be4fa | 1 year ago |
king | fd06be84d5 | 1 year ago |
king | 22e012e711 | 1 year ago |
king | a004bceaf9 | 1 year ago |
king | a538461a9b | 1 year ago |
king | c1e6f8a5bc | 1 year ago |
56de47bf2c | 1 year ago | |
king | 93064963a7 | 1 year ago |
king | cecc425f17 | 1 year ago |
king | a318591244 | 1 year ago |
king | 278a408137 | 1 year ago |
一杯沧海 | 887b7a4bb6 | 1 year ago |
king | 4cc69d9c2f | 1 year ago |
一杯沧海 | e4acf12d87 | 1 year ago |
king | 86bc1a4cc0 | 1 year ago |
一杯沧海 | 5e00de014c | 1 year ago |
king | ae52d73588 | 1 year ago |
king | 353e1d51e0 | 1 year ago |
king | d47392954a | 1 year ago |
king | e97f59d7f8 | 1 year ago |
一杯沧海 | 29006f34f3 | 1 year ago |
一杯沧海 | c332fdca11 | 1 year ago |
king | c37e360cb6 | 1 year ago |
king | 5137d3297f | 1 year ago |
king | 805b47435f | 1 year ago |
king | 3e4a30cf1f | 1 year ago |
king | 68d99f86b1 | 1 year ago |
king | adc5d17a5d | 1 year ago |
king | fa81566f32 | 1 year ago |
king | 389827ac79 | 1 year ago |
king | 85da032f41 | 1 year ago |
4d2b83b950 | 1 year ago | |
king | ae57654a46 | 1 year ago |
king | 8f104e2a54 | 1 year ago |
king | a70dcc2de1 | 1 year ago |
king | 13c6a2d7b9 | 1 year ago |
king | 9f5e6af772 | 1 year ago |
king | 9fb280b866 | 1 year ago |
king | 485bc0815b | 1 year ago |
king | 70e61e187e | 1 year ago |
a43fc7498c | 1 year ago | |
king | dfe30478ea | 1 year ago |
king | 451828b70c | 1 year ago |
king | 9d8b36f300 | 1 year ago |
king | ce42bfd06f | 1 year ago |
king | 54e581f2ae | 1 year ago |
19e01cf623 | 1 year ago | |
king | 017d05d456 | 1 year ago |
king | c99493e9de | 1 year ago |
king | e437e131cd | 1 year ago |
king | c0038a7e93 | 1 year ago |
一杯沧海 | 5af5eb80da | 1 year ago |
king | 6b052df04c | 1 year ago |
king | 178c227b1a | 1 year ago |
一杯沧海 | e629874abe | 1 year ago |
一杯沧海 | 3c8e8f626c | 1 year ago |
一杯沧海 | eea8f99fe7 | 1 year ago |
king | 4605f01aeb | 1 year ago |
king | 38afb77146 | 1 year ago |
一杯沧海 | 806614f5d6 | 1 year ago |
king | eb218db694 | 1 year ago |
king | f8703c0754 | 1 year ago |
一杯沧海 | 8cbad8268b | 1 year ago |
一杯沧海 | 02c1a92a37 | 1 year ago |
king | be402c557b | 1 year ago |
king | cfd404dda1 | 1 year ago |
一杯沧海 | 3e5190d0bd | 1 year ago |
一杯沧海 | 1cd8fba6a3 | 1 year ago |
king | fe67088214 | 1 year ago |
king | 7075cd45ac | 1 year ago |
一杯沧海 | 3fe6226304 | 1 year ago |
king | f1bef606a9 | 1 year ago |
king | a51502d559 | 1 year ago |
king | 2fdf0d3085 | 1 year ago |
king | 8ae17e1bfc | 1 year ago |
king | d742560fb2 | 1 year ago |
一杯沧海 | aa94eb9a27 | 1 year ago |
一杯沧海 | 1775c39136 | 1 year ago |
king | bcbf3b8e41 | 1 year ago |
king | 20564f42fb | 1 year ago |
一杯沧海 | 1eb468a699 | 1 year ago |
king | 8e465b9d03 | 1 year ago |
king | 76b08de3c1 | 1 year ago |
king | b3fd259076 | 1 year ago |
king | 8cb14727c0 | 1 year ago |
一杯沧海 | 26fbcc475a | 1 year ago |
3794aca0a7 | 1 year ago | |
king | 0e46e2d76b | 1 year ago |
king | 7e03c67400 | 1 year ago |
king | 4fd03d0b8d | 1 year ago |
king | cccb057722 | 1 year ago |
king | c2ee704cf9 | 1 year ago |
一杯沧海 | 2efdeb0bd2 | 1 year ago |
一杯沧海 | 5380bc35a0 | 1 year ago |
king | 7ee05c66d0 | 1 year ago |
一杯沧海 | ea75fdc4c9 | 1 year ago |
king | c961892d9b | 1 year ago |
一杯沧海 | 7844106068 | 1 year ago |
king | 2029037d7a | 1 year ago |
king | 7b0c91dfcd | 1 year ago |
一杯沧海 | c2cea8feef | 1 year ago |
king | 50460bd891 | 1 year ago |
king | a6fed2ec3a | 1 year ago |
king | ad75871248 | 1 year ago |
king | ed2c5aedb0 | 1 year ago |
一杯沧海 | 39aa8c808c | 1 year ago |
king | 736f9387b6 | 1 year ago |
一杯沧海 | 14a6cfac38 | 1 year ago |
一杯沧海 | 6caf55b914 | 1 year ago |
@ -1,5 +1,6 @@ |
||||
#TARO_APP_API=https://yjx.dev.yaojiankang.top |
||||
TARO_APP_API=https://mooc.yaojiankang.top |
||||
#TARO_APP_API=https://mooc.yaojiankang.top |
||||
TARO_APP_API=https://xingui.yaojiankang.top |
||||
#TARO_APP_API=https://shopfix.yaojiankang.top |
||||
#TARO_APP_API=https://playedu.yaojiankang.top |
||||
TARO_APP_LGOIN=true |
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,58 @@ |
||||
import {request} from "@/api/request"; |
||||
import {Illness} from "@/api/illness"; |
||||
|
||||
export type BrandRecord = { |
||||
logo: string; |
||||
name: string; |
||||
id: number |
||||
introductory_video: string |
||||
brand_album: string[] |
||||
brand_album: string |
||||
graphic_introduction: string |
||||
disabled: number |
||||
introductory_video_resource:any |
||||
introductory_video_resource: any |
||||
article_count: number |
||||
created_at: string |
||||
page_view: number |
||||
collect: boolean |
||||
last_publish_time: string |
||||
} |
||||
export type ArticleRecord = { |
||||
id: number; |
||||
title: string |
||||
page_view: number |
||||
created_at: string |
||||
content: string |
||||
brands: BrandRecord[] |
||||
illness: Illness[] |
||||
collect: boolean |
||||
cover: string |
||||
owner_type: 1 | 2 // 疾病
|
||||
} |
||||
|
||||
export const brandApi = { |
||||
/** 品牌列表 */ |
||||
list(page: number , page_size: number) { |
||||
list(page: number, page_size: number) { |
||||
return request<{ |
||||
list: BrandRecord[], |
||||
total: number |
||||
}>(`/home/v1/brand/list?page=${page}&page_size=${page_size}` , "GET") |
||||
}>(`/home/v1/brand/list?page=${page}&page_size=${page_size}`, "GET") |
||||
}, |
||||
/** 品牌详情 */ |
||||
info(id: number) { |
||||
return request<BrandRecord>(`/home/v1/brand/${id}`, "GET") |
||||
}, |
||||
/** 文章列表 */ |
||||
articleList(owner_id: number,page:number) { |
||||
articleList(owner_id: number, page: number) { |
||||
return request<{ |
||||
list: ArticleRecord[], |
||||
list: Articles[], |
||||
total: number |
||||
}>(`/home/v1/article/list?owner_id=${owner_id}&page=${page}&page_size=10` , "GET") |
||||
}>(`/home/v1/article/list?owner_id=${owner_id}&page=${page}&page_size=10`, "GET") |
||||
}, |
||||
articleInfo(id: number ) { |
||||
return request<ArticleRecord>(`/home/v1/article/${id}` , "GET") |
||||
articleInfo(id: number) { |
||||
return request<ArticleRecord>(`/home/v1/article/${id}`, "GET") |
||||
}, |
||||
/** 品牌 & 健康详情 */ |
||||
videoInfo(id: number | string) { |
||||
return request<VideList>(`/home/v1/health/${id}`, "GET") |
||||
} |
||||
} |
||||
|
@ -0,0 +1,11 @@ |
||||
import {request} from "@/api/request"; |
||||
|
||||
export const SearchApi = { |
||||
/** 品牌列表 */ |
||||
list(page: number , page_size: number, name: string) { |
||||
return request<{ |
||||
data: any[], |
||||
total: number |
||||
}>(`/home/v1/search/home?page=${page}&page_size=${page_size}&keywords=${name}&sort_type=1&sort=0` , "GET") |
||||
}, |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,88 @@ |
||||
import { CSSProperties, FC } from "react" |
||||
import { Text, ITouchEvent } from "@tarojs/components" |
||||
import './icon.css' |
||||
|
||||
export type IconName = |
||||
| 'arrow-clockwise' |
||||
| 'arrow-counterclockwise' |
||||
| 'backspace' |
||||
| 'bar-chart' |
||||
| 'bar-chart-line' |
||||
| 'caret-down' |
||||
| 'caret-up' |
||||
| 'caret-left' |
||||
| 'caret-right' |
||||
| 'check' |
||||
| 'check-all' |
||||
| 'chevron-double-down' |
||||
| 'chevron-double-up' |
||||
| 'chevron-double-left' |
||||
| 'chevron-double-right' |
||||
| 'chevron-down' |
||||
| 'chevron-up' |
||||
| 'chevron-left' |
||||
| 'chevron-right' |
||||
| 'circle' |
||||
| 'exclamation' |
||||
| 'gear' |
||||
| 'gender-female' |
||||
| 'gender-male' |
||||
| 'heart' |
||||
| 'info' |
||||
| 'list' |
||||
| 'lock' |
||||
| 'unlock' |
||||
| 'play-btn' |
||||
| 'play' |
||||
| 'question' |
||||
| 'shield' |
||||
| 'square' |
||||
| 'star' |
||||
| 'three-dots-vertical' |
||||
| 'three-dots' |
||||
| 'trash' |
||||
| 'x' |
||||
|
||||
export type IconShape = |
||||
| 'circle' |
||||
| 'square' |
||||
| 'diamond' |
||||
| 'octagon' |
||||
| 'triangle' |
||||
|
||||
export interface IconProps { |
||||
name: IconName |
||||
shape?: IconShape |
||||
color?: string |
||||
size?: string | number |
||||
fill?: boolean |
||||
className?: string |
||||
style?: CSSProperties |
||||
onClick?: (event: ITouchEvent) => void |
||||
} |
||||
|
||||
const XgIcon: FC<IconProps> = (props) => { |
||||
const name = [ |
||||
props.name, |
||||
props.shape, |
||||
props.fill ? 'fill' : undefined |
||||
].filter(Boolean).join('-') |
||||
|
||||
const color = props.color ?? 'currentColor' |
||||
const size = typeof props.size === 'string' ? props.size : `${props.size ?? 16}px` |
||||
const fontStyle: CSSProperties = { |
||||
...(props.style ?? {}), |
||||
fontSize: size, |
||||
color, |
||||
} |
||||
|
||||
return ( |
||||
<Text |
||||
className={`xg-icon xg-icon-${name} ${props.className}`} |
||||
style={fontStyle} |
||||
onClick={props.onClick} |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export default XgIcon |
@ -0,0 +1,40 @@ |
||||
.artcles { |
||||
padding: 30rpx 0; |
||||
border-bottom: 1px solid #F5F8F7; |
||||
} |
||||
|
||||
.outside { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
|
||||
|
||||
.title { |
||||
font-size: 28rpx; |
||||
font-family: PingFang SC-Bold, PingFang SC; |
||||
font-weight: bold; |
||||
color: #323635; |
||||
} |
||||
|
||||
.intro { |
||||
font-size: 24rpx; |
||||
font-family: PingFang SC-Medium, PingFang SC; |
||||
font-weight: 500; |
||||
color: #909795; |
||||
margin-top: 10rpx; |
||||
} |
||||
} |
||||
|
||||
.img { |
||||
overflow: hidden; |
||||
border-radius: 10rpx; |
||||
} |
||||
|
||||
.pageView { |
||||
font-size: 22rpx; |
||||
font-family: PingFang SC-Medium, PingFang SC; |
||||
font-weight: 500; |
||||
color: #909795; |
||||
margin-top: 20rpx; |
||||
} |
||||
|
||||
|
@ -0,0 +1,68 @@ |
||||
import {FC, useEffect, useState} from "react"; |
||||
import {Text, View} from "@tarojs/components"; |
||||
import Taro from "@tarojs/taro"; |
||||
import styles from './articlesBox.module.scss' |
||||
import Img from "@/components/image/image"; |
||||
import {beforeTime} from "@/utils/time"; |
||||
import articlesEvent from "@/hooks/articlesEvent"; |
||||
|
||||
interface Props { |
||||
data: Articles |
||||
} |
||||
|
||||
const ArticlesBox: FC<Props> = (props) => { |
||||
const [data, setData] = useState(props.data) |
||||
|
||||
useEffect(() => { |
||||
setData(props.data) |
||||
}, [props.data]) |
||||
|
||||
useEffect(() => { |
||||
articlesEvent.recordsOn(data.id, ({view}) => { |
||||
setData({ |
||||
...data, |
||||
page_view: view |
||||
}) |
||||
}) |
||||
}, []) |
||||
|
||||
Taro.useUnload(() => { |
||||
articlesEvent.off(data.id) |
||||
}) |
||||
|
||||
Taro.useDidShow(()=>{ |
||||
articlesEvent.recordsOn(data.id, ({view}) => { |
||||
setData({ |
||||
...data, |
||||
page_view: view |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
const toArticlePage = () => { |
||||
Taro.navigateTo({ |
||||
url: `/pages/preview/brand/article/article?id=${data.id}` |
||||
}) |
||||
} |
||||
|
||||
return ( |
||||
<View className={styles.artcles} onClick={toArticlePage}> |
||||
<View className={styles.outside}> |
||||
<View className='flex-1 mr-2'> |
||||
<View className={`${styles.title} text-ellipsis-1`}>{data.title}</View> |
||||
<View className={`${styles.intro} text-ellipsis-2`}>{data.intro}</View> |
||||
<View className={styles.pageView}> |
||||
<Text>{beforeTime(data.created_at)}</Text>· |
||||
<Text>{data.page_view || 0}阅读</Text> |
||||
</View> |
||||
</View> |
||||
<Img src={data.cover || ''} |
||||
errorType={data.owner_type === 1 ? 'brand' : 'health'} |
||||
width={148} |
||||
height={116} |
||||
className={styles.img}/> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
export default ArticlesBox |
@ -0,0 +1,66 @@ |
||||
.collect { |
||||
width: 130rpx; |
||||
display: flex; |
||||
align-items: center; |
||||
color: #909795; |
||||
padding: 0 10rpx; |
||||
} |
||||
|
||||
.collectImage { |
||||
position: relative; |
||||
margin-right: 10rpx; |
||||
|
||||
|
||||
} |
||||
|
||||
.zoom { |
||||
|
||||
position: absolute; |
||||
content: ''; |
||||
width: 32rpx; |
||||
height: 32rpx; |
||||
border-radius: 50%; |
||||
border: 1px solid #FF9E5F; |
||||
padding: 0 !important; |
||||
margin: 0 !important; |
||||
animation: Zooms 300ms ease forwards; |
||||
opacity: 1; |
||||
transform-origin: -100%, -100% 0; |
||||
} |
||||
|
||||
.pulse { |
||||
animation: Pulse 300ms; |
||||
} |
||||
|
||||
@keyframes Zooms { |
||||
0% { |
||||
transform: scale(1); |
||||
} |
||||
30% { |
||||
transform: scale(1.5); |
||||
left: -5%; |
||||
top: -5%; |
||||
opacity: 1; |
||||
} |
||||
100% { |
||||
transform: scale(1.5); |
||||
left: -5%; |
||||
top: -5%; |
||||
opacity: 0; |
||||
} |
||||
} |
||||
|
||||
@keyframes Pulse { |
||||
0% { |
||||
transform: scale(.2); |
||||
} |
||||
60% { |
||||
transform: scale(1.2); |
||||
} |
||||
80% { |
||||
transform: scale(.8); |
||||
} |
||||
100% { |
||||
transform: scale(1); |
||||
} |
||||
} |
@ -0,0 +1,82 @@ |
||||
import {CSSProperties, FC, useCallback, useEffect, useState} from "react"; |
||||
// import star from '@/static/img/star.png'
|
||||
// import starLine from '@/static/img/starLine.png'
|
||||
import {View,Text} from "@tarojs/components"; |
||||
import styles from './collect.module.scss' |
||||
import {Profile} from "@/store" |
||||
import Taro from "@tarojs/taro"; |
||||
import {userApi} from "@/api"; |
||||
import IconFont from "@/components/IconFont"; |
||||
|
||||
interface Props { |
||||
onClick?: () => void |
||||
select?: boolean |
||||
stylesImage?: CSSProperties |
||||
styles?: CSSProperties |
||||
owner_id: number |
||||
owner_type: CreateOwnerType |
||||
textHidden?: boolean |
||||
onUpdate?: (v: boolean) => void |
||||
} |
||||
|
||||
const versions = new Map() |
||||
|
||||
const collect = async (scope: string, params: Create, setSelect: (v: boolean) => void, value: boolean) => { |
||||
const localVersion = (versions.get(scope) || 0) + 1 |
||||
versions.set(scope, localVersion) |
||||
await userApi.create(params) |
||||
if (localVersion === versions.get(scope)) { |
||||
setSelect(value) |
||||
} |
||||
} |
||||
|
||||
/** 收藏 */ |
||||
const Collect: FC<Props> = (props) => { |
||||
const {token} = Profile.useContainer() |
||||
const [loading, setLoading] = useState(false) |
||||
const [select, setSelect] = useState(props.select) |
||||
|
||||
useEffect(() => { |
||||
setSelect(props.select) |
||||
}, [props]) |
||||
|
||||
const change = useCallback(async () => { |
||||
if (!token) { |
||||
Taro.navigateTo({url: '/pages/login/login'}) |
||||
return |
||||
} |
||||
props.onClick?.() |
||||
setSelect(!select) |
||||
setLoading(true) |
||||
collect(`${props.owner_id}#${props.owner_type}`, { |
||||
owner_id: props.owner_id, |
||||
owner_type: props.owner_type, |
||||
}, (v) => { |
||||
setSelect(v) |
||||
props.onUpdate?.(v) |
||||
}, !select) |
||||
setTimeout(() => { |
||||
setLoading(false) |
||||
}, 300) |
||||
}, [loading, token, select]) |
||||
|
||||
return ( |
||||
<View className={styles.collect} onClick={change} style={props.styles}> |
||||
<View className={styles.collectImage} style={props.stylesImage}> |
||||
{/*<View className={`${loading && select && styles.zoom}`}/>*/} |
||||
<View className={`${loading ? styles.pulse : ''}`}> |
||||
<IconFont name='star' fill={!!(select && token)} color={(select && token) ? '#FF9E5F' : undefined} /> |
||||
</View> |
||||
{/*<Image src={(select && token) ? star : starLine} className={`${loading && styles.pulse}`}/>*/} |
||||
</View> |
||||
{/*{!props.textHidden && (select ? '已收藏' : '收藏')}*/} |
||||
{ |
||||
!props.textHidden && |
||||
<Text style={{fontSize:'30rpx',lineHeight:'30rpx'}}>收藏</Text> |
||||
} |
||||
{/*<Text>{!props.textHidden && '收藏'}</Text>*/} |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default Collect |
@ -0,0 +1,16 @@ |
||||
.box { |
||||
background: #fff; |
||||
border-radius: 20rpx; |
||||
display: flex; |
||||
align-items: center; |
||||
box-sizing: border-box; |
||||
line-height: 1.75; |
||||
} |
||||
|
||||
.total { |
||||
font-size: 40rpx; |
||||
font-family: PingFang SC-Heavy, PingFang SC; |
||||
font-weight: 800; |
||||
color: #323635; |
||||
margin-top: 48rpx; |
||||
} |
@ -0,0 +1,99 @@ |
||||
import {Text, View} from "@tarojs/components"; |
||||
import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs"; |
||||
import {everyDay, formatTime, getMonday, getSunday, monthEnd, monthFirst} from "@/utils/time"; |
||||
import LineChart from "@/components/lineChart/lineChart"; |
||||
import {CSSProperties, FC, useEffect, useState} from "react"; |
||||
import {StatisticsParam, userApi} from "@/api"; |
||||
import styles from './learningRecord.module.scss' |
||||
import Spin from "@/components/spinner"; |
||||
import {Profile} from "@/store"; |
||||
import Taro from "@tarojs/taro"; |
||||
import debounce from "@/utils/debounce"; |
||||
|
||||
const tabList: TabList<any>[] = [ |
||||
{ |
||||
title: '月', value: { |
||||
start_time: monthFirst(), |
||||
end_time: monthEnd() |
||||
} |
||||
}, |
||||
{ |
||||
title: '周', value: { |
||||
start_time: getMonday(), |
||||
end_time: getSunday() |
||||
} |
||||
}, |
||||
{ |
||||
title: '日', |
||||
value: { |
||||
start_time: new Date().setHours(0, 0, 0, 0), |
||||
end_time: new Date().setHours(24, 0, 0, 0) |
||||
} |
||||
}, |
||||
] |
||||
|
||||
interface Props { |
||||
userId?: string | number |
||||
style?: CSSProperties |
||||
className?: string |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 学习记录折线图 |
||||
*/ |
||||
const LearningRecord: FC<Props> = ({userId, style, className}) => { |
||||
const [lineData, setLineData] = useState<any[]>([]) |
||||
const [loading, setLoading] = useState(false) |
||||
const {token} = Profile.useContainer() |
||||
|
||||
async function getStatistics(data: StatisticsParam) { |
||||
try { |
||||
if (!userId) return; |
||||
setLineData([]) |
||||
const res = await userApi.statistics(userId, data) |
||||
const everyDayValue = everyDay(data.start_time, Number(data.end_time), res.data) |
||||
setLineData(everyDayValue) |
||||
} catch (e) { |
||||
setLineData([]) |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
function tabChange({tab}: OnChangOpt<StatisticsParam>) { |
||||
if (!token) { |
||||
Taro.navigateTo({url: '/pages/login/login'}) |
||||
return |
||||
} |
||||
setLoading(true) |
||||
debounce(() => { |
||||
try { |
||||
getStatistics(tab?.value! as StatisticsParam).then() |
||||
} catch (e) { |
||||
} |
||||
setLoading(false) |
||||
}) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
userId && setTimeout(() => { |
||||
getStatistics(tabList[0].value) |
||||
}, 500) |
||||
}, [userId]) |
||||
|
||||
return (<View className={[styles.box, className].filter(Boolean).join(' ')} style={{display: 'block', ...style}}> |
||||
<Tabs tabList={tabList} onChange={tabChange} backMode/> |
||||
<View style={{position: "relative"}}> |
||||
{loading && <Spin enable={loading} block/>} |
||||
<View className={styles.total}> |
||||
总共学习 |
||||
<Text style={{margin: '0 10px', color: '#00D6AC'}}> |
||||
{formatTime(lineData.reduce((pre, cur) => pre + cur.value, 0) || 0)} |
||||
</Text> |
||||
</View> |
||||
<LineChart data={lineData}/> |
||||
</View> |
||||
</View>) |
||||
} |
||||
|
||||
export default LearningRecord |
@ -0,0 +1,38 @@ |
||||
.fixedBox { |
||||
position: relative; |
||||
width: 100vw; |
||||
height: 100vw; |
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1), rgba(255, 255, 255, 1)); |
||||
//border: 1px solid #f40; |
||||
|
||||
&-inner { |
||||
position: absolute; |
||||
width: 100vw; |
||||
bottom: 24rpx; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
|
||||
&-icon { |
||||
image { |
||||
width: 32rpx; |
||||
height: 32rpx; |
||||
} |
||||
} |
||||
|
||||
&-box { |
||||
margin-top: 24rpx; |
||||
width: 680rpx; |
||||
left: 35rpx; |
||||
height: 76rpx; |
||||
background-color: #45D4A8; |
||||
color: #fff; |
||||
line-height: 76rpx; |
||||
text-align: center; |
||||
font-size: 32rpx; |
||||
font-weight: 500; |
||||
border-radius: 8rpx 8rpx 8rpx 8rpx; |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
import styles from "./index.module.scss"; |
||||
import { Text, View} from "@tarojs/components"; |
||||
import Taro from "@tarojs/taro"; |
||||
import {CSSProperties, FC} from "react"; |
||||
import IconFont from "@/components/IconFont"; |
||||
|
||||
type Props = { |
||||
className?: string |
||||
style?: CSSProperties |
||||
} |
||||
|
||||
const LoginView3: FC<Props> = ({className,style}) => { |
||||
return ( |
||||
|
||||
<View className={`${styles.fixedBox} ${className}`} style={style}> |
||||
<View className={styles['fixedBox-inner']}> |
||||
<View className={styles['fixedBox-inner-icon']}> |
||||
<IconFont color='#909795' name='chevron-double-down'/> |
||||
</View> |
||||
<View className={styles['fixedBox-inner-box']} |
||||
onClick={() => Taro.navigateTo({url: '/pages/login/login'})}> |
||||
<Text>登录查看更多内容1</Text> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
export default LoginView3 |
@ -0,0 +1,47 @@ |
||||
.content { |
||||
position: relative; |
||||
width: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
|
||||
image { |
||||
width: 320rpx; |
||||
height: 208rpx; |
||||
} |
||||
|
||||
.title { |
||||
font-size: 28rpx; |
||||
font-weight: bold; |
||||
color: #323635; |
||||
} |
||||
|
||||
.label { |
||||
font-size: 24rpx; |
||||
font-weight: 500; |
||||
color: #909795; |
||||
line-height: 24rpx; |
||||
margin: 20rpx 0 ; |
||||
} |
||||
|
||||
.button { |
||||
padding: 0 60rpx; |
||||
height: 76rpx; |
||||
background: #45D4A8; |
||||
border-radius: 38rpx 38rpx 38rpx 38rpx; |
||||
color: #fff; |
||||
line-height: 76rpx; |
||||
text-align: center; |
||||
font-size: 32rpx; |
||||
font-weight: 500; |
||||
} |
||||
|
||||
.nextLabel { |
||||
font-size: 24rpx; |
||||
font-weight: 500; |
||||
color: #909795; |
||||
line-height: 24rpx; |
||||
margin-top: 15rpx; |
||||
} |
||||
} |
@ -0,0 +1,102 @@ |
||||
import {CSSProperties, FC, useState} from "react"; |
||||
import {View, Image} from "@tarojs/components"; |
||||
import styles from './index.module.scss' |
||||
import NoLogin from '@/static/img/noLogin.png' |
||||
import {Profile} from "@/store"; |
||||
import Taro from "@tarojs/taro"; |
||||
import {userApi} from "@/api"; |
||||
|
||||
interface Props { |
||||
tips?: string |
||||
height?: number |
||||
paddingTop?: number |
||||
style?: CSSProperties |
||||
offImage?: boolean |
||||
onSuccess?: VoidFunction |
||||
} |
||||
|
||||
export const LoginView: FC<Props> = (props) => { |
||||
const [isLoading, setLoading] = useState(false) |
||||
const {setUser, setToken, setCompany} = Profile.useContainer() |
||||
const sizeStyle: CSSProperties = { |
||||
height: `${props.height || 1000}rpx`, |
||||
paddingTop: `${props.paddingTop || 0}rpx`, |
||||
...props.style, |
||||
} |
||||
|
||||
function login() { |
||||
if (isLoading) return; |
||||
Taro.showLoading({title: '微信授权中...'}) |
||||
setLoading(true) |
||||
Taro.login({ |
||||
success: async (res) => { |
||||
try { |
||||
const {catch_key, user, token, company} = await userApi.login(res.code) |
||||
Taro.hideLoading() |
||||
if (token) { |
||||
Taro.showToast({title: '授权成功', duration: 1500, icon: 'success', mask: true}) |
||||
setTimeout(() => { |
||||
setUser(user) |
||||
setToken(token) |
||||
setCompany(company) |
||||
setLoading(false) |
||||
if (props.onSuccess) { |
||||
props.onSuccess() |
||||
} else { |
||||
Taro.switchTab({url: '/pages/home/home'}) |
||||
} |
||||
}, 1500) |
||||
} else { |
||||
Taro.setStorageSync('openid', catch_key) |
||||
Taro.reLaunch({url: '/pages/check/check'}) |
||||
} |
||||
} catch (e) { |
||||
Taro.hideLoading() |
||||
} |
||||
setLoading(false) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
return ( |
||||
<View className={styles.content} style={sizeStyle}> |
||||
{ |
||||
!props.offImage && <> |
||||
<Image src={NoLogin}/> |
||||
<View className={styles.title}>暂未登录</View> |
||||
</> |
||||
} |
||||
<View className={styles.label}>点击按钮完成微信授权</View> |
||||
<View onClick={login} className={styles.button}>立即登录</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
const LoginView2: FC<Props> = (props) => { |
||||
|
||||
const sizeStyle: CSSProperties = { |
||||
height: `${props.height || 1000}rpx`, |
||||
paddingTop: `${props.paddingTop || 0}rpx`, |
||||
...props.style, |
||||
} |
||||
|
||||
|
||||
|
||||
return ( |
||||
<View className={styles.content} style={sizeStyle}> |
||||
{ |
||||
!props.offImage && <> |
||||
<Image src={NoLogin}/> |
||||
{/*<View className={styles.title}>暂未登录</View>*/} |
||||
</> |
||||
} |
||||
<View onClick={() => { |
||||
Taro.navigateTo({ |
||||
url:'/pages/login/login' |
||||
}) |
||||
}} className={styles.button}>去登录</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default LoginView2 |
@ -0,0 +1,34 @@ |
||||
.navigation { |
||||
position: sticky; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
z-index: 100; |
||||
overflow: hidden; |
||||
background: #fff; |
||||
} |
||||
|
||||
.leftNode { |
||||
position: absolute; |
||||
display: flex; |
||||
left: 20rpx; |
||||
bottom: 0; |
||||
align-items: center; |
||||
z-index: 1; |
||||
} |
||||
|
||||
.text { |
||||
position: absolute; |
||||
left: 0; |
||||
right: 0; |
||||
margin: auto; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
font-size: 28rpx; |
||||
} |
||||
|
||||
.arrow { |
||||
width: 32px; |
||||
height: 32px; |
||||
} |
@ -0,0 +1,60 @@ |
||||
import {Image, View} from "@tarojs/components"; |
||||
import React, {CSSProperties, FC, ReactNode, useMemo} from "react"; |
||||
import styles from './navigationBar.module.scss' |
||||
import Taro from "@tarojs/taro"; |
||||
import leftArrow from '@/static/img/leftArrow.png' |
||||
|
||||
interface Props { |
||||
// 文本
|
||||
text?: string | ReactNode |
||||
children?: ReactNode | ReactNode[] |
||||
// 左边节点
|
||||
leftNode?: string | ReactNode | ReactNode[] |
||||
// 字体颜色
|
||||
color?: string |
||||
// 背景颜色
|
||||
backgroundColor?: string |
||||
// 取消返回按钮
|
||||
cancelBack?: boolean |
||||
// 跟随页面滚动
|
||||
inherit?: boolean |
||||
className?: string |
||||
style?: CSSProperties |
||||
} |
||||
|
||||
const NavigationBar: FC<Props> = (props) => { |
||||
const globalData = Taro.getApp().globalData |
||||
const navigationBarStyle = useMemo((): React.CSSProperties => ({ |
||||
background: props.backgroundColor, |
||||
position: props.inherit ? 'inherit' : "sticky", |
||||
paddingTop: globalData.statusBarHeight + 'px', |
||||
height: globalData.textBarHeight + globalData.statusBarHeight + 'px', |
||||
boxSizing: 'border-box', |
||||
...props.style |
||||
}), [props]) |
||||
|
||||
|
||||
const navigationTextStyle = useMemo((): React.CSSProperties => ({ |
||||
color: props.color, |
||||
height: globalData.textBarHeight + 'px', |
||||
}), [props]) |
||||
|
||||
return ( |
||||
<View className={`${props.className} ${styles.navigation}`} style={navigationBarStyle}> |
||||
<View style={navigationTextStyle} className={styles.leftNode}> |
||||
{ |
||||
!props.cancelBack && <View className="flex flex-column justify-center align-center" style={{ |
||||
width: globalData.textBarHeight + 'px', |
||||
height: globalData.textBarHeight + 'px', |
||||
}} onClick={() => Taro.navigateBack()}> |
||||
<Image src={leftArrow} mode='aspectFill' className={styles.arrow}/> |
||||
</View> |
||||
} |
||||
{props.leftNode} |
||||
</View> |
||||
<View style={navigationTextStyle} className={styles.text}>{props.children || props.text}</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default NavigationBar |
@ -0,0 +1,18 @@ |
||||
import {CSSProperties, FC} from "react"; |
||||
import {View} from "@tarojs/components"; |
||||
|
||||
interface Props { |
||||
text?: string |
||||
styles?: CSSProperties |
||||
className?: string |
||||
} |
||||
|
||||
const PageScript: FC<Props> = (props) => { |
||||
return <View |
||||
className={'text-center font-24 text-muted py-3 ' + props.className} |
||||
style={props.styles}> |
||||
{props.text || "暂无更多"} |
||||
</View> |
||||
} |
||||
|
||||
export default PageScript |
@ -0,0 +1,27 @@ |
||||
.container { |
||||
width: 100%; |
||||
padding: 20rpx; |
||||
box-sizing: border-box; |
||||
columns: 2; |
||||
column-gap: 20rpx; |
||||
} |
||||
|
||||
.health { |
||||
break-inside: avoid; |
||||
background: #fff; |
||||
border-radius: 10px; |
||||
overflow: hidden; |
||||
margin-bottom: 20rpx; |
||||
position: relative; |
||||
} |
||||
|
||||
.play { |
||||
position: absolute; |
||||
min-height: 70rpx !important; |
||||
z-index: 9999; |
||||
width: 40rpx !important; |
||||
height: 40rpx !important; |
||||
top: 20rpx; |
||||
right: 20rpx; |
||||
background: transparent !important; |
||||
} |
@ -0,0 +1,88 @@ |
||||
import {FC, useEffect, useState} from "react"; |
||||
import styles from "@/pages/preview/health/health.module.scss"; |
||||
import {Image, Text, View} from "@tarojs/components"; |
||||
import Img from "@/components/image/image"; |
||||
import Taro from "@tarojs/taro"; |
||||
import videoEvent from "@/hooks/videoEvent"; |
||||
import playWhite from '@/static/img/palyWhite.png' |
||||
import starWhite from '@/static/img/starWhite.png' |
||||
import {formatMinute} from "@/utils/time"; |
||||
import { getWfsInfo } from '@/utils/wfs' |
||||
|
||||
interface Props { |
||||
data: VideList |
||||
errorType?: ImgErrType |
||||
} |
||||
|
||||
const VideoList: FC<Props> = (props) => { |
||||
const [data, setData] = useState<VideList>(props.data) |
||||
const [cover,setCover] = useState<string>('') |
||||
|
||||
console.log(data,'data现在的数据') |
||||
useEffect(() => { |
||||
|
||||
getWfsInfo(data.resource_id).then(res => { |
||||
setData((data) =>({...data, url_path:res.object.thumb,video_width:res.object.width,video_height:res.object.height})) |
||||
setCover(res.object.thumb) |
||||
}).catch(()=>{ |
||||
console.log('获取失败') |
||||
setData((data) => ({...data,video_width:data.resource.width,video_height:data.resource.height})) |
||||
}) |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
videoEvent.videoOn(data.id, ({view}) => { |
||||
setData((data) => ({ |
||||
...data, |
||||
video_view: view |
||||
})) |
||||
}) |
||||
}, []) |
||||
|
||||
|
||||
|
||||
Taro.useUnload(() => { |
||||
videoEvent.off(data.id) |
||||
}) |
||||
|
||||
function jump() { |
||||
Taro.preload(data) |
||||
Taro.navigateTo({url: `/pages/preview/videoFull/videoFull?id=${data.id}`}) |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
{ data.video_height && data.video_width |
||||
&& |
||||
<View className={styles.health}> |
||||
<View key={data.id} onClick={jump} className="bg-white"> |
||||
|
||||
<Img src={cover || data.url_path} height={data.video_height/data.video_width*348} errorType={props.errorType} /> |
||||
<View className='p-2 relative'> |
||||
<View className='text-ellipsis-1 font-28 text-dark'>{data.title}</View> |
||||
<View className='text-ellipsis-2 mt-2 font-24 text-secondary'>{data.introduction}</View> |
||||
|
||||
<View className={styles.info}> |
||||
<View className='flex'> |
||||
<View className='flex align-center mr-3'> |
||||
<Image src={playWhite}/> |
||||
<Text>{(data.video_view || 0)}</Text> |
||||
</View> |
||||
|
||||
<View className='flex align-center'> |
||||
<Image src={starWhite}/> |
||||
<Text>{(data.collect_quantity || 0)}</Text> |
||||
</View> |
||||
</View> |
||||
|
||||
<View>{formatMinute(data.duration || 0)}</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
} |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default VideoList |
@ -0,0 +1,8 @@ |
||||
.progressBottom{ |
||||
bottom: calc(env(safe-area-inset-bottom) + 100rpx); |
||||
//bottom:0; |
||||
position: fixed; |
||||
z-index: 1000; |
||||
left:0; |
||||
right:0; |
||||
} |
@ -0,0 +1,247 @@ |
||||
import React, {FC, useEffect, useRef, useState, useImperativeHandle} from "react"; |
||||
import {Image, Text, Video, View} from "@tarojs/components"; |
||||
import styles from "./index.module.scss" |
||||
import Icon from "@/components/icon"; |
||||
import {AtSlider} from "taro-ui"; |
||||
import Taro from "@tarojs/taro"; |
||||
import full from "@/static/img/fullscreen.png" |
||||
import unFull from "@/static/img/exitFullscreen.png" |
||||
|
||||
type Props = { |
||||
src: string |
||||
onRef?: any |
||||
showFull?: boolean |
||||
height?: number |
||||
progress2bottom?: boolean |
||||
} |
||||
|
||||
const VideoPro:FC<Props> = ({src,onRef,showFull,height,progress2bottom}) => { |
||||
const globalData = Taro.getApp().globalData |
||||
|
||||
if(onRef){ |
||||
//用useImperativeHandle暴露一些外部ref能访问的属性
|
||||
useImperativeHandle(onRef, () => { |
||||
return { |
||||
func: pause, |
||||
} |
||||
}) |
||||
} |
||||
|
||||
// 视频ui控制需要的响应式数据
|
||||
const videoContext = useRef<any>() |
||||
const [isPlay, setIsPlay] = useState(true) |
||||
const [duration,setDuration] = useState(0) // 视频长度 单位秒
|
||||
const updateState = useRef(false) |
||||
const [sliderValue,setSlidervalue] = useState(0) |
||||
const [process_duration, set_process_duration] = useState('00:00') |
||||
const [total_duration, set_total_duration] = useState('00:00') |
||||
const [showMenu,setShowMenu] = useState(true) |
||||
const [isFull,setIsFull] = useState(false) |
||||
const time = useRef<NodeJS.Timeout>() |
||||
|
||||
const [videoHeight] = useState<number>(height || 600) |
||||
|
||||
|
||||
useEffect(() => { |
||||
console.log('组件加载') |
||||
videoContext.current = Taro.createVideoContext('video') |
||||
updateState.current = true |
||||
}, []); |
||||
|
||||
|
||||
function onTouchStart(){ |
||||
if(!showMenu) { |
||||
setShowMenu(true) |
||||
} |
||||
} |
||||
|
||||
function onTouchEnd(){ |
||||
if(time.current) { |
||||
clearTimeout(time.current) |
||||
time.current = undefined |
||||
} |
||||
time.current = setTimeout(() => { |
||||
setShowMenu(false) |
||||
},5000) |
||||
|
||||
} |
||||
|
||||
function bindTimeupdateFun(e) { |
||||
if (updateState.current) { |
||||
let sliderValue = e.detail.currentTime / e.detail.duration * 100; |
||||
setSlidervalue(sliderValue) |
||||
setDuration(e.detail.duration) |
||||
set_total_duration(formatSeconds(e.detail.duration)) |
||||
set_process_duration(formatSeconds(e.detail.currentTime)) |
||||
} |
||||
} |
||||
|
||||
function addZero(i){ |
||||
i = typeof i === 'string' ? Number(i) : i; |
||||
return i < 10 ? "0" + i : "" + i; |
||||
} |
||||
|
||||
function formatSeconds(value){ |
||||
if (value == undefined) { |
||||
value = 0; |
||||
} |
||||
let second = parseInt(value); |
||||
let min = 0; |
||||
let secondStr = '' |
||||
let minStr = '' |
||||
let result = '' |
||||
if (second > 60) { |
||||
min = parseInt(String(second / 60)); |
||||
second = parseInt(String(second % 60)); |
||||
if (min > 60) { |
||||
// hour = parseInt(String(min / 60));
|
||||
min = parseInt(String(min % 60)); |
||||
} |
||||
} |
||||
if (min > 0) { |
||||
minStr = addZero(parseInt(String(min))); |
||||
result = minStr + ":"; |
||||
}else{ |
||||
result = "00:"; |
||||
} |
||||
if(second > 0){ |
||||
secondStr = addZero(parseInt(String(second))); |
||||
result = result + secondStr; |
||||
}else{ |
||||
result = result + '00'; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
function bindEnded(){ |
||||
setIsPlay(false) |
||||
if(!showMenu){ |
||||
setShowMenu(true) |
||||
} |
||||
} |
||||
|
||||
function onFullScreenChange(e){ |
||||
console.log(e.detail.fullScreen) |
||||
if(e.detail.fullScreen){ |
||||
setIsFull(true) |
||||
}else{ |
||||
setIsFull(false) |
||||
} |
||||
} |
||||
|
||||
|
||||
function play(e){ |
||||
e.stopPropagation(); |
||||
videoContext.current.play() |
||||
setIsPlay(true) |
||||
} |
||||
|
||||
function pause(){ |
||||
setIsPlay(false) |
||||
videoContext.current.pause() |
||||
} |
||||
function sliderOnChange(e){ |
||||
if (duration) { |
||||
// 视频跳转到指定位置
|
||||
videoContext.current.seek(e / 100 * duration); |
||||
videoContext.current.play() |
||||
setIsPlay(true) |
||||
updateState.current = true |
||||
setSlidervalue(e) |
||||
} |
||||
} |
||||
function sliderOnChanging(){ |
||||
updateState.current = false |
||||
} |
||||
|
||||
return ( |
||||
<View style={{width: '750rpx', height: `${videoHeight}rpx`}}> |
||||
<Video |
||||
style={{width: '750rpx', height: `${videoHeight}rpx`}} |
||||
id='video' |
||||
autoplay |
||||
src={src} |
||||
controls={false} |
||||
showPlayBtn={false} |
||||
showCenterPlayBtn={false} |
||||
showProgress={false} |
||||
showFullscreenBtn={false} |
||||
enableProgressGesture={false} |
||||
onTimeUpdate={bindTimeupdateFun} |
||||
onEnded={bindEnded} |
||||
onFullScreenChange={onFullScreenChange} |
||||
> |
||||
<View onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} style={{ |
||||
width:isFull?`${globalData.screenHeight}px`:'750rpx', |
||||
height:isFull?'750rpx':`${videoHeight}rpx`,display:'flex',flexDirection:'column', |
||||
boxSizing:'border-box',paddingLeft:isFull?`${globalData.statusBarHeight}px`:'0', |
||||
paddingRight:isFull?`${globalData.statusBarHeight}px`:'0'}} |
||||
> |
||||
<View onClick={pause} className="justify-center align-center flex pt-5" style={{flex:'1',boxSizing:'border-box'}}> |
||||
{ !isPlay && showMenu && |
||||
<View className="flex justify-center align-center rounded-50 pl-1" style={{width:'50px',height:'50px',backgroundColor:'rgba(0,0,0,0.5)',boxSizing:'border-box'}}> |
||||
<Icon onClick={play} name="play" color="#fff" size="35px"></Icon> |
||||
</View> |
||||
} |
||||
</View> |
||||
<View className="flex align-center px-3" style={{height:'40px',boxSizing:'border-box'}}> |
||||
{ duration > 0 && showMenu && !progress2bottom && |
||||
<> |
||||
{ |
||||
isPlay ? |
||||
<Icon name="pause" color="#fff" size="23px" onClick={() => {videoContext.current.pause();setIsPlay(false)}} /> : |
||||
<Icon name="play" color="#fff" size="23px" onClick={() => {videoContext.current.play();setIsPlay(true)}} /> |
||||
} |
||||
<Text className="text-white pl-2 font-26">{process_duration}</Text> |
||||
|
||||
|
||||
<View style={{flex:'1'}}> |
||||
<AtSlider onChange={sliderOnChange} onChanging={sliderOnChanging} step={1} value={sliderValue} activeColor='#fff' backgroundColor='#BDBDBD' blockColor='#fff' blockSize={10}></AtSlider> |
||||
</View> |
||||
|
||||
|
||||
|
||||
<Text className="text-white font-26 pr-2">{total_duration}</Text> |
||||
{ showFull && |
||||
<> |
||||
{isFull? <Image style={{width:'25px',height:'25px'}} onClick={() => videoContext.current.exitFullScreen() } src={unFull} /> |
||||
: <Image style={{width:'23px',height:'23px'}} src={full} onClick={() => videoContext.current.requestFullScreen({direction:90}) } />} |
||||
</> |
||||
} |
||||
</> |
||||
} |
||||
</View> |
||||
</View> |
||||
</Video> |
||||
|
||||
<View className={`${styles.progressBottom} flex align-center px-3`} style={{height:'40px',boxSizing:'border-box'}}> |
||||
{ duration > 0 && showMenu && progress2bottom && |
||||
<> |
||||
{ |
||||
isPlay ? |
||||
<Icon name="pause" color="#fff" size="23px" onClick={() => {videoContext.current.pause();setIsPlay(false)}} /> : |
||||
<Icon name="play" color="#fff" size="23px" onClick={() => {videoContext.current.play();setIsPlay(true)}} /> |
||||
} |
||||
<Text className="text-white pl-2 font-26" style={{width:'60rpx'}}>{process_duration}</Text> |
||||
|
||||
|
||||
<View style={{flex:'1'}}> |
||||
<AtSlider onChange={sliderOnChange} onChanging={sliderOnChanging} step={1} value={sliderValue} activeColor='#fff' backgroundColor='#BDBDBD' blockColor='#fff' blockSize={10}></AtSlider> |
||||
</View> |
||||
|
||||
|
||||
|
||||
<Text className="text-white font-26 pr-2">{total_duration}</Text> |
||||
{ showFull && |
||||
<> |
||||
{isFull? <Image style={{width:'25px',height:'25px'}} onClick={() => videoContext.current.exitFullScreen() } src={unFull} /> |
||||
: <Image style={{width:'23px',height:'23px'}} src={full} onClick={() => videoContext.current.requestFullScreen({direction:90}) } />} |
||||
</> |
||||
} |
||||
</> |
||||
} |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
export default VideoPro |
@ -0,0 +1,73 @@ |
||||
.wang_product{ |
||||
width:100%; |
||||
} |
||||
|
||||
.wang_product .product_water .water { |
||||
border-radius: 16rpx !important; |
||||
//background-color: #ffffff; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.wang_product .product_water .water .image { |
||||
border-radius: 16rpx 16rpx 0 0 !important; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
.wang_product .product_water.col_2_20 .water { |
||||
margin-bottom: 20rpx; |
||||
} |
||||
|
||||
.wang_product .product_water.col_2_20 .water.right { |
||||
margin-left: 10rpx; |
||||
} |
||||
|
||||
.wang_product .product_water.col_2_20 .water.left { |
||||
margin-right: 10rpx; |
||||
} |
||||
|
||||
|
||||
|
||||
/* 二列布局 */ |
||||
.wang_product .product_list[class*='col-2-'] .item image, |
||||
.wang_product .product_list[class*='col-2-'] .item .img-wrap { |
||||
width: 100%; |
||||
height: 400rpx; |
||||
} |
||||
|
||||
.wang_product .product_list[class*='col-2-'] .comment { |
||||
display: none; |
||||
} |
||||
|
||||
.wang_product .product_list[class*='col-2-'] .content { |
||||
height: 200rpx; |
||||
} |
||||
|
||||
|
||||
|
||||
.wang_product .product_list.col_2_20:after { |
||||
content: ''; |
||||
width: calc((100% - 20rpx) / 2); |
||||
} |
||||
|
||||
.wang_product .product_list.col_2_20 .item { |
||||
width: calc((100% - 20rpx) / 2); |
||||
margin-bottom: 20rpx; |
||||
} |
||||
|
||||
.wang_waterfall { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: flex-start; |
||||
.wang_column { |
||||
display: flex; |
||||
flex: 1; |
||||
flex-direction: column; |
||||
height: auto; |
||||
width: 50%; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,102 @@ |
||||
import { FC, useEffect, useState } from 'react' |
||||
import { View } from '@tarojs/components' |
||||
import VideoList from '@/components/videoList/videoList' |
||||
import Taro from '@tarojs/taro' |
||||
import styles from './index.module.scss' |
||||
|
||||
type Props = { |
||||
data: any[] |
||||
} |
||||
const WaterFull: FC<Props> = ({data}) => { |
||||
const [leftList,setLeftList] = useState<any[]>([]) |
||||
const [rightList,setRightList] = useState<any[]>([]) |
||||
const [tempList,setTempList] = useState<any[]>([]) |
||||
const [copyFlowList,setCopyFlowList] = useState<any[]>([]) |
||||
const startLength = copyFlowList.length |
||||
// const query = Taro.createSelectorQuery()
|
||||
|
||||
console.log(rightList.length,leftList.length,"高") |
||||
|
||||
|
||||
useEffect(() => { |
||||
setTempList(cloneData(data).splice(startLength)) |
||||
setCopyFlowList(cloneData(data)) |
||||
}, [data]); |
||||
|
||||
useEffect(() => { |
||||
let tempArr = cloneData(tempList) |
||||
splitData(tempArr) |
||||
}, [tempList]) |
||||
|
||||
|
||||
async function splitData(arr: any[]){ |
||||
|
||||
if (!arr.length) { |
||||
return |
||||
} |
||||
// let leftHeight = await getRect('#wang-left-column')
|
||||
// let rightHeight = await getRect('#wang-right-column')
|
||||
// console.log("左侧高度",leftHeight,)
|
||||
let item = arr[0] |
||||
Taro.nextTick(() => { |
||||
arr.length % 2 == 0 ? setLeftList((data) => ([...data, item])) : setRightList((data) => ([...data, item])) |
||||
}) |
||||
// if (leftHeight < rightHeight) {
|
||||
// console.log("左侧插入",leftHeight,rightHeight)
|
||||
// setLeftList((data) => ([...data,item]))
|
||||
// } else if (leftHeight > rightHeight) {
|
||||
// console.log("右侧插入",leftHeight,rightHeight)
|
||||
// setRightList((data) => ([...data,item]))
|
||||
// } else {
|
||||
// console.log("左侧插入")
|
||||
// setLeftList((data) => ([...data,item]))
|
||||
// }
|
||||
// 移除临时列表的第一项
|
||||
arr.splice(0, 1) |
||||
// 如果临时数组还有数据,继续循环
|
||||
Taro.nextTick(() => { |
||||
if (arr.length) { |
||||
splitData(arr) // 在当前同步流程结束后,下一个时间片执行
|
||||
} |
||||
}) |
||||
|
||||
} |
||||
|
||||
// async function getRect(selector:string): Promise<number> {
|
||||
// return new Promise((resolve) => {
|
||||
//
|
||||
// query.select(selector).boundingClientRect()
|
||||
// // 执行查询,获取结果
|
||||
// query.exec((res) => {
|
||||
// if (res && res[0]) {
|
||||
// console.log('Element height:',res, res[0].height)
|
||||
// resolve(res[0].height)
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
function cloneData(data:unknown[]) { |
||||
return JSON.parse(JSON.stringify(data)) |
||||
} |
||||
|
||||
return ( |
||||
<View className={styles.wang_product}> |
||||
<View className={[styles.product_water,styles.col_2_20].join(" ")}> |
||||
<View className={styles.wang_waterfall}> |
||||
<View id="wang-left-column" className={styles.wang_column}> |
||||
<View className={`${styles.water} ${styles.left}`}> |
||||
|
||||
{leftList.map(d => <VideoList key={d.id} data={d} errorType='health'/>)} |
||||
</View> |
||||
</View> |
||||
<View id="wang-right-column" className={styles.wang_column}> |
||||
<View className={[styles.water,styles.right].join(" ")}> |
||||
{rightList.map(d => <VideoList key={d.id} data={d} errorType='health'/>)} |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</View> |
||||
) |
||||
} |
||||
export default WaterFull |
@ -0,0 +1,28 @@ |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
const KEY = 'ARTICLES_EVENTS' |
||||
|
||||
interface Data { |
||||
id: number |
||||
view: number |
||||
} |
||||
|
||||
function recordsAdd(data: Data) { |
||||
Taro.eventCenter.trigger(KEY + data.id, data) |
||||
} |
||||
|
||||
function recordsOn(id: number, fn: (data: Data) => void) { |
||||
Taro.eventCenter.on(KEY + id, fn) |
||||
} |
||||
|
||||
function off(id: number) { |
||||
Taro.eventCenter.off(KEY + id) |
||||
} |
||||
|
||||
const ArticlesEvent = { |
||||
recordsAdd, |
||||
recordsOn, |
||||
off |
||||
} |
||||
|
||||
export default ArticlesEvent |
@ -1,25 +0,0 @@ |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
/** |
||||
* 首页全局事件 |
||||
* 当某个课程看完-触发未完成移动已完成 |
||||
*/ |
||||
const KEY = 'REFRESH_INDEX' |
||||
|
||||
interface RefreshIndex { |
||||
id: number |
||||
} |
||||
|
||||
function on(fn: (arg: RefreshIndex) => void) { |
||||
Taro.eventCenter.on(KEY, fn) |
||||
} |
||||
|
||||
function trigger(data: RefreshIndex) { |
||||
Taro.eventCenter.trigger(KEY, data) |
||||
} |
||||
|
||||
export default { |
||||
on, |
||||
trigger |
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
type Topic = string | symbol |
||||
type Subscriber = (...args: any[]) => void |
||||
|
||||
export default function usePubsub() { |
||||
const events = new Map<Topic, Subscriber[]>() |
||||
const registers = new Map<Topic, Subscriber>() |
||||
|
||||
const pub = (topic: Topic, data: any) => { |
||||
console.log("发布没有") |
||||
Taro.eventCenter.trigger(topic, data) |
||||
}; |
||||
|
||||
const sub = (topic: Topic, callback: Subscriber) => { |
||||
const cbs = events.get(topic) ?? [] |
||||
events.set(topic, cbs.concat(callback)) |
||||
if (!registers.has(topic)) { |
||||
console.log({topic}) |
||||
const listener = (...args: any[]): void => { |
||||
events.get(topic)?.forEach((cb) => cb(...args)) |
||||
} |
||||
registers.set(topic, listener) |
||||
Taro.eventCenter.on(topic, listener) |
||||
} |
||||
}; |
||||
|
||||
Taro.useUnload(() => { |
||||
registers.forEach((listener, topic) => { |
||||
Taro.eventCenter.off(topic, listener) |
||||
}) |
||||
registers.clear() |
||||
events.clear() |
||||
}) |
||||
|
||||
return { |
||||
pub, |
||||
sub |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
const KEY = 'VIDEO_EVENTS' |
||||
|
||||
interface Data { |
||||
id: number |
||||
view: number |
||||
} |
||||
|
||||
function videoAdd(data: Data) { |
||||
Taro.eventCenter.trigger(KEY + data.id, data) |
||||
} |
||||
|
||||
function videoOn(id: number, fn: (data: Data) => void) { |
||||
Taro.eventCenter.on(KEY + id, fn) |
||||
} |
||||
|
||||
function off(id: number) { |
||||
Taro.eventCenter.off(KEY + id) |
||||
} |
||||
|
||||
const VideoEvent = { |
||||
videoAdd, |
||||
videoOn, |
||||
off |
||||
} |
||||
|
||||
export default VideoEvent |
@ -0,0 +1,3 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: 'waterFull' |
||||
}) |
@ -0,0 +1,37 @@ |
||||
import VirtualList from '@tarojs/components/virtual-list' |
||||
import React, { Component } from 'react' |
||||
import { View } from '@tarojs/components' |
||||
function buildData(offset = 0) { |
||||
return Array(500) |
||||
.fill(0) |
||||
.map((_, i) => i + offset) |
||||
} |
||||
|
||||
const Row = React.memo(({ id, index, data }:{id: any,index:any,data:any}) => { |
||||
return ( |
||||
<View id={id} className={index % 2 ? 'ListItemOdd' : 'ListItemEven'}> |
||||
Row {index} : {data[index]} |
||||
</View> |
||||
) |
||||
}) |
||||
|
||||
export default class Index extends Component { |
||||
state = { |
||||
data: buildData(0), |
||||
} |
||||
|
||||
render() { |
||||
const { data } = this.state |
||||
const dataLen = data.length |
||||
return ( |
||||
<VirtualList |
||||
height={800} /* 列表的高度 */ |
||||
width="100%" /* 列表的宽度 */ |
||||
item={Row} /* 列表单项组件,这里只能传入一个组件 */ |
||||
itemData={data} /* 渲染列表的数据 */ |
||||
itemCount={dataLen} /* 渲染列表的长度 */ |
||||
itemSize={100} /* 列表单项的高度 */ |
||||
/> |
||||
) |
||||
} |
||||
} |
@ -1,3 +1,3 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '绑定手机号' |
||||
navigationBarTitleText: '员工验证', |
||||
}) |
||||
|
@ -1,21 +1,20 @@ |
||||
import {FC} from "react"; |
||||
import {Input, View} from "@tarojs/components"; |
||||
import {Image, View} from "@tarojs/components"; |
||||
import styles from "../home.module.scss"; |
||||
import Icon from "@/components/icon"; |
||||
import Taro from "@tarojs/taro"; |
||||
import search from "@/static/img/search.png" |
||||
|
||||
interface Props { |
||||
onConfirm: (value: string) => void |
||||
} |
||||
|
||||
export const Search: FC<Props> = (props) => { |
||||
export const Search: FC = () => { |
||||
|
||||
function jump() { |
||||
Taro.navigateTo({url: '/pages/preview/search/search/index'}) |
||||
} |
||||
|
||||
return ( |
||||
<View className={styles.search}> |
||||
<Icon name='search' size={18} color='#808080'/> |
||||
<Input |
||||
placeholder='搜索疾病名称' |
||||
confirmType='search' |
||||
onConfirm={(e) => props.onConfirm((e.target as any).value!)} |
||||
className='flex-1 pl-1'/> |
||||
<View className={styles.search} onClick={jump}> |
||||
<Image src={search} mode='widthFix' style={{width: 16, height: 16, verticalAlign: 'middle',paddingRight:'10rpx'}}/> |
||||
<View>点击搜索</View> |
||||
</View> |
||||
) |
||||
} |
||||
|
@ -1,7 +1,9 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '康一诺', |
||||
// navigationStyle: 'custom',
|
||||
navigationStyle: 'custom', |
||||
navigationBarBackgroundColor: '#92ecc5', |
||||
navigationBarTextStyle: 'white', |
||||
onReachBottomDistance: 50 |
||||
// navigationBarTextStyle: 'white',
|
||||
onReachBottomDistance: 50, |
||||
enableShareTimeline: true, |
||||
enableShareAppMessage: true |
||||
}) |
||||
|
@ -1,5 +1,5 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '医学道', |
||||
navigationBarBackgroundColor: '#92ecc5', |
||||
navigationBarTextStyle: 'white', |
||||
navigationStyle: 'custom', |
||||
enableShareTimeline: true, |
||||
enableShareAppMessage: true |
||||
}) |
||||
|
@ -1,32 +1,87 @@ |
||||
import {FC, useState} from "react"; |
||||
import {View} from "@tarojs/components"; |
||||
import {FC, useEffect, useState} from "react"; |
||||
import {View, Text} from "@tarojs/components"; |
||||
import styles from './index.module.scss' |
||||
import {VideoList} from "@/pages/index/components/videoList"; |
||||
import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs"; |
||||
import {CoursesKey} from "@/api/public"; |
||||
import {CoursesKey, publicApi} from "@/api/public"; |
||||
import NavigationBar from "@/components/navigationBar/navigationBar"; |
||||
import Spin from "@/components/spinner"; |
||||
import {isBoolean} from "@tarojs/shared"; |
||||
import ArticlesBox from "@/components/articlesBox/articlesBox"; |
||||
import PageScript from "@/components/pageScript/pageScript"; |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
const category: TabList[] = [ |
||||
{title: "企选课程", value: 'is_required'}, |
||||
{title: "平台课程", value: 'is_not_required'}, |
||||
] |
||||
|
||||
const Index: FC = () => { |
||||
const category: TabList[] = [ |
||||
{title: "必修", value: 'is_required'}, |
||||
{title: "选修", value: 'is_not_required'}, |
||||
{title: "已完成", value: 'is_finished'}, |
||||
{title: "未完成", value: 'is_not_finished'}, |
||||
] |
||||
const [categoryKey, setCategoryKey] = useState<CoursesKey>('is_required') |
||||
|
||||
function tabChange(data: OnChangOpt) { |
||||
setCategoryKey(data.tab?.value as CoursesKey) |
||||
} |
||||
|
||||
Taro.useShareAppMessage((res) => { |
||||
if(res.from === 'button'){ |
||||
|
||||
} |
||||
return { |
||||
title:'信桂', |
||||
path: '/pages/index/index' |
||||
} |
||||
}) |
||||
|
||||
Taro.useShareTimeline(() => { |
||||
return { |
||||
title: '信桂' |
||||
} |
||||
}) |
||||
|
||||
return ( |
||||
<> |
||||
<View className={styles.content}> |
||||
<Tabs tabList={category} onChange={tabChange} current={categoryKey}/> |
||||
<VideoList categoryKey={categoryKey} setCategoryKey={(categoryKey) => setCategoryKey(categoryKey)}/> |
||||
<NavigationBar |
||||
cancelBack |
||||
leftNode={<Tabs tabList={category} onChange={tabChange} current={categoryKey} scrollable={false} hiddenSliding/>} |
||||
/> |
||||
<VideoList |
||||
categoryKey={categoryKey} |
||||
setCategoryKey={(categoryKey) => setCategoryKey(categoryKey)} |
||||
/> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
const AuditMode: FC = () => { |
||||
const [auditMode, setAuditMode] = useState(true) |
||||
const [articles, setArticles] = useState<any[]>([]) |
||||
const [enable, setEnable] = useState(true) |
||||
useEffect(() => { |
||||
publicApi.course({page: 1, pageSize: 10}).then(res => { |
||||
setAuditMode(isBoolean(res.audit_mode) ? res.audit_mode : res.audit_mode === 'true') |
||||
setArticles(res.articles) |
||||
setEnable(false) |
||||
}) |
||||
}, []) |
||||
return ( |
||||
<> |
||||
<Spin enable={enable} overlay/> |
||||
{ |
||||
auditMode ? |
||||
<> |
||||
<NavigationBar cancelBack leftNode={<Text className="bold font-36" style={{color:'#323635'}} >文章列表</Text>} /> |
||||
<View className='bg-white rounded-20 clip m-3 px-3'> |
||||
{ |
||||
articles.map(d => <ArticlesBox key={d.id} data={d}/>) |
||||
} |
||||
</View> |
||||
<PageScript/> |
||||
</> |
||||
: <Index/> |
||||
} |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default Index |
||||
export default AuditMode |
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue