diff --git a/src/components/image/image.module.scss b/src/components/image/image.module.scss new file mode 100644 index 0000000..44a776b --- /dev/null +++ b/src/components/image/image.module.scss @@ -0,0 +1,12 @@ +.imgBox { + position: relative; +} + +.imgError { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin: auto; +} diff --git a/src/components/image/image.tsx b/src/components/image/image.tsx index 4297130..9651106 100644 --- a/src/components/image/image.tsx +++ b/src/components/image/image.tsx @@ -1,7 +1,8 @@ import {FC, useEffect, useState} from "react"; import {Image, ImageProps, View} from "@tarojs/components"; -import {AtActivityIndicator} from "taro-ui"; import shard from '@/static/img/shard.png' +import styles from './image.module.scss' +import Taro from "@tarojs/taro"; interface Props extends ImageProps { width: number @@ -13,14 +14,12 @@ const Img: FC = ({src, mode = 'aspectFill', width, height, fallback = sha const [isError, setIsError] = useState(false) const [loading, setLoading] = useState(true) + const imgAnimation = Taro.createAnimation({duration: 0}).opacity(0).step() + const [animationData, setAnimationData] = useState(imgAnimation.export()) + useEffect(() => { - if (!src) { - setIsError(true) - setLoading(false) - } else { - setIsError(false) - setLoading(false) - } + setIsError(!src) + setLoading(!!src) }, [src]) // 图片加载失败 @@ -32,25 +31,37 @@ const Img: FC = ({src, mode = 'aspectFill', width, height, fallback = sha function onLoadHandler() { setLoading(false) setIsError(false) + imgAnimation.opacity(1).step({duration: 200}) + setAnimationData(imgAnimation.export()) } return ( - + {!isError && - - + + + } - {loading && } { isError && !loading && - + } ) diff --git a/src/components/spinner/index.tsx b/src/components/spinner/index.tsx index 0ecfb5e..a99d6f9 100644 --- a/src/components/spinner/index.tsx +++ b/src/components/spinner/index.tsx @@ -1,6 +1,6 @@ import Taro from "@tarojs/taro"; -import { View, Image } from '@tarojs/components' -import { Component, ReactNode } from "react"; +import {View, Image} from '@tarojs/components' +import {Component, ReactNode} from "react"; import indicator from './loading.svg' import './style.scss' @@ -12,8 +12,9 @@ type Status = | 'completed' interface Props { - enable?: boolean - overlay?: boolean + enable?: boolean // 控制显现 + overlay?: boolean // 页面覆盖 + block?: boolean // 块级 } interface State { @@ -30,8 +31,8 @@ type Controller = { } function createController(setState: StateSetter): Controller { - const background = Taro.createAnimation({ duration: 600 }) - const rotation = Taro.createAnimation({ duration: 600 }) + const background = Taro.createAnimation({duration: 600}) + const rotation = Taro.createAnimation({duration: 600}) let rotateTimer: ReturnType | undefined let status: Status | undefined @@ -49,8 +50,8 @@ function createController(setState: StateSetter): Controller { } // 清空动画 - background.export() - rotation.export() + background.step().export() + rotation.step().export() // 通知 UI 刷新 if (notify) { @@ -66,7 +67,7 @@ function createController(setState: StateSetter): Controller { // 旋转动画定时器 const rotate = () => { - rotation.opacity(opacity).rotate(360).step({ duration: 600 }) + rotation.opacity(opacity).rotate(360).step({duration: 600}) notifyListener() rotateTimer = setTimeout(rotate, 600) } @@ -80,23 +81,20 @@ function createController(setState: StateSetter): Controller { const onFinish = (opacity: number, nextStatus: Status) => { const lockStatus = status - setTimeout(() => { - if (lockStatus === status) { - background.backgroundColor(`rgba(255,255,255,${opacity})`).step({ duration: 0 }) - if (nextStatus === 'dismissed') { - clearAnimation() - } - status = nextStatus - notifyListener() + if (lockStatus === status) { + background.backgroundColor(`rgba(255,255,255,${opacity})`).step({duration: 0}) + if (nextStatus === 'dismissed') { + clearAnimation() } - }, 600) + status = nextStatus + notifyListener() + } } const setStatus = (newStatus: Status, opacity: number) => { if (status !== newStatus) { status = newStatus setAnimation(opacity) - if (status === 'reverse') { onFinish(0, 'dismissed') } else if (status === 'forward') { @@ -140,17 +138,16 @@ export default class Spin extends Component { this.setState((s) => ({...s, ...state})) } - componentDidMount(): void { - console.log(this.props.enable) - this.controller.setTick(this.props.enable) - } + // componentDidMount(): void { + // this.controller.setTick(this.props.enable) + // } componentDidUpdate(): void { this.controller.setTick(this.props.enable) } componentWillUnmount(): void { - this.controller.clear() + this.controller.clear() } shouldComponentUpdate(nextProps: Readonly, nextState: Readonly): boolean { @@ -161,9 +158,10 @@ export default class Spin extends Component { render(): ReactNode { return ( - + - + ) diff --git a/src/components/spinner/style.scss b/src/components/spinner/style.scss index 1ff6261..dbaa3e9 100644 --- a/src/components/spinner/style.scss +++ b/src/components/spinner/style.scss @@ -1,8 +1,17 @@ .spinner-wrapper { - background-color: rgba( #fff, 1.0); transition: background-color 1200ms ease-out; + &.is-block { + width: auto; + position: absolute; + z-index: 99999; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } + &.is-fixed { + background-color: rgba(#fff, 1.0); z-index: 99999; position: fixed; width: 100%; @@ -15,7 +24,7 @@ &.reverse, &.dismissed { - background-color: rgba( #fff, 0.0); + background-color: rgba(#fff, 0.0); } &.dismissed { @@ -36,6 +45,7 @@ flex-direction: column; justify-content: center; align-items: center; + width: 100%; } .spinner { diff --git a/src/pages/business/videoInfo/videoInfo.scss b/src/pages/business/videoInfo/videoInfo.scss index 5c0a7ad..1e6f13a 100644 --- a/src/pages/business/videoInfo/videoInfo.scss +++ b/src/pages/business/videoInfo/videoInfo.scss @@ -7,11 +7,7 @@ height: 500rpx; } - .image { - width: 100%; - height: 100%; - display: block; - } + .header { margin-bottom: 10px; diff --git a/src/pages/business/videoInfo/videoInfo.tsx b/src/pages/business/videoInfo/videoInfo.tsx index bec6f10..731c360 100644 --- a/src/pages/business/videoInfo/videoInfo.tsx +++ b/src/pages/business/videoInfo/videoInfo.tsx @@ -1,4 +1,4 @@ -import {Image, Text, View} from "@tarojs/components"; +import {Text, View} from "@tarojs/components"; import {FC, useCallback, useEffect, useState} from "react"; import {CourseDepData, curriculum} from "@/api"; import './videoInfo.scss' @@ -9,6 +9,8 @@ import eventsIndex from "@/hooks/eventsIndex"; import {formatMinute} from "@/utils/time"; import videoEvents from "@/hooks/videoEvents"; import unique_ident from "@/hooks/unique_ident"; +import Spin from "@/components/spinner"; +import Img from "@/components/image/image"; const VideoInfo: FC = () => { const {id, depId} = Taro.getCurrentInstance()?.router?.params as any @@ -16,15 +18,16 @@ const VideoInfo: FC = () => { const [playId, setPlayId] = useState(null) const [preview, setPreview] = useState(false) // 预览 const [playing, setPlaying] = useState(false) // 学习中 + const [enable, setEnable] = useState(true) const getData = useCallback(async (playing: boolean) => { - const res = await curriculum.courseDep(id, depId) - if (res) { - setData(res) - } - if (playId != null) { // 用于自动播放 判断当前课程是否完成 - currentVideo(res, playing) + try { + const res = await curriculum.courseDep(id, depId) + res && setData(res) + playId != null && currentVideo(res, playing) // 用于自动播放 判断当前课程是否完成 + } catch (e) { } + setEnable(false) }, [playing, playId]) const curEnd = (test?: boolean) => { @@ -73,9 +76,7 @@ const VideoInfo: FC = () => { } }, [playId, data, preview]) - /** - * 判断当前课程是否完成 - */ + /** 判断当前课程是否完成 */ const currentVideo = useCallback((data: CourseDepData, playing: boolean) => { const courseHourRecordsFinish = data?.learn_hour_records.find(d => d.id === playId)?.courseHourRecordsFinish if (typeof courseHourRecordsFinish === 'number') { @@ -106,12 +107,13 @@ const VideoInfo: FC = () => { }) return ( <> + { playId ? - : + : } diff --git a/src/pages/home/components/feature_recommended.tsx b/src/pages/home/components/feature_recommended.tsx index 31fbb5f..cb08eab 100644 --- a/src/pages/home/components/feature_recommended.tsx +++ b/src/pages/home/components/feature_recommended.tsx @@ -150,8 +150,8 @@ const FeatureRecommended: FC = (props) => { } return ( - - + + { data.map(d => { const [page, setPage] = useState(1) const [data, setData] = useState([]) const [total, setTotal] = useState(0) + const [enable, setEnable] = useState(true) async function getData(page: number) { - const res = await HomeApi.health(page, 10) - setData(res.data) - setTotal(res.total) + try { + const res = await HomeApi.health(page, 10) + setData(res.data) + setTotal(res.total) + } catch (e) { + } + setEnable(false) } useReachBottom(() => { @@ -32,6 +38,7 @@ const Health: FC = () => { return ( + { data.length > 0 diff --git a/src/pages/preview/illness/sort/sort.tsx b/src/pages/preview/illness/sort/sort.tsx index 695f3ea..54e4b82 100644 --- a/src/pages/preview/illness/sort/sort.tsx +++ b/src/pages/preview/illness/sort/sort.tsx @@ -7,6 +7,7 @@ import Tabs, {TabList} from "@/components/tabs/tabs"; import {illnessApi} from "@/api/illness"; import Empty from "@/components/empty/empty"; import leftArrow from "@/static/img/leftArrow.png" +import Spin from "@/components/spinner"; const prefix = 'SORT' const Sort: FC = () => { @@ -14,16 +15,24 @@ const Sort: FC = () => { const [firstId, setFirstId] = useState(undefined) // 一级分类 const [secondId, setSecondId] = useState(undefined) // 二级分类 const [list, setList] = useState([]) + const [enable, setEnable] = useState(true) + const [loading, setLoading] = useState(false) const globalData = Taro.getApp().globalData const menu = Taro.getMenuButtonBoundingClientRect() async function getData() { - const res = await HomeApi.category(3) - setData(res) - if (res.length) { - setFirstId(res[0].id) - setSecondId(res[0]?.resource_category?.[0].id) + try { + + const res = await HomeApi.category(3) + setData(res) + if (res.length) { + setFirstId(res[0].id) + setSecondId(res[0]?.resource_category?.[0].id) + } + } catch (e) { + setLoading(false) } + setEnable(false) } function jump(id: number) { @@ -54,17 +63,16 @@ const Sort: FC = () => { useEffect(() => { if (secondId) { - Taro.showLoading({title: '加载中'}) + setLoading(true) illnessApi.list(secondId, 1, 100).then(res => { setList(res.list) - }).finally(() => { - Taro.hideLoading() }).catch(() => { setList([]) }) } else { setList([]) } + setLoading(false) }, [secondId]) function swiperChange(e) { @@ -76,6 +84,7 @@ const Sort: FC = () => { return ( + { className={styles.tree} scrollWithAnimation> { - list.length ? - list.map(d => jump(d.id)}>{d.name}) - : + loading ? : <> + { + list.length ? + list.map(d => jump(d.id)}>{d.name}) + : + } + } ) diff --git a/src/pages/preview/profession/profession.tsx b/src/pages/preview/profession/profession.tsx index 404d374..bafc47d 100644 --- a/src/pages/preview/profession/profession.tsx +++ b/src/pages/preview/profession/profession.tsx @@ -5,6 +5,7 @@ import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs"; import Empty from "@/components/empty/empty"; import Taro from "@tarojs/taro"; import styles from './profession.module.scss' +import Spin from "@/components/spinner"; interface KillData { data: Kill[] @@ -16,6 +17,8 @@ const Profession = () => { const [tabs, setTabs] = useState([]) const [categoryId, setCategoryId] = useState(null) const [data, setData] = useState>(new Map) + const [enable, setEnable] = useState(true) + const [loading, setLoading] = useState(false) /** * more 开启加载更多 @@ -32,9 +35,7 @@ const Profession = () => { } try { - if (!data.has(categoryId)) { - Taro.showLoading() - } + setLoading(true) const res = await HomeApi.skillList(categoryId!, page, 10) const dataList = res.data.reduce((pre, cur) => { const index = pre.findIndex(d => d.id === cur.id) @@ -54,7 +55,7 @@ const Profession = () => { setData(oldData) } catch (e) { } - Taro.hideLoading() + setLoading(false) } useEffect(() => { @@ -62,10 +63,14 @@ const Profession = () => { }, [categoryId]) async function getCategory() { - const res = await HomeApi.skillCategory() - const newTabs = res.map(d => ({title: d.name, value: d.id})) - setTabs(newTabs) - setCategoryId(newTabs[0].value as number) + try { + const res = await HomeApi.skillCategory() + const newTabs = res.map(d => ({title: d.name, value: d.id})) + setTabs(newTabs) + setCategoryId(newTabs[0].value as number) + } catch (e) { + } + setEnable(false) } function tabsChange(tab: OnChangOpt) { @@ -101,6 +106,7 @@ const Profession = () => { ) } + ) } @@ -108,6 +114,7 @@ const Profession = () => { return ( <> +