微信公众号对接 && 弹窗 && 按钮 && 详情立即学习

main
king 1 year ago
parent 7f4092cc8a
commit 8e34ddb5af
  1. 6
      config/index.js
  2. 2
      pnpm-lock.yaml
  3. 2
      project.config.json
  4. 2
      project.tt.json
  5. 12
      src/api/login.ts
  6. 49
      src/api/request.ts
  7. 8
      src/api/user.ts
  8. 3
      src/app.config.ts
  9. 12
      src/app.tsx
  10. 40
      src/components/button/MyButton.tsx
  11. 10
      src/components/button/myButton.module.scss
  12. 27
      src/components/custom-page-container/custom-page-container.module.scss
  13. 59
      src/components/custom-page-container/custom-page-container.tsx
  14. 12
      src/components/empty/empty.module.scss
  15. 4
      src/components/empty/empty.tsx
  16. 8
      src/components/popPut/popPut.tsx
  17. 3
      src/components/tabs/tabs.scss
  18. 1
      src/components/tabs/tabs.tsx
  19. 2
      src/components/topic/judge.tsx
  20. 2
      src/components/topic/multi.tsx
  21. 2
      src/components/topic/shortAnswer.tsx
  22. 8
      src/components/video/video.tsx
  23. 42
      src/components/videoCover/videoCover.scss
  24. 6
      src/components/videoCover/videoCover.tsx
  25. 2
      src/index.html
  26. 9
      src/pages/business/curHistory/curHistory.tsx
  27. 4
      src/pages/business/history/history.tsx
  28. 59
      src/pages/business/videoInfo/components/catalogue.tsx
  29. 16
      src/pages/business/videoInfo/components/course.tsx
  30. 5
      src/pages/business/videoInfo/components/hours.tsx
  31. 38
      src/pages/business/videoInfo/videoInfo.scss
  32. 27
      src/pages/business/videoInfo/videoInfo.tsx
  33. 4
      src/pages/check/check.config.ts
  34. 68
      src/pages/check/check.module.scss
  35. 90
      src/pages/check/check.tsx
  36. 6
      src/pages/index/components/videoList.tsx
  37. 1
      src/pages/index/index.config.ts
  38. 19
      src/pages/index/index.tsx
  39. 1
      src/pages/login/login.config.ts
  40. 10
      src/pages/login/login.module.scss
  41. 89
      src/pages/login/login.tsx
  42. 8
      src/pages/manage/addStudent/addStudent.tsx
  43. 2
      src/pages/manage/bingUser/bingUser.config.ts
  44. 104
      src/pages/manage/bingUser/bingUser.tsx
  45. 7
      src/pages/manage/depAdmin/depAdmin.tsx
  46. 111
      src/pages/manage/offline/offline.tsx
  47. 2
      src/pages/my/components/header/header.tsx
  48. 4
      src/pages/my/components/header/service.tsx
  49. 4
      src/pages/my/my.module.scss
  50. 12
      src/static/css/module.scss
  51. 2
      src/store/profile.ts

@ -30,9 +30,6 @@ const config = {
postcss: {
pxtransform: {
enable: true,
config: {
selectorBlackList: ['nut-']
}
},
url: {
enable: true,
@ -58,9 +55,6 @@ const config = {
postcss: {
pxtransform: {
enable: true,
config: {
selectorBlackList: ['nut-']
}
},
autoprefixer: {
enable: true,

@ -8001,7 +8001,7 @@ packages:
/history@5.3.0:
resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
dependencies:
'@babel/runtime': 7.7.7
'@babel/runtime': 7.22.5
/hls.js@1.4.7:
resolution: {integrity: sha512-dvwJXLlYES6wb7DR42uuTrio5sUTsIoWbuNeQS4xHMqfVBZ0KAlJlBmjFAo4s20/0XRhsMjWf5bx0kq5Lgvv1w==}

@ -2,7 +2,7 @@
"miniprogramRoot": "./dist",
"projectname": "video",
"description": "",
"appid": "wx703940a70f0f1be7",
"appid": "wxd8ae2141559dcf5b",
"setting": {
"urlCheck": true,
"es6": false,

@ -2,7 +2,7 @@
"miniprogramRoot": "Progress/",
"projectname": "video",
"description": "",
"appid": "wx703940a70f0f1be7",
"appid": "wxd8ae2141559dcf5b",
"setting": {
"urlCheck": true,
"es6": false,

@ -0,0 +1,12 @@
import {request} from "@/api/request";
export interface LoginParams {
appid: string
route: string
}
export const loginApi = {
getParams() {
return request<LoginParams>("/api/v1/auth/login/app", "GET");
}
}

@ -44,11 +44,11 @@ export const ERROR_STATUS: Record<number | string, string> = {
'OVERSTEP': '请求越界~'
}
const whitelist = [
'/api/v1/auth/login/code',
'/api/v1/auth/login/checkout',
'/api/v1/auth/login/wechat'
]
// const whitelist = [
// '/api/v1/auth/login/code',
// '/api/v1/auth/login/checkout',
// '/api/v1/auth/login/wechat'
// ]
export function request<T = unknown>(
url: string,
@ -68,16 +68,17 @@ export function request<T = unknown>(
if (token) {
option.header ??= {}
option.header['Authorization'] = `Bearer ${token}`
} else {
/** 登录页面白名单 */
if (Taro.getCurrentInstance().router?.path === '/pages/login/login') {
if (!whitelist.includes(url)) {
return new Promise((_, reject) => reject())
}
} else {
return new Promise((_, reject) => reject())
}
}
// else {
/** 登录页面白名单 */
// if (Taro.getCurrentInstance().router?.path === '/pages/login/login') {
// if (!whitelist.includes(url)) {
// return new Promise((_, reject) => reject())
// }
// } else {
// return new Promise((_, reject) => reject())
// }
// }
if (method === 'GET' && data) {
let parameter = ''
Object.entries(data).forEach(([key, value], index) => {
@ -88,7 +89,6 @@ export function request<T = unknown>(
data && (option.data = data)
return new Promise<T>((resolve, reject) => {
Taro.request<T>({
...option,
success(res) {
@ -97,23 +97,24 @@ export function request<T = unknown>(
if (data?.code === 0 && res.statusCode === 200) {
resolve(data.data || [])
} else if (res.statusCode === 401) {
Taro.showModal({
title: "登录过期,需重新登陆",
showCancel: false,
success({confirm}) {
confirm && Taro.reLaunch({url: '/pages/login/login'})
}
})
// Taro.showModal({
// title: "登录过期,需重新登陆",
// showCancel: false,
// success({confirm}) {
// confirm && Taro.reLaunch({url: '/pages/login/login'})
// }
// })
} else {
Taro.showToast({title: data.msg || ERROR_STATUS[res.statusCode] || '请求错误~', icon: 'error'})
reject(null)
Taro.showToast({title: data.msg || data.message || ERROR_STATUS[res.statusCode] || '请求错误~', icon: 'error'})
}
} catch (e) {
reject(null)
}
},
fail(err) {
const errMsg = err.errMsg
const errMsg = err?.errMsg
console.log(err)
Taro.showToast({title: ERROR_STATUS[errMsg] || ERROR_STATUS['DEFAULT'], icon: 'error'})
reject(null)
}

@ -4,7 +4,7 @@ interface Code {
image: string
}
interface Login {
export interface LoginData {
access_token: string
code?: Code
company: Company
@ -48,7 +48,7 @@ interface HourCourse{
export const userApi = {
login(code: string) {
return request<Login>('/api/v1/auth/login/wechat', 'POST', {code})
return request<LoginData>('/api/v1/auth/login/wechat', 'POST', {code})
},
checkout(data: CheckoutBody) {
return request<CheckoutData>('/api/v1/auth/login/checkout', 'POST', data)
@ -78,5 +78,9 @@ export const userApi = {
},
hourCourse(course_id: number, unique_ident: number) {
return request<HourCourse>(`/api/v1/course/${course_id}/info/${unique_ident}`, "GET")
},
meetingSave(data: any) {
return request<LoginData>(`/api/v1/user/meeting/save`, "POST", data)
}
}

@ -1,7 +1,8 @@
export default defineAppConfig({
pages: [
'pages/index/index',
'pages/login/login',
'pages/check/check',
'pages/index/index',
'pages/my/my'
],
window: {

@ -4,7 +4,7 @@ import {CustomWrapper} from "@tarojs/components";
import unique_ident from "@/hooks/unique_ident";
function updateApp() {
if (Taro.canIUse('getUpdateManager')) {
if (Taro.canIUse('getUpdateManager.onCheckForUpdate')) {
const updateManager = Taro.getUpdateManager()
updateManager.onCheckForUpdate((res) => {
console.log('新版本', res.hasUpdate)
@ -35,10 +35,10 @@ function App(props) {
Taro.useLaunch(() => {
updateApp()
const token = JSON.parse(Taro.getStorageSync('profile') || '{}')?.token
if (!token) {
Taro.reLaunch({url: '/pages/login/login'})
}
// const token = JSON.parse(Taro.getStorageSync('profile') || '{}')?.token
// if (!token) {
// Taro.reLaunch({url: '/pages/login/login'})
// }
unique_ident.put()
unique_ident.del()
})
@ -47,7 +47,7 @@ function App(props) {
Taro.getSystemInfo({
success(res) {
Taro.getApp().globalData = {
statusBarHeight: res.statusBarHeight,
statusBarHeight: res.statusBarHeight || 0,
screenWidth: res.screenWidth,
screenHeight: res.screenHeight,
safeArea: res.safeArea,

@ -0,0 +1,40 @@
import {CSSProperties, FC} from "react";
import {Button} from "@tarojs/components";
import styles from './myButton.module.scss'
import {ButtonProps} from "@tarojs/components/types/Button";
interface Props extends ButtonProps {
fillet?: boolean
loading?: boolean
}
const MyButton: FC<Props> = (props) => {
const buttonStyle = (): CSSProperties => {
const style: CSSProperties = {}
switch (props.type) {
case 'default':
style.background = '#f5f8f7'
break
case 'warn':
style.background = '#c94f4f'
break
}
if (props.fillet) {
style.borderRadius = '100px'
}
return style
}
return (
<Button
{...props}
style={buttonStyle()}
className={styles.Mybutton}>
{props.children}
</Button>
)
}
export default MyButton

@ -0,0 +1,10 @@
.Mybutton {
line-height: 76rpx;
background: #45D4A8;
border-radius: 10rpx;
color: #fff;
font-size: 32rpx;
border: none !important;
outline: none !important;
position: sticky;
}

@ -0,0 +1,27 @@
.customPageContainer {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
}
.overlay {
background: rgba(#000, .5);
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.content {
width: 100%;
background: #fff;
position: absolute;
padding: 0 30px env(safe-area-inset-bottom);
box-sizing: border-box;
}

@ -0,0 +1,59 @@
import {PageContainer, PageContainerProps, View} from "@tarojs/components";
import {CSSProperties, FC, useEffect, useState} from "react";
import styles from './custom-page-container.module.scss'
const PageContainerInner: FC<PageContainerProps> = (props) => {
const [visible, setVisible] = useState(props.show)
useEffect(() => {
if (props.show != visible) {
setVisible(props.show);
// todo 目前仅仅支持 after
if (!props.show) {
(props.onBeforeLeave as Function)?.();
(props.onAfterLeave as Function)?.();
}
}
}, [props.show])
if (!props.show) {
return null
}
const contentStyle = (): CSSProperties => {
let style: CSSProperties = {}
switch (props.position) {
case "top":
style.borderRadius = '0 0 30px 30px'
style.top = 0
break
case 'bottom':
style.borderRadius = '30px 30px 0 0'
style.bottom = 0
break
case 'center':
style.borderRadius = '30px'
break
}
return style
}
return (
<View className={styles.customPageContainer}>
<View className={styles.overlay} onClick={props.onClickOverlay}></View>
<View className={styles.content} style={contentStyle()}>{props.children}</View>
</View>
)
}
const CustomPageContainer: FC<PageContainerProps> = (props) => {
if (process.env.TARO_ENV !== 'h5') {
return (<PageContainer {...props} />)
}
return (<PageContainerInner {...props} />);
}
export default CustomPageContainer

@ -3,19 +3,19 @@
text-align: center;
color: #6c757d;
position: relative;
}
Image {
.image {
display: block;
margin: auto;
width: 400px;
margin-bottom: 10px;
width: 600rpx;
}
View {
.name {
font-size: 30rpx;
position: absolute;
left: 0;
right: 0;
bottom: 40px;
bottom: 50rpx;
margin: auto;
}
}

@ -10,8 +10,8 @@ interface Props {
const Empty: FC<Props> = ({name}) => {
return (
<View className={styles.empty}>
<Image src={emptyImg} mode='widthFix'/>
<View>{name}</View>
<Image src={emptyImg} mode='widthFix' className={styles.image}/>
<View className={styles.name}>{name}</View>
</View>
)
}

@ -1,6 +1,7 @@
import {FC, ReactNode, useEffect, useState} from "react";
import {View, Image, Text, PageContainer} from "@tarojs/components";
import {View, Image, Text} from "@tarojs/components";
import Icon from "@/components/icon";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
interface Props {
@ -57,11 +58,10 @@ const PopPut: FC<Props> = ({title, chevron, content, image, isProp, children, sh
</View>
{
isProp
&& <PageContainer show={PageShow} position='bottom' round onBeforeLeave={() => setShow(false)}>
&& <CustomPageContainer show={PageShow} position='bottom' round onBeforeLeave={() => setShow(false)}>
{children}
</PageContainer>
</CustomPageContainer>
}
</>
)
}

@ -6,6 +6,7 @@ View::-webkit-scrollbar {
width: 100%;
height: 100%;
overflow: hidden;
font-size: 30rpx;
.tabs-scroll {
@ -38,7 +39,7 @@ View::-webkit-scrollbar {
}
.tabs-item {
padding: 25rpx;
padding: 20rpx;
}
}

@ -51,6 +51,7 @@ const Tabs: FC<TabsProps> = (opt: TabsProps) => {
<ScrollView scrollX scrollLeft={left} scrollWithAnimation showScrollbar={false}>
<View className={'tabs-scroll ' + (opt.tabList.length < 5 ? 'justify-around' : '')}>
{opt.tabList.map((d, index) => <View
key={index}
className={'tabs-item ' + (is_current(d.value, index) ? 'current' : null)}
onClick={(event) => onChange(event, index, d)}>
{d.title}

@ -9,7 +9,7 @@ interface Props {
index: number
validate: boolean
frequency?: number
end: boolean
end?: boolean
}
const Judge: FC<Props> = ({data, onAnswer, onUpAndDown, index, validate, frequency, end}) => {

@ -9,7 +9,7 @@ interface Props {
index: number
validate: boolean
frequency?: number
end: boolean
end?: boolean
}
const Multi: FC<Props> = ({data, onAnswer, onUpAndDown, index, validate, frequency, end}) => {

@ -10,7 +10,7 @@ interface Props {
index: number
validate: boolean
frequency?: number
end:boolean
end?:boolean
}
const ShortAnswer: FC<Props> = ({data, onAnswer, onUpAndDown, index, validate, frequency,end}) => {

@ -3,18 +3,15 @@ import {HVideoOptions} from "@/components/video/type";
import Taro from "@tarojs/taro";
import {FC, useState} from "react";
import unique_ident from "@/hooks/unique_ident";
// import {Profile} from '@/store'
const deviation: number = 0.5
const deviation: number = 1
const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
// const {user} = Profile.useContainer()
const video = Taro.createVideoContext('myVideo')
const [currentTime, setCurrentTime] = useState(0)
function onTimeUpdate(event: BaseEventOrig<VideoProps.onTimeUpdateEventDetail>) {
// if (opt.preview) return;
// if (user?.role_type === 2) return;
const time = event.detail.currentTime
/** 前进回退 */
if (currentTime + deviation < time) {
@ -42,7 +39,6 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
function onEnded() {
// if (opt.preview) return;
// if (user?.role_type === 2) return;
if (currentTime + 1 > opt.duration) {
opt.onEnded()
} else {
@ -60,7 +56,7 @@ const HVideo: FC<HVideoOptions> = (opt: HVideoOptions) => {
<Video
id={'myVideo'}
autoplay
style={{width: '100%', height: '100%'}}
style={{width: '100%', height: '100%',position:"relative"}}
poster={opt?.poster || ''}
src={opt.src}
enableProgressGesture={false}

@ -10,13 +10,17 @@
background: #fff;
border-radius: 10rpx;
overflow: hidden;
.upper {
overflow: hidden;
position: relative;
height: 180rpx;
.content {
position: absolute;
color: #fff;
left: 0;
top: 172rpx;
bottom: 0;
width: 100%;
line-height: 48rpx;
font-size: 24rpx;
@ -27,20 +31,6 @@
text-align: center;
}
.box {
padding: 20rpx;
.title{
width: 100%;
font-size: 28rpx;
word-break: break-all;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
.marker {
position: absolute;
background: rgba(#000, .5);
@ -53,12 +43,28 @@
Image {
width: 100%;
height: 220rpx;
display: block;
height: 100px;
}
}
.box {
box-sizing: border-box;
padding: 15rpx;
.title {
width: 100%;
font-size: 28rpx;
word-break: break-all;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
.videoButton {
margin-top: 20rpx;
margin-top: 10rpx;
color: #909795;
font-size: 22rpx;
}

@ -26,9 +26,9 @@ const VideoCover: FC<VideoCoverProps> = (opt: VideoCoverProps) => {
return (
<View className='videoBox'>
<View className='video'>
<View onClick={jump}>
<Image src={opt.thumb} mode='scaleToFill'/>
<View className='video' onClick={jump}>
<View className='upper'>
<Image src={opt.thumb} mode='aspectFit'/>
{opt.content && <View className='content'>{opt.content}</View>}
{opt.marker && <View className='marker'>{opt.marker}</View>}
</View>

@ -8,7 +8,7 @@
<meta name="format-detection" content="telephone=no,address=no">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
<title>video</title>
<title>医学道</title>
<script><%= htmlWebpackPlugin.options.script %></script>
</head>
<body>

@ -1,9 +1,10 @@
import {Image, PageContainer, Progress, View} from "@tarojs/components";
import {Image, Progress, View} from "@tarojs/components";
import Taro from "@tarojs/taro";
import styles from './curHistory.module.scss'
import {useState} from "react";
import {userApi} from "@/api";
import {formatDateTime, formatMinute} from "@/utils/time";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
const CurHistory = () => {
const [show, setShow] = useState(false)
@ -56,7 +57,7 @@ const CurHistory = () => {
<View className={styles.record}>
{data.map(d => <View onClick={() => setHour(d.unique_ident)} className={styles.recordItem}>
{data.map(d => <View key={d.id} onClick={() => setHour(d.unique_ident)} className={styles.recordItem}>
<View>{formatDateTime(new Date(d.start_at), "MM/dd HH:ss")} - {formatDateTime(new Date(d.end_at), "MM/dd HH:ss")}</View>
<View>{formatMinute(d.duration)}</View>
<Progress
@ -71,7 +72,7 @@ const CurHistory = () => {
</View>)}
</View>
<PageContainer
<CustomPageContainer
show={show}
round
onAfterLeave={() => setShow(false)}>
@ -96,7 +97,7 @@ const CurHistory = () => {
)
}
</View>}
</PageContainer>
</CustomPageContainer>
</View>
)
}

@ -1,6 +1,5 @@
import {Image, View} from "@tarojs/components";
import CategoryTabs from "@/pages/business/history/components/CategoryTabs";
// import {useState} from "react";
import styles from './history.module.scss'
import Taro from "@tarojs/taro";
import {userApi} from "@/api";
@ -26,8 +25,7 @@ const History = () => {
<CategoryTabs changeTabs={getData}/>
<View className='mt-3'>
{data.length ? data.map(d => <View className={styles.category}
onClick={() => jump(d.course_id, d.course.title)}>
{data.length ? data.map((d, index) => <View key={index} className={styles.category} onClick={() => jump(d.course_id, d.course.title)}>
<View className={styles.thumb}>
<Image src={d.course.thumb}/>
<View>{d.total_hour_count}/{d.finished_count}</View>

@ -1,10 +1,14 @@
import {FC, useState} from "react";
import Tabs, {OnChangOpt} from "@/components/tabs/tabs";
import {Button, View} from "@tarojs/components";
import {Image, View} from "@tarojs/components";
import {CourseDepData} from "@/api";
import Collapse from "@/components/collapse/collapse";
import Hours from "@/pages/business/videoInfo/components/hours";
import Taro from "@tarojs/taro";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
import MyButton from "@/components/button/MyButton";
import curRecord from '@/static/img/curRecord.png'
import hourRecord from '@/static/img/hourRecord.png'
interface Props {
data: CourseDepData | null
@ -21,6 +25,7 @@ const tabList = [
const Catalogue: FC<Props> = ({data, setHors, id}) => {
const [current, setCurrent] = useState(1)
const [show, setShow] = useState(false)
function jumCurHistory() {
Taro.preload({course_id: id, name: data?.course.title})
@ -67,6 +72,29 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
}
}
function learning() {
const flats: Hour[] = Object.values(data?.hours || {}).flat(Infinity) as Hour[]
if (data?.learn_record?.finished_count === data?.learn_record?.hour_count && flats.length) {
setHors(true, flats[0].id)
return
}
if (data?.learn_hour_records.length) {
const lastTimeId = data.learn_hour_records[data.learn_hour_records.length - 1].id
for (const [index, flat] of flats.entries()) {
if (flat.id === lastTimeId) {
const next = flats[index + 1]
if (next) {
setHors(true, lastTimeId)
}
}
}
} else {
Taro.showToast({title: "无播放视频", icon: 'error'})
}
}
return (
<>
<View className='catalogue'>
@ -74,7 +102,7 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
<View className='py-2 hours'>
{current === 0 && <View className='short_desc'>{data?.course.short_desc || '无'}</View>}
{current === 1 && <View>
<View className='font-weight mb-2'></View>
<View className='font-weight'></View>
{data?.chapters.length ? Object.values(data?.chapters || {}).map((d, index) => <View>
<Collapse title={`${index + 1}.${d.name}`}>
<Hours
@ -91,10 +119,33 @@ const Catalogue: FC<Props> = ({data, setHors, id}) => {
</View>
<View className='Videobutton'>
<Button className='button' onClick={jumCurHistory}></Button>
<MyButton onClick={learning}></MyButton>
<View className='px-3' onClick={() => setShow(true)}>...</View>
</View>
<CustomPageContainer
show={show}
position='bottom'
onClickOverlay={() => setShow(false)}>
<View className='more'>
<View></View>
<View className='flex justify-around'>
<View onClick={() => Taro.navigateTo({url: '/pages/business/history/history'})}>
<Image src={curRecord} className='image'/>
</View>
<View onClick={jumCurHistory}>
<Image src={hourRecord} className='image'/>
</View>
</View>
<MyButton onClick={() => setShow(false)} type='default' fillet></MyButton>
</View>
</CustomPageContainer>
</>
)
);
}
export default Catalogue

@ -1,4 +1,4 @@
import {Button, PageContainer, ScrollView, Swiper, SwiperItem, View} from "@tarojs/components";
import {Button, ScrollView, Swiper, SwiperItem, View} from "@tarojs/components";
import {FC, useEffect, useState} from "react";
import HVideo from "@/components/video/video";
import {CurEndParam, curriculum, HourPlayData} from "@/api";
@ -8,6 +8,7 @@ import Multi from "@/components/topic/multi";
import Judge from "@/components/topic/judge";
import ShortAnswer from "@/components/topic/shortAnswer";
import unique_ident from "@/hooks/unique_ident";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
interface Props {
id: number,
@ -40,17 +41,10 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
startRecording && curriculum.curEnd(courseId, id, {...startRecording, duration: data?.duration!}) //结束
if (testId) {
Taro.navigateTo({
url: `/pages/business/test/test?testId=${testId}`
})
Taro.navigateTo({url: `/pages/business/test/test?testId=${testId}`})
} else {
Taro.showModal({
title: "学习完成",
success() {
curEnd()
}
})
}
}
/** 进入断点 */
@ -186,7 +180,7 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
/>
<view>
<PageContainer
<CustomPageContainer
show={show}
position='bottom'
round
@ -244,7 +238,7 @@ const Course: FC<Props> = ({id, courseId, preview, curEnd}: Props) => {
{frequency > 0 && validate && <Button className='button' onClick={onceMore}></Button>}
{frequency === 0 && validate && <Button className='button' onClick={again}></Button>}
</View>
</PageContainer>
</CustomPageContainer>
</view>
</>

@ -57,7 +57,7 @@ const Hours: FC<Props> = ({data, click, learn_hour_records}) => {
return
}
console.log(id)
click(is_complete !== undefined, id)
}
@ -66,8 +66,9 @@ const Hours: FC<Props> = ({data, click, learn_hour_records}) => {
{
data?.map((d, index) =>
<View className={'hor' + ` ${complete(d.id) ? 'complete' : null}`}
key={index}
onClick={() => onClick(d.id, complete(d.id), d, data?.[index - 1]?.id,)}>
<Image src={complete(d.id) ? playOk : play} mode='aspectFit'/>
<Image src={complete(d.id) ? playOk : play} mode="scaleToFill" className='image'/>
<View>
<View>{index + 1}.{d.title}</View>
<View className='font-26'>{formatMinute(d.duration)}</View>

@ -1,7 +1,8 @@
.content {
&-video {
height: 480rpx;
.content-video {
width: 100%;
height: 500rpx;
}
.image {
@ -15,19 +16,16 @@
border-radius: 0 0 40rpx 40rpx;
padding: 30rpx 30rpx;
background: #fff;
margin-bottom: 30px;
}
}
.catalogue {
background: #fff;
border-radius: 40rpx;
padding: 24px;
padding-bottom: env(safe-area-inset-bottom);
padding: 0 24px 10px 24px;
margin-top: 20rpx;
.hours {
padding-bottom: 80px;
}
margin-bottom: env(safe-area-inset-bottom);
.short_desc {
color: #606563;
@ -40,10 +38,10 @@
padding: 20px 0;
display: flex;
Image {
.image {
width: 40rpx;
height: 40rpx;
margin-top: 6px;
margin: 10px;
}
& > View {
@ -64,11 +62,29 @@
}
.Videobutton {
display: flex;
background: #fff;
position: fixed;
width: 100%;
left: 0;
bottom: 0;
padding-bottom: env(safe-area-inset-bottom);
box-sizing: border-box;
padding: 10px 20px env(safe-area-inset-bottom);
}
.more {
height: 50vh;
padding: 20px 0;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-between;
.image {
width: 100px;
height: 100px;
display: block;
margin: auto;
}
}

@ -1,24 +1,23 @@
import {Image, Text, View} from "@tarojs/components";
import {FC, useState} from "react";
import {FC, useEffect, useState} from "react";
import {CourseDepData, curriculum} from "@/api";
import './videoInfo.scss'
import {Profile} from '@/store'
import Catalogue from "./components/catalogue";
import Course from "./components/course";
import Taro from "@tarojs/taro";
import eventsIndex from "@/hooks/eventsIndex";
// import eventsIndex from "@/hooks/eventsIndex";
const VideoInfo: FC = () => {
const {id, depId} = Taro.getCurrentInstance().preloadData as { id: number, depId: number | null }
const {id, depId} = Taro.getCurrentInstance()?.router?.params as any
const [data, setData] = useState<CourseDepData | null>(null)
const [playId, setPlayId] = useState<number | null>(null)
const [preview, setPreview] = useState(false)
eventsIndex.trigger(12)
// eventsIndex.trigger(12)
async function getData() {
const getData = async () => {
const res = await curriculum.courseDep(id, depId)
if (res) {
setData(res)
@ -34,14 +33,14 @@ const VideoInfo: FC = () => {
setPlayId(play_id)
}
Taro.useLoad(() => {
useEffect(() => {
getData()
})
}, [])
/** 播放下一个视频 */
function playNext() {
const flats: Hour[] = Object.values(data?.hours || {}).flat(Infinity) as Hour[]
if (playId === flats.at(-1)?.id) {
if (playId === flats?.[flats.length - 1]?.id) {
Taro.showModal({title: '当前课程结束'})
return;
}
@ -50,7 +49,7 @@ const VideoInfo: FC = () => {
const next = flats[index + 1]
if (next) {
Taro.showModal({
title: '是否播放下一个视频',
title: '继续播放下个视频',
success({confirm}) {
if (confirm) {
setPlayId(next.id)
@ -86,9 +85,7 @@ const VideoInfo: FC = () => {
}
Taro.useDidShow(() => {
if (data) {
getData().then()
}
data && getData()
})
@ -106,14 +103,14 @@ const VideoInfo: FC = () => {
<Text className='font-34 text-warning'>{data?.is_required ? '必修' : '选修'}</Text>
<Text>{data?.course.class_hour}</Text>
</View>
<View className='font-weight font-40 my-4'>{data?.course.title}</View>
<View className='font-weight font-40 my-3'>{data?.course.title}</View>
<View className='text-muted font-26'>
<Text>{data?.learn_hour_records.length || 0}/{data?.course.class_hour}</Text>
</View>
</View>
<Catalogue data={data} setHors={setHors} id={id}/>
</View>
</Profile.Provider>
)
}

@ -0,0 +1,4 @@
export default definePageConfig({
navigationStyle: 'custom',
navigationBarTitleText: '验证'
})

@ -0,0 +1,68 @@
.container {
position: relative;
}
.navbar,
.brand {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.navbar {
position: relative;
line-height: 1;
font-size: 28px;
}
.brand {
width: 140px;
height: 140px;
background: #fff;
border-radius: 20px;
margin: 250px auto 145px;
image {
width: 100px;
height: 100px;
}
}
.loginTips {
margin: 24px;
text-align: center;
}
.submit {
display: flex;
justify-content: center;
align-items: center;
gap: 12px;
margin: 0 auto;
a {
color: #fff;
}
}
.errorTips {
position: fixed;
top: 10%;
left: 24px;
right: 24px;
background: red;
color: white;
padding: 24px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 12px;
}
.bing {
height: 50vh;
padding: 50px 30px 0;
}

@ -0,0 +1,90 @@
import {FC, useRef, useState} from "react";
import {Button, Form, Image, Input, View} from "@tarojs/components";
import {Profile} from "@/store";
import {userApi} from "@/api";
import Taro, {useRouter} from "@tarojs/taro";
import {regexTel} from "@/utils/regu";
import styles from './check.module.scss'
const uuid = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0
const v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
}).replace(/-/g, '')
}
const Bing: FC = () => {
const form = useRef<HTMLFormElement | null>(null)
const [loading, setLoading] = useState(false)
const {setUser, setToken, setCompany} = Profile.useContainer()
const [captchaKey, setCaptchaKey] = useState(uuid())
const router = useRouter()
async function Submit(data) {
setLoading(true)
const value = data.target.value
if (!regexTel.exec(value.phone_number)) {
Taro.showToast({title: '手机号错误', icon: 'error'})
setLoading(false)
return
}
try {
const res = await userApi.checkout({
...value,
catch_key: captchaKey,
openid: router.params.openid,
})
if (res) {
setCompany(res.company)
setUser(res.user)
setToken(res.token)
Taro.switchTab({url: '/pages/index/index'})
}
} catch (e) {
setCaptchaKey(uuid)
}
setLoading(false)
}
return (
<View className='h-10 pt-6 px-3 bg-white'>
<Form className='form' onSubmit={Submit} ref={form}>
<View className='item'>
<View></View>
<Input name='phone_number' placeholder={'请输入手机号'} value='18708100736'/>
</View>
<View className='item'>
<View></View>
<View className='flex align-center flex-1'>
<Input name='code' className='flex-1' placeholder={'请输入验证码'}/>
<Image className='w-2 ml-1' style='height:28px'
src={process.env.TARO_APP_API + '/api/v1/captcha?key=' + captchaKey}
onClick={() => setCaptchaKey(uuid)}/>
</View>
</View>
<Button
className={'button ' + styles.submit}
style={{margin: '30px auto'}}
formType='submit'
disabled={loading}
>
</Button>
</Form>
</View>
)
}
const Index: FC = () => {
return (
<Profile.Provider>
<Bing/>
</Profile.Provider>
);
}
export default Index;

@ -7,7 +7,6 @@ import styles from '../index.module.scss'
import {formatMinute} from "@/utils/time";
import {userApi} from "@/api";
import Empty from "@/components/empty/empty";
// import eventsIndex from "@/hooks/eventsIndex";
interface Props {
categoryKey: CoursesKey
@ -50,13 +49,13 @@ export const VideoList: FC<Props> = ({categoryKey}) => {
const find = records.find(d => d?.course_id === id)
if (find) {
if (class_hour === find.finished_count) {
return <View className='text-danger'></View>
return <View></View>
}
return (<View>{`${class_hour}节/已学${find.finished_count}`}</View>)
}
return (<View>{`${class_hour}节/已学0节`}</View>)
case "is_finished":
return (<View>{`${class_hour}节/已学${class_hour}`}</View>)
return <View></View>
}
return (<View>sd</View>)
}
@ -88,6 +87,7 @@ export const VideoList: FC<Props> = ({categoryKey}) => {
title={c.title}
id={c.id}
depId={c.id}
key={c.id}
time={formatMinute(c.course_duration)}
content={rateOfLearning(c.id, c.class_hour)}
/>) : <Empty name='暂无数据'/>}

@ -1,4 +1,5 @@
export default definePageConfig({
navigationStyle: 'custom',
navigationBarTitleText:'医学道',
onReachBottomDistance: 30
})

@ -1,16 +1,17 @@
import {FC, useState} from "react";
import {FC, useEffect, useState} from "react";
import {View} from "@tarojs/components";
import styles from './index.module.scss'
import {Profile} from '@/store'
// import {Search} from "@/pages/index/components/search";
import {VideoList} from "@/pages/index/components/videoList";
import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs";
import {CoursesKey} from "@/api/public";
import Taro from '@tarojs/taro'
import Taro, {useRouter} from '@tarojs/taro'
// import {Search} from "@/pages/index/components/search";
const Index: FC = () => {
const globalData = Taro.getApp().globalData
const router = useRouter();
const category: TabList[] = [
{title: "必修", value: 'is_required'},
{title: "选修", value: 'is_not_required'},
@ -19,16 +20,22 @@ const Index: FC = () => {
]
const [categoryKey, setCategoryKey] = useState<CoursesKey>('is_required')
function tabChange(data: OnChangOpt) {
setCategoryKey(data.tab?.value as CoursesKey)
}
useEffect(() => {
if (router.params.d) {
Taro.setStorageSync("profile", decodeURIComponent(router.params.d))
Taro.reLaunch({url:"/pages/index/index"})
}
}, [])
return (
<Profile.Provider>
<View className={styles.content} style={`paddingTop:${globalData.statusBarHeight}px`}>
<View className='text-center font-weight font-34 mt-3'></View>
{process.env.TARO_ENV !== "h5" && <View className='text-center font-weight font-34 mt-3'></View>}
{/*<Search/>*/}
<Tabs tabList={category} onChange={tabChange} current={categoryKey}/>
<VideoList categoryKey={categoryKey}/>

@ -1,3 +1,4 @@
export default definePageConfig({
navigationStyle: 'custom',
navigationBarTitleText: '登录'
})

@ -40,12 +40,18 @@
align-items: center;
gap: 12px;
margin: 0 auto;
padding: 25px 0;
a {
color: #fff;
}
}
.errorTips {
position: absolute;
top: 100%;
position: fixed;
top: 10%;
left: 24px;
right: 24px;
background: red;

@ -1,12 +1,14 @@
import {FC, useEffect, useRef, useState} from "react";
import {Profile} from "@/store";
import {Button, Form, Image, Input, PageContainer, Text, View} from "@tarojs/components";
import {Button, Form, Image, Input, Text, View} from "@tarojs/components";
import Taro from "@tarojs/taro";
import styles from './login.module.scss'
import Loading from "@/components/loading";
import Icon from "@/components/icon";
import {userApi} from "@/api";
import {LoginData, userApi} from "@/api";
import {regexTel} from "@/utils/regu";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
import {loginApi, LoginParams} from "@/api/login";
interface BingProps {
code: string
@ -79,22 +81,74 @@ const Bing: FC<BingProps> = ({code, catch_key}: BingProps) => {
)
}
const Login = () => {
function getMenuButtonBoundingClientRect() {
if (process.env.TARO_ENV === 'h5') {
return {top: 0, bottom: 44}
}
return Taro.getMenuButtonBoundingClientRect()
}
const Login: FC = () => {
const {statusBarHeight = 0} = Taro.getSystemInfoSync()
const bbc = Taro.getMenuButtonBoundingClientRect();
const bbc = getMenuButtonBoundingClientRect();
const navHeight = bbc.bottom + (bbc.top - statusBarHeight) - statusBarHeight
const [isLoading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [validateCode, setCode] = useState<string | null>(null)
const [catch_key, setCatch_key] = useState<string | null>(null)
const {setUser, setToken, setCompany} = Profile.useContainer()
const [h5params, setH5Params] = useState<LoginParams | null>(null)
const params = Taro.getCurrentInstance()?.router?.params as unknown as { data: LoginData }
useEffect(() => {
console.log(params)
if (params?.data) {
if (!params.data?.code) {
setUser(params.data.user)
setToken(params.data.token)
setCompany(params.data.company)
setLoading(false)
Taro.switchTab({url: '/pages/index/index'})
return
}
setCatch_key(catch_key)
setCode(params.data.code.image)
return
}
if (process.env.TARO_ENV === 'h5') {
setLoading(true);
loginApi.getParams().then((res) => {
setH5Params(res);
setError(null);
}).catch(e => {
setError(e?.errMsg ?? '系统内部错误')
}).finally(() => {
setLoading(false);
})
}
}, [])
function login() {
if (isLoading) return;
if (h5params == null) {
setError('页面参数错误,请刷新页面!')
return;
}
setLoading(true)
if (process.env.TARO_ENV === 'h5') {
location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=" + h5params!.appid +
"&redirect_uri=" + encodeURIComponent(h5params!.route) +
"&response_type=code" +
"&scope=snsapi_userinfo" +
"#wechat_redirect";
} else {
Taro.login({
success: async (res) => {
try {
@ -103,6 +157,7 @@ const Login = () => {
setUser(user)
setToken(token)
setCompany(company)
setLoading(false)
Taro.switchTab({url: '/pages/index/index'})
return
}
@ -118,12 +173,22 @@ const Login = () => {
},
})
}
}
return (
<View className={styles.container}>
<PageContainer show={!!validateCode} position='bottom' round onBeforeLeave={() => setCode(null)}>
<CustomPageContainer show={!!validateCode} position='bottom' onBeforeLeave={() => setCode(null)}>
{validateCode && <Bing code={validateCode!} catch_key={catch_key!}/>}
</PageContainer>
</CustomPageContainer>
<View className={styles.brand}>
<Image mode={'scaleToFill'} src="https://playedu.yaojiankang.top/favicon.ico"/>
</View>
<View className={styles.loginTips}>
<Text>使</Text>
</View>
<Button className={'button ' + styles.submit} onClick={login} disabled={isLoading}>
{isLoading ? <Loading/> : null}
<View className={styles.navbar} style={`height:${navHeight}px;margin-top:${statusBarHeight}px`}>
<Text></Text>
{error ? <View className={styles.errorTips}>
@ -133,15 +198,6 @@ const Login = () => {
</View>
</View> : null}
</View>
<View className={styles.brand}>
<Image mode={'scaleToFill'} src="https://admin.playedu.xyz/favicon.ico"/>
</View>
<View className={styles.loginTips}>
<Text>使</Text>
</View>
<Button className={'button ' + styles.submit} onClick={login} disabled={isLoading}>
{isLoading ? <Loading/> : null}
<Text></Text>
</Button>
</View>
)
@ -154,4 +210,5 @@ const Index: FC = () => {
</Profile.Provider>
)
}
export default Index

@ -1,4 +1,4 @@
import {Button, Form, Input, PageContainer, View} from "@tarojs/components";
import {Button, Form, Input, View} from "@tarojs/components";
import {FC, useEffect, useState} from "react";
import {ManageApi, Student} from "@/api/manage";
import Icon from "@/components/icon";
@ -7,13 +7,13 @@ import {curriculum} from "@/api";
import './addStudent.scss'
import {getCurrentInstance} from "@tarojs/runtime";
import {Profile} from '@/store'
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
interface Department {
id: number
title: string
}
const AddStudent = () => {
const [userInfo, setUerInfo] = useState<Student | null>(null)
const [department, setDepartment] = useState<Department[]>([])
@ -127,7 +127,7 @@ const AddStudent = () => {
<Button className='add button' formType='submit' disabled={disable}></Button>
</Form>
<PageContainer show={show} round>
<CustomPageContainer show={show} round>
<View className='px-2 pt-1' style='text-align:right' onClick={() => setShow(false)}></View>
<View className='h-4 p-2 flex flex-wrap align-start'>
{department?.map(item => {
@ -139,7 +139,7 @@ const AddStudent = () => {
)
})}
</View>
</PageContainer>
</CustomPageContainer>
</View>
)
}

@ -1,3 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '注册',
navigationBarTitleText: '登记',
})

@ -1,49 +1,103 @@
import {FC} from "react";
import {Button, Form, Input, View} from "@tarojs/components";
import {getCurrentInstance} from "@tarojs/runtime";
import Taro from "@tarojs/taro";
import Taro, {useLoad, useRouter} from "@tarojs/taro";
import {userApi} from "@/api";
import {Profile} from '@/store'
const BingUser: FC = () => {
const {company_id, department_id, start_time, end_time} = getCurrentInstance()?.router?.params as unknown as Offline
Taro.useLoad(() => {
const time = Date.now()
if (!company_id
|| !department_id
|| !start_time
|| !end_time
|| time > new Date(end_time).getTime()
|| time < new Date(start_time).getTime()) {
Taro.showModal({
title: '二维码已过期',
success() {
Taro.reLaunch({url: '/pages/login/login'})
}
// const {depid, start_time, end_time} = getCurrentInstance()?.router?.params as unknown as Offline
const {setUser, setToken, setCompany} = Profile.useContainer()
const router = useRouter()
// Taro.useLoad(() => {
// const time = Date.now()
// if (!depid
// || !start_time
// || !end_time
// || time > new Date(end_time).getTime()
// || time < new Date(start_time).getTime()) {
// Taro.showModal({
// title: '二维码已过期',
// success() {
// Taro.reLaunch({url: '/pages/login/login'})
// }
// })
// return
// }
// })
function submit(e) {
const value = e.detail.value
// if (!value.user_name || !value.phone_number) {
// Taro.showToast({title: '请认真填写', icon: "error"})
// return
// }
//
// if (!regexTel.exec(value.phone_number)) {
// Taro.showToast({title: '手机号错误', icon: 'error'})
// return
// }
Taro.showLoading()
Taro.login({
success: async (res) => {
const data = await userApi.meetingSave({
...value,
code: res.code,
ticket: router.params.ticket,
})
return
setCompany(data.company)
setUser(data.user)
setToken(data.token)
Taro.switchTab({url: '/pages/index/index'})
},
fail: () => {
Taro.showToast({title: '获取微信登录失败', icon: "error"})
},
complete() {
Taro.hideLoading()
}
})
}
return (
<View className='h-10 bg-white p-2'>
<Form className='form'>
<Form className='form' onSubmit={submit}>
<View className='item'>
<View></View>
<Input placeholder='请输入用户名' focus/>
<Input placeholder='请输入用户名' focus name='user_name'/>
</View>
<View className='item'>
<View></View>
<Input type='number' placeholder='请输入手机号'/>
<Input type='number' placeholder='请输入手机号' name='phone_number'/>
</View>
<Button className='button mt-3' formType='submit'></Button>
<Button className='button mt-3' formType='submit'></Button>
</Form>
</View>
)
}
export default BingUser
const BingUserIndex = () => {
const router = useRouter()
useLoad(() => {
if (!router.params.ticket) {
Taro.reLaunch({
url: "/pages/index/index"
})
}
})
return (
<Profile.Provider>
<BingUser/>
</Profile.Provider>
)
}
export default BingUserIndex

@ -1,12 +1,13 @@
import {FC, useEffect, useState} from "react";
import {AddDepProps, ManageApi} from "@/api/manage";
import {Button, View, PageContainer, Input, Form} from "@tarojs/components";
import {Button, View, Input, Form} from "@tarojs/components";
import Taro from "@tarojs/taro";
import {Profile} from '@/store'
import './depAdmin.scss'
import PopPut from "@/components/popPut/popPut";
import folder from '@/static/img/folder.png'
import {getCurrentInstance} from "@tarojs/runtime";
import CustomPageContainer from "@/components/custom-page-container/custom-page-container";
interface ChangeDataProps {
putCompany: Manage | null
@ -255,11 +256,11 @@ const DepAdmin: FC = () => {
</View>
</View>
<PageContainer show={show} round onAfterLeave={() => setShow(false)}>
<CustomPageContainer show={show} round onAfterLeave={() => setShow(false)}>
<View>
{show && <ChangeData getDeps={getData} putCompany={putCompany} parent_id={Number(params.id)}/>}
</View>
</PageContainer>
</CustomPageContainer>
</Profile.Provider>
)
}

@ -1,4 +1,4 @@
import {FC, useCallback, useState} from "react";
import {FC, useCallback, useEffect, useState} from "react";
import {Button, Image, Picker, View} from "@tarojs/components";
import styles from './offline.module.scss'
import Taro from "@tarojs/taro";
@ -7,13 +7,14 @@ import {formatDate} from "@/utils/time";
import {getSetting, authorize} from "@tarojs/taro";
import PopPut from "@/components/popPut/popPut";
const Offline: FC = () => {
const [manages, setManages] = useState<Manage[]>([])
const [start, setStart] = useState<string>(formatDate(new Date(), "YY-MM-dd"))
const [end, setEnd] = useState<string>(formatDate(new Date(), "YY-MM-dd"))
const [start, setStart] = useState<string>(formatDate(new Date(), "YY-MM-dd 08:00:00"))
const [end, setEnd] = useState<string>(formatDate(new Date(), "YY-MM-dd 18:00:00"))
const [tempFilePath, setTempFilePath] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null)
const [depid, setDepid] = useState<number | null>(null)
const [isDownloading, setDownloading] = useState(false)
Taro.useLoad(() => {
curriculum.department().then(res => {
@ -21,78 +22,53 @@ const Offline: FC = () => {
})
})
async function change(e) {
const depName = e.detail.value
if (!depName) {
Taro.showToast({title: '请选择部门', icon: 'error'})
return null
useEffect(() => {
if (!depid) {
// Taro.showToast({title: '请选择部门', icon: 'error'})
return
}
if (isDownloading) {
return;
}
setDownloading(true)
const startTime = new Date(start).getTime()
const endTime = new Date(end).getTime()
const depid = manages.find(d => d.name === depName)?.id
const path = encodeURIComponent("/pages/meeting/index")
const path = encodeURIComponent("/pages/meeting/meeting")
const qrcodeUrl = `${process.env.TARO_APP_API}/wechat/link?depid=${depid}&start_time=${startTime}&end_time=${endTime}&path=${path}`
const qrcodeUrl = `${process.env.TARO_APP_API}/official/qrcode?depid=${depid}&start_time=${startTime}&end_time=${endTime}&path=${path}&t=${Date.now()}`
if (process.env.TARO_ENV === 'h5') {
setTempFilePath(qrcodeUrl)
} else {
Taro.downloadFile({
url: qrcodeUrl,
success(res) {
console.log({res})
setTempFilePath(res.tempFilePath)
},
fail() {
Taro.showToast({title: '二维码生产失败', icon: 'error'})
}
fail(err) {
console.log({err})
setError("请求失败");
// Taro.showToast({title: '操作失败', icon: 'error'})
},
complete() {
setDownloading(false)
}
)
})
}
}, [depid])
// const paintingCanvas = () => {
// Taro.showLoading()
// try {
// Taro.createSelectorQuery().select('#canvasId').fields({node: true, size: true}).exec((res) => {
// const width = res[0].width
// const height = res[0].height
//
// const canvas = res[0].node
//
// Mycanvas = canvas
// const ctx = canvas.getContext('2d')
//
// const dpr = Taro.getSystemInfoSync().pixelRatio
// canvas.width = width * dpr
// canvas.height = height * dpr
// ctx.scale(dpr, dpr)
//
// ctx.fillStyle = '#ffffff'
// ctx.fillRect(0, 0, width, height)
//
// ctx.font = '16px Microsoft YaiHei'
// ctx.fillStyle = 'black'
// ctx.textAlign = 'center';
// ctx.fillText(`${start === end ? `有效期${start}` : `开始:${start} 结束:${end}`}`, width / 2, 280)
//
// Taro.getImageInfo({
// src: 'http://81.69.44.74:39200/kynyxd/images/ILBNTCk3PhC3xEV1zCDAsggudZzkiHJi.png',
// success(res) {
// const img = canvas.createImage()
// img.src = res.path
// img.onload = () => {
// ctx.drawImage(img, (width - 200) / 2, 20, 200, 200)
// }
// }
// })
// })
// } catch (e) {
// Taro.showToast({title: '二维码生成失败', icon: 'error'})
// }
// Taro.hideLoading()
// }
function change(e) {
setDepid(() => manages[Number(e.detail.value)]?.id)
}
const handleWriteFile = useCallback(() => {
if (tempFilePath) {
if (tempFilePath == null) {
Taro.showToast({title: '下载失败', icon: 'error'})
} else if (process.env.TARO_ENV !== 'h5') {
Taro.saveImageToPhotosAlbum({
filePath: tempFilePath,
success() {
@ -103,7 +79,7 @@ const Offline: FC = () => {
}
})
} else {
Taro.showToast({title: '下载失败', icon: 'error'})
Taro.showToast({title: '请截屏', icon: 'error'})
}
}, [tempFilePath])
@ -153,12 +129,19 @@ const Offline: FC = () => {
<View>
<Picker mode='selector' range={manages} onChange={change} rangeKey='name'>
<PopPut title='部门'/>
<PopPut title='部门' content={manages?.find(x => x.id === depid)?.name}/>
</Picker>
</View>
{tempFilePath && <View className='text-center'>
<Image src={tempFilePath} mode='widthFix'/>
<Image
src={tempFilePath}
mode='widthFix'
onLoad={() => setError(null)}
onError={(e) => setError(e.detail.errMsg)}
style={{width: '80%'}}
/>
{error && <View>{error}</View>}`
<Button className='button' onClick={handleSaveCode}></Button>
</View>}
</View>

@ -9,7 +9,7 @@ const Header = () => {
return (
<View className={styles.header}>
<View className='flex'>
<Image src={avatar}/>
<Image src={avatar} className={styles.avatar}/>
<View className='flex-1'>
<View className='font-32 font-weight'>{user?.name}</View>
<View className='login font-24 mt-2 text-secondary flex justify-between content-start'>

@ -27,7 +27,7 @@ const Service = () => {
oldList.unshift(...[
{title: '部门管理', src: dep, router: '/pages/manage/depAdmin/depAdmin'},
{title: '课程购买', src: buy, router: '/pages/manage/curriculum/curriculum'},
{title: '见面会', src: buy, router: '/pages/manage/offline/offline'},
{title: '现场会', src: buy, router: '/pages/manage/offline/offline'},
])
setList(oldList)
}
@ -44,7 +44,7 @@ const Service = () => {
{
list.map(d => {
return (
<View onClick={() => jump(d.router)}>
<View onClick={() => jump(d.router)} key={d.title}>
<Image src={d.src} mode='aspectFit' className={styles.serviceImage}/>
<View>{d.title}</View>
</View>

@ -35,10 +35,10 @@ page {
}
.header {
padding: 130px 20px 0;
font-size: 10rpx;
Image {
.avatar {
width: 100px;
height: 100px;
margin-right: 30px;

@ -1,7 +1,12 @@
page {
background-color: #efeff7;
page,
.taro_router .taro_page {
background-color: #efeff7 !important;
font-family: PingFang SC-Bold, PingFang SC;
-webkit-overflow-scrolling: touch
-webkit-overflow-scrolling: touch;
}
body {
font-size: 32rpx;
}
.input {
@ -64,7 +69,6 @@ page {
}
.button {
width: 690rpx;
line-height: 76rpx;
background: #45D4A8;
border-radius: 10rpx;

@ -22,7 +22,7 @@ function useProfile() {
setUser(null)
setToken(null)
Taro.removeStorageSync('profile')
Taro.reLaunch({url: '/pages/login/login'})
// Taro.reLaunch({url: '/pages/login/login'})
}
useEffect(() => {

Loading…
Cancel
Save