Compare commits
133 Commits
Author | SHA1 | Date |
---|---|---|
一杯沧海 | bae5ece6d7 | 11 months ago |
一杯沧海 | 0c7d7bc52b | 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://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://shopfix.yaojiankang.top |
||||||
#TARO_APP_API=https://playedu.yaojiankang.top |
TARO_APP_API=https://playedu.yaojiankang.top |
||||||
TARO_APP_LGOIN=true |
TARO_APP_LGOIN=true |
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,58 @@ |
|||||||
import {request} from "@/api/request"; |
import {request} from "@/api/request"; |
||||||
|
import {Illness} from "@/api/illness"; |
||||||
|
|
||||||
export type BrandRecord = { |
export type BrandRecord = { |
||||||
logo: string; |
logo: string; |
||||||
name: string; |
name: string; |
||||||
id: number |
id: number |
||||||
introductory_video: string |
introductory_video: string |
||||||
brand_album: string[] |
brand_album: string |
||||||
graphic_introduction: string |
graphic_introduction: string |
||||||
disabled: number |
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 = { |
export type ArticleRecord = { |
||||||
|
id: number; |
||||||
title: string |
title: string |
||||||
page_view: number |
page_view: number |
||||||
created_at: string |
created_at: string |
||||||
content: string |
content: string |
||||||
|
brands: BrandRecord[] |
||||||
|
illness: Illness[] |
||||||
|
collect: boolean |
||||||
|
cover: string |
||||||
|
owner_type: 1 | 2 // 疾病
|
||||||
} |
} |
||||||
|
|
||||||
export const brandApi = { |
export const brandApi = { |
||||||
/** 品牌列表 */ |
/** 品牌列表 */ |
||||||
list(page: number , page_size: number) { |
list(page: number, page_size: number) { |
||||||
return request<{ |
return request<{ |
||||||
list: BrandRecord[], |
list: BrandRecord[], |
||||||
total: number |
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) { |
info(id: number) { |
||||||
return request<BrandRecord>(`/home/v1/brand/${id}`, "GET") |
return request<BrandRecord>(`/home/v1/brand/${id}`, "GET") |
||||||
}, |
}, |
||||||
/** 文章列表 */ |
/** 文章列表 */ |
||||||
articleList(owner_id: number,page:number) { |
articleList(owner_id: number, page: number) { |
||||||
return request<{ |
return request<{ |
||||||
list: ArticleRecord[], |
list: Articles[], |
||||||
total: number |
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 ) { |
articleInfo(id: number) { |
||||||
return request<ArticleRecord>(`/home/v1/article/${id}` , "GET") |
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,73 @@ |
|||||||
|
.collect { |
||||||
|
width: 130rpx; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
color: #909795; |
||||||
|
font-size: 24rpx; |
||||||
|
padding: 0 10rpx; |
||||||
|
} |
||||||
|
|
||||||
|
.collectImage { |
||||||
|
position: relative; |
||||||
|
width: 32rpx; |
||||||
|
height: 32rpx; |
||||||
|
margin-right: 10rpx; |
||||||
|
|
||||||
|
//Image { |
||||||
|
// width: 100%; |
||||||
|
// height: 100%; |
||||||
|
//} |
||||||
|
} |
||||||
|
|
||||||
|
.zoom { |
||||||
|
//top: 0; |
||||||
|
//left: 0; |
||||||
|
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,78 @@ |
|||||||
|
import {CSSProperties, FC, useCallback, useEffect, useState} from "react"; |
||||||
|
// import star from '@/static/img/star.png'
|
||||||
|
// import starLine from '@/static/img/starLine.png'
|
||||||
|
import {View} 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 && '收藏'} |
||||||
|
</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: 20rpx; |
||||||
|
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,70 @@ |
|||||||
|
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 palyWhite from '@/static/img/palyWhite.png' |
||||||
|
import starWhite from '@/static/img/starWhite.png' |
||||||
|
import {formatMinute} from "@/utils/time"; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
data: VideList |
||||||
|
errorType?: ImgErrType |
||||||
|
} |
||||||
|
|
||||||
|
const VideoList: FC<Props> = (props) => { |
||||||
|
const [data, setData] = useState(props.data) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setData(props.data) |
||||||
|
}, [props.data]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
videoEvent.videoOn(data.id, ({view}) => { |
||||||
|
setData({ |
||||||
|
...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 ( |
||||||
|
<View className={styles.health}> |
||||||
|
<View key={data.id} onClick={jump} className="bg-white"> |
||||||
|
|
||||||
|
<Img src={data.url_path} errorType={props.errorType} height={346}/> |
||||||
|
<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={palyWhite}/> |
||||||
|
<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,4 @@ |
|||||||
|
.container{ |
||||||
|
width:750rpx; |
||||||
|
height:600rpx; |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
import React, {FC, useEffect, useRef, useState, useImperativeHandle} from "react"; |
||||||
|
import {Image, Text, Video, View} from "@tarojs/components"; |
||||||
|
import "./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 |
||||||
|
} |
||||||
|
|
||||||
|
const VideoPro:FC<Props> = ({src,onRef}) => { |
||||||
|
const globalData = Taro.getApp().globalData |
||||||
|
//用useImperativeHandle暴露一些外部ref能访问的属性
|
||||||
|
useImperativeHandle(onRef, () => { |
||||||
|
return { |
||||||
|
func: pause, |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
// 视频ui控制需要的响应式数据
|
||||||
|
const videoContext = useRef<any>() |
||||||
|
const [isPlay, setIsPlay] = useState(false) |
||||||
|
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<any>() |
||||||
|
|
||||||
|
|
||||||
|
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 = null |
||||||
|
} |
||||||
|
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); |
||||||
|
updateState.current = true |
||||||
|
setSlidervalue(e) |
||||||
|
} |
||||||
|
} |
||||||
|
function sliderOnChanging(){ |
||||||
|
updateState.current = false |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<View className="container"> |
||||||
|
<Video |
||||||
|
style={{width: '750rpx', height: '600rpx'}} |
||||||
|
id='video' |
||||||
|
// src={brandInfo?.introductory_video_resource?.url}
|
||||||
|
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':'600rpx',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 && showMenu && |
||||||
|
<> |
||||||
|
{ |
||||||
|
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-1 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 onClick={() => { |
||||||
|
isFull?videoContext.current.exitFullScreen():videoContext.current.requestFullScreen({direction:90}) |
||||||
|
}} className="text-white font-26 pr-1">{total_duration}</Text> |
||||||
|
{ |
||||||
|
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> |
||||||
|
) |
||||||
|
} |
||||||
|
export default VideoPro |
@ -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,39 @@ |
|||||||
|
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) => { |
||||||
|
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 |
@ -1,3 +1,3 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '绑定手机号' |
navigationBarTitleText: '员工验证', |
||||||
}) |
}) |
||||||
|
@ -1,21 +1,20 @@ |
|||||||
import {FC} from "react"; |
import {FC} from "react"; |
||||||
import {Input, View} from "@tarojs/components"; |
import {Image, View} from "@tarojs/components"; |
||||||
import styles from "../home.module.scss"; |
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 ( |
return ( |
||||||
<View className={styles.search}> |
<View className={styles.search} onClick={jump}> |
||||||
<Icon name='search' size={18} color='#808080'/> |
<Image src={search} mode='widthFix' style={{width: 16, height: 16, verticalAlign: 'middle',paddingRight:'10rpx'}}/> |
||||||
<Input |
<View>点击搜索</View> |
||||||
placeholder='搜索疾病名称' |
|
||||||
confirmType='search' |
|
||||||
onConfirm={(e) => props.onConfirm((e.target as any).value!)} |
|
||||||
className='flex-1 pl-1'/> |
|
||||||
</View> |
</View> |
||||||
) |
) |
||||||
} |
} |
||||||
|
@ -1,7 +1,9 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '康一诺', |
navigationBarTitleText: '康一诺', |
||||||
// navigationStyle: 'custom',
|
navigationStyle: 'custom', |
||||||
navigationBarBackgroundColor: '#92ecc5', |
navigationBarBackgroundColor: '#92ecc5', |
||||||
navigationBarTextStyle: 'white', |
// navigationBarTextStyle: 'white',
|
||||||
onReachBottomDistance: 50 |
onReachBottomDistance: 50, |
||||||
|
enableShareTimeline: true, |
||||||
|
enableShareAppMessage: true |
||||||
}) |
}) |
||||||
|
@ -1,5 +1,5 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '医学道', |
navigationStyle: 'custom', |
||||||
navigationBarBackgroundColor: '#92ecc5', |
enableShareTimeline: true, |
||||||
navigationBarTextStyle: 'white', |
enableShareAppMessage: true |
||||||
}) |
}) |
||||||
|
@ -1,32 +1,87 @@ |
|||||||
import {FC, useState} from "react"; |
import {FC, useEffect, useState} from "react"; |
||||||
import {View} from "@tarojs/components"; |
import {View, Text} from "@tarojs/components"; |
||||||
import styles from './index.module.scss' |
import styles from './index.module.scss' |
||||||
import {VideoList} from "@/pages/index/components/videoList"; |
import {VideoList} from "@/pages/index/components/videoList"; |
||||||
import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs"; |
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 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') |
const [categoryKey, setCategoryKey] = useState<CoursesKey>('is_required') |
||||||
|
|
||||||
function tabChange(data: OnChangOpt) { |
function tabChange(data: OnChangOpt) { |
||||||
setCategoryKey(data.tab?.value as CoursesKey) |
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}> |
||||||
|
<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 ( |
return ( |
||||||
<> |
<> |
||||||
<View className={styles.content}> |
<Spin enable={enable} overlay/> |
||||||
<Tabs tabList={category} onChange={tabChange} current={categoryKey}/> |
{ |
||||||
<VideoList categoryKey={categoryKey} setCategoryKey={(categoryKey) => setCategoryKey(categoryKey)}/> |
auditMode ? |
||||||
</View> |
<> |
||||||
|
<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 |
||||||
|
@ -1,4 +1,3 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '', |
navigationBarTitleText: '', |
||||||
onReachBottomDistance: 50 |
|
||||||
}) |
}) |
@ -0,0 +1,17 @@ |
|||||||
|
.dep { |
||||||
|
background: #ddd; |
||||||
|
padding: 5px 15px; |
||||||
|
border-radius: 3px; |
||||||
|
} |
||||||
|
|
||||||
|
.depSelected { |
||||||
|
background: #9ee0a3; |
||||||
|
color: #3f6942; |
||||||
|
} |
||||||
|
|
||||||
|
.add { |
||||||
|
//position: fixed; |
||||||
|
width: 710rpx; |
||||||
|
margin-top: 30px; |
||||||
|
border: none !important; |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import {Button, Form, Input, View} from "@tarojs/components"; |
||||||
|
import {useEffect, useState} from "react"; |
||||||
|
import {ManageApi} from "@/api/manage"; |
||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
import './addDepartment.scss'; |
||||||
|
import {getCurrentInstance} from "@tarojs/runtime"; |
||||||
|
|
||||||
|
const AddDepartment = () => { |
||||||
|
const [departmentInfo, setDepartmentInfo] = useState<{ name: string } | null>(null) |
||||||
|
const params = getCurrentInstance()?.router?.params as { id?: string;parent_id?: string;sort?: string;company_id?:string } |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
Taro.setNavigationBarTitle({ title:'添加部门' }) |
||||||
|
if (params.id) { |
||||||
|
Taro.setNavigationBarTitle({ title:'修改部门' }) |
||||||
|
} |
||||||
|
}, []) |
||||||
|
|
||||||
|
|
||||||
|
async function submit(e) { |
||||||
|
const value: {name: string} = e.detail.value |
||||||
|
for (const [_, val] of Object.entries(value)) { |
||||||
|
if (!val) { |
||||||
|
Taro.showToast({title: "请填写部门名称a", icon: 'error'}) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
Taro.showLoading() |
||||||
|
try { |
||||||
|
if (params.id) { |
||||||
|
await ManageApi.putDep(params.id, departmentInfo!.name) |
||||||
|
} else { |
||||||
|
await ManageApi.addDep({ |
||||||
|
name: departmentInfo!.name, |
||||||
|
parent_id: Number(params.parent_id), |
||||||
|
company_id: Number(params.company_id), |
||||||
|
sort: Number(params.sort), |
||||||
|
}) |
||||||
|
} |
||||||
|
Taro.hideLoading() |
||||||
|
Taro.showToast({title: "添加成功", icon: 'success'}) |
||||||
|
setTimeout(() => { |
||||||
|
Taro.navigateBack() |
||||||
|
}, 500) |
||||||
|
} catch (e) { |
||||||
|
} |
||||||
|
Taro.hideLoading() |
||||||
|
} |
||||||
|
|
||||||
|
function onBlur(e){ |
||||||
|
setDepartmentInfo(p => ({...p,name:e.detail.value})) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<View className='bg-white px-2 mt-2'> |
||||||
|
<Form className='form' onSubmit={submit}> |
||||||
|
<View className='item'> |
||||||
|
<View>部门</View> |
||||||
|
<Input placeholder='请输入部门名称' name='name' value={departmentInfo?.name} |
||||||
|
onBlur={onBlur}/> |
||||||
|
</View> |
||||||
|
<Button className='add button' formType='submit'>{params.id ? '修改部门' : "添加部门"}</Button> |
||||||
|
</Form> |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default AddDepartment |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue