修复已知问题
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '一键报修'
|
||||
})
|
||||
@@ -1,116 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {getHjmCar, pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import {Image,Cell} from '@nutui/nutui-react-taro'
|
||||
import './location.scss'
|
||||
|
||||
/**
|
||||
* 电子围栏
|
||||
* @constructor
|
||||
*/
|
||||
const Fence = () => {
|
||||
const {params} = useRouter();
|
||||
const [keywords, setKeywords] = useState<string>()
|
||||
const [item, setItem] = useState<HjmCar>()
|
||||
|
||||
// 打开地图选择位置
|
||||
// const chooseLocation = async () => {
|
||||
// try {
|
||||
// const res = await Taro.chooseLocation({
|
||||
// latitude, // 默认纬度
|
||||
// longitude // 默认经度
|
||||
// })
|
||||
// console.log('选择的位置:', res);
|
||||
// } catch (err) {
|
||||
// console.error('选择位置失败:', err);
|
||||
// }
|
||||
// }
|
||||
const reload = () => {
|
||||
const id = Number(params.id);
|
||||
// 执行搜索
|
||||
if (keywords) {
|
||||
pageHjmCar({keywords}).then(res => {
|
||||
if (res?.list && res?.list?.length > 0) {
|
||||
const data = res?.list[0];
|
||||
setItem(data)
|
||||
setKeywords(data.code)
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
// 获取车辆信息
|
||||
if (id) {
|
||||
getHjmCar(id).then(data => {
|
||||
setItem(data)
|
||||
setKeywords(data.code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<div className={'fixed z-20 top-5 left-0 w-full'}>*/}
|
||||
{/* <div className={'px-4'}>*/}
|
||||
{/* <div*/}
|
||||
{/* style={{*/}
|
||||
{/* display: 'flex',*/}
|
||||
{/* alignItems: 'center',*/}
|
||||
{/* background: '#fff',*/}
|
||||
{/* padding: '0 10px',*/}
|
||||
{/* borderRadius: '20px'*/}
|
||||
{/* }}*/}
|
||||
{/* >*/}
|
||||
{/* <Search/>*/}
|
||||
{/* <Input*/}
|
||||
{/* placeholder="车辆编号"*/}
|
||||
{/* value={keywords}*/}
|
||||
{/* onChange={onKeywords}*/}
|
||||
{/* />*/}
|
||||
{/* <div*/}
|
||||
{/* className={'flex items-center'}*/}
|
||||
{/* >*/}
|
||||
{/* <Button type="warning" onClick={reload}>*/}
|
||||
{/* 查询*/}
|
||||
{/* </Button>*/}
|
||||
{/* </div>*/}
|
||||
{/* </div>*/}
|
||||
{/* </div>*/}
|
||||
{/*</div>*/}
|
||||
{item ? (
|
||||
<div className={'car-info w-full bg-white'}>
|
||||
<Image src={item?.image} mode={'widthFix'} width={'100%'} className={'bg-gray-50'}/>
|
||||
<div className={'px-2'}>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
车辆编号:{item?.code}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
快递公司:{item?.kuaidi}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
管理负责人:{item?.kuaidiAdmin}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
操作员:{item?.driver}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
保险状态:{item?.insuranceStatus}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
GPS编号:{item?.gpsNo}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
电子围栏:{item?.fenceName}
|
||||
</Cell>
|
||||
</div>
|
||||
</div>
|
||||
) : ''}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Fence
|
||||
202
src/hjm/bx/BestSellers.tsx
Normal file
202
src/hjm/bx/BestSellers.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React from "react";
|
||||
import {Image, Space, Tag, Button} from '@nutui/nutui-react-taro'
|
||||
import {Truck, User, Shield, Location} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
|
||||
interface BestSellersProps {
|
||||
data: HjmCar[]
|
||||
onRefresh?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 车辆列表组件
|
||||
*/
|
||||
const BestSellers: React.FC<BestSellersProps> = ({data, onRefresh}) => {
|
||||
|
||||
// 获取保险状态显示
|
||||
const getInsuranceStatusDisplay = (status?: number) => {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return {text: '未投保', color: '#ff4d4f', bgColor: '#fff2f0'}
|
||||
case 1:
|
||||
return {text: '已投保', color: '#52c41a', bgColor: '#f6ffed'}
|
||||
case 2:
|
||||
return {text: '即将到期', color: '#faad14', bgColor: '#fffbe6'}
|
||||
default:
|
||||
return {text: '未知', color: '#8c8c8c', bgColor: '#f5f5f5'}
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到车辆详情
|
||||
const navigateToDetail = (item: HjmCar) => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/query?id=${item.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 快速报险
|
||||
const quickInsurance = (item: HjmCar, event: any) => {
|
||||
event.stopPropagation()
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/bx/bx-add?carId=${item.id}&carCode=${item.code}`
|
||||
})
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{padding: '0 16px', marginBottom: '16px'}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '12px'
|
||||
}}>
|
||||
{data.map((item, index) => {
|
||||
const insuranceStatus = getInsuranceStatusDisplay(item.insuranceStatus)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)',
|
||||
border: '1px solid #f0f0f0'
|
||||
}}
|
||||
onClick={() => navigateToDetail(item)}
|
||||
>
|
||||
<div style={{display: 'flex', gap: '12px'}}>
|
||||
{/* 车辆图片 */}
|
||||
<div style={{flexShrink: 0}}>
|
||||
<Image
|
||||
src={item.image || 'https://via.placeholder.com/80x80?text=车辆'}
|
||||
mode="aspectFill"
|
||||
radius="8px"
|
||||
width="80"
|
||||
height="80"
|
||||
style={{
|
||||
border: '1px solid #f0f0f0'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 车辆信息 */}
|
||||
<div style={{flex: 1, minWidth: 0}}>
|
||||
<Space direction="vertical" size={8}>
|
||||
{/* 车辆编号 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Truck size={16} color="#1890ff"/>
|
||||
<span style={{
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
color: '#262626'
|
||||
}}>
|
||||
{item.code || '未知编号'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 快递公司 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Location size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
快递公司:
|
||||
</span>
|
||||
<span style={{fontSize: '13px', color: '#595959'}}>
|
||||
{item.parentOrganization || '未知'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 保险状态 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Shield size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
保险状态:
|
||||
</span>
|
||||
<Tag
|
||||
color={insuranceStatus.color}
|
||||
style={{
|
||||
backgroundColor: insuranceStatus.bgColor,
|
||||
border: `1px solid ${insuranceStatus.color}`,
|
||||
fontSize: '12px'
|
||||
}}
|
||||
>
|
||||
{insuranceStatus.text}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
{/* 操作员 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<User size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
操作员:
|
||||
</span>
|
||||
<span style={{fontSize: '13px', color: '#595959'}}>
|
||||
{item.driver || '未绑定'}
|
||||
</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div style={{
|
||||
marginTop: '12px',
|
||||
paddingTop: '12px',
|
||||
borderTop: '1px solid #f0f0f0',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={(e) => quickInsurance(item, e)}
|
||||
style={{
|
||||
borderRadius: '16px',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
>
|
||||
一键报险
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
onClick={() => navigateToDetail(item)}
|
||||
style={{
|
||||
borderRadius: '16px',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BestSellers
|
||||
3
src/hjm/bx/bx-add.config.ts
Normal file
3
src/hjm/bx/bx-add.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '一键报险'
|
||||
})
|
||||
366
src/hjm/bx/bx-add.tsx
Normal file
366
src/hjm/bx/bx-add.tsx
Normal file
@@ -0,0 +1,366 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {
|
||||
Image,
|
||||
Button,
|
||||
TextArea,
|
||||
Cell,
|
||||
Loading,
|
||||
Space
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Camera, Truck} from '@nutui/icons-react-taro'
|
||||
import {addHjmBxLog} from "@/api/hjm/hjmBxLog";
|
||||
import {pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {uploadFile} from "@/api/system/file";
|
||||
import {HjmBxLog} from "@/api/hjm/hjmBxLog/model";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
|
||||
/**
|
||||
* 一键报险 - 添加报险记录页面
|
||||
*/
|
||||
function BxAdd() {
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [uploading, setUploading] = useState<boolean>(false)
|
||||
const [carInfo, setCarInfo] = useState<HjmCar | null>(null)
|
||||
const [formData, setFormData] = useState<HjmBxLog>({
|
||||
carId: undefined,
|
||||
image: '',
|
||||
comments: '',
|
||||
status: 0 // 0: 待审核, 1: 已通过, 2: 已驳回
|
||||
})
|
||||
|
||||
// 事故类型选项
|
||||
const accidentTypes = [
|
||||
{text: '轻微刮擦', value: '轻微刮擦'},
|
||||
{text: '碰撞事故', value: '碰撞事故'},
|
||||
{text: '追尾事故', value: '追尾事故'},
|
||||
{text: '侧翻事故', value: '侧翻事故'},
|
||||
{text: '其他事故', value: '其他事故'}
|
||||
]
|
||||
|
||||
const [accidentType, setAccidentType] = useState<string>('')
|
||||
const [accidentDescription, setAccidentDescription] = useState<string>('')
|
||||
|
||||
// 初始化页面数据
|
||||
const initPageData = async () => {
|
||||
try {
|
||||
pageHjmCar({driverId: Taro.getStorageSync('UserId')}).then(res => {
|
||||
const car = res?.list[0];
|
||||
setLoading(true)
|
||||
if (car) {
|
||||
setCarInfo(car)
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
carId: car.id
|
||||
}))
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: '获取车辆信息失败',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取车辆信息失败:', error)
|
||||
Taro.showToast({
|
||||
title: '获取车辆信息失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 拍照上传
|
||||
const takePhoto = () => {
|
||||
Taro.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['camera'],
|
||||
success: async () => {
|
||||
try {
|
||||
setUploading(true)
|
||||
// 这里应该调用实际的上传接口
|
||||
uploadFile().then(data => {
|
||||
setFormData({
|
||||
...formData,
|
||||
image: data.url
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setUploading(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 表单验证
|
||||
if (!formData.carId) {
|
||||
Taro.showToast({
|
||||
title: '请选择车辆',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!accidentType) {
|
||||
Taro.showToast({
|
||||
title: '请选择事故类型',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.image) {
|
||||
Taro.showToast({
|
||||
title: '请上传事故现场照片',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
// 构建提交数据
|
||||
const submitData: HjmBxLog = {
|
||||
...formData,
|
||||
comments: `事故类型:${accidentType}\n事故描述:${accidentDescription || '无'}`
|
||||
}
|
||||
|
||||
await addHjmBxLog(submitData)
|
||||
|
||||
Taro.showToast({
|
||||
title: '报险提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
}, 1500)
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
Taro.showToast({
|
||||
title: '提交失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initPageData()
|
||||
}, [])
|
||||
|
||||
if (loading && !carInfo) {
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100vh'
|
||||
}}>
|
||||
<Loading type="spinner">加载中...</Loading>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
backgroundColor: '#f5f5f5',
|
||||
minHeight: '100vh',
|
||||
paddingBottom: '80px'
|
||||
}}>
|
||||
{/* 车辆信息卡片 */}
|
||||
{carInfo && (
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
margin: '16px',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginBottom: '12px'
|
||||
}}>
|
||||
<Truck size={18} color="#1890ff"/>
|
||||
<span style={{fontSize: '16px', fontWeight: 'bold'}}>车辆信息</span>
|
||||
</div>
|
||||
|
||||
<Space direction="vertical" size={8}>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
<span style={{color: '#8c8c8c'}}>车辆编号:</span>
|
||||
<span style={{fontWeight: 'bold'}}>{carInfo.code}</span>
|
||||
</div>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
<span style={{color: '#8c8c8c'}}>快递公司:</span>
|
||||
<span>{carInfo.parentOrganization}</span>
|
||||
</div>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
<span style={{color: '#8c8c8c'}}>操作员:</span>
|
||||
<span>{carInfo.driver || '未绑定'}</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 事故信息表单 */}
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
margin: '16px 16px 16px',
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
<div style={{
|
||||
padding: '16px',
|
||||
borderBottom: '1px solid #f0f0f0'
|
||||
}}>
|
||||
<span style={{fontSize: '16px', fontWeight: 'bold'}}>事故信息</span>
|
||||
</div>
|
||||
|
||||
<Cell.Group>
|
||||
{/* 事故类型 */}
|
||||
<Cell
|
||||
title="事故类型"
|
||||
description={accidentType || '请选择事故类型'}
|
||||
onClick={() => {
|
||||
Taro.showActionSheet({
|
||||
itemList: accidentTypes.map(item => item.text),
|
||||
success: (res) => {
|
||||
setAccidentType(accidentTypes[res.tapIndex].value)
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 事故时间 */}
|
||||
{/*<Cell*/}
|
||||
{/* title="事故时间"*/}
|
||||
{/* description={accidentTime ? new Date(accidentTime).toLocaleString() : '请选择事故时间'}*/}
|
||||
{/* onClick={() => {*/}
|
||||
{/* const now = new Date()*/}
|
||||
{/* setAccidentTime(now.toISOString().slice(0, 16))*/}
|
||||
{/* }}*/}
|
||||
{/*/>*/}
|
||||
</Cell.Group>
|
||||
</div>
|
||||
|
||||
{/* 事故描述 */}
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
margin: '0 16px 16px',
|
||||
borderRadius: '12px',
|
||||
padding: '16px'
|
||||
}}>
|
||||
<div style={{marginBottom: '12px'}}>
|
||||
<span style={{fontSize: '16px', fontWeight: 'bold'}}>事故描述</span>
|
||||
<span style={{color: '#8c8c8c', fontSize: '12px', marginLeft: '8px'}}>(选填)</span>
|
||||
</div>
|
||||
<TextArea
|
||||
placeholder={'请详细描述事故经过、损失情况等...'}
|
||||
value={accidentDescription}
|
||||
onChange={setAccidentDescription}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 现场照片 */}
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
margin: '0 16px 16px',
|
||||
borderRadius: '12px',
|
||||
padding: '16px'
|
||||
}}>
|
||||
<div style={{marginBottom: '12px'}}>
|
||||
<span style={{fontSize: '16px', fontWeight: 'bold'}}>现场照片</span>
|
||||
<span style={{color: '#ff4d4f', fontSize: '12px', marginLeft: '8px'}}>*必填</span>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '12px'
|
||||
}}>
|
||||
{formData.image && (
|
||||
<div style={{position: 'relative'}}>
|
||||
<Image
|
||||
src={formData.image}
|
||||
width="100"
|
||||
height="100"
|
||||
radius="8px"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="default"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-8px',
|
||||
right: '-8px',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
borderRadius: '12px',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
onClick={() => setFormData(prev => ({...prev, image: ''}))}
|
||||
>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!formData.image && (
|
||||
<Button
|
||||
size="small"
|
||||
loading={uploading}
|
||||
onClick={takePhoto}
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '2px dashed #d9d9d9'
|
||||
}}
|
||||
>
|
||||
<Camera size={24}/>
|
||||
<span style={{fontSize: '12px', marginTop: '4px'}}>拍照</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 提交按钮 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: '#fff',
|
||||
padding: '16px',
|
||||
borderTop: '1px solid #f0f0f0'
|
||||
}}>
|
||||
<Button
|
||||
type="primary"
|
||||
block
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
提交报险申请
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BxAdd
|
||||
291
src/hjm/bx/bx-list.tsx
Normal file
291
src/hjm/bx/bx-list.tsx
Normal file
@@ -0,0 +1,291 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {
|
||||
InfiniteLoading,
|
||||
Loading,
|
||||
Empty,
|
||||
Button,
|
||||
Input,
|
||||
Tag,
|
||||
Image,
|
||||
Space,
|
||||
Cell
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Search, Calendar, Truck, File} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {pageHjmBxLog} from "@/api/hjm/hjmBxLog";
|
||||
import {HjmBxLog} from "@/api/hjm/hjmBxLog/model";
|
||||
|
||||
/**
|
||||
* 报险记录列表页面
|
||||
*/
|
||||
const BxList: React.FC = () => {
|
||||
const [list, setList] = useState<HjmBxLog[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
|
||||
// 获取状态显示
|
||||
const getStatusDisplay = (status?: number) => {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return {text: '待审核', color: '#faad14', bgColor: '#fffbe6'}
|
||||
case 1:
|
||||
return {text: '已通过', color: '#52c41a', bgColor: '#f6ffed'}
|
||||
case 2:
|
||||
return {text: '已驳回', color: '#ff4d4f', bgColor: '#fff2f0'}
|
||||
default:
|
||||
return {text: '未知', color: '#8c8c8c', bgColor: '#f5f5f5'}
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async (showLoading = true) => {
|
||||
try {
|
||||
if (showLoading) setLoading(true)
|
||||
setRefreshing(true)
|
||||
|
||||
const res = await pageHjmBxLog({
|
||||
keywords: keywords.trim() || undefined
|
||||
})
|
||||
|
||||
setList(res?.list || [])
|
||||
} catch (error) {
|
||||
console.error('获取报险记录失败:', error)
|
||||
Taro.showToast({
|
||||
title: '获取报险记录失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setRefreshing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
reload()
|
||||
}
|
||||
|
||||
const onKeywordsChange = (value: string) => {
|
||||
setKeywords(value)
|
||||
}
|
||||
|
||||
const onAddInsurance = () => {
|
||||
Taro.navigateTo({
|
||||
url: '/hjm/bx/bx-add'
|
||||
})
|
||||
}
|
||||
|
||||
const viewDetail = (item: HjmBxLog) => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/bx/bx-detail?id=${item.id}`
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 搜索栏 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '20px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 20,
|
||||
padding: '0 16px',
|
||||
backgroundColor: '#f5f5f5'
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
padding: '8px 12px',
|
||||
borderRadius: '20px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<Search size={16} color="#999"/>
|
||||
<Input
|
||||
placeholder="搜索报险记录"
|
||||
value={keywords}
|
||||
onChange={onKeywordsChange}
|
||||
onConfirm={onSearch}
|
||||
style={{
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
flex: 1,
|
||||
marginLeft: '8px'
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={onSearch}
|
||||
loading={loading}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 报险记录列表 */}
|
||||
<div style={{
|
||||
marginTop: '80px',
|
||||
paddingBottom: '80px'
|
||||
}}>
|
||||
{loading && list.length === 0 ? (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '200px'
|
||||
}}>
|
||||
<Loading type="spinner">加载中...</Loading>
|
||||
</div>
|
||||
) : list.length === 0 ? (
|
||||
<Empty description="暂无报险记录">
|
||||
<Button type="primary" onClick={onAddInsurance}>
|
||||
立即报险
|
||||
</Button>
|
||||
</Empty>
|
||||
) : (
|
||||
<div style={{padding: '0 16px'}}>
|
||||
{list.map((item, index) => {
|
||||
const statusDisplay = getStatusDisplay(item.status)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
marginBottom: '12px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)',
|
||||
border: '1px solid #f0f0f0'
|
||||
}}
|
||||
onClick={() => viewDetail(item)}
|
||||
>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: '12px'
|
||||
}}>
|
||||
<div style={{flex: 1}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
<File size={16} color="#1890ff"/>
|
||||
<span style={{
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
color: '#262626'
|
||||
}}>
|
||||
报险记录 #{item.id}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Space direction="vertical" size={4}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Truck size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
车辆ID:{item.carId}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Calendar size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
提交时间:{item.createTime}
|
||||
</span>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Tag
|
||||
color={statusDisplay.color}
|
||||
style={{
|
||||
backgroundColor: statusDisplay.bgColor,
|
||||
border: `1px solid ${statusDisplay.color}`,
|
||||
fontSize: '12px'
|
||||
}}
|
||||
>
|
||||
{statusDisplay.text}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
{/* 事故照片预览 */}
|
||||
{item.image && (
|
||||
<div style={{marginBottom: '12px'}}>
|
||||
<Image
|
||||
src={item.image}
|
||||
width="60"
|
||||
height="60"
|
||||
radius="6px"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 备注信息 */}
|
||||
{item.comments && (
|
||||
<div style={{
|
||||
backgroundColor: '#f8f9fa',
|
||||
padding: '8px 12px',
|
||||
borderRadius: '6px',
|
||||
fontSize: '13px',
|
||||
color: '#595959',
|
||||
lineHeight: '1.4'
|
||||
}}>
|
||||
{item.comments.length > 50
|
||||
? `${item.comments.substring(0, 50)}...`
|
||||
: item.comments
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 浮动添加按钮 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
bottom: '20px',
|
||||
right: '20px',
|
||||
zIndex: 30
|
||||
}}>
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
size="large"
|
||||
onClick={onAddInsurance}
|
||||
style={{
|
||||
width: '56px',
|
||||
height: '56px',
|
||||
borderRadius: '28px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BxList
|
||||
3
src/hjm/bx/bx.config.ts
Normal file
3
src/hjm/bx/bx.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '报险记录'
|
||||
})
|
||||
162
src/hjm/bx/bx.tsx
Normal file
162
src/hjm/bx/bx.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {InfiniteLoading, Loading, Empty, Button, Input} from '@nutui/nutui-react-taro'
|
||||
import {Search, Plus} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import BestSellers from "./BestSellers";
|
||||
|
||||
/**
|
||||
* 一键报险 - 车辆列表页面
|
||||
* @constructor
|
||||
*/
|
||||
const InsuranceList = () => {
|
||||
const [list, setList] = useState<HjmCar[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
|
||||
const reload = async (showLoading = true) => {
|
||||
try {
|
||||
if (showLoading) setLoading(true)
|
||||
setRefreshing(true)
|
||||
|
||||
// 获取车辆列表 - 只获取正常状态的车辆
|
||||
const res = await pageHjmCar({
|
||||
status: 1,
|
||||
keywords: keywords.trim() || undefined
|
||||
})
|
||||
|
||||
setList(res?.list || [])
|
||||
} catch (error) {
|
||||
console.error('获取车辆列表失败:', error)
|
||||
Taro.showToast({
|
||||
title: '获取车辆列表失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setRefreshing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
reload()
|
||||
}
|
||||
|
||||
const onKeywordsChange = (value: string) => {
|
||||
setKeywords(value)
|
||||
}
|
||||
|
||||
const onAddInsurance = () => {
|
||||
Taro.navigateTo({
|
||||
url: '/hjm/bx/bx-add'
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 搜索栏 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '20px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 20,
|
||||
padding: '0 16px',
|
||||
backgroundColor: '#f5f5f5'
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
padding: '8px 12px',
|
||||
borderRadius: '20px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<Search size={16} color="#999"/>
|
||||
<Input
|
||||
placeholder="搜索车辆编号或快递公司"
|
||||
value={keywords}
|
||||
onChange={onKeywordsChange}
|
||||
onConfirm={onSearch}
|
||||
style={{
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
flex: 1,
|
||||
marginLeft: '8px'
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={onSearch}
|
||||
loading={loading}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 车辆列表 */}
|
||||
<div style={{
|
||||
marginTop: '80px',
|
||||
paddingBottom: '80px'
|
||||
}}>
|
||||
{loading && list.length === 0 ? (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '200px'
|
||||
}}>
|
||||
<Loading type="spinner">加载中...</Loading>
|
||||
</div>
|
||||
) : list.length === 0 ? (
|
||||
<Empty description="暂无车辆数据">
|
||||
<Button type="primary" onClick={() => reload()}>
|
||||
重新加载
|
||||
</Button>
|
||||
</Empty>
|
||||
) : (
|
||||
<InfiniteLoading
|
||||
style={{width: '100%'}}
|
||||
hasMore={false}
|
||||
onLoadMore={() => {}}
|
||||
>
|
||||
<BestSellers data={list} onRefresh={() => reload(false)}/>
|
||||
</InfiniteLoading>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 浮动添加按钮 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
bottom: '20px',
|
||||
right: '20px',
|
||||
zIndex: 30
|
||||
}}>
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
size="large"
|
||||
onClick={onAddInsurance}
|
||||
style={{
|
||||
width: '56px',
|
||||
height: '56px',
|
||||
borderRadius: '28px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
|
||||
}}
|
||||
>
|
||||
<Plus size={24}/>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default InsuranceList
|
||||
131
src/hjm/exam/exam-simple.tsx
Normal file
131
src/hjm/exam/exam-simple.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Button} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {pageHjmQuestions} from "@/api/hjm/hjmQuestions";
|
||||
import {HjmQuestions} from "@/api/hjm/hjmQuestions/model";
|
||||
|
||||
/**
|
||||
* 简化版考试系统 - 用于测试
|
||||
* @constructor
|
||||
*/
|
||||
const ExamSimple = () => {
|
||||
const [questions, setQuestions] = useState<HjmQuestions[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
// 加载题目
|
||||
const loadQuestions = () => {
|
||||
setLoading(true)
|
||||
console.log('开始加载题目...')
|
||||
|
||||
pageHjmQuestions({}).then(data => {
|
||||
console.log('API返回数据:', data)
|
||||
const questionList = data?.list || []
|
||||
setQuestions(questionList)
|
||||
console.log('加载题目成功:', questionList)
|
||||
|
||||
Taro.showToast({
|
||||
title: `加载成功,共${questionList.length}道题`,
|
||||
icon: 'success'
|
||||
})
|
||||
}).catch(error => {
|
||||
console.error('加载题目失败:', error)
|
||||
Taro.showToast({
|
||||
title: '加载题目失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadQuestions()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{ padding: '16px' }}>
|
||||
<h1 style={{ fontSize: '20px', fontWeight: 'bold', marginBottom: '16px' }}>
|
||||
考试系统测试页面
|
||||
</h1>
|
||||
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<p>题目数量:{questions.length} 道</p>
|
||||
<p>加载状态:{loading ? '加载中...' : '加载完成'}</p>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={loadQuestions}
|
||||
loading={loading}
|
||||
>
|
||||
重新加载题目
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{questions.length > 0 && (
|
||||
<div style={{ backgroundColor: 'white', padding: '16px', borderRadius: '8px' }}>
|
||||
<h3 style={{ marginBottom: '12px' }}>题目列表:</h3>
|
||||
{questions.slice(0, 5).map((question, index) => (
|
||||
<div key={index} style={{ marginBottom: '12px', padding: '8px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
|
||||
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>
|
||||
第{index + 1}题:{question.question}
|
||||
</div>
|
||||
<div style={{ fontSize: '12px', color: '#666' }}>
|
||||
类型:{question.type === 0 ? '选择题' : question.type === 1 ? '填空题' : '问答题'} |
|
||||
难度:{question.difficulty === 0 ? '简单' : question.difficulty === 1 ? '中等' : '困难'}
|
||||
</div>
|
||||
{question.type === 0 && (
|
||||
<div style={{ fontSize: '12px', marginTop: '4px' }}>
|
||||
{question.choicesList && question.choicesList.length > 0 ? (
|
||||
// 使用 choicesList 显示选项
|
||||
<>
|
||||
{question.choicesList.map((choice, index) => {
|
||||
const optionLabel = String.fromCharCode(65 + index); // A, B, C, D
|
||||
return (
|
||||
<div key={index} style={{ color: choice.isCorrect ? '#52c41a' : '#333' }}>
|
||||
{optionLabel}: {choice.content} {choice.isCorrect && '✓'}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
// 备用方案:使用传统字段
|
||||
<>
|
||||
A: {question.choicesA}<br/>
|
||||
B: {question.choicesB}<br/>
|
||||
C: {question.choicesC}<br/>
|
||||
D: {question.choicesD}<br/>
|
||||
正确答案: {question.correctAnswer}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{questions.length > 5 && (
|
||||
<div style={{ textAlign: 'center', color: '#666', fontSize: '14px' }}>
|
||||
还有 {questions.length - 5} 道题目...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<Button
|
||||
type="success"
|
||||
onClick={() => {
|
||||
Taro.navigateTo({
|
||||
url: '/hjm/exam/exam'
|
||||
})
|
||||
}}
|
||||
disabled={questions.length === 0}
|
||||
>
|
||||
进入完整考试系统
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExamSimple
|
||||
3
src/hjm/exam/exam.config.ts
Normal file
3
src/hjm/exam/exam.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '开始考试'
|
||||
})
|
||||
608
src/hjm/exam/exam.tsx
Normal file
608
src/hjm/exam/exam.tsx
Normal file
@@ -0,0 +1,608 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {ArrowLeft, ArrowRight} from '@nutui/icons-react-taro'
|
||||
import {Button, Radio, Input, Progress} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { TextArea } from '@nutui/nutui-react-taro'
|
||||
import {pageHjmQuestions} from "@/api/hjm/hjmQuestions";
|
||||
import {HjmQuestions} from "@/api/hjm/hjmQuestions/model";
|
||||
import {addHjmExamLog} from "@/api/hjm/hjmExamLog";
|
||||
|
||||
// 用户答案接口
|
||||
interface UserAnswer {
|
||||
questionId: number;
|
||||
answer: string;
|
||||
isCorrect: boolean;
|
||||
score: number;
|
||||
}
|
||||
|
||||
// 考试状态枚举
|
||||
enum ExamStatus {
|
||||
NOT_STARTED = 'not_started',
|
||||
IN_PROGRESS = 'in_progress',
|
||||
COMPLETED = 'completed'
|
||||
}
|
||||
|
||||
/**
|
||||
* 考试系统
|
||||
* @constructor
|
||||
*/
|
||||
const Exam = () => {
|
||||
const [questions, setQuestions] = useState<HjmQuestions[]>([])
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState<number>(0)
|
||||
const [userAnswers, setUserAnswers] = useState<UserAnswer[]>([])
|
||||
const [currentAnswer, setCurrentAnswer] = useState<any>('')
|
||||
const [examStatus, setExamStatus] = useState<ExamStatus>(ExamStatus.NOT_STARTED)
|
||||
const [totalScore, setTotalScore] = useState<number>(0)
|
||||
const [timeRemaining, setTimeRemaining] = useState<number>(600) // 10分钟考试时间
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
|
||||
// 加载题目
|
||||
const loadQuestions = () => {
|
||||
setLoading(true)
|
||||
setError('')
|
||||
console.log('开始加载题目...')
|
||||
|
||||
pageHjmQuestions({}).then(data => {
|
||||
console.log('API返回数据:', data)
|
||||
const questionList = data?.list || []
|
||||
// 限制为10道题目
|
||||
const limitedQuestions = questionList.slice(0, 10)
|
||||
setQuestions(limitedQuestions)
|
||||
console.log('加载题目成功:', limitedQuestions)
|
||||
|
||||
// 调试:检查选择题的 choicesList 数据
|
||||
limitedQuestions.forEach((question, index) => {
|
||||
if (question.type === 0) {
|
||||
console.log(`第${index + 1}题 (选择题):`, {
|
||||
question: question.question,
|
||||
choicesList: question.choicesList,
|
||||
correctAnswer: question.correctAnswer
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (limitedQuestions.length === 0) {
|
||||
setError('没有找到题目数据')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('加载题目失败:', error)
|
||||
setError('加载题目失败: ' + (error.message || '未知错误'))
|
||||
Taro.showToast({
|
||||
title: '加载题目失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
// 开始考试
|
||||
const startExam = () => {
|
||||
if (questions.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '没有题目数据',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
console.log('开始考试,题目数量:', questions.length)
|
||||
setExamStatus(ExamStatus.IN_PROGRESS)
|
||||
setCurrentQuestionIndex(0)
|
||||
setUserAnswers([])
|
||||
setCurrentAnswer('')
|
||||
setTotalScore(0)
|
||||
setTimeRemaining(600)
|
||||
}
|
||||
|
||||
// 获取当前题目
|
||||
const getCurrentQuestion = (): HjmQuestions | null => {
|
||||
try {
|
||||
if (questions && questions.length > 0 &&
|
||||
currentQuestionIndex >= 0 &&
|
||||
currentQuestionIndex < questions.length) {
|
||||
return questions[currentQuestionIndex]
|
||||
}
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('获取当前题目时出错:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 提交当前题目答案
|
||||
const submitCurrentAnswer = () => {
|
||||
const currentQuestion = getCurrentQuestion()
|
||||
if (!currentQuestion || !currentAnswer.trim()) {
|
||||
Taro.showToast({
|
||||
title: '请选择或填写答案',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 计算得分
|
||||
const isCorrect = checkAnswer(currentQuestion, currentAnswer)
|
||||
const score = isCorrect ? 10 : 0 // 每题10分
|
||||
|
||||
const userAnswer: UserAnswer = {
|
||||
questionId: currentQuestion.id!,
|
||||
answer: currentAnswer,
|
||||
isCorrect,
|
||||
score
|
||||
}
|
||||
|
||||
const newUserAnswers = [...userAnswers, userAnswer]
|
||||
setUserAnswers(newUserAnswers)
|
||||
|
||||
// 显示答题反馈
|
||||
const currentScore = userAnswers.reduce((sum, answer) => sum + answer.score, 0) + score
|
||||
Taro.showToast({
|
||||
title: isCorrect ? `回答正确!+10分 (总分:${currentScore})` : `回答错误!(总分:${currentScore})`,
|
||||
icon: isCorrect ? 'success' : 'none',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 清空当前答案
|
||||
setCurrentAnswer('')
|
||||
|
||||
// 延迟跳转,让用户看到反馈
|
||||
setTimeout(() => {
|
||||
// 检查是否是最后一题
|
||||
if (currentQuestionIndex === questions.length - 1) {
|
||||
// 考试结束
|
||||
finishExam(newUserAnswers)
|
||||
} else {
|
||||
// 下一题
|
||||
setCurrentQuestionIndex(currentQuestionIndex + 1)
|
||||
}
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
// 检查答案是否正确
|
||||
const checkAnswer = (question: HjmQuestions, answer: string): boolean => {
|
||||
try {
|
||||
if (question.type === 0) { // 选择题
|
||||
// 使用 choicesList 来检查答案
|
||||
if (question.choicesList && question.choicesList.length > 0) {
|
||||
// 找到用户选择的选项索引
|
||||
let selectedIndex = -1;
|
||||
|
||||
// 如果答案是字母格式(A, B, C, D)
|
||||
if (answer.length === 1 && answer >= 'A' && answer <= 'D') {
|
||||
selectedIndex = answer.charCodeAt(0) - 65; // A=0, B=1, C=2, D=3
|
||||
}
|
||||
// 如果答案是数字格式(0, 1, 2, 3)
|
||||
else if (!isNaN(Number(answer))) {
|
||||
selectedIndex = Number(answer);
|
||||
}
|
||||
|
||||
// 检查索引是否有效
|
||||
if (selectedIndex >= 0 && selectedIndex < question.choicesList.length) {
|
||||
return question.choicesList[selectedIndex]?.isCorrect || false;
|
||||
}
|
||||
}
|
||||
// 备用方案:使用 correctAnswer 字段
|
||||
return answer === question.correctAnswer
|
||||
} else if (question.type === 1) { // 填空题
|
||||
// 简单的字符串匹配,可以根据需要改进
|
||||
return answer.trim().toLowerCase() === question.correctAnswer?.trim().toLowerCase()
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('检查答案时出错:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 完成考试
|
||||
const finishExam = (answers: UserAnswer[]) => {
|
||||
const total = answers.reduce((sum, answer) => sum + answer.score, 0)
|
||||
setTotalScore(total)
|
||||
setExamStatus(ExamStatus.COMPLETED)
|
||||
Taro.showToast({
|
||||
title: `考试完成!得分:${total}分`,
|
||||
icon: 'success',
|
||||
duration: 3000
|
||||
})
|
||||
// 考试得满分完成本月学习任务
|
||||
addHjmExamLog({total: total.toString(), status: total == 100 ? 1 : 0, useTime: formatTime(600 - timeRemaining)}).then(() => {})
|
||||
}
|
||||
|
||||
// 重新开始考试
|
||||
const restartExam = () => {
|
||||
setExamStatus(ExamStatus.NOT_STARTED)
|
||||
setCurrentQuestionIndex(0)
|
||||
setUserAnswers([])
|
||||
setCurrentAnswer('')
|
||||
setTotalScore(0)
|
||||
setTimeRemaining(600)
|
||||
}
|
||||
|
||||
// 计算进度百分比
|
||||
const getProgress = (): number => {
|
||||
if (questions.length === 0) return 0
|
||||
return Math.round(((currentQuestionIndex + 1) / questions.length) * 100)
|
||||
}
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTime = (seconds: number): string => {
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const remainingSeconds = seconds % 60
|
||||
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 获取成绩等级
|
||||
const getGradeLevel = (score: number, totalScore: number): { level: string, color: string, description: string } => {
|
||||
const percentage = (score / totalScore) * 100
|
||||
if (percentage >= 90) {
|
||||
return {level: '优秀', color: '#52c41a', description: '恭喜您!成绩优异!'}
|
||||
} else if (percentage >= 80) {
|
||||
return {level: '良好', color: '#1890ff', description: '成绩良好,继续努力!'}
|
||||
} else if (percentage >= 70) {
|
||||
return {level: '中等', color: '#faad14', description: '成绩中等,还有提升空间!'}
|
||||
} else if (percentage >= 60) {
|
||||
return {level: '及格', color: '#fa8c16', description: '刚好及格,需要加强学习!'}
|
||||
} else {
|
||||
return {level: '不及格', color: '#f5222d', description: '成绩不理想,建议重新学习!'}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取答题统计
|
||||
const getAnswerStats = () => {
|
||||
const correctCount = userAnswers.filter(answer => answer.isCorrect).length
|
||||
const wrongCount = userAnswers.length - correctCount
|
||||
const totalTime = 600 - timeRemaining
|
||||
return {
|
||||
correctCount,
|
||||
wrongCount,
|
||||
totalTime: formatTime(totalTime),
|
||||
accuracy: userAnswers.length > 0 ? Math.round((correctCount / userAnswers.length) * 100) : 0
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadQuestions()
|
||||
}, [])
|
||||
|
||||
// 倒计时效果
|
||||
useEffect(() => {
|
||||
let timer: NodeJS.Timeout
|
||||
if (examStatus === ExamStatus.IN_PROGRESS && timeRemaining > 0) {
|
||||
timer = setTimeout(() => {
|
||||
setTimeRemaining(prev => prev - 1)
|
||||
}, 1000)
|
||||
} else if (timeRemaining === 0 && examStatus === ExamStatus.IN_PROGRESS) {
|
||||
// 时间到,自动提交
|
||||
finishExam(userAnswers)
|
||||
}
|
||||
return () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
}, [examStatus, timeRemaining]) // 移除 userAnswers 依赖,避免无限循环
|
||||
|
||||
const currentQuestion = getCurrentQuestion()
|
||||
|
||||
return (
|
||||
<div style={{padding: '16px', minHeight: '100vh', backgroundColor: '#f5f5f5'}}>
|
||||
{/* 加载状态 */}
|
||||
{loading && (
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '24px', textAlign: 'center'}}>
|
||||
<h1 style={{fontSize: '20px', fontWeight: 'bold', marginBottom: '16px'}}>正在加载题目...</h1>
|
||||
<div style={{color: '#666'}}>请稍候</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 错误状态 */}
|
||||
{error && !loading && (
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '24px', textAlign: 'center'}}>
|
||||
<h1 style={{fontSize: '20px', fontWeight: 'bold', marginBottom: '16px', color: '#f5222d'}}>加载失败</h1>
|
||||
<div style={{marginBottom: '16px', color: '#666'}}>{error}</div>
|
||||
<Button type="primary" onClick={loadQuestions}>
|
||||
重新加载
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 考试未开始 */}
|
||||
{!loading && !error && examStatus === ExamStatus.NOT_STARTED && (
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '24px', textAlign: 'center'}}>
|
||||
<h1 style={{fontSize: '20px', fontWeight: 'bold', marginBottom: '16px'}}>在线考试系统</h1>
|
||||
<div style={{marginBottom: '16px', color: '#666'}}>
|
||||
<p>题目数量:{questions.length} 道</p>
|
||||
<p>考试时间:10 分钟</p>
|
||||
<p>每题分值:10 分</p>
|
||||
<p>总分:{questions.length * 10} 分</p>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={startExam}
|
||||
disabled={questions.length === 0}
|
||||
>
|
||||
开始考试
|
||||
</Button>
|
||||
{questions.length === 0 && (
|
||||
<div style={{marginTop: '8px', fontSize: '14px', color: '#f5222d'}}>
|
||||
暂无题目数据,请联系管理员
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 考试进行中 */}
|
||||
{examStatus === ExamStatus.IN_PROGRESS && currentQuestion && (
|
||||
<div>
|
||||
{/* 顶部信息栏 */}
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '16px', marginBottom: '16px'}}>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px'}}>
|
||||
<span style={{fontSize: '14px', color: '#666'}}>
|
||||
第 {currentQuestionIndex + 1} 题 / 共 {questions.length} 题
|
||||
</span>
|
||||
<span style={{fontSize: '14px', fontWeight: 'bold', color: '#f5222d'}}>
|
||||
剩余时间:{formatTime(timeRemaining)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px'}}>
|
||||
<span style={{fontSize: '14px', color: '#1890ff'}}>
|
||||
当前得分:{userAnswers.reduce((sum, answer) => sum + answer.score, 0)} 分
|
||||
</span>
|
||||
<span style={{fontSize: '14px', color: '#666'}}>
|
||||
已答:{userAnswers.length} 题
|
||||
</span>
|
||||
</div>
|
||||
<Progress percent={getProgress()} color="#1890ff"/>
|
||||
</div>
|
||||
|
||||
{/* 题目内容 */}
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '16px', marginBottom: '16px'}}>
|
||||
<div style={{marginBottom: '16px'}}>
|
||||
<h2 style={{fontSize: '18px', fontWeight: 'bold', marginBottom: '8px'}}>
|
||||
{currentQuestionIndex + 1}. {currentQuestion.question}
|
||||
</h2>
|
||||
<div style={{fontSize: '14px', color: '#999'}}>
|
||||
{currentQuestion.type === 0 ? '选择题' : currentQuestion.type === 1 ? '填空题' : '问答题'}
|
||||
({currentQuestion.difficulty === 0 ? '简单' : currentQuestion.difficulty === 1 ? '中等' : '困难'})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 选择题选项 */}
|
||||
{currentQuestion.type === 0 && (
|
||||
<div>
|
||||
<Radio.Group
|
||||
value={currentAnswer}
|
||||
onChange={(value) => setCurrentAnswer(value)}
|
||||
>
|
||||
{currentQuestion.choicesList && currentQuestion.choicesList.length > 0 ? (
|
||||
// 使用 choicesList 显示选项
|
||||
currentQuestion.choicesList.map((choice, index) => {
|
||||
const optionLabel = String.fromCharCode(65 + index); // A, B, C, D
|
||||
return (
|
||||
<div key={index} style={{marginBottom: '8px'}}>
|
||||
<Radio value={optionLabel}>
|
||||
{optionLabel}. {choice.content}
|
||||
</Radio>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
// 备用方案:使用传统的 choicesA, choicesB 等字段
|
||||
<>
|
||||
{currentQuestion.choicesA && (
|
||||
<div style={{marginBottom: '8px'}}>
|
||||
<Radio value="A">A. {currentQuestion.choicesA}</Radio>
|
||||
</div>
|
||||
)}
|
||||
{currentQuestion.choicesB && (
|
||||
<div style={{marginBottom: '8px'}}>
|
||||
<Radio value="B">B. {currentQuestion.choicesB}</Radio>
|
||||
</div>
|
||||
)}
|
||||
{currentQuestion.choicesC && (
|
||||
<div style={{marginBottom: '8px'}}>
|
||||
<Radio value="C">C. {currentQuestion.choicesC}</Radio>
|
||||
</div>
|
||||
)}
|
||||
{currentQuestion.choicesD && (
|
||||
<div style={{marginBottom: '8px'}}>
|
||||
<Radio value="D">D. {currentQuestion.choicesD}</Radio>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Radio.Group>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 填空题输入 */}
|
||||
{currentQuestion.type === 1 && (
|
||||
<div style={{marginBottom: '16px'}}>
|
||||
<Input
|
||||
placeholder="请输入答案"
|
||||
value={currentAnswer}
|
||||
onChange={(value) => setCurrentAnswer(value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 问答题输入 */}
|
||||
{currentQuestion.type === 2 && (
|
||||
<div style={{marginBottom: '16px'}}>
|
||||
<TextArea
|
||||
placeholder={'个性签名'}
|
||||
value={currentAnswer}
|
||||
onChange={(value) => setCurrentAnswer(value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '16px'}}>
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
<Button
|
||||
type="default"
|
||||
disabled={currentQuestionIndex === 0}
|
||||
onClick={() => setCurrentQuestionIndex(currentQuestionIndex - 1)}
|
||||
>
|
||||
<ArrowLeft size={16}/> 上一题
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={submitCurrentAnswer}
|
||||
disabled={!currentAnswer.trim()}
|
||||
>
|
||||
{currentQuestionIndex === questions.length - 1 ? '提交试卷' : '下一题'}
|
||||
<ArrowRight size={16}/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 考试完成 */}
|
||||
{examStatus === ExamStatus.COMPLETED && (
|
||||
<div style={{backgroundColor: 'white', borderRadius: '8px', padding: '24px', textAlign: 'center'}}>
|
||||
<h1 style={{fontSize: '24px', fontWeight: 'bold', marginBottom: '16px', color: '#52c41a'}}>🎉
|
||||
考试完成!</h1>
|
||||
|
||||
{/* 成绩展示 */}
|
||||
<div style={{marginBottom: '24px'}}>
|
||||
<div style={{fontSize: '36px', fontWeight: 'bold', color: '#1890ff', marginBottom: '8px'}}>{totalScore} 分
|
||||
</div>
|
||||
<div style={{color: '#666', marginBottom: '8px'}}>总分:{questions.length * 10} 分</div>
|
||||
|
||||
{/* 成绩等级 */}
|
||||
{(() => {
|
||||
const gradeInfo = getGradeLevel(totalScore, questions.length * 10)
|
||||
return (
|
||||
<div style={{marginBottom: '16px'}}>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '20px',
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '8px',
|
||||
backgroundColor: gradeInfo.color
|
||||
}}
|
||||
>
|
||||
{gradeInfo.level}
|
||||
</div>
|
||||
<div style={{fontSize: '14px', color: '#666'}}>{gradeInfo.description}</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* 答题统计 */}
|
||||
<div style={{marginBottom: '24px', backgroundColor: '#f5f5f5', borderRadius: '8px', padding: '16px'}}>
|
||||
<h3 style={{fontWeight: 'bold', marginBottom: '12px', textAlign: 'left'}}>📊 答题统计</h3>
|
||||
{(() => {
|
||||
const stats = getAnswerStats()
|
||||
return (
|
||||
<div style={{display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', fontSize: '14px'}}>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<div style={{color: '#52c41a', fontWeight: 'bold', fontSize: '18px'}}>{stats.correctCount}</div>
|
||||
<div style={{color: '#666'}}>答对题数</div>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<div style={{color: '#f5222d', fontWeight: 'bold', fontSize: '18px'}}>{stats.wrongCount}</div>
|
||||
<div style={{color: '#666'}}>答错题数</div>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<div style={{color: '#1890ff', fontWeight: 'bold', fontSize: '18px'}}>{stats.accuracy}%</div>
|
||||
<div style={{color: '#666'}}>正确率</div>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<div style={{color: '#722ed1', fontWeight: 'bold', fontSize: '18px'}}>{stats.totalTime}</div>
|
||||
<div style={{color: '#666'}}>用时</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* 答题详情 */}
|
||||
<div style={{marginBottom: '24px', textAlign: 'left'}}>
|
||||
<h3 style={{fontWeight: 'bold', marginBottom: '12px'}}>📝 答题详情</h3>
|
||||
<div style={{maxHeight: '320px', overflowY: 'auto'}}>
|
||||
{userAnswers.map((answer, index) => {
|
||||
const question = questions.find(q => q.id === answer.questionId)
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
borderLeft: `4px solid ${answer.isCorrect ? '#52c41a' : '#f5222d'}`,
|
||||
backgroundColor: answer.isCorrect ? '#f6ffed' : '#fff2f0',
|
||||
marginBottom: '12px'
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
<span style={{fontWeight: '500', fontSize: '14px'}}>第{index + 1}题</span>
|
||||
<span style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
color: answer.isCorrect ? '#52c41a' : '#f5222d'
|
||||
}}>
|
||||
{answer.isCorrect ? '✓ 正确' : '✗ 错误'} ({answer.score}分)
|
||||
</span>
|
||||
</div>
|
||||
<div style={{fontSize: '14px', color: '#333', marginBottom: '8px'}}>
|
||||
<strong>题目:</strong>{question?.question}
|
||||
</div>
|
||||
<div style={{fontSize: '14px'}}>
|
||||
<span style={{color: '#666'}}>您的答案:</span>
|
||||
<span style={{color: answer.isCorrect ? '#52c41a' : '#f5222d'}}>{answer.answer}</span>
|
||||
</div>
|
||||
{!answer.isCorrect && (
|
||||
<div style={{fontSize: '14px', marginTop: '4px'}}>
|
||||
<span style={{color: '#666'}}>正确答案:</span>
|
||||
<span style={{color: '#52c41a'}}>
|
||||
{(() => {
|
||||
// 如果是选择题,显示正确选项的内容
|
||||
if (question?.type === 0 && question.choicesList) {
|
||||
const correctChoice = question.choicesList.find(choice => choice.isCorrect);
|
||||
if (correctChoice) {
|
||||
const correctIndex = question.choicesList.indexOf(correctChoice);
|
||||
const correctLabel = String.fromCharCode(65 + correctIndex);
|
||||
return `${correctLabel}. ${correctChoice.content}`;
|
||||
}
|
||||
}
|
||||
// 备用方案:使用 correctAnswer 字段
|
||||
return question?.correctAnswer || '未知';
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{display: 'flex', flexDirection: 'column', gap: '8px'}}>
|
||||
<Button type="primary" size="large" onClick={restartExam}>
|
||||
重新考试
|
||||
</Button>
|
||||
<Button type="default" size="large" onClick={() => Taro.navigateBack()}>
|
||||
返回
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Exam
|
||||
@@ -163,8 +163,7 @@ const Location = () => {
|
||||
{
|
||||
points: points,
|
||||
color: '#ff0000',
|
||||
fillColor: '#ffcccc',
|
||||
strokeWidth: 2
|
||||
strokeWidth: 3
|
||||
}
|
||||
] : []}
|
||||
onTap={() => {
|
||||
|
||||
3
src/hjm/video/video.config.ts
Normal file
3
src/hjm/video/video.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '观看视频'
|
||||
})
|
||||
69
src/hjm/video/video.tsx
Normal file
69
src/hjm/video/video.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Video} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {getCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
import {View} from '@tarojs/components'
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
*/
|
||||
const VideoForm = () => {
|
||||
const {params} = useRouter();
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
const [source, setSource] = useState({
|
||||
src: '',
|
||||
type: 'video/mp4',
|
||||
})
|
||||
const options = {
|
||||
autoplay: true,
|
||||
muted: true,
|
||||
controls: true,
|
||||
}
|
||||
const play = (elm: any) => console.log('play', elm)
|
||||
const pause = (elm: any) => console.log('pause', elm)
|
||||
const playend = () => {
|
||||
Taro.navigateTo({
|
||||
url: '/hjm/exam/exam',
|
||||
})
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
getCmsArticle(Number(params.id)).then(data => {
|
||||
setItem(data)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${data.title}`
|
||||
})
|
||||
console.log(item)
|
||||
setSource({
|
||||
src: `${data.pdfUrl || 'https://oss.wsdns.cn/20250605/9e88d2100425471288d4115cc48660ed.mp4'}`,
|
||||
type: 'video/mp4',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-3 mt-4 mb-10'}>
|
||||
<div className={'flex flex-col justify-between items-center bg-white rounded-lg p-2'}>
|
||||
<Video
|
||||
source={source}
|
||||
options={options}
|
||||
onPlay={play}
|
||||
onPause={pause}
|
||||
onPlayEnd={playend}
|
||||
style={{ height: '163px' }}
|
||||
/>
|
||||
</div>
|
||||
<View className={'content text-gray-700 text-sm py-4 text-center'}>
|
||||
观看完视频后开始考试!
|
||||
</View>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default VideoForm
|
||||
Reference in New Issue
Block a user