1.线上测试版自动部署

2.本地环境测试搭建
3.邀约登录对接公众号
4.解决刷新持久化失败
5.全局底部安全距离
main
king 1 year ago
parent 4b250d5acb
commit 7da7471724
  1. 1
      .env
  2. 4
      src/api/index.ts
  3. 4
      src/api/login.ts
  4. 7
      src/api/meeting.ts
  5. 4
      src/app.config.ts
  6. 2
      src/components/button/MyButton.tsx
  7. 7
      src/components/topic/shortAnswer.tsx
  8. 1
      src/components/videoCover/videoCover.scss
  9. 6
      src/config.ts
  10. 5
      src/pages/business/userInfo/userInfo.module.scss
  11. 5
      src/pages/business/userInfo/userInfo.tsx
  12. 12
      src/pages/business/videoInfo/components/hours.tsx
  13. 28
      src/pages/business/videoInfo/videoInfo.scss
  14. 1
      src/pages/business/videoInfo/videoInfo.tsx
  15. 1
      src/pages/index/index.tsx
  16. 2
      src/pages/login/login.tsx
  17. 94
      src/pages/manage/bingUser/bingUser.tsx
  18. 10
      src/pages/manage/depAdmin/depAdmin.tsx
  19. 21
      src/pages/manage/spotMeeting/spotMeeting.tsx
  20. 0
      src/pages/meeting/meeting.config.ts
  21. 105
      src/pages/meeting/meeting.tsx
  22. 3
      src/static/css/module.scss
  23. BIN
      src/static/img/course.png
  24. BIN
      src/static/img/lock.png
  25. BIN
      src/static/img/order.png
  26. BIN
      src/static/img/userInfo.png
  27. 11
      src/store/profile.ts

@ -1,2 +1,3 @@
TARO_APP_API=https://yjx.dev.yaojiankang.top
TARO_APP_LGOIN=true

@ -1,2 +1,6 @@
export * from './user'
export * from './curriculum'
export * from './login'
export * from './meeting'
export * from './public'
export * from './manage'

@ -6,9 +6,13 @@ export interface LoginParams {
}
export const loginApi = {
/** 公众号登录参数 */
getParams() {
return request<LoginParams>("/api/v1/auth/login/app", "GET");
},
getMeetingParams() {
return request<LoginParams>("/api/v1/auth/login/meeting", "GET");
},
testLogin() {
return request('/token/18708100736', "GET")
}

@ -0,0 +1,7 @@
import {request} from "@/api/request";
export const meetingAPi={
qrcodeKey() {
return request('/api/v1/auth/login/qrcode/key', "GET")
}
}

@ -1,9 +1,10 @@
export default defineAppConfig({
pages: [
'pages/index/index',
'pages/meeting/meeting',
'pages/login/login',
'pages/check/check',
'pages/my/my'
'pages/my/my',
],
window: {
backgroundTextStyle: 'light',
@ -60,7 +61,6 @@ export default defineAppConfig({
'depCur/depCur',
'addCur/addCur',
'spotMeeting/spotMeeting',
'bingUser/bingUser',
]
}
],

@ -36,7 +36,7 @@ const MyButton: FC<Props> = (props) => {
<Button
{...props}
style={buttonStyle()}
className={styles.Mybutton}>
className={`${styles.Mybutton} ${props.className}`}>
{props.children}
</Button>
)

@ -38,15 +38,14 @@ const ShortAnswer: FC<Props> = ({data, onAnswer, onUpAndDown, index, validate, f
<Text className='topicType'></Text>
<Text>{data.question}</Text>
</View>
<Textarea
placeholder='请输入答案'
adjustPosition
autoHeight
className='Textarea'
onBlur={onBlur}
maxlength={255}
value={value}
onInput={(e) => setValue((e.target as HTMLTextAreaElement).value)}/>
onInput={(e) => setValue(e.detail.value)}/>
<View className='text-muted mt-2'>3</View>
{onUpAndDown && <View className='upAndDown'>

@ -29,6 +29,7 @@
white-space: nowrap;
background: rgba(#000, .5);
text-align: center;
padding: 0;
}
.marker {

@ -0,0 +1,6 @@
/** 白名单 */
export const whiteList: string[] = [
'/pages/check/check',
'/pages/login/login',
'/pages/meeting/meeting'
]

@ -12,5 +12,8 @@
.buttonFixed {
position: fixed;
bottom: 100px;
left: 30rpx;
left: 0;
width: 100%;
padding: 0 30rpx;
box-sizing: border-box;
}

@ -6,6 +6,7 @@ import {Button, Input, View} from "@tarojs/components";
import Taro from "@tarojs/taro";
import {userApi} from "@/api";
import styles from './userInfo.module.scss'
import MyButton from "@/components/button/MyButton";
const List = () => {
@ -62,7 +63,9 @@ const List = () => {
<PopPut title='解绑微信' onClick={unbind} no_border/>
</View>
<Button className={`button ${styles.buttonFixed}`} onClick={empty}>退</Button>
<View className={styles.buttonFixed}>
<MyButton onClick={empty}>退</MyButton>
</View>
</>
)
}

@ -6,6 +6,7 @@ import play from "@/static/img/play.png";
import {formatMinute} from "@/utils/time";
import Taro from "@tarojs/taro";
import {curriculum} from "@/api";
import lock from '@/static/img/lock.png'
interface Props {
data?: Hour[] | null
@ -63,19 +64,20 @@ const Hours: FC<Props> = ({data, click, learn_hour_records}) => {
return (
<>
{
data?.map((d, index) =>
{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="scaleToFill" className='image'/>
<View>
<View className='title'>
<View className='text'>
<View>{index + 1}.{d.title}</View>
<View className='font-26'>{formatMinute(d.duration)}</View>
{complete(d.id) === 0 && <View className='font-26 text-danger'></View>}
</View>
</View>)
}
{complete(d.id) == null && <Image className='lock' src={lock} mode='aspectFit'/>}
</View>
</View>)}
</>
)
}

@ -1,4 +1,6 @@
.content {
padding-bottom: 30rpx;
box-sizing: border-box;
.content-video {
width: 100%;
@ -23,9 +25,8 @@
.catalogue {
background: #fff;
border-radius: 40rpx;
padding: 0 24px 10px 24px;
padding: 0 24px 0 24px;
margin-top: 20rpx;
margin-bottom: env(safe-area-inset-bottom);
.short_desc {
color: #606563;
@ -41,19 +42,30 @@
.image {
width: 40rpx;
height: 40rpx;
margin: 10px;
margin-top: 10rpx;
}
& > View {
.title {
flex: 1;
margin-left: 20px;
width: 710rpx;
padding: 0 0 20rpx 20rpx;
display: flex;
justify-content: space-between;
box-sizing: border-box;
border-bottom: 1px solid #ddd;
View {
width: 630rpx;
margin-bottom: 20px;
.text {
width: 600rpx;
word-wrap: break-word;
}
.lock {
width: 30rpx;
margin-left: 10rpx;
margin-top: 10rpx;
}
}
}

@ -14,7 +14,6 @@ const VideoInfo: FC = () => {
const [playId, setPlayId] = useState<number | null>(null)
const [preview, setPreview] = useState(false)
// eventsIndex.trigger(12)
const getData = async () => {
const res = await curriculum.courseDep(id, depId)

@ -6,7 +6,6 @@ import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs";
import {CoursesKey} from "@/api/public";
import Taro from '@tarojs/taro'
import {Profile} from '@/store'
// import {Search} from "@/pages/index/components/search";

@ -177,7 +177,7 @@ const Login: FC = () => {
async function TESTLOGIN() {
const res = await loginApi.testLogin()
Taro.setStorageSync('profile', JSON.stringify(res))
Taro.switchTab({url: '/pages/index/index'})
Taro.reLaunch({url: '/pages/index/index'})
}

@ -1,94 +0,0 @@
import {FC} from "react";
import {Button, Form, Input, View} from "@tarojs/components";
import Taro, {useLoad, useRouter} from "@tarojs/taro";
import {userApi} from "@/api";
import {Profile} from '@/store'
const BingUser: FC = () => {
// const {depid, start_time, end_time} = getCurrentInstance()?.router?.params as unknown as Offline
const {setUser, setToken, setCompany} = Profile.useContainer()
const router = useRouter()
useLoad(() => {
if (!router.params.ticket) {
Taro.reLaunch({
url: "/pages/index/index"
})
}
})
// 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,
})
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' onSubmit={submit}>
<View className='item'>
<View></View>
<Input placeholder='请输入用户名' focus name='user_name'/>
</View>
<View className='item'>
<View></View>
<Input type='number' placeholder='请输入手机号' name='phone_number'/>
</View>
<Button className='button mt-3' formType='submit'></Button>
</Form>
</View>
)
}
export default BingUser

@ -62,8 +62,8 @@ const DepAdmin: FC = () => {
itemList: [
'查看部门课程',
'查看子部门和学员',
'修改',
'删除'
'修改部门',
'删除部门'
],
success({tapIndex}) {
switch (tapIndex) {
@ -99,9 +99,9 @@ const DepAdmin: FC = () => {
function userManagesSheet(user: User) {
const itemList = [
'修改',
'删除',
"学习记录",
'修改学员',
'删除学员',
"学习记录",
]
if (user.role_type === 1) {
itemList.push('取消管理员')

@ -2,7 +2,7 @@ import {FC, useCallback, useEffect, useState} from "react";
import {Image, Picker, View} from "@tarojs/components";
import styles from './spotMeeting.module.scss'
import Taro from "@tarojs/taro";
import {curriculum} from "@/api";
import {curriculum, meetingAPi} from "@/api";
import {formatDate} from "@/utils/time";
import {getSetting, authorize} from "@tarojs/taro";
import PopPut from "@/components/popPut/popPut";
@ -18,10 +18,22 @@ const SpotMeeting: FC = () => {
const [isDownloading, setDownloading] = useState(false)
const change = useCallback((e) => {
setDepid(() => manages[Number(e.detail.value)]?.id)
}, [manages])
useEffect(() => {
curriculum.department().then(res => {
setManages(res.data)
})
meetingAPi.qrcodeKey().then((res: any) => {
if (res.dep_id && res.end_time && res.start_time) {
setDepid(res.dep_id)
setEnd(formatDate(new Date(res.end_time), "YY-MM-dd 18:00:00"))
setStart(formatDate(new Date(res.start_time), "YY-MM-dd 08:00:00"))
}
})
}, [])
useEffect(() => {
@ -55,11 +67,8 @@ const SpotMeeting: FC = () => {
})
}
Taro.hideLoading()
}, [depid])
}, [depid, end, start])
function change(e) {
setDepid(() => manages[Number(e.detail.value)]?.id)
}
const handleWriteFile = useCallback(() => {
if (tempFilePath == null) {
@ -116,7 +125,7 @@ const SpotMeeting: FC = () => {
<View>
<Picker mode='selector' range={manages} onChange={change} rangeKey='name'>
<PopPut title='部门' content={manages?.find(x => x.id === depid)?.name}/>
<PopPut title='部门' content={manages?.find(x => x.id == depid)?.name}/>
</Picker>
</View>

@ -0,0 +1,105 @@
import {FC, useEffect, useState} from "react";
import {Button, Form, Input, View} from "@tarojs/components";
import Taro, {useRouter} from "@tarojs/taro";
import {userApi, loginApi, LoginParams} from "@/api";
import {Profile} from '@/store'
import {getCurrentInstance} from "@tarojs/runtime";
import {regexTel} from "@/utils/regu";
const Meeting: FC = () => {
const {depid, start_time, end_time} = getCurrentInstance()?.router?.params as unknown as Offline
const {setUser, setToken, setCompany} = Profile.useContainer()
const [h5params, setH5Params] = useState<LoginParams | null>(null)
const router = useRouter()
useEffect(() => {
loginApi.getMeetingParams().then((res) => {
setH5Params(res)
})
}, [])
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
}
if (process.env.TARO_ENV === 'h5') {
if (h5params == null) {
Taro.showToast({title: '页面参数错误,请刷新页面!', icon: 'error'})
return;
}
location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=" + h5params!.appid +
"&redirect_uri=" + encodeURIComponent(h5params!.route + `?depid=1&phone_number=${value.phone_number}&user_name=${value.user_name}`) +
"&response_type=code" +
"&scope=snsapi_userinfo" +
"#wechat_redirect";
} else {
Taro.login({
success: async (res) => {
const data = await userApi.meetingSave({
...value,
code: res.code,
ticket: router.params.ticket,
})
setCompany(data.company)
setUser(data.user)
setToken(data.token)
Taro.switchTab({url: '/pages/index/index'})
},
fail: () => {
Taro.showToast({title: '获取微信登录失败', icon: "error"})
}
})
}
}
return (
<View className='h-10 bg-white p-2'>
<Form className='form' onSubmit={submit}>
<View className='item'>
<View></View>
<Input placeholder='请输入用户名' focus name='user_name'/>
</View>
<View className='item'>
<View></View>
<Input type='number' placeholder='请输入手机号' name='phone_number'/>
</View>
<Button className='button mt-3' formType='submit'></Button>
</Form>
</View>
)
}
export default Meeting

@ -3,6 +3,9 @@ page,
background-color: #efeff7 !important;
font-family: PingFang SC-Bold, PingFang SC;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
min-height: 100vh;
}
body {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -1,6 +1,7 @@
import {useEffect, useState} from "react";
import {createContainer} from "unstated-next";
import Taro from "@tarojs/taro";
import {whiteList} from "@/config";
function DataKey(data: any) {
@ -12,12 +13,6 @@ function DataKey(data: any) {
&& 'company' in data.data
}
const whitelist = [
'/pages/check/check',
'/pages/login/login',
]
function useProfile() {
let data
@ -27,7 +22,7 @@ function useProfile() {
if (DataKey(profileCopy)) {
data = profileCopy.data || null
localStorage.removeItem("profileCopy")
Taro.switchTab({url: '/pages/index/index'})
Taro.reLaunch({url: '/pages/index/index'})
}
} catch (e) {
console.error(e)
@ -46,7 +41,7 @@ function useProfile() {
}
if (data?.token == null) {
if (!whitelist.includes(Taro.getCurrentInstance().router?.path?.split('?')?.[0] || '')) {
if (!whiteList.includes(Taro.getCurrentInstance().router?.path?.split('?')?.[0] || '')) {
Taro.reLaunch({url: '/pages/login/login'})
}
}

Loading…
Cancel
Save