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. 20
      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_API=https://yjx.dev.yaojiankang.top
TARO_APP_LGOIN=true TARO_APP_LGOIN=true

@ -1,2 +1,6 @@
export * from './user' export * from './user'
export * from './curriculum' 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 = { export const loginApi = {
/** 公众号登录参数 */
getParams() { getParams() {
return request<LoginParams>("/api/v1/auth/login/app", "GET"); return request<LoginParams>("/api/v1/auth/login/app", "GET");
}, },
getMeetingParams() {
return request<LoginParams>("/api/v1/auth/login/meeting", "GET");
},
testLogin() { testLogin() {
return request('/token/18708100736', "GET") 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({ export default defineAppConfig({
pages: [ pages: [
'pages/index/index', 'pages/index/index',
'pages/meeting/meeting',
'pages/login/login', 'pages/login/login',
'pages/check/check', 'pages/check/check',
'pages/my/my' 'pages/my/my',
], ],
window: { window: {
backgroundTextStyle: 'light', backgroundTextStyle: 'light',
@ -60,7 +61,6 @@ export default defineAppConfig({
'depCur/depCur', 'depCur/depCur',
'addCur/addCur', 'addCur/addCur',
'spotMeeting/spotMeeting', 'spotMeeting/spotMeeting',
'bingUser/bingUser',
] ]
} }
], ],

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

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

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

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

@ -12,5 +12,8 @@
.buttonFixed { .buttonFixed {
position: fixed; position: fixed;
bottom: 100px; 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 Taro from "@tarojs/taro";
import {userApi} from "@/api"; import {userApi} from "@/api";
import styles from './userInfo.module.scss' import styles from './userInfo.module.scss'
import MyButton from "@/components/button/MyButton";
const List = () => { const List = () => {
@ -62,7 +63,9 @@ const List = () => {
<PopPut title='解绑微信' onClick={unbind} no_border/> <PopPut title='解绑微信' onClick={unbind} no_border/>
</View> </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 {formatMinute} from "@/utils/time";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import {curriculum} from "@/api"; import {curriculum} from "@/api";
import lock from '@/static/img/lock.png'
interface Props { interface Props {
data?: Hour[] | null data?: Hour[] | null
@ -63,19 +64,20 @@ const Hours: FC<Props> = ({data, click, learn_hour_records}) => {
return ( return (
<> <>
{ {data?.map((d, index) =>
data?.map((d, index) => <View className={'hor' + ` ${complete(d.id) ? 'complete' : null}`}
<View className={'hor' + ` ${complete(d.id) ? 'complete' : null}`} key={index}
key={index} onClick={() => onClick(d.id, complete(d.id), d, data?.[index - 1]?.id,)}>
onClick={() => onClick(d.id, complete(d.id), d, data?.[index - 1]?.id,)}> <Image src={complete(d.id) ? playOk : play} mode="scaleToFill" className='image'/>
<Image src={complete(d.id) ? playOk : play} mode="scaleToFill" className='image'/> <View className='title'>
<View> <View className='text'>
<View>{index + 1}.{d.title}</View> <View>{index + 1}.{d.title}</View>
<View className='font-26'>{formatMinute(d.duration)}</View> <View className='font-26'>{formatMinute(d.duration)}</View>
{complete(d.id) === 0 && <View className='font-26 text-danger'></View>} {complete(d.id) === 0 && <View className='font-26 text-danger'></View>}
</View> </View>
</View>) {complete(d.id) == null && <Image className='lock' src={lock} mode='aspectFit'/>}
} </View>
</View>)}
</> </>
) )
} }

@ -1,4 +1,6 @@
.content { .content {
padding-bottom: 30rpx;
box-sizing: border-box;
.content-video { .content-video {
width: 100%; width: 100%;
@ -23,9 +25,8 @@
.catalogue { .catalogue {
background: #fff; background: #fff;
border-radius: 40rpx; border-radius: 40rpx;
padding: 0 24px 10px 24px; padding: 0 24px 0 24px;
margin-top: 20rpx; margin-top: 20rpx;
margin-bottom: env(safe-area-inset-bottom);
.short_desc { .short_desc {
color: #606563; color: #606563;
@ -41,19 +42,30 @@
.image { .image {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
margin: 10px; margin-top: 10rpx;
} }
& > View {
.title {
flex: 1; 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; border-bottom: 1px solid #ddd;
View { .text {
width: 630rpx; width: 600rpx;
margin-bottom: 20px;
word-wrap: break-word; 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 [playId, setPlayId] = useState<number | null>(null)
const [preview, setPreview] = useState(false) const [preview, setPreview] = useState(false)
// eventsIndex.trigger(12)
const getData = async () => { const getData = async () => {
const res = await curriculum.courseDep(id, depId) 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 {CoursesKey} from "@/api/public";
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {Profile} from '@/store' import {Profile} from '@/store'
// import {Search} from "@/pages/index/components/search"; // import {Search} from "@/pages/index/components/search";

@ -177,7 +177,7 @@ const Login: FC = () => {
async function TESTLOGIN() { async function TESTLOGIN() {
const res = await loginApi.testLogin() const res = await loginApi.testLogin()
Taro.setStorageSync('profile', JSON.stringify(res)) 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: [ itemList: [
'查看部门课程', '查看部门课程',
'查看子部门和学员', '查看子部门和学员',
'修改', '修改部门',
'删除' '删除部门'
], ],
success({tapIndex}) { success({tapIndex}) {
switch (tapIndex) { switch (tapIndex) {
@ -99,9 +99,9 @@ const DepAdmin: FC = () => {
function userManagesSheet(user: User) { function userManagesSheet(user: User) {
const itemList = [ const itemList = [
'修改', '修改学员',
'删除', '删除学员',
"学习记录", "学习记录",
] ]
if (user.role_type === 1) { if (user.role_type === 1) {
itemList.push('取消管理员') itemList.push('取消管理员')

@ -2,7 +2,7 @@ import {FC, useCallback, useEffect, useState} from "react";
import {Image, Picker, View} from "@tarojs/components"; import {Image, Picker, View} from "@tarojs/components";
import styles from './spotMeeting.module.scss' import styles from './spotMeeting.module.scss'
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import {curriculum} from "@/api"; import {curriculum, meetingAPi} from "@/api";
import {formatDate} from "@/utils/time"; import {formatDate} from "@/utils/time";
import {getSetting, authorize} from "@tarojs/taro"; import {getSetting, authorize} from "@tarojs/taro";
import PopPut from "@/components/popPut/popPut"; import PopPut from "@/components/popPut/popPut";
@ -18,10 +18,22 @@ const SpotMeeting: FC = () => {
const [isDownloading, setDownloading] = useState(false) const [isDownloading, setDownloading] = useState(false)
const change = useCallback((e) => {
setDepid(() => manages[Number(e.detail.value)]?.id)
}, [manages])
useEffect(() => { useEffect(() => {
curriculum.department().then(res => { curriculum.department().then(res => {
setManages(res.data) 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(() => { useEffect(() => {
@ -55,11 +67,8 @@ const SpotMeeting: FC = () => {
}) })
} }
Taro.hideLoading() Taro.hideLoading()
}, [depid]) }, [depid, end, start])
function change(e) {
setDepid(() => manages[Number(e.detail.value)]?.id)
}
const handleWriteFile = useCallback(() => { const handleWriteFile = useCallback(() => {
if (tempFilePath == null) { if (tempFilePath == null) {
@ -116,7 +125,7 @@ const SpotMeeting: FC = () => {
<View> <View>
<Picker mode='selector' range={manages} onChange={change} rangeKey='name'> <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> </Picker>
</View> </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; background-color: #efeff7 !important;
font-family: PingFang SC-Bold, PingFang SC; font-family: PingFang SC-Bold, PingFang SC;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
min-height: 100vh;
} }
body { 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 {useEffect, useState} from "react";
import {createContainer} from "unstated-next"; import {createContainer} from "unstated-next";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import {whiteList} from "@/config";
function DataKey(data: any) { function DataKey(data: any) {
@ -12,12 +13,6 @@ function DataKey(data: any) {
&& 'company' in data.data && 'company' in data.data
} }
const whitelist = [
'/pages/check/check',
'/pages/login/login',
]
function useProfile() { function useProfile() {
let data let data
@ -27,7 +22,7 @@ function useProfile() {
if (DataKey(profileCopy)) { if (DataKey(profileCopy)) {
data = profileCopy.data || null data = profileCopy.data || null
localStorage.removeItem("profileCopy") localStorage.removeItem("profileCopy")
Taro.switchTab({url: '/pages/index/index'}) Taro.reLaunch({url: '/pages/index/index'})
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -46,7 +41,7 @@ function useProfile() {
} }
if (data?.token == null) { 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'}) Taro.reLaunch({url: '/pages/login/login'})
} }
} }

Loading…
Cancel
Save