parent
84fbd98fa9
commit
2a29617017
@ -1,5 +1,6 @@ |
|||||||
#TARO_APP_API=https://yjx.dev.yaojiankang.top |
#TARO_APP_API=https://yjx.dev.yaojiankang.top |
||||||
TARO_APP_API=https://mooc.yaojiankang.top |
#TARO_APP_API=https://mooc.yaojiankang.top |
||||||
|
TARO_APP_API=https://xingui.yaojiankang.top |
||||||
#TARO_APP_API=https://shopfix.yaojiankang.top |
#TARO_APP_API=https://shopfix.yaojiankang.top |
||||||
#TARO_APP_API=https://playedu.yaojiankang.top |
#TARO_APP_API=https://playedu.yaojiankang.top |
||||||
TARO_APP_LGOIN=true |
TARO_APP_LGOIN=true |
||||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,88 @@ |
|||||||
|
import { CSSProperties, FC } from "react" |
||||||
|
import { Text, ITouchEvent } from "@tarojs/components" |
||||||
|
import './icon.css' |
||||||
|
|
||||||
|
export type IconName = |
||||||
|
| 'arrow-clockwise' |
||||||
|
| 'arrow-counterclockwise' |
||||||
|
| 'backspace' |
||||||
|
| 'bar-chart' |
||||||
|
| 'bar-chart-line' |
||||||
|
| 'caret-down' |
||||||
|
| 'caret-up' |
||||||
|
| 'caret-left' |
||||||
|
| 'caret-right' |
||||||
|
| 'check' |
||||||
|
| 'check-all' |
||||||
|
| 'chevron-double-down' |
||||||
|
| 'chevron-double-up' |
||||||
|
| 'chevron-double-left' |
||||||
|
| 'chevron-double-right' |
||||||
|
| 'chevron-down' |
||||||
|
| 'chevron-up' |
||||||
|
| 'chevron-left' |
||||||
|
| 'chevron-right' |
||||||
|
| 'circle' |
||||||
|
| 'exclamation' |
||||||
|
| 'gear' |
||||||
|
| 'gender-female' |
||||||
|
| 'gender-male' |
||||||
|
| 'heart' |
||||||
|
| 'info' |
||||||
|
| 'list' |
||||||
|
| 'lock' |
||||||
|
| 'unlock' |
||||||
|
| 'play-btn' |
||||||
|
| 'play' |
||||||
|
| 'question' |
||||||
|
| 'shield' |
||||||
|
| 'square' |
||||||
|
| 'star' |
||||||
|
| 'three-dots-vertical' |
||||||
|
| 'three-dots' |
||||||
|
| 'trash' |
||||||
|
| 'x' |
||||||
|
|
||||||
|
export type IconShape = |
||||||
|
| 'circle' |
||||||
|
| 'square' |
||||||
|
| 'diamond' |
||||||
|
| 'octagon' |
||||||
|
| 'triangle' |
||||||
|
|
||||||
|
export interface IconProps { |
||||||
|
name: IconName |
||||||
|
shape?: IconShape |
||||||
|
color?: string |
||||||
|
size?: string | number |
||||||
|
fill?: boolean |
||||||
|
className?: string |
||||||
|
style?: CSSProperties |
||||||
|
onClick?: (event: ITouchEvent) => void |
||||||
|
} |
||||||
|
|
||||||
|
const XgIcon: FC<IconProps> = (props) => { |
||||||
|
const name = [ |
||||||
|
props.name, |
||||||
|
props.shape, |
||||||
|
props.fill ? 'fill' : undefined |
||||||
|
].filter(Boolean).join('-') |
||||||
|
|
||||||
|
const color = props.color ?? 'currentColor' |
||||||
|
const size = typeof props.size === 'string' ? props.size : `${props.size ?? 16}px` |
||||||
|
const fontStyle: CSSProperties = { |
||||||
|
...(props.style ?? {}), |
||||||
|
fontSize: size, |
||||||
|
color, |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<Text |
||||||
|
className={`xg-icon xg-icon-${name} ${props.className}`} |
||||||
|
style={fontStyle} |
||||||
|
onClick={props.onClick} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default XgIcon |
@ -0,0 +1,38 @@ |
|||||||
|
.fixedBox { |
||||||
|
position: relative; |
||||||
|
width: 100vw; |
||||||
|
height: 100vw; |
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1), rgba(255, 255, 255, 1)); |
||||||
|
//border: 1px solid #f40; |
||||||
|
|
||||||
|
&-inner { |
||||||
|
position: absolute; |
||||||
|
width: 100vw; |
||||||
|
bottom: 24rpx; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
|
||||||
|
&-icon { |
||||||
|
image { |
||||||
|
width: 32rpx; |
||||||
|
height: 32rpx; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&-box { |
||||||
|
margin-top: 24rpx; |
||||||
|
width: 680rpx; |
||||||
|
left: 35rpx; |
||||||
|
height: 76rpx; |
||||||
|
background-color: #45D4A8; |
||||||
|
color: #fff; |
||||||
|
line-height: 76rpx; |
||||||
|
text-align: center; |
||||||
|
font-size: 32rpx; |
||||||
|
font-weight: 500; |
||||||
|
border-radius: 8rpx 8rpx 8rpx 8rpx; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
import styles from "./index.module.scss"; |
||||||
|
import { Text, View} from "@tarojs/components"; |
||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
import {CSSProperties, FC} from "react"; |
||||||
|
import IconFont from "@/components/IconFont"; |
||||||
|
|
||||||
|
type Props = { |
||||||
|
className?: string |
||||||
|
style?: CSSProperties |
||||||
|
} |
||||||
|
|
||||||
|
const LoginView3: FC<Props> = ({className,style}) => { |
||||||
|
return ( |
||||||
|
|
||||||
|
<View className={`${styles.fixedBox} ${className}`} style={style}> |
||||||
|
<View className={styles['fixedBox-inner']}> |
||||||
|
<View className={styles['fixedBox-inner-icon']}> |
||||||
|
<IconFont color='#909795' name='chevron-double-down'/> |
||||||
|
</View> |
||||||
|
<View className={styles['fixedBox-inner-box']} |
||||||
|
onClick={() => Taro.navigateTo({url: '/pages/login/login'})}> |
||||||
|
<Text>登录查看更多内容1</Text> |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
export default LoginView3 |
@ -0,0 +1,39 @@ |
|||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
|
||||||
|
type Topic = string | symbol |
||||||
|
type Subscriber = (...args: any[]) => void |
||||||
|
|
||||||
|
export default function usePubsub() { |
||||||
|
const events = new Map<Topic, Subscriber[]>() |
||||||
|
const registers = new Map<Topic, Subscriber>() |
||||||
|
|
||||||
|
const pub = (topic: Topic, data: any) => { |
||||||
|
Taro.eventCenter.trigger(topic, data) |
||||||
|
}; |
||||||
|
|
||||||
|
const sub = (topic: Topic, callback: Subscriber) => { |
||||||
|
const cbs = events.get(topic) ?? [] |
||||||
|
events.set(topic, cbs.concat(callback)) |
||||||
|
if (!registers.has(topic)) { |
||||||
|
console.log({topic}) |
||||||
|
const listener = (...args: any[]): void => { |
||||||
|
events.get(topic)?.forEach((cb) => cb(...args)) |
||||||
|
} |
||||||
|
registers.set(topic, listener) |
||||||
|
Taro.eventCenter.on(topic, listener) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Taro.useUnload(() => { |
||||||
|
registers.forEach((listener, topic) => { |
||||||
|
Taro.eventCenter.off(topic, listener) |
||||||
|
}) |
||||||
|
registers.clear() |
||||||
|
events.clear() |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
pub, |
||||||
|
sub |
||||||
|
} |
||||||
|
} |
@ -1,3 +1,3 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '绑定手机号' |
navigationBarTitleText: '员工验证', |
||||||
}) |
}) |
||||||
|
@ -0,0 +1,4 @@ |
|||||||
|
export default definePageConfig({ |
||||||
|
navigationBarTitleText: '现场会', |
||||||
|
onReachBottomDistance: 30 |
||||||
|
}) |
@ -0,0 +1,39 @@ |
|||||||
|
.page { |
||||||
|
box-sizing: border-box; |
||||||
|
min-height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
.box { |
||||||
|
width: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
.empty{ |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
top: 0; |
||||||
|
bottom: 0; |
||||||
|
margin: auto; |
||||||
|
width: 400rpx; |
||||||
|
height: 40rpx; |
||||||
|
text-align: center; |
||||||
|
font-weight: bold; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.choice { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
margin: 30rpx auto; |
||||||
|
box-shadow: 0 0 10rpx rgba(#00d6ac, .5); |
||||||
|
} |
||||||
|
|
||||||
|
.buttonDel { |
||||||
|
color: #fff; |
||||||
|
margin: 30rpx auto; |
||||||
|
box-shadow: 0 0 10rpx rgba(#c94f4f, .5); |
||||||
|
} |
@ -0,0 +1,198 @@ |
|||||||
|
import {FC, useCallback, useEffect, useState} from "react"; |
||||||
|
import {Form, Input, Picker, View} from "@tarojs/components"; |
||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
import {curriculum, Meeting, meetingAPi} from "@/api"; |
||||||
|
import {formatDate} from "@/utils/time"; |
||||||
|
import PopPut from "@/components/popPut/popPut"; |
||||||
|
import MyButton from "@/components/button/MyButton"; |
||||||
|
import styles from './form.module.scss' |
||||||
|
|
||||||
|
import {Profile} from "@/store"; |
||||||
|
import DateTimePicker from "@/components/dateTimePicker/dateTimePicker"; |
||||||
|
|
||||||
|
const MeetingForm: FC = () => { |
||||||
|
const params = Taro.getCurrentInstance().router?.params as { id: string | undefined } |
||||||
|
const [manages, setManages] = useState<Manage[]>([]) |
||||||
|
const [start, setStart] = useState<string>(formatDate(new Date(new Date().getTime() + 24 * 60 * 60 * 1000), "YY-MM-dd 08:00")) |
||||||
|
const [end, setEnd] = useState<string>(formatDate(new Date(new Date().getTime() + 24 * 60 * 60 * 1000), "YY-MM-dd 18:00")) |
||||||
|
const [depid, setDepid] = useState<number | null>(null) |
||||||
|
const [imgUrl, setImgUrl] = useState('') |
||||||
|
const [name, setName] = useState('') |
||||||
|
const [loading, setLoading] = useState(false) |
||||||
|
const [status, setStatus] = useState(0) // 状态
|
||||||
|
const [id, setId] = useState<number | undefined>(params.id ? Number(params.id) : undefined) |
||||||
|
const {company} = Profile.useContainer() |
||||||
|
|
||||||
|
function setData(res: Meeting | null) { |
||||||
|
/** |
||||||
|
* params.id 查看详情请求 |
||||||
|
* 当onShow的时候 id === res.id 数据相同不进行以下流程 |
||||||
|
*/ |
||||||
|
if (params.id || id !== res?.id) { |
||||||
|
setImgUrl('') |
||||||
|
setDepid(res?.dep_id || null) |
||||||
|
setName(res?.name || '') |
||||||
|
setStatus(res?.status || 0) |
||||||
|
setId(res?.id || 0) |
||||||
|
|
||||||
|
if (res) { |
||||||
|
setEnd(formatDate(new Date(res.estimate_end_time), "YY-MM-dd hh:mm")) |
||||||
|
setStart(formatDate(new Date(res.estimate_start_time), "YY-MM-dd hh:mm")) |
||||||
|
} |
||||||
|
} |
||||||
|
setLoading(false) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
curriculum.department().then(res => { |
||||||
|
setManages(res.data) |
||||||
|
}) |
||||||
|
}, []) |
||||||
|
|
||||||
|
Taro.useDidShow(() => { |
||||||
|
if (params.id != undefined) { |
||||||
|
meetingAPi.setMeeting(params.id).then(setData) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
const depChange = useCallback((e) => { |
||||||
|
setDepid(manages[Number(e.detail.value)]?.id) |
||||||
|
}, [manages]) |
||||||
|
|
||||||
|
const change_start = useCallback((time: string) => { |
||||||
|
const mil = new Date(time).getTime() |
||||||
|
const endMil = new Date(end).getTime() |
||||||
|
if (mil > endMil) { |
||||||
|
Taro.showToast({title: '大于结束时间', icon: 'error'}) |
||||||
|
} else { |
||||||
|
setStart(time) |
||||||
|
} |
||||||
|
}, [end]) |
||||||
|
|
||||||
|
const change_end = useCallback((time: string) => { |
||||||
|
const mil = new Date(time).getTime() |
||||||
|
const startMil = new Date(start).getTime() |
||||||
|
if (mil < startMil) { |
||||||
|
Taro.showToast({title: '小于开始时间', icon: 'error'}) |
||||||
|
} else { |
||||||
|
setEnd(time) |
||||||
|
} |
||||||
|
}, [start]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const submit = useCallback(async (e) => { |
||||||
|
if (loading && status === 2) return; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const values: Meeting = e.detail.value |
||||||
|
if (!values.name) { |
||||||
|
Taro.showToast({title: '请填写会议名称', icon: 'error'}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
setLoading(true) |
||||||
|
try { |
||||||
|
let req = meetingAPi.setMeetings |
||||||
|
if(!params.id){ |
||||||
|
req = meetingAPi.addMeetings |
||||||
|
} |
||||||
|
await req({ |
||||||
|
...values, |
||||||
|
estimate_end_time: new Date(end).getTime(), |
||||||
|
estimate_start_time: new Date(start).getTime(), |
||||||
|
dep_id: depid, |
||||||
|
company_id: company?.id!, |
||||||
|
id: parseInt(params.id!), |
||||||
|
}) |
||||||
|
setLoading(false) |
||||||
|
Taro.eventCenter.trigger('updateMeetingList') |
||||||
|
Taro.showToast({title: '操作成功', icon: 'success',mask:true}) |
||||||
|
Taro.navigateBack() |
||||||
|
} catch (e) { |
||||||
|
setLoading(false) |
||||||
|
} |
||||||
|
}, [imgUrl, depid, end, start]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<View className={styles.page}> |
||||||
|
|
||||||
|
<View className={styles.box}> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<View className={styles.choice}> |
||||||
|
<Form onSubmit={submit}> |
||||||
|
<PopPut |
||||||
|
title='会议名称' |
||||||
|
chevron |
||||||
|
content={<Input |
||||||
|
className='input' |
||||||
|
name='name' |
||||||
|
value={name} |
||||||
|
disabled={status > 0} |
||||||
|
maxlength={11} |
||||||
|
placeholder='请输入会议标题' |
||||||
|
onInput={(e) => setName(e.detail.value)} |
||||||
|
/>} |
||||||
|
/> |
||||||
|
|
||||||
|
<DateTimePicker |
||||||
|
name='estimate_start_time' |
||||||
|
defaultValue={start} |
||||||
|
disabled={status === 1} |
||||||
|
onChange={(time) => change_start(time)} |
||||||
|
> |
||||||
|
<PopPut title='开始时间' content={start}/> |
||||||
|
</DateTimePicker> |
||||||
|
|
||||||
|
|
||||||
|
<DateTimePicker |
||||||
|
name='estimate_end_time' |
||||||
|
defaultValue={end} |
||||||
|
disabled={status === 1} |
||||||
|
onChange={(time) => change_end(time)} |
||||||
|
> |
||||||
|
<PopPut title='结束时间' content={end}/> |
||||||
|
</DateTimePicker> |
||||||
|
|
||||||
|
<Picker |
||||||
|
mode='selector' |
||||||
|
range={manages} |
||||||
|
onChange={depChange} |
||||||
|
disabled={status === 1} |
||||||
|
rangeKey='name' |
||||||
|
name='dep_id'> |
||||||
|
<PopPut title='选择部门' content={manages?.find(x => x.id == depid)?.name}/> |
||||||
|
</Picker> |
||||||
|
|
||||||
|
<MyButton |
||||||
|
formType='submit' |
||||||
|
fillet |
||||||
|
width={200} |
||||||
|
className={styles.button} |
||||||
|
loading={loading}> |
||||||
|
{params.id ? '保存':'新建'} |
||||||
|
</MyButton> |
||||||
|
</Form> |
||||||
|
</View> |
||||||
|
|
||||||
|
|
||||||
|
</View> |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default MeetingForm |
@ -0,0 +1,4 @@ |
|||||||
|
export default definePageConfig({ |
||||||
|
navigationBarTitleText: '现场会', |
||||||
|
onReachBottomDistance: 30 |
||||||
|
}) |
@ -0,0 +1,11 @@ |
|||||||
|
.meeting { |
||||||
|
margin-bottom: 20rpx; |
||||||
|
border-radius: 16rpx; |
||||||
|
padding: 30rpx 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
background-color: #fff; |
||||||
|
position: relative; |
||||||
|
height: 138rpx; |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
export default definePageConfig({ |
export default definePageConfig({ |
||||||
navigationBarTitleText: '链接', |
navigationBarTitleText: '', |
||||||
onReachBottomDistance: 30 |
onReachBottomDistance: 30 |
||||||
}) |
}) |
||||||
|
@ -1,9 +1,54 @@ |
|||||||
import {FC} from "react"; |
import {FC, useState} from "react"; |
||||||
import {WebView} from "@tarojs/components"; |
import {Button, Text, View, WebView} from "@tarojs/components"; |
||||||
import {useRouter} from "@tarojs/taro"; |
import Taro, {useRouter} from "@tarojs/taro"; |
||||||
|
import IconFont from "@/components/IconFont"; |
||||||
|
|
||||||
const Web:FC = () => { |
const Web: FC = () => { |
||||||
|
const [hasError, showError] = useState(false) |
||||||
const {url} = useRouter().params as unknown as { url: string } |
const {url} = useRouter().params as unknown as { url: string } |
||||||
return <WebView src={url}></WebView> |
|
||||||
|
const onError = () => { |
||||||
|
showError(true) |
||||||
|
Taro.hideNavigationBarLoading() |
||||||
|
} |
||||||
|
|
||||||
|
const onRefresh = () => { |
||||||
|
showError(false) |
||||||
|
Taro.showNavigationBarLoading() |
||||||
|
} |
||||||
|
|
||||||
|
Taro.useDidShow(() => { |
||||||
|
Taro.showNavigationBarLoading() |
||||||
|
}) |
||||||
|
|
||||||
|
if (hasError) { |
||||||
|
return ( |
||||||
|
<View className='flex flex-column align-center fixed left right top bottom'> |
||||||
|
<View className='flex flex-column justify-end align-center' style={{height:'40%'}}> |
||||||
|
<View className='mb-7'>加载失败</View> |
||||||
|
</View> |
||||||
|
<View className='flex justify-center align-center gap20rpx'> |
||||||
|
<Button className='inlineblock' size='mini' onClick={() => Taro.navigateBack()}> |
||||||
|
<IconFont name='chevron-left' /> |
||||||
|
<Text>返回</Text> |
||||||
|
</Button> |
||||||
|
<Button className='inlineblock' size='mini' type='primary' onClick={onRefresh}> |
||||||
|
<IconFont name='arrow-clockwise' /> |
||||||
|
<Text>刷新</Text> |
||||||
|
</Button> |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<WebView |
||||||
|
progressbarColor='#45D4A8' |
||||||
|
src={decodeURIComponent(url)} |
||||||
|
onLoad={() => Taro.hideNavigationBarLoading()} |
||||||
|
onError={onError} |
||||||
|
/> |
||||||
|
) |
||||||
} |
} |
||||||
|
|
||||||
export default Web |
export default Web |
||||||
|
@ -0,0 +1,58 @@ |
|||||||
|
.box { |
||||||
|
margin-left:30rpx; |
||||||
|
margin-bottom: 24rpx; |
||||||
|
width:690rpx; |
||||||
|
display: flex; |
||||||
|
margin-bottom: 20rpx; |
||||||
|
background-color: #fff; |
||||||
|
border-radius: 16rpx; |
||||||
|
padding: 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
.image{ |
||||||
|
background-color: #ededed; |
||||||
|
border-radius: 8rpx; |
||||||
|
} |
||||||
|
.play { |
||||||
|
position: absolute; |
||||||
|
z-index: 9999; |
||||||
|
width: 50rpx !important; |
||||||
|
height: 50rpx !important; |
||||||
|
top: 99rpx; |
||||||
|
left: 99rpx; |
||||||
|
background: transparent !important; |
||||||
|
} |
||||||
|
.rightBox{ |
||||||
|
padding-left: 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
.articleLeftBox{ |
||||||
|
padding-right: 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
.videoRightBox{ |
||||||
|
padding-left: 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
.courseRightBox{ |
||||||
|
padding-left: 24rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
.desc{ |
||||||
|
font-size: 24rpx; |
||||||
|
font-weight: 500; |
||||||
|
color: #909795; |
||||||
|
line-height: 34rpx; |
||||||
|
display: -webkit-box; |
||||||
|
word-break: break-all; |
||||||
|
text-overflow: ellipsis; |
||||||
|
overflow: hidden; |
||||||
|
-webkit-box-orient:vertical; |
||||||
|
-webkit-line-clamp:2; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,167 @@ |
|||||||
|
import {FC, useCallback, useEffect, useState} from "react"; |
||||||
|
import {Image, ScrollView, View} from "@tarojs/components"; |
||||||
|
import styles from './list.module.scss' |
||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
import Empty from "@/components/empty/empty"; |
||||||
|
import Img from "@/components/image/image"; |
||||||
|
import {SearchApi} from "@/api/search"; |
||||||
|
import {rfc33392time} from "@/utils/day"; |
||||||
|
import play from "@/static/img/play-back.png"; |
||||||
|
import Spin from "@/components/spinner"; |
||||||
|
|
||||||
|
type Props = { |
||||||
|
name: string |
||||||
|
clear: boolean |
||||||
|
} |
||||||
|
const SearchList: FC<Props> = ({name, clear}) => { |
||||||
|
console.log(name, 'name') |
||||||
|
const globalData = Taro.getApp().globalData |
||||||
|
const [page, setPage] = useState(1) |
||||||
|
const [brands, setBrands] = useState<any[]>([]) |
||||||
|
const [total, setTotal] = useState(0) |
||||||
|
const [text, setText] = useState('') |
||||||
|
const [loading, setLoading] = useState(true) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (!clear) { |
||||||
|
setBrands([]) |
||||||
|
setLoading(true) |
||||||
|
} |
||||||
|
}, [clear]) |
||||||
|
useEffect(() => { |
||||||
|
if (name && clear) { |
||||||
|
getData() |
||||||
|
} |
||||||
|
}, [page, name, clear]) |
||||||
|
|
||||||
|
const getData = useCallback(async () => { |
||||||
|
try { |
||||||
|
const data = await SearchApi.list(page, 10, name) |
||||||
|
if (page === 1) { |
||||||
|
if (data.data.length < 10) { |
||||||
|
setText('没有更多了~') |
||||||
|
} else { |
||||||
|
setText('上拉加载更多~') |
||||||
|
} |
||||||
|
setBrands([ |
||||||
|
...data.data |
||||||
|
]) |
||||||
|
} else { |
||||||
|
setBrands([ |
||||||
|
...brands, |
||||||
|
...data.data |
||||||
|
]) |
||||||
|
} |
||||||
|
setTotal(data.total) |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
} |
||||||
|
setLoading(false) |
||||||
|
}, [page, name]) |
||||||
|
|
||||||
|
|
||||||
|
function jumpInfo(id: number, type: string) { |
||||||
|
console.log(type, 'type') |
||||||
|
let url = '' |
||||||
|
switch (type) { |
||||||
|
case 'brand': |
||||||
|
url = '/pages/preview/brand/info/info'; |
||||||
|
break; |
||||||
|
case 'article': |
||||||
|
url = '/pages/preview/brand/article/article' |
||||||
|
break |
||||||
|
case 'video_records': |
||||||
|
url = '/pages/preview/videoFull/videoFull' |
||||||
|
break |
||||||
|
case 'courses': |
||||||
|
url = '/pages/business/videoInfo/videoInfo' |
||||||
|
break |
||||||
|
} |
||||||
|
Taro.navigateTo({url: `${url}?id=${id}`}) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function onScrollToLower() { |
||||||
|
if (brands?.length < total) { |
||||||
|
setPage(page + 1) |
||||||
|
getData().then() |
||||||
|
} else { |
||||||
|
setText('没有更多了~') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<ScrollView |
||||||
|
className='scrollview' |
||||||
|
scrollY |
||||||
|
scrollWithAnimation |
||||||
|
style={{height: `${globalData.windowHeight - 140}px`, backgroundColor: `#f5f5f5`, position: 'relative'}} |
||||||
|
onScrollToLower={onScrollToLower}> |
||||||
|
<Spin enable={loading} block overlay/> |
||||||
|
{ |
||||||
|
brands.length >= 1 && !loading && |
||||||
|
<> |
||||||
|
{brands.map((d) => |
||||||
|
<View onClick={() => jumpInfo(d.data.id, d.data.table)} className={styles.box} key={d.data.id}> |
||||||
|
{ |
||||||
|
d.data.table === 'brand' && |
||||||
|
<> |
||||||
|
<Img width={128} height={128} src={d.data['@data'].logo} mode='aspectFill' className={styles.image} |
||||||
|
errorType='brand'/> |
||||||
|
<View className={styles.rightBox}> |
||||||
|
<View className='font-weight mb-2 font-28'>{d.data.title}</View> |
||||||
|
<View className={styles.desc}>{d.data.content || '暂无品牌简介'}</View> |
||||||
|
</View> |
||||||
|
</> |
||||||
|
} |
||||||
|
{ |
||||||
|
d.data.table === 'article' && |
||||||
|
<> |
||||||
|
<View className={styles.articleLeftBox}> |
||||||
|
<View className='font-weight mb-2 font-28 lh-40'>{d.data['@data'].title}</View> |
||||||
|
<View |
||||||
|
className={styles.desc}>{rfc33392time(d.data['@data'].created_at)} {d.data['@data'].page_view}阅读</View> |
||||||
|
</View> |
||||||
|
<Img width={172} height={128} src={d.data['@data'].logo} mode='aspectFill' className={styles.image} |
||||||
|
errorType='course'/> |
||||||
|
</> |
||||||
|
} |
||||||
|
{ |
||||||
|
d.data.table === 'video_records' && |
||||||
|
<> |
||||||
|
<Img width={192} height={192} src={d.data['@data'].url_path} mode='aspectFill' errorType='health' |
||||||
|
className={styles.image}/> |
||||||
|
<Image src={play} className={styles.play} mode='aspectFit'/> |
||||||
|
<View className={styles.videoRightBox}> |
||||||
|
<View className='font-weight mb-2 font-28 lh-40'>{d.data['@data'].title}</View> |
||||||
|
<View className={styles.desc}>{d.data['@data'].introduction}</View> |
||||||
|
<View className={`${styles.desc} mt-2`}>播放量: {d.data['@data'].video_view}</View> |
||||||
|
</View> |
||||||
|
</> |
||||||
|
} |
||||||
|
{ |
||||||
|
d.data.table === 'courses' && |
||||||
|
<> |
||||||
|
<Img width={192} height={138} src={d.data['@data'].thumb} mode='aspectFill' className={styles.image} |
||||||
|
errorType='course'/> |
||||||
|
<View className={styles.courseRightBox}> |
||||||
|
<View className='font-weight mb-2 font-28 lh-40'>{d.data['@data'].title}</View> |
||||||
|
<View className={styles.desc}>课时:{d.data['@data'].class_hour}节 |
||||||
|
销量:{d.data['@data'].charge}</View> |
||||||
|
</View> |
||||||
|
</> |
||||||
|
} |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
<View style={{width: '750rpx', textAlign: 'center', color: '#999'}} |
||||||
|
className="font-28 mt-3 mb-3">{text}</View> |
||||||
|
</> |
||||||
|
} |
||||||
|
{!loading && brands.length === 0 && <Empty name='空空如也'/>} |
||||||
|
</ScrollView> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default SearchList |
||||||
|
|
@ -0,0 +1,3 @@ |
|||||||
|
export default definePageConfig({ |
||||||
|
navigationStyle: 'custom' |
||||||
|
}) |
@ -0,0 +1,66 @@ |
|||||||
|
page { |
||||||
|
background-color: #F2F8F6; |
||||||
|
|
||||||
|
.searchHeader { |
||||||
|
margin-top: 10px; |
||||||
|
padding: 0 20rpx; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: space-between; |
||||||
|
transition: all 200ms; |
||||||
|
position: sticky; |
||||||
|
z-index: 200 ; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
.searchBox { |
||||||
|
flex: 1; |
||||||
|
height: 68rpx; |
||||||
|
background: #FFFFFF; |
||||||
|
border-radius: 32rpx; |
||||||
|
box-sizing: border-box; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
padding: 0 24rpx; |
||||||
|
|
||||||
|
.input { |
||||||
|
font-size: 28rpx; |
||||||
|
margin-left: 20rpx; |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.titleBox { |
||||||
|
margin-top: 40rpx; |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
box-sizing: border-box; |
||||||
|
padding-right: 30rpx; |
||||||
|
justify-content: space-between; |
||||||
|
|
||||||
|
.title { |
||||||
|
font-size: 30rpx; |
||||||
|
font-weight: bold; |
||||||
|
color: #323635; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.contentBox { |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
|
||||||
|
.items { |
||||||
|
display: inline-block; |
||||||
|
background: #FFFFFF; |
||||||
|
border-radius: 32rpx; |
||||||
|
padding: 10rpx 24rpx; |
||||||
|
font-size: 24rpx; |
||||||
|
font-weight: 500; |
||||||
|
color: #323635; |
||||||
|
line-height: 24rpx; |
||||||
|
margin-top: 24rpx; |
||||||
|
margin-right: 24rpx; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,154 @@ |
|||||||
|
import {Input, View, Text, PageContainer, Image} from "@tarojs/components"; |
||||||
|
import {FC, useEffect, useMemo, useState} from "react"; |
||||||
|
import styles from './index.module.scss' |
||||||
|
import Taro from "@tarojs/taro"; |
||||||
|
import {useDidShow} from '@tarojs/taro' |
||||||
|
import SearchList from './components/list' |
||||||
|
import NavigationBar from "@/components/navigationBar/navigationBar"; |
||||||
|
import search from '@/static/img/search.png' |
||||||
|
import del from '@/static/img/del.png' |
||||||
|
|
||||||
|
|
||||||
|
const Search: FC = () => { |
||||||
|
const [value, setValue] = useState('') |
||||||
|
const [recentSearch, setRecentSearch] = useState<string[]>([]) |
||||||
|
const [hotSearch] = useState<any[]>([]) |
||||||
|
const [show, setShow] = useState(false) |
||||||
|
const [focus, setFocus] = useState(false) |
||||||
|
|
||||||
|
|
||||||
|
useDidShow(getRecentSearch) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
!show && getRecentSearch() |
||||||
|
}, [show]) |
||||||
|
|
||||||
|
function inpFn(e) { |
||||||
|
setValue(e.detail.value) |
||||||
|
} |
||||||
|
|
||||||
|
function clearSearch() { |
||||||
|
Taro.removeStorageSync('recentSearch') |
||||||
|
getRecentSearch() |
||||||
|
Taro.showToast({title: '删除成功'}) |
||||||
|
} |
||||||
|
|
||||||
|
function getRecentSearch() { |
||||||
|
setRecentSearch(Taro.getStorageSync('recentSearch')) |
||||||
|
} |
||||||
|
|
||||||
|
function getSearchItem(value) { |
||||||
|
setValue(value) |
||||||
|
setShow(true) |
||||||
|
} |
||||||
|
|
||||||
|
function searchInput() { |
||||||
|
if (value === "") return; |
||||||
|
getSearchItem(value) |
||||||
|
//记录最近搜索
|
||||||
|
let recentSearch = Taro.getStorageSync('recentSearch') || []; |
||||||
|
recentSearch.unshift(value); |
||||||
|
Taro.setStorageSync('recentSearch', [...new Set(recentSearch)]) |
||||||
|
} |
||||||
|
|
||||||
|
function cancelSearch(){ |
||||||
|
setValue('') |
||||||
|
setShow(false) |
||||||
|
setFocus(false) |
||||||
|
// Taro.navigateBack()
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const searchStyles = useMemo((): React.CSSProperties | undefined => { |
||||||
|
if (focus || show) { |
||||||
|
return { |
||||||
|
// transform: "translateY(-43px)",
|
||||||
|
// width: "100%",
|
||||||
|
} |
||||||
|
} |
||||||
|
}, [focus, show]) |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<View className="flex flex-column"> |
||||||
|
<NavigationBar cancelBack={true} backgroundColor={'#F2F8F6'} leftNode={ |
||||||
|
<Text className="bold font-36 ml-1" style={{color:'#323635'}} >搜索</Text> |
||||||
|
} /> |
||||||
|
<View className={styles.searchHeader} style={searchStyles}> |
||||||
|
<View className={styles.searchBox}> |
||||||
|
<Image src={search} style={{width: '32rpx', height: '32rpx'}} mode='widthFix'/> |
||||||
|
<Input |
||||||
|
onFocus={() => setFocus(true)} |
||||||
|
onBlur={() => setFocus(false)} |
||||||
|
className={styles.input} |
||||||
|
placeholder={(focus || show) ? '' : "输入关键字搜索"} |
||||||
|
type='text' |
||||||
|
value={value} |
||||||
|
confirmType='search' |
||||||
|
onInput={inpFn} |
||||||
|
onConfirm={searchInput}/> |
||||||
|
|
||||||
|
</View> |
||||||
|
<View className='px-2 text-dark' onClick={cancelSearch}>清空</View> |
||||||
|
{/*{focus || show ? <View className='px-2 text-dark' onClick={cancelSearch}>取消</View> : null}*/} |
||||||
|
</View> |
||||||
|
|
||||||
|
|
||||||
|
{ |
||||||
|
recentSearch.length >= 1 && !show && |
||||||
|
<View className='px-2'> |
||||||
|
<View className={styles.titleBox}> |
||||||
|
<Text className={styles.title}>最近搜索</Text> |
||||||
|
<Image |
||||||
|
src={del} |
||||||
|
mode='widthFix' |
||||||
|
style={{width: '16px', height: '16px', padding: '0 20rpx'}} |
||||||
|
onClick={clearSearch}/> |
||||||
|
</View> |
||||||
|
<View className={styles.contentBox}> |
||||||
|
{ |
||||||
|
recentSearch.length > 0 && |
||||||
|
recentSearch?.map(d => |
||||||
|
<View className={styles.items}> |
||||||
|
<View onClick={() => getSearchItem(d)} className="font-28">{d}</View> |
||||||
|
</View>) |
||||||
|
} |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
} |
||||||
|
{ |
||||||
|
hotSearch.length >= 1 && !show && |
||||||
|
<> |
||||||
|
<View className={`flex justify-between ${styles.titleBox}`}> |
||||||
|
|
||||||
|
<Text className="font-32 fwb">热门搜索</Text> |
||||||
|
</View> |
||||||
|
<View className="wid100 pt-3 pl-3 box-b"> |
||||||
|
<View className="wid100 flex flex-wrap"> |
||||||
|
{ |
||||||
|
hotSearch.length && |
||||||
|
hotSearch.map(d => |
||||||
|
<View className="py-1 px-2 rounded-40 mr-2 mb-2 bg-warning text-white"> |
||||||
|
<View onClick={() => { |
||||||
|
getSearchItem(d) |
||||||
|
}} className="font-28">{d}</View> |
||||||
|
</View>) |
||||||
|
} |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
</> |
||||||
|
} |
||||||
|
|
||||||
|
<PageContainer |
||||||
|
onBeforeLeave={cancelSearch} |
||||||
|
onClickOverlay={cancelSearch} |
||||||
|
show={show} |
||||||
|
round |
||||||
|
overlay |
||||||
|
overlayStyle={'background:rgba(0,0,0,0)'}> |
||||||
|
{show && <SearchList name={value} clear={show}/>} |
||||||
|
</PageContainer> |
||||||
|
</View> |
||||||
|
) |
||||||
|
} |
||||||
|
export default Search |
Before Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 828 B |
After Width: | Height: | Size: 803 B |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.7 KiB |
Loading…
Reference in new issue