Merge branch 'master' of https://git.yaojiankang.top/xing/video
@ -0,0 +1,56 @@ |
||||
import {request} from "@/api/request"; |
||||
|
||||
export interface AdwareParams { |
||||
key: string |
||||
value: string |
||||
|
||||
[key: string]: string |
||||
} |
||||
|
||||
export interface AdwareLinkType { |
||||
id: number |
||||
api_name: string |
||||
path: string |
||||
param: string |
||||
} |
||||
|
||||
export interface AdwareType { |
||||
advert_link: AdwareLinkType |
||||
scope_id: number | string |
||||
id: number |
||||
name: string |
||||
status: 0 | 1 // 禁用
|
||||
sort: number |
||||
image_url: string |
||||
image_link: string |
||||
start_time: number |
||||
end_time?: number |
||||
} |
||||
|
||||
export const HomeApi = { |
||||
advert(only_flag: string) { |
||||
return request<AdwareType[]>("/home/v1/advert/unique?only_flag=" + only_flag, "GET") |
||||
}, |
||||
course(page: number, page_size: number) { |
||||
return request<{ data: Curriculum[], total: number }>('/home/v1/course/course', "GET", {page, page_size}) |
||||
}, |
||||
/** 健康管理 */ |
||||
healthTop(count: number) { |
||||
return request<Health[]>('/home/v1/health/top', "GET", {count}) |
||||
}, |
||||
health(page: number, page_size: number) { |
||||
return request<{ data: Health[], total: number }>('/home/v1/health/index', "GET", {page, page_size}) |
||||
}, |
||||
/** 增加播放量 */ |
||||
healthSetPlay(id) { |
||||
return request(`/home/v1/health/set_play/${id}`, "PUT") |
||||
}, |
||||
/** 品牌 */ |
||||
brand(page: number, page_size: number) { |
||||
return request<{ data: Brand[], total: number }>('/home/v1/brand/list', "GET", {page, page_size}) |
||||
}, |
||||
/** 技能 */ |
||||
skillTop(count: number) { |
||||
return request<Kill[]>('/home/v1/skill/top', "GET", {count}) |
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '分类' |
||||
}) |
@ -0,0 +1,21 @@ |
||||
import {FC} from "react"; |
||||
import {View} from "@tarojs/components"; |
||||
import {useRouter} from "@tarojs/taro"; |
||||
|
||||
type SortType = '' |
||||
|
||||
interface Params { |
||||
id: number |
||||
type: SortType |
||||
jumpUrl: () => void |
||||
} |
||||
|
||||
const Sort: FC = () => { |
||||
const params = useRouter().params as unknown as Params |
||||
|
||||
return ( |
||||
<View>ds</View> |
||||
) |
||||
} |
||||
|
||||
export default Sort |
@ -0,0 +1,6 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: ' ', |
||||
backgroundColor:'#000000', |
||||
navigationBarTextStyle:'white', |
||||
navigationBarBackgroundColor:'#000000' |
||||
}) |
@ -0,0 +1,27 @@ |
||||
import {Video} from "@tarojs/components"; |
||||
import {FC} from "react"; |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
interface Props { |
||||
url: string |
||||
} |
||||
|
||||
const VideoFull: FC<Props> = () => { |
||||
const {url} = Taro.useRouter().params |
||||
return ( |
||||
<Video |
||||
style={{width: '100%', height: '100vh'}} |
||||
src={url!} |
||||
autoplay |
||||
loop |
||||
showFullscreenBtn={false} |
||||
showPlayBtn={false} |
||||
showCenterPlayBtn={true} |
||||
enableProgressGesture={false} |
||||
enablePlayGesture |
||||
autoPauseIfOpenNative |
||||
/> |
||||
) |
||||
} |
||||
|
||||
export default VideoFull |
@ -0,0 +1,4 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '健康知识', |
||||
onReachBottomDistance: 30 |
||||
}) |
@ -0,0 +1,34 @@ |
||||
.container { |
||||
width: 100%; |
||||
padding: 0 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; |
||||
|
||||
image, |
||||
Image { |
||||
background: #eee; |
||||
width: 100%; |
||||
min-height: 345rpx; |
||||
} |
||||
} |
||||
|
||||
.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,48 @@ |
||||
import {FC, useEffect, useState} from "react"; |
||||
import {Image, ScrollView, View} from "@tarojs/components"; |
||||
import {HomeApi} from "@/api"; |
||||
import Taro, {useReachBottom} from "@tarojs/taro"; |
||||
import styles from './health.module.scss' |
||||
import play from '@/static/img/play.png' |
||||
|
||||
const Health: FC = () => { |
||||
const [page, setPage] = useState(1) |
||||
const [data, setData] = useState<Health[]>([]) |
||||
const [total, setTotal] = useState(0) |
||||
|
||||
async function getData(page: number) { |
||||
const res = await HomeApi.health(page, 10) |
||||
setData(res.data) |
||||
setTotal(res.total) |
||||
} |
||||
|
||||
useReachBottom(() => { |
||||
data.length < total && setPage(page + 1) |
||||
}) |
||||
|
||||
useEffect(() => { |
||||
getData(page) |
||||
}, [page]) |
||||
|
||||
function jump(url: string, id: number) { |
||||
HomeApi.healthSetPlay(id) |
||||
Taro.navigateTo({url: '/pages/business/videoFull/videoFull?url=' + url}) |
||||
} |
||||
|
||||
return ( |
||||
<ScrollView> |
||||
<View className={styles.container}> |
||||
{ |
||||
data.map(d => <View key={d.id} className={styles.health} onClick={() => jump(d.resource.url, d.id)}> |
||||
<Image src={d.url_path} mode='widthFix' lazyLoad fadeIn/> |
||||
<Image src={play} className={styles.play} mode='aspectFit'/> |
||||
<View className='text-ellipsis-2 m-2 text-dark'>{d.title}</View> |
||||
<View className='font-26 text-muted mx-2 mb-2'>{d.video_view}观看</View> |
||||
</View>) |
||||
} |
||||
</View> |
||||
</ScrollView> |
||||
) |
||||
} |
||||
|
||||
export default Health |
@ -0,0 +1,51 @@ |
||||
import {Image, Swiper, SwiperItem, View} from "@tarojs/components"; |
||||
import {FC, useState} from "react"; |
||||
import {AdwareType, HomeApi} from "@/api"; |
||||
import Taro from "@tarojs/taro"; |
||||
import styles from '../home.module.scss' |
||||
import {jumpAdware} from "@/utils/pathFormt"; |
||||
|
||||
const Adware: FC = () => { |
||||
const [data, setData] = useState<AdwareType[]>([]) |
||||
|
||||
async function getAdware() { |
||||
const res = await HomeApi.advert('home_space') |
||||
setData(res) |
||||
} |
||||
|
||||
Taro.useLoad(getAdware) |
||||
|
||||
return ( |
||||
<View> |
||||
{ |
||||
data.length === 1 && <Image |
||||
src={data[0].image_url} |
||||
mode='scaleToFill' |
||||
lazyLoad |
||||
fadeIn |
||||
onClick={() => jumpAdware(data[0].advert_link)} |
||||
className={styles.adware}/> |
||||
} |
||||
{ |
||||
data.length > 1 && <Swiper |
||||
indicatorDots |
||||
autoplay |
||||
circular |
||||
indicatorActiveColor='rgba(255,255,255,0.5)' |
||||
className={styles.adware}> |
||||
{data.map(d => <SwiperItem key={d.id}> |
||||
<Image |
||||
src={d.image_url} |
||||
mode='widthFix' |
||||
lazyLoad |
||||
fadeIn |
||||
onClick={() => jumpAdware(d.advert_link)} |
||||
style={{width: "100%"}}/> |
||||
</SwiperItem>)} |
||||
</Swiper> |
||||
} |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default Adware |
@ -0,0 +1,59 @@ |
||||
import {FC, useEffect, useState} from "react"; |
||||
import {Image, View} from "@tarojs/components"; |
||||
import {HomeApi} from "@/api"; |
||||
import {useReachBottom} from "@tarojs/taro"; |
||||
import styles from "../home.module.scss"; |
||||
import VideoCover from "@/components/videoCover/videoCover"; |
||||
import courseTag from '@/static/img/courseTag.png' |
||||
|
||||
const CurRecommended: FC = () => { |
||||
const [page, setPage] = useState(1) |
||||
const [data, setData] = useState<Curriculum[]>([]) |
||||
const [total, setTotal] = useState(0) |
||||
|
||||
async function getData() { |
||||
const res = await HomeApi.course(page, 4) |
||||
setTotal(res.total) |
||||
const newData = res.data.reduce((pre, cut) => { |
||||
const index = pre.findIndex(d => d.id === cut.id) |
||||
if (index === 1) { |
||||
pre.push(cut) |
||||
} else { |
||||
pre.splice(index, 1, cut) |
||||
} |
||||
return pre |
||||
}, data) |
||||
setData(newData) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
getData() |
||||
}, [page]) |
||||
|
||||
useReachBottom(() => { |
||||
data.length < total && setPage(page + 1) |
||||
}) |
||||
|
||||
return ( |
||||
<> |
||||
{ |
||||
data.length > 0 && <View className='mt-6'> |
||||
<Image src={courseTag} mode='widthFix' className={styles.courseTag}/> |
||||
<View className={'py-2 flex justify-between flex-wrap ' + styles.videoListBox}> |
||||
{ |
||||
data.map(c => <VideoCover |
||||
thumb={c.thumb} |
||||
title={c.title} |
||||
id={c.id} |
||||
depId={c.id} |
||||
key={c.id} |
||||
/>) |
||||
} |
||||
</View> |
||||
</View> |
||||
} |
||||
</> |
||||
) |
||||
} |
||||
|
||||
export default CurRecommended |
@ -0,0 +1,33 @@ |
||||
import {FC} from "react"; |
||||
import {Image, View} from "@tarojs/components"; |
||||
import illness from '@/static/img/illness.png' |
||||
import profession from '@/static/img/profession.png' |
||||
import health from '@/static/img/health.png' |
||||
import article from '@/static/img/article.png' |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
const Feature: FC = () => { |
||||
const list = [ |
||||
{url: '', image: article, text: '品牌'}, |
||||
{url: '', image: health, text: '健康管理'}, |
||||
{url: '', image: profession, text: '专业技能'}, |
||||
{url: '', image: illness, text: '疾病知识'}, |
||||
] |
||||
|
||||
function jump(url) { |
||||
Taro.navigateTo({url}) |
||||
} |
||||
|
||||
return ( |
||||
<View className='flex justify-around mt-4'> |
||||
{ |
||||
list.map(d => <View className='text-center' onClick={() => jump(d.url)}> |
||||
<Image src={d.image} style={{width: '40px'}} mode='widthFix'/> |
||||
<View className='mt-1 text-dark font-26 mt-1'>{d.text}</View> |
||||
</View>) |
||||
} |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default Feature |
@ -0,0 +1,134 @@ |
||||
import {FC, useEffect, useState} from "react"; |
||||
import {Image, Swiper, SwiperItem, View} from "@tarojs/components"; |
||||
import styles from '../home.module.scss' |
||||
import Taro from "@tarojs/taro"; |
||||
import {HomeApi} from "@/api"; |
||||
import first from '@/static/img/first.png' |
||||
import second from '@/static/img/second.png' |
||||
import third from '@/static/img/third.png' |
||||
|
||||
interface DataContent { |
||||
id: number, |
||||
imageUrl: string |
||||
title: string, |
||||
description: string |
||||
path: string |
||||
} |
||||
|
||||
interface Data { |
||||
title: string |
||||
url: string |
||||
detailsUrl: string |
||||
data: DataContent[] |
||||
} |
||||
|
||||
const FeatureRecommended: FC = () => { |
||||
const [data, setData] = useState<Data[]>([ |
||||
{title: "品牌TOP3", url: '', detailsUrl: '', data: []}, |
||||
{ |
||||
title: "健康知识TOP3", |
||||
url: '/pages/health/health', |
||||
detailsUrl: '/pages/business/videoFull/videoFull?url=', |
||||
data: [] |
||||
}, |
||||
{ |
||||
url: '', |
||||
title: "专业技能TOP3", |
||||
detailsUrl: '/pages/business/videoFull/videoFull?url=', |
||||
data: [] |
||||
}, |
||||
{title: "疾病知识TOP3", url: '', detailsUrl: '', data: []}, |
||||
]) |
||||
|
||||
/** 品牌 */ |
||||
async function getBrand(): Promise<DataContent[]> { |
||||
try { |
||||
const res = await HomeApi.brand(1, 3) |
||||
return res.data.map<DataContent>(d => ({ |
||||
id: d.id, |
||||
title: d.name, |
||||
imageUrl: d.brand_album, |
||||
description: d.graphic_introduction, |
||||
path: '', |
||||
})) |
||||
} catch (e) { |
||||
} |
||||
return [] |
||||
} |
||||
|
||||
/** 健康知识 */ |
||||
async function getHealth(): Promise<DataContent[]> { |
||||
try { |
||||
const res = await HomeApi.healthTop(3) |
||||
return res.map<DataContent>(d => ({ |
||||
id: d.id, |
||||
title: d.title, |
||||
imageUrl: d.url_path, |
||||
description: d.introduction, |
||||
path: d.resource.url |
||||
})) |
||||
} catch (e) { |
||||
} |
||||
return [] |
||||
} |
||||
|
||||
/** 技能 */ |
||||
async function getKill(): Promise<DataContent[]> { |
||||
try { |
||||
const res = await HomeApi.skillTop(3) |
||||
return res.map<DataContent>(d => ({ |
||||
id: d.id, |
||||
imageUrl: d.url_path, |
||||
description: '', |
||||
title: d.resource.name, |
||||
path: d.resource.url |
||||
})) |
||||
} catch (e) { |
||||
} |
||||
return [] |
||||
} |
||||
|
||||
useEffect(() => { |
||||
Promise.all([getBrand(), getHealth(), getKill()]).then(([brand, health, kill]) => { |
||||
const oldData: Data[] = JSON.parse(JSON.stringify(data)) |
||||
oldData[0].data = brand |
||||
oldData[1].data = health |
||||
oldData[2].data = kill |
||||
setData(oldData) |
||||
}) |
||||
}, []) |
||||
|
||||
function jump(url: string) { |
||||
console.log(url) |
||||
Taro.navigateTo({url}) |
||||
} |
||||
|
||||
return ( |
||||
<View className={styles.feature}> |
||||
<Swiper nextMargin='30px' style={{height: '225px'}}> |
||||
{ |
||||
data.map(d => <SwiperItem key={d.title}> |
||||
<View className={styles.featureTitle} onClick={() => jump(d.url)}>{d.title}</View> |
||||
{ |
||||
d.data.map((c, index) => <View |
||||
className='flex mb-3' |
||||
key={c.id} |
||||
onClick={() => jump(d.detailsUrl + c.path)}> |
||||
<View> |
||||
<Image src={c.imageUrl} className={styles.featureImage}/> |
||||
<Image src={[first, second, third][index]} className={styles.ranking} mode='aspectFill'/> |
||||
</View> |
||||
<View className={styles.featureText}> |
||||
<View className='text-ellipsis text-secondary'>{c.title}</View> |
||||
<View className='font-26 mt-1 text-muted text-ellipsis'>{c.description}</View> |
||||
</View> |
||||
</View>) |
||||
} |
||||
</SwiperItem>) |
||||
} |
||||
</Swiper> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
export default FeatureRecommended |
@ -0,0 +1,13 @@ |
||||
import {FC} from "react"; |
||||
import {Text, View} from "@tarojs/components"; |
||||
import styles from "../home.module.scss"; |
||||
import Icon from "@/components/icon"; |
||||
|
||||
export const Search: FC = () => { |
||||
return ( |
||||
<View className={styles.search}> |
||||
<Icon name='search' size={18}/> |
||||
<Text className='ml-1'>搜索课程</Text> |
||||
</View> |
||||
) |
||||
} |
@ -1,4 +1,5 @@ |
||||
export default definePageConfig({ |
||||
navigationBarTitleText: '首页', |
||||
navigationStyle: 'custom', |
||||
onReachBottomDistance: 30 |
||||
}) |
||||
|
@ -0,0 +1,89 @@ |
||||
.content { |
||||
position: relative; |
||||
padding: 0 20px; |
||||
min-height: 90vh; |
||||
box-sizing: border-box; |
||||
width: 750rpx; |
||||
overflow: hidden; |
||||
|
||||
&:after { |
||||
min-height: 100vh; |
||||
position: absolute; |
||||
top: 0; |
||||
left: -10%; |
||||
width: 120%; |
||||
content: ''; |
||||
display: block; |
||||
background: linear-gradient(40deg, #fff 50rpx, #caf0e2, #92ecc5) no-repeat; |
||||
min-height: 100vh; |
||||
background-size: 100% 600rpx; |
||||
filter: blur(50px); |
||||
z-index: -1; |
||||
} |
||||
} |
||||
|
||||
.search { |
||||
width: 710rpx; |
||||
margin: 40rpx 0 0; |
||||
background: #fff; |
||||
border-radius: 100px; |
||||
line-height: 68rpx; |
||||
color: #bbb; |
||||
font-size: 28rpx; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.adware { |
||||
margin-top: 40rpx; |
||||
width: 100%; |
||||
height: 260rpx; |
||||
border-radius: 16rpx; |
||||
overflow: hidden; |
||||
background: #eee; |
||||
} |
||||
|
||||
.videoListBox { |
||||
border-radius: 20px; |
||||
} |
||||
|
||||
.courseTag { |
||||
width: 200px; |
||||
margin: auto; |
||||
display: block; |
||||
} |
||||
|
||||
.feature { |
||||
color: #323635; |
||||
background: #fff; |
||||
padding: 30rpx 0 30rpx 30rpx; |
||||
margin-top: 40rpx; |
||||
border-radius: 30rpx; |
||||
} |
||||
|
||||
.featureTitle { |
||||
font-weight: 500; |
||||
font-size: 38rpx; |
||||
padding-bottom: 40rpx; |
||||
} |
||||
|
||||
.featureText { |
||||
width: calc(100% - 140px - 50px); |
||||
} |
||||
|
||||
.ranking { |
||||
position: absolute; |
||||
left: 24rpx; |
||||
width: 24px; |
||||
height: 24px; |
||||
} |
||||
|
||||
.featureImage { |
||||
width: 140px; |
||||
height: 90px; |
||||
background: #eee; |
||||
border-radius: 12rpx; |
||||
overflow: hidden; |
||||
margin-right: 20rpx; |
||||
} |
@ -1,13 +0,0 @@ |
||||
import {FC} from "react"; |
||||
import {Text, View} from "@tarojs/components"; |
||||
import styles from "@/pages/index/index.module.scss"; |
||||
import Icon from "@/components/icon"; |
||||
|
||||
export const Search: FC = () => { |
||||
return ( |
||||
<View className={styles.search}> |
||||
<Icon name='search' size={18}/> |
||||
<Text className='ml-1'>搜索课程</Text> |
||||
</View> |
||||
) |
||||
} |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 453 B |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 457 B |
After Width: | Height: | Size: 466 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,17 @@ |
||||
import {AdwareLinkType, AdwareParams} from "@/api"; |
||||
import Taro from "@tarojs/taro"; |
||||
|
||||
/** 拼接完整路径 */ |
||||
export function jumpAdware(advert_link: AdwareLinkType) { |
||||
if (advert_link.path && advert_link.param) { |
||||
let url = advert_link.path + "?" |
||||
const params: AdwareParams[] = JSON.parse(advert_link.param) |
||||
params.forEach((d, index) => { |
||||
url += d.key + "=" + d.value |
||||
if (index !== params.length - 1) { |
||||
url += "&" |
||||
} |
||||
}) |
||||
Taro.navigateTo({url}) |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
interface Health { |
||||
id: number |
||||
title: string |
||||
introduction: string |
||||
url_path: string |
||||
resource: Resource |
||||
/** 播放量 */ |
||||
video_view:number |
||||
} |
||||
|
||||
interface Brand { |
||||
id: number |
||||
brand_album: string |
||||
graphic_introduction: string |
||||
name: string |
||||
resource: Resource |
||||
} |
||||
|
||||
interface Resource { |
||||
id: number |
||||
name: string |
||||
url: string |
||||
} |
||||
|
||||
interface Kill { |
||||
id: number |
||||
resource: Resource |
||||
title: string |
||||
url_path: string |
||||
} |