forked from gxwebsoft/mp-10550
删除无用代码
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
import request from '@/utils/request';
|
||||
import type { ApiResult, PageResult } from '@/api/index';
|
||||
import type { OrderGoods, OrderGoodsParam } from './model';
|
||||
import {SERVER_API_URL} from '@/config/setting';
|
||||
|
||||
/**
|
||||
* 分页查询订单商品
|
||||
*/
|
||||
export async function pageOrderGoods(params: OrderGoodsParam) {
|
||||
const res = await request.get<ApiResult<PageResult<OrderGoods>>>(
|
||||
SERVER_API_URL + '/system/order-goods/page',
|
||||
{
|
||||
params
|
||||
}
|
||||
'/shop/shop-goods/page',
|
||||
params
|
||||
);
|
||||
if (res.code === 0) {
|
||||
return res.data;
|
||||
@@ -24,10 +21,8 @@ export async function pageOrderGoods(params: OrderGoodsParam) {
|
||||
*/
|
||||
export async function listOrderGoods(params?: OrderGoodsParam) {
|
||||
const res = await request.get<ApiResult<OrderGoods[]>>(
|
||||
SERVER_API_URL + '/system/order-goods',
|
||||
{
|
||||
params
|
||||
}
|
||||
'/shop/shop-goods',
|
||||
params
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
@@ -40,7 +35,7 @@ export async function listOrderGoods(params?: OrderGoodsParam) {
|
||||
*/
|
||||
export async function addOrderGoods(data: OrderGoods) {
|
||||
const res = await request.post<ApiResult<unknown>>(
|
||||
SERVER_API_URL + '/system/order-goods',
|
||||
'/shop/shop-goods',
|
||||
data
|
||||
);
|
||||
if (res.code === 0) {
|
||||
@@ -54,7 +49,7 @@ export async function addOrderGoods(data: OrderGoods) {
|
||||
*/
|
||||
export async function updateOrderGoods(data: OrderGoods) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
SERVER_API_URL + '/system/order-goods',
|
||||
'/shop/shop-goods',
|
||||
data
|
||||
);
|
||||
if (res.code === 0) {
|
||||
@@ -68,7 +63,7 @@ export async function updateOrderGoods(data: OrderGoods) {
|
||||
*/
|
||||
export async function removeOrderGoods(id?: number) {
|
||||
const res = await request.del<ApiResult<unknown>>(
|
||||
SERVER_API_URL + '/system/order-goods/' + id
|
||||
'/shop/shop-goods/' + id
|
||||
);
|
||||
if (res.code === 0) {
|
||||
return res.message;
|
||||
@@ -81,7 +76,7 @@ export async function removeOrderGoods(id?: number) {
|
||||
*/
|
||||
export async function removeBatchOrderGoods(data: (number | undefined)[]) {
|
||||
const res = await request.del<ApiResult<unknown>>(
|
||||
SERVER_API_URL + '/system/order-goods/batch',
|
||||
'/shop/shop-goods/batch',
|
||||
{
|
||||
data
|
||||
}
|
||||
@@ -97,7 +92,7 @@ export async function removeBatchOrderGoods(data: (number | undefined)[]) {
|
||||
*/
|
||||
export async function getOrderGoods(id: number) {
|
||||
const res = await request.get<ApiResult<OrderGoods>>(
|
||||
SERVER_API_URL + '/system/order-goods/' + id
|
||||
'/shop/shop-goods/' + id
|
||||
);
|
||||
if (res.code === 0 && res.data) {
|
||||
return res.data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PageParam } from '@/api';
|
||||
import type { PageParam } from '@/api/index';
|
||||
|
||||
/**
|
||||
* 订单商品
|
||||
@@ -12,6 +12,8 @@ export interface OrderGoods {
|
||||
orderId?: number;
|
||||
// 项目ID
|
||||
itemId?: number;
|
||||
// 商品ID
|
||||
goodsId?: number;
|
||||
// 实际付款
|
||||
payPrice?: string;
|
||||
// 购买数量
|
||||
@@ -51,5 +53,6 @@ export interface OrderGoods {
|
||||
*/
|
||||
export interface OrderGoodsParam extends PageParam {
|
||||
id?: number;
|
||||
orderId?: number; // 添加 orderId 属性
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export default defineAppConfig({
|
||||
'pages/order/order',
|
||||
'pages/kefu/kefu',
|
||||
'pages/user/user',
|
||||
'pages/find/find'
|
||||
// 'pages/find/find'
|
||||
],
|
||||
"subpackages": [
|
||||
{
|
||||
@@ -37,29 +37,14 @@ export default defineAppConfig({
|
||||
"userVerify/index"
|
||||
]
|
||||
},
|
||||
// {
|
||||
// "root": "hjm",
|
||||
// "pages": [
|
||||
// "list",
|
||||
// "location",
|
||||
// "query",
|
||||
// "fence",
|
||||
// "video/video",
|
||||
// "exam/exam",
|
||||
// "bx/bx",
|
||||
// "bx/bx-add",
|
||||
// "trajectory/trajectory",
|
||||
// "gps-log/gps-log"
|
||||
// // "bx/bx-list",
|
||||
// // "question/detail"
|
||||
// ]
|
||||
// }
|
||||
// {
|
||||
// "root": "shop",
|
||||
// "pages": [
|
||||
//
|
||||
// ]
|
||||
// }
|
||||
{
|
||||
"root": "shop",
|
||||
"pages": [
|
||||
'orderDetail/index',
|
||||
'goodsDetail/index',
|
||||
'orderConfirm/index',
|
||||
]
|
||||
}
|
||||
],
|
||||
window: {
|
||||
backgroundTextStyle: 'dark',
|
||||
@@ -79,12 +64,12 @@ export default defineAppConfig({
|
||||
selectedIconPath: "assets/tabbar/home-active.png",
|
||||
text: "首页",
|
||||
},
|
||||
{
|
||||
pagePath: "pages/find/find",
|
||||
iconPath: "assets/tabbar/find.png",
|
||||
selectedIconPath: "assets/tabbar/find-active.png",
|
||||
text: "发现",
|
||||
},
|
||||
// {
|
||||
// pagePath: "pages/find/find",
|
||||
// iconPath: "assets/tabbar/find.png",
|
||||
// selectedIconPath: "assets/tabbar/find-active.png",
|
||||
// text: "发现",
|
||||
// },
|
||||
{
|
||||
pagePath: "pages/order/order",
|
||||
iconPath: "assets/tabbar/order.png",
|
||||
|
||||
15
src/app.scss
15
src/app.scss
@@ -34,3 +34,18 @@ button[open-type="chooseAvatar"] {
|
||||
line-height: inherit !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.buy-btn{
|
||||
height: 70px;
|
||||
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
||||
border-radius: 100px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
.cart-icon{
|
||||
background: linear-gradient(to bottom, #bbe094, #4ee265);
|
||||
border-radius: 100px 0 0 100px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '邀请函',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
.cert-bg{
|
||||
width: 750px;
|
||||
height: 1320px;
|
||||
margin: 0 auto;
|
||||
background: url("https://oss.wsdns.cn/20250304/5147356628a3460e811cf5fb90df26a7.png") no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
.cert-content{
|
||||
width: 480px;
|
||||
height: 720px;
|
||||
font-size: 24px;
|
||||
padding-top: 250px;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
import './bm-cert.scss'
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {getPoster} from "@/api/bszx/bszxBm";
|
||||
import {ConfigProvider, Loading} from "@nutui/nutui-react-taro";
|
||||
|
||||
function BmCert() {
|
||||
const [poster, setPoster] = useState<string>('')
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
|
||||
const generatePoster = () => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// setPoster("https://oss.wsdns.cn/20250306/deec1c968aed4bc58a168a0f63ac8c87.jpg")
|
||||
}
|
||||
// 保存到临时地址
|
||||
Taro.downloadFile({
|
||||
url: poster,
|
||||
success: function (res) {
|
||||
Taro.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
Taro.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
fail: function (err) {
|
||||
if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const reload = () => {
|
||||
getPoster().then(img => {
|
||||
setPoster(`${img}`)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
},1000)
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// setPoster("https://oss.wsdns.cn/20250306/deec1c968aed4bc58a168a0f63ac8c87.jpg")
|
||||
}
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
loading ?
|
||||
<div className={'bg-white w-full flex justify-center items-center'} style={{height: '100vh'}}>
|
||||
<ConfigProvider theme={{ nutuiLoadingIconSize: '28px' }}>
|
||||
<Loading type="spinner">加载中</Loading>
|
||||
</ConfigProvider>
|
||||
</div>
|
||||
:
|
||||
<div className={'flex justify-center'}>
|
||||
<img src={poster} width={'100vw'} height={'91vh'}/>
|
||||
{/*<div className={'cert-content'}>*/}
|
||||
{/* <p className={'py-2'}>尊敬的 {nickName} 校友 :</p>*/}
|
||||
{/* 百廿风华,桃李百中。2025年,<i className={'text-red-500 font-bold'}>百色市百色中学</i>迎来建校 120*/}
|
||||
{/* 周年。十秩沧桑砺洗、薪火相继,百色中学走过百折不挠、上下求索的光辉历程;爱党爱国、无私奉献、团结务实、积极进取的百中精神,始终引领学校风雨兼程、砥砺奋进;一代代百中人的耕耘与拼搏,闪耀着不同时期特有的亮点,积淀深厚的文化底蕴,承载着国家和人民殷切期望,铸造了今日的辉煌。兹定于*/}
|
||||
{/* 2025年5月1日(星期四)上午9:00 举行百色中学建校 120*/}
|
||||
{/* 周年庆典活动。诚邀您拨冗莅临出席、共襄盛举,为母校的改革发展献计献策!相约浓浓绎夏,畅谈别后心路,共话同窗师友情;相信您的到来必定令校庆活动添光异彩。*/}
|
||||
{/* 专此诚邀,敬祈惠允。*/}
|
||||
{/* <p className={'mt-2 text-right'}>百色市百色中学</p>*/}
|
||||
{/* <p className={'text-right'}>2025年1月2日</p>*/}
|
||||
{/*</div>*/}
|
||||
<div className={'fixed bottom-10'}>
|
||||
{/*<Button />*/}
|
||||
<button
|
||||
className={'item px-20 py-3 bg-yellow-300 text-black flex items-center gap-2 text-nowrap whitespace-nowrap'}
|
||||
onClick={() => {
|
||||
generatePoster()
|
||||
}}>
|
||||
保存到相册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BmCert;
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我的报名',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Cell } from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState} from "react";
|
||||
import {myPageBszxBm} from "@/api/bszx/bszxBm";
|
||||
import {BszxBm} from "@/api/bszx/bszxBm/model";
|
||||
import navTo from "@/utils/common";
|
||||
|
||||
const BmLog = () => {
|
||||
const [list, setList] = useState<BszxBm[]>()
|
||||
const reload = () => {
|
||||
myPageBszxBm({limit: 1000}).then(res => {
|
||||
if(res.list){
|
||||
setList(res.list);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'p-4'}>
|
||||
{list?.map((item, index) => {
|
||||
return (
|
||||
<Cell title={item.name} extra={item.createTime} key={index} onClick={() => {
|
||||
navTo('/bszx/bm-cert/bm-cert?id=' + item.id,true)
|
||||
}} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default BmLog
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我要报名',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
388
src/bszx/bm.tsx
388
src/bszx/bm.tsx
@@ -1,388 +0,0 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import Taro, {getCurrentInstance} from '@tarojs/taro'
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Input,
|
||||
Radio,
|
||||
SideNavBar,
|
||||
SubSideNavBar,
|
||||
SideNavBarItem
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {DictData} from "@/api/system/dict-data/model";
|
||||
import {Picker} from '@nutui/nutui-react-taro'
|
||||
import {pageDictData} from "@/api/system/dict-data";
|
||||
import {addBszxBm, myPageBszxBm} from "@/api/bszx/bszxBm";
|
||||
import {BszxBm} from "@/api/bszx/bszxBm/model";
|
||||
import {getBszxClassForTree} from "@/api/bszx/bszxClass";
|
||||
// import {User} from "@/api/system/user/model";
|
||||
// import Banner from "../pages/index/Banner";
|
||||
|
||||
const {router} = getCurrentInstance()
|
||||
const Bm = () => {
|
||||
const formId = Number(router?.params.id)
|
||||
|
||||
const [form] = Form.useForm()
|
||||
const [sex, setSex] = useState<DictData[]>()
|
||||
const [gradeName, setGradeName] = useState<string>('')
|
||||
const [className, setClassName] = useState<string>('')
|
||||
const [phone, setPhone] = useState<string>('')
|
||||
const [classList, setClassList] = useState<any[]>()
|
||||
const [gradeList, setGradeList] = useState<any[]>()
|
||||
const [present, setPresent] = useState<DictData[]>()
|
||||
const [isVisibleClass, setIsVisibleClass] = useState(false)
|
||||
const [isVisibleGrade, setIsVisibleGrade] = useState(false)
|
||||
const [FormData, setFormData] = useState<BszxBm>(
|
||||
{
|
||||
type: 0,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
phone: undefined,
|
||||
className: undefined,
|
||||
gradeName: undefined,
|
||||
address: undefined,
|
||||
workUnit: undefined,
|
||||
position: undefined,
|
||||
present: undefined,
|
||||
formId: undefined,
|
||||
comments: undefined
|
||||
}
|
||||
)
|
||||
|
||||
// 提交表单
|
||||
const submitSucceed = (values: any) => {
|
||||
addBszxBm({
|
||||
formId,
|
||||
name: values.name || FormData.name,
|
||||
sex: values.sex || FormData.sex,
|
||||
phone: phone,
|
||||
type: values.type || FormData.type,
|
||||
className: className || FormData.className,
|
||||
gradeName: gradeName || FormData.gradeName,
|
||||
address: values.address || FormData.address,
|
||||
workUnit: values.workUnit || FormData.workUnit,
|
||||
position: values.position || FormData.position,
|
||||
present: values.present ? '能' : '',
|
||||
comments: values.comments || FormData.comments
|
||||
}).then((data) => {
|
||||
if (data) {
|
||||
console.log(data,'data')
|
||||
Taro.showToast({title: `报名成功`, icon: 'success'})
|
||||
// setFormData();
|
||||
Taro.setStorageSync('NickName', values.name || FormData.name)
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({
|
||||
url: '/bszx/bm-cert/bm-cert'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}).catch(() => {
|
||||
Taro.showToast({
|
||||
title: '请勿重复报名',
|
||||
icon: 'error'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const submitFailed = (error: any) => {
|
||||
console.log(error, 'err...')
|
||||
// Taro.showToast({ title: error[0].message, icon: 'error' })
|
||||
}
|
||||
|
||||
const classConfirmPicker = (
|
||||
options: any[]
|
||||
) => {
|
||||
let description = ''
|
||||
options.forEach((option: any) => {
|
||||
description += ` ${option.text}`
|
||||
})
|
||||
setClassName(description)
|
||||
}
|
||||
const gradeNameConfirmPicker = (
|
||||
options: any[]
|
||||
) => {
|
||||
let description = ''
|
||||
options.forEach((option: any) => {
|
||||
description += ` ${option.text}`
|
||||
})
|
||||
setGradeName(description)
|
||||
}
|
||||
const changePicker = (options: any[], values: any, columnIndex: number) => {
|
||||
form.setFieldValue('className', values)
|
||||
console.log(options, columnIndex)
|
||||
}
|
||||
|
||||
const [navBarState, setNavBarState] = useState({
|
||||
visible: false
|
||||
})
|
||||
const changeNarBar = (visible) => {
|
||||
setNavBarState({
|
||||
visible
|
||||
})
|
||||
}
|
||||
|
||||
const onClassChange = (gradeName: string, className: string) => {
|
||||
console.log(gradeName,className)
|
||||
setGradeName(gradeName);
|
||||
setClassName(className);
|
||||
setFormData({
|
||||
...FormData,
|
||||
gradeName,
|
||||
className
|
||||
})
|
||||
setNavBarState({
|
||||
visible: false
|
||||
})
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
if (!Taro.getStorageSync('access_token')) {
|
||||
Taro.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录',
|
||||
showCancel: false,
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
Taro.switchTab({
|
||||
url: '/pages/user/user'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
myPageBszxBm().then(res => {
|
||||
const item = res.list[0];
|
||||
console.log(item, 'myPageBszxBm')
|
||||
if (item) {
|
||||
setFormData(item)
|
||||
if (item.gradeName) {
|
||||
setGradeName(item.gradeName)
|
||||
}
|
||||
if (item.className) {
|
||||
setClassName(item.className)
|
||||
}
|
||||
}
|
||||
})
|
||||
getBszxClassForTree().then(res => {
|
||||
console.log(res, 'setClassList');
|
||||
setClassList(res);
|
||||
})
|
||||
pageDictData({limit: 200}).then(res => {
|
||||
setSex(res?.list.filter((item) => item.dictCode === 'sex'))
|
||||
setPresent(res?.list.filter((item) => item.dictCode === 'present'))
|
||||
// setClassList([res?.list.filter((item) => item.dictCode === 'Class')])
|
||||
setGradeList(res?.list.filter((item) => {
|
||||
if (item.dictCode === 'Grade') {
|
||||
item.value = item.dictDataCode;
|
||||
item.text = item.dictDataName;
|
||||
return item
|
||||
}
|
||||
}))
|
||||
})
|
||||
setPhone(Taro.getStorageSync('Phone'))
|
||||
form.setFieldValue('phone', Taro.getStorageSync('Phone')) // 确保 form 已经初始化
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [form]) // 确保 form 已经初始化
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<Banner/>*/}
|
||||
{/*<div className={'fixed top-14 left-4 text-white'}>*/}
|
||||
{/* <image src={{ 'https://oss.wsdns.cn/20250224/5187dab5bc5047bda4521069696fd4dd.png' }} />*/}
|
||||
{/* <span>返回</span>*/}
|
||||
{/*</div>*/}
|
||||
<div className={'p-4'}>
|
||||
<Form
|
||||
divider
|
||||
initialValues={FormData}
|
||||
labelPosition="left"
|
||||
onFinish={(values) => submitSucceed(values)}
|
||||
onFinishFailed={(errors) => submitFailed(errors)}
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button nativeType="submit" block type="primary">
|
||||
提交
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="类型"
|
||||
name="type"
|
||||
style={{marginTop: '10px'}}
|
||||
required
|
||||
initialValue={FormData.type}
|
||||
>
|
||||
<Radio.Group defaultValue={FormData.type} direction="horizontal">
|
||||
<Radio value={0}>校友</Radio>
|
||||
<Radio value={1}>单位</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="姓名"
|
||||
name="name"
|
||||
required
|
||||
initialValue={FormData.name}
|
||||
rules={[{message: '请输入你的真实姓名'}]}
|
||||
>
|
||||
<Input placeholder="请输入姓名" type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="性别"
|
||||
name="sex"
|
||||
required
|
||||
initialValue={FormData.sex}
|
||||
rules={[
|
||||
{message: '请选择性别'}
|
||||
]}
|
||||
>
|
||||
<Radio.Group defaultValue={FormData.sex} direction="horizontal">
|
||||
{
|
||||
sex?.map((item, index) => (
|
||||
<Radio key={index} value={item.dictDataCode}>
|
||||
{item.dictDataName}
|
||||
</Radio>
|
||||
))
|
||||
}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="手机号码"
|
||||
name="phone"
|
||||
required
|
||||
initialValue={phone}
|
||||
rules={[{max: 11, message: '请输入手机号码'}]}
|
||||
>
|
||||
<Input placeholder="手机号码" maxLength={11} type="number"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="所在班级"
|
||||
name="className"
|
||||
required
|
||||
initialValue={FormData.className}
|
||||
rules={[{message: '请输入您的年级'}]}
|
||||
>
|
||||
<Button onClick={() => changeNarBar(true)}>{ className ? gradeName + ' ' + className : '请选择班级' }</Button>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="居住地"
|
||||
name="address"
|
||||
initialValue={FormData.address}
|
||||
rules={[{message: '请输入现居住地'}]}
|
||||
>
|
||||
<Input placeholder="请输入现居住地" type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="工作单位"
|
||||
name="workUnit"
|
||||
required
|
||||
initialValue={FormData.workUnit}
|
||||
rules={[{message: '请输入您就职工作单位'}]}
|
||||
>
|
||||
<Input placeholder="请输入您就职工作单位" type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="职务"
|
||||
name="position"
|
||||
required
|
||||
initialValue={FormData.position}
|
||||
rules={[{message: '请输入您的职务'}]}
|
||||
>
|
||||
<Input placeholder="请输入您的职务" type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="是否能到场"
|
||||
name="present"
|
||||
required
|
||||
rules={[{required: true, message: '请选择是否能到场'}]}
|
||||
>
|
||||
<Radio.Group direction="horizontal">
|
||||
{
|
||||
present?.map((item, index) => (
|
||||
<Radio key={index} value={item.dictDataCode}>
|
||||
{item.dictDataName}
|
||||
</Radio>
|
||||
))
|
||||
}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="备注信息"
|
||||
name="comments"
|
||||
initialValue={FormData.comments}
|
||||
rules={[{message: '备注信息'}]}
|
||||
>
|
||||
<Input placeholder="如找不到或忘记所在班级请备注一下" type="text"/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Picker
|
||||
visible={isVisibleClass}
|
||||
options={classList}
|
||||
onConfirm={(list) => classConfirmPicker(list)}
|
||||
defaultValue={[2]}
|
||||
threeDimensional={false}
|
||||
duration={1000}
|
||||
onClose={() => setIsVisibleClass(false)}
|
||||
onChange={changePicker}
|
||||
/>
|
||||
<Picker
|
||||
visible={isVisibleGrade}
|
||||
options={gradeList}
|
||||
onConfirm={(list) => gradeNameConfirmPicker(list)}
|
||||
defaultValue={[2]}
|
||||
threeDimensional={false}
|
||||
duration={1000}
|
||||
onClose={() => setIsVisibleGrade(false)}
|
||||
onChange={changePicker}
|
||||
/>
|
||||
<SideNavBar
|
||||
title="选择班级"
|
||||
visible={navBarState.visible}
|
||||
onClose={() => {
|
||||
changeNarBar(false)
|
||||
}}
|
||||
>
|
||||
{
|
||||
classList?.map((item) => {
|
||||
return (
|
||||
<SubSideNavBar title={item.name} open={false} value={item.id}>
|
||||
{
|
||||
item.children?.map((sub) => {
|
||||
return (
|
||||
<>
|
||||
<SubSideNavBar title={sub.name} open={false} value={item.id + '-' + sub.id}>
|
||||
{
|
||||
sub.children?.map((sub2) => {
|
||||
return (
|
||||
<SideNavBarItem title={sub2.name} value={item.id + '-' + sub.id + '-' + sub2.id} onClick={() => {
|
||||
onClassChange(sub.name,sub2.name)
|
||||
}}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</SubSideNavBar>
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
</SubSideNavBar>
|
||||
);
|
||||
})
|
||||
}
|
||||
</SideNavBar>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bm
|
||||
@@ -1,13 +0,0 @@
|
||||
.cert-bg{
|
||||
width: 720px;
|
||||
height: 1320px;
|
||||
margin: 15px auto;
|
||||
background: url("https://oss.wsdns.cn/20250127/cb1088c3b1354a118477a0b1a3cdac41.png") no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
.cert-content{
|
||||
width: 480px;
|
||||
height: 720px;
|
||||
font-size: 24px;
|
||||
padding-top: 450px;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '捐款凭证',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,39 +0,0 @@
|
||||
import './pay-cert.scss'
|
||||
import {useEffect, useState} from "react";
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import dayjs from 'dayjs'
|
||||
import {getBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
function CertQuery() {
|
||||
const {params} = useRouter();
|
||||
const [nickName, setNickName] = useState<string>()
|
||||
const [payLog, setPayLog] = useState<BszxPay>()
|
||||
|
||||
const reload = () => {
|
||||
if (params.id) {
|
||||
getBszxPay(Number(params.id)).then(res => {
|
||||
if(res){
|
||||
setNickName(res.name)
|
||||
setPayLog(res);
|
||||
console.log(nickName)
|
||||
console.log(res,'log.....')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, []);
|
||||
return (
|
||||
<div className={'cert-bg flex justify-center'}>
|
||||
<div className={'cert-content'}>
|
||||
<p className={'py-2'}>亲爱的 { payLog?.name || '匿名'} :</p>
|
||||
感谢您支持捐赠百色市百色中学温暖校园项目!您捐赠金额:{payLog?.price || 0}元,单号:{payLog?.id},时间:{payLog?.createTime}。对您的奉献表示诚挚的敬意,特此证明!
|
||||
<p className={'mt-2 text-right'}>百色市百色中学</p>
|
||||
<p className={'text-right'}>{dayjs(payLog?.createTime).format('YYYY-MM-DD')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CertQuery;
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '烟花特效',
|
||||
navigationBarBackgroundColor: '#000000'
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
.fireworks-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 40%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.firework {
|
||||
position: absolute;
|
||||
left: var(--x);
|
||||
top: var(--y);
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
transform-origin: center;
|
||||
animation: explode 1.5s ease-out forwards;
|
||||
opacity: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
animation: particle-glow 1.5s ease-out forwards;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes explode {
|
||||
0% {
|
||||
transform: rotate(var(--angle)) translateX(0) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: rotate(var(--angle)) translateX(100px) scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes particle-glow {
|
||||
0% {
|
||||
filter: brightness(1) blur(0);
|
||||
}
|
||||
100% {
|
||||
filter: brightness(2) blur(3px);
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import './flash.scss'
|
||||
import {CmsAd} from "@/api/cms/cmsAd/model";
|
||||
import {getCmsAd} from "@/api/cms/cmsAd";
|
||||
import { Swiper } from '@nutui/nutui-react-taro'
|
||||
|
||||
interface Firework {
|
||||
id: number
|
||||
x: number
|
||||
y: number
|
||||
color: string
|
||||
}
|
||||
|
||||
const MyPage = () => {
|
||||
const [item, setItem] = useState<CmsAd>()
|
||||
const reload = () => {
|
||||
getCmsAd(366).then(data => {
|
||||
setItem(data)
|
||||
})
|
||||
}
|
||||
const [fireworks, setFireworks] = useState<Firework[]>([])
|
||||
|
||||
// 生成随机颜色
|
||||
const getRandomColor = () => {
|
||||
const colors = [
|
||||
'#ff0000', '#00ff00', '#0000ff', '#ffff00',
|
||||
'#ff00ff', '#00ffff', '#ff9900', '#ff0099'
|
||||
]
|
||||
return colors[Math.floor(Math.random() * colors.length)]
|
||||
}
|
||||
|
||||
// 创建新烟花
|
||||
const createFirework = () => {
|
||||
const firework: Firework = {
|
||||
id: Date.now(),
|
||||
x: Math.random() * 100, // 随机横向位置 (0-100%)
|
||||
y: Math.random() * 40, // 修改为随机纵向位置 (0-40%)
|
||||
color: getRandomColor()
|
||||
}
|
||||
setFireworks(prev => [...prev, firework])
|
||||
|
||||
// 1.5秒后移除烟花
|
||||
setTimeout(() => {
|
||||
setFireworks(prev => prev.filter(f => f.id !== firework.id))
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
// 初始创建更多烟花
|
||||
for (let i = 0; i < 6; i++) { // 修改初始烟花数量为6个
|
||||
createFirework()
|
||||
}
|
||||
|
||||
// 缩短创建间隔,增加密度
|
||||
const interval = setInterval(() => {
|
||||
createFirework()
|
||||
}, 500) // 修改为500ms创建一次
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Swiper defaultValue={0} height={279} indicator style={{ height: '280px' }}>
|
||||
{item?.imageList?.map((item) => (
|
||||
<Swiper.Item key={item}>
|
||||
<img width="100%" height="100%" src={item.url} alt="" style={{ height: '280px' }} />
|
||||
</Swiper.Item>
|
||||
))}
|
||||
</Swiper>
|
||||
<div className="fireworks-container">
|
||||
{fireworks.map(firework => (
|
||||
<div
|
||||
key={firework.id}
|
||||
className="firework"
|
||||
style={{
|
||||
'--x': `${firework.x}%`,
|
||||
'--y': `${firework.y}%`,
|
||||
'--color': firework.color
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{/* 生成30个粒子 */}
|
||||
{Array.from({ length: 30 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="particle"
|
||||
style={{
|
||||
'--angle': `${(i * 12)}deg`,
|
||||
'--delay': `${Math.random() * 0.2}s`
|
||||
} as React.CSSProperties}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyPage
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '文章详情',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
.content{
|
||||
padding: 32px;
|
||||
line-height: 2.4rem;
|
||||
}
|
||||
.content p{
|
||||
text-indent: 2rem;
|
||||
}
|
||||
|
||||
.nr-bg{
|
||||
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Tag} from '@nutui/nutui-react-taro'
|
||||
import {useRouter, useShareAppMessage, useShareTimeline} from '@tarojs/taro'
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import {Eye} from '@nutui/icons-react-taro'
|
||||
// 显示html富文本
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import './item.scss'
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticle} from "@/api/cms/cmsArticle";
|
||||
|
||||
function Item() {
|
||||
const {params} = useRouter();
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
// 浏览量
|
||||
const [views, setViews] = useState<number>()
|
||||
|
||||
const reload = () => {
|
||||
getCmsArticle(Number(params.id)).then(data => {
|
||||
if(data){
|
||||
data.content = wxParse(data.content)
|
||||
setItem(data)
|
||||
setViews(data.actualViews)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useShareTimeline(() => {
|
||||
return {
|
||||
title: item?.title,
|
||||
path: `/bszx/item?id=${item?.articleId}`
|
||||
};
|
||||
});
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: item?.title,
|
||||
path: `/bszx/item?id=${item?.articleId}`,
|
||||
success: function (res) {
|
||||
console.log('分享成功', res);
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log('分享失败', res);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={'bg-white nr-bg'}>
|
||||
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
|
||||
<div className={'flex justify-between px-3'}>
|
||||
<Tag type={'success'}>{item?.categoryName}</Tag>
|
||||
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
|
||||
</div>
|
||||
<View className={'content text-gray-700 text-sm'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Item
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '捐款凭证',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
.cert-bg{
|
||||
width: 750px;
|
||||
height: 100vh;
|
||||
margin: 0 auto;
|
||||
background: url("https://oss.wsdns.cn/20250127/cb1088c3b1354a118477a0b1a3cdac41.png") no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
.cert-content{
|
||||
width: 480px;
|
||||
height: 720px;
|
||||
font-size: 24px;
|
||||
padding-top: 450px;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import './pay-cert.scss'
|
||||
import {useEffect, useState} from "react";
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {getPayCert} from "@/api/bszx/bszxPay";
|
||||
import {ConfigProvider, Loading} from "@nutui/nutui-react-taro";
|
||||
|
||||
function PayCert() {
|
||||
const {params} = useRouter();
|
||||
const [poster, setPoster] = useState<string>('')
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
|
||||
const generatePayCert = () => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// setPoster("https://oss.wsdns.cn/20250304/8a9aac182ac94494a806c4bda5766fee.png")
|
||||
}
|
||||
// 保存到临时地址
|
||||
Taro.downloadFile({
|
||||
url: poster,
|
||||
success: function (res) {
|
||||
Taro.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
Taro.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
fail: function (err) {
|
||||
if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
if (params.id) {
|
||||
getPayCert(Number(params.id)).then(img => {
|
||||
console.log(img, 'img....')
|
||||
setPoster(`${img}`)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
},1000)
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// setPoster("https://oss.wsdns.cn/20250304/8a9aac182ac94494a806c4bda5766fee.png")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
{loading ?
|
||||
<div className={'bg-white w-full flex justify-center items-center'} style={{height: '100vh'}}>
|
||||
<ConfigProvider theme={{nutuiLoadingIconSize: '28px'}}>
|
||||
<Loading type="spinner">加载中</Loading>
|
||||
</ConfigProvider>
|
||||
</div> :
|
||||
|
||||
<div className={'flex justify-center'}>
|
||||
<img src={poster} width={'750rpx'} height={'1060rpx'}/>
|
||||
<div className={'fixed bottom-10'}>
|
||||
{/*<Button />*/}
|
||||
<button
|
||||
className={'item px-20 py-3 bg-yellow-300 text-black flex items-center gap-2 text-nowrap whitespace-nowrap'}
|
||||
onClick={() => {
|
||||
generatePayCert()
|
||||
}}>
|
||||
保存到相册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default PayCert;
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '捐款记录',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,104 +0,0 @@
|
||||
import {Avatar, Button, Cell, Space} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
import {myPageBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import dayjs from "dayjs";
|
||||
import navTo from "@/utils/common";
|
||||
|
||||
const InfiniteUlStyle: CSSProperties = {
|
||||
height: '80vh',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}
|
||||
function PayRecord() {
|
||||
const [list, setList] = useState<BszxPay[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const reload = async () => {
|
||||
myPageBszxPay({page}).then(res => {
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
const newList = list?.concat(res.list)
|
||||
setList(newList);
|
||||
setHasMore(true)
|
||||
} else {
|
||||
setHasMore(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1)
|
||||
reload();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setPage(2)
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-2'}>
|
||||
<Cell>
|
||||
<ul style={InfiniteUlStyle} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
hasMore={hasMore}
|
||||
onLoadMore={reloadMore}
|
||||
onScroll={() => {
|
||||
console.log('onScroll')
|
||||
}}
|
||||
onScrollToUpper={() => {
|
||||
console.log('onScrollToUpper')
|
||||
}}
|
||||
loadingText={
|
||||
<>
|
||||
加载中
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
<>
|
||||
没有更多了
|
||||
</>
|
||||
}
|
||||
>
|
||||
{list?.map(item => {
|
||||
return (
|
||||
<Cell style={{padding: '0'}}>
|
||||
<div className={'flex w-full justify-between items-center'}>
|
||||
<div className={'flex'}>
|
||||
<Space>
|
||||
<Avatar
|
||||
src={item.avatar}
|
||||
/>
|
||||
<div className={'flex flex-col'}>
|
||||
<div className={'real-name text-lg'}>
|
||||
{item.name || '匿名'}
|
||||
</div>
|
||||
<div style={{maxWidth: '200px'}} className={'text-gray-400'}>{item.formName},{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}</div>
|
||||
<div className={'text-green-600 my-1'}>心愿:{item.comments}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div className={'price'}>
|
||||
<span className={' text-red-500 text-xl text-center font-bold'}>¥{item.price}</span>
|
||||
<Button className={'whitespace-nowrap'} onClick={() => {
|
||||
navTo('/bszx/pay-cert/pay-cert?id=' + item.id, true)
|
||||
}}>
|
||||
查看证书
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
)
|
||||
})}
|
||||
</InfiniteLoading>
|
||||
</ul>
|
||||
</Cell>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PayRecord
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '善款明细',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,118 +0,0 @@
|
||||
import {Avatar, Cell, Space} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
import {getCount, pageBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const InfiniteUlStyle: CSSProperties = {
|
||||
height: '70vh',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}
|
||||
function PayRecord() {
|
||||
const [list, setList] = useState<BszxPay[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [totalMoney, setTotalMoney] = useState()
|
||||
const [numbers, setNumbers] = useState()
|
||||
const reload = async () => {
|
||||
pageBszxPay({page}).then(res => {
|
||||
let newList: BszxPay[] | undefined = []
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
newList = list?.concat(res.list)
|
||||
setHasMore(true)
|
||||
} else {
|
||||
newList = res?.list
|
||||
setHasMore(false)
|
||||
}
|
||||
setList(newList || []);
|
||||
})
|
||||
getCount().then(res => {
|
||||
setNumbers(res.numbers);
|
||||
setTotalMoney(res.totalMoney);
|
||||
})
|
||||
}
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1)
|
||||
reload();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setPage(2)
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-2'}>
|
||||
<Cell>
|
||||
<div className={'flex w-full text-center justify-around'}>
|
||||
<div className={'item py-1'}>
|
||||
<span className={'text-gray-400'}>已筹资金(元)</span>
|
||||
<span className={'text-xl py-1 font-bold'}>¥{totalMoney}元</span>
|
||||
</div>
|
||||
<div className={'item py-1'}>
|
||||
<span className={'text-gray-400'}>爱心人次</span>
|
||||
<span className={'text-xl py-1 font-bold'}>{numbers}次</span>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<ul style={InfiniteUlStyle} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
hasMore={hasMore}
|
||||
onLoadMore={reloadMore}
|
||||
onScroll={() => {
|
||||
console.log('onScroll')
|
||||
}}
|
||||
onScrollToUpper={() => {
|
||||
console.log('onScrollToUpper')
|
||||
}}
|
||||
loadingText={
|
||||
<>
|
||||
加载中
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
<>
|
||||
没有更多了
|
||||
</>
|
||||
}
|
||||
>
|
||||
{list?.map(item => {
|
||||
return (
|
||||
<Cell style={{padding: '0'}}>
|
||||
<div className={'flex w-full justify-between items-center'}>
|
||||
<div className={'flex'}>
|
||||
<Space>
|
||||
<Avatar
|
||||
src={item.avatar}
|
||||
/>
|
||||
<div className={'flex flex-col'}>
|
||||
<div className={'real-name text-lg'}>
|
||||
{item.name || '匿名'}
|
||||
</div>
|
||||
<div style={{maxWidth: '240px'}} className={'text-gray-400'}>{item.formName},{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}</div>
|
||||
<div className={'text-green-600 my-1'}>心愿:{item.comments}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div className={'price text-red-500 text-xl font-bold'}>
|
||||
¥{item.price}
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
)
|
||||
})}
|
||||
</InfiniteLoading>
|
||||
</ul>
|
||||
</Cell>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PayRecord
|
||||
@@ -1,6 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '捐款详情',
|
||||
enableShareTimeline: true,
|
||||
enableShareAppMessage: true,
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
.content {
|
||||
padding: 32px;
|
||||
line-height: 2.4rem;
|
||||
}
|
||||
.content-bg{
|
||||
background-image: url("https://oss.wsdns.cn/20250224/7230c35b276f420a8527d08358759d8c.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
background-position: bottom;
|
||||
}
|
||||
|
||||
page{
|
||||
background-color: #fff2ee !important;
|
||||
}
|
||||
|
||||
.nr-bg{
|
||||
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Image, Tag} from '@nutui/nutui-react-taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {Divider} from '@nutui/nutui-react-taro'
|
||||
import dayjs from 'dayjs'
|
||||
import Taro, {
|
||||
useLoad,
|
||||
useShareAppMessage,
|
||||
useShareTimeline,
|
||||
} from '@tarojs/taro';
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import {Eye, Clock, PickedUp, Purse, Coupon} from '@nutui/icons-react-taro'
|
||||
import AddCartBar from "@/components/AddCartBar";
|
||||
// 显示html富文本
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import './detail.scss'
|
||||
import Line from "@/components/Gap";
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {pageBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {getUserInfo, getWxOpenId} from "@/api/layout";
|
||||
|
||||
function Detail() {
|
||||
const {params} = useRouter();
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
// 浏览量
|
||||
const [views, setViews] = useState<number>()
|
||||
// 报名人数
|
||||
const [bmUsers, setBmUsers] = useState<number>()
|
||||
// 是否登录
|
||||
const [isLogin, setIsLogin] = useState<boolean>(false)
|
||||
const reload = () => {
|
||||
getCmsArticle(Number(params.id)).then(data => {
|
||||
if (data) {
|
||||
data.content = wxParse(data.content)
|
||||
setItem(data)
|
||||
setViews(data.actualViews)
|
||||
}
|
||||
})
|
||||
pageBszxPay({
|
||||
formId: Number(params.id),
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
setBmUsers(res.count)
|
||||
}
|
||||
})
|
||||
// 登录
|
||||
Taro.getUserInfo({
|
||||
success: (res) => {
|
||||
console.log(res.userInfo.avatarUrl)
|
||||
getUserInfo().then((data) => {
|
||||
if (data) {
|
||||
setIsLogin(true);
|
||||
console.log(isLogin,'isLogin')
|
||||
Taro.setStorageSync('UserId', data.userId)
|
||||
// 获取openId
|
||||
if (!data.openid) {
|
||||
Taro.login({
|
||||
success: (res) => {
|
||||
getWxOpenId({code: res.code}).then(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
console.log('未登录')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useLoad(async () => {
|
||||
//获取进入页面参数 scene为1154===朋友圈进入
|
||||
const data = Taro.getLaunchOptionsSync();
|
||||
console.log(data, 'data')
|
||||
//开启分享
|
||||
Taro.showShareMenu({
|
||||
withShareTicket: true,
|
||||
});
|
||||
})
|
||||
|
||||
useShareTimeline(() => {
|
||||
return {
|
||||
title: item?.title,
|
||||
path: `/bszx/pay/detail?id=${item?.articleId}`,
|
||||
imageUrl: item?.image,
|
||||
};
|
||||
});
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: item?.title,
|
||||
path: `/bszx/pay/detail?id=${item?.articleId}`,
|
||||
imageUrl: item?.image,
|
||||
success: function (res) {
|
||||
console.log('分享成功', res);
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log('分享失败', res);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image src={item?.image} height={375}/>
|
||||
{/*linear-gradient(to bottom, #ffe0e0, #ffb6c1)*/}
|
||||
<div className={'pb-5 nr-bg'}>
|
||||
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
|
||||
<div className={'flex justify-between px-3'}>
|
||||
<Tag type={'success'}>{item?.categoryName}</Tag>
|
||||
<div className={'flex items-center gap-2 text-sm text-gray-500'}><Eye size={14}/>{views}</div>
|
||||
</div>
|
||||
<Divider/>
|
||||
{
|
||||
item?.model == 'bm' ? '' :
|
||||
<div>
|
||||
{
|
||||
!item?.endTime ? '' :
|
||||
<>
|
||||
<div className={'flex px-3 items-center gap-2'}>
|
||||
<Clock size={14}/>
|
||||
<div
|
||||
className={'text-sm font-bold'}>{dayjs(item?.startTime).format('YYYY-MM-DD')} 至 {dayjs(item?.endTime).format('YYYY-MM-DD')}</div>
|
||||
</div>
|
||||
<Divider/>
|
||||
</>
|
||||
}
|
||||
{
|
||||
!item?.price ? '' :
|
||||
<>
|
||||
<div className={'flex px-3 items-center gap-2'}>
|
||||
<Purse size={14}/>
|
||||
<div
|
||||
className={'text-sm font-bold text-orange-500'}>{item?.price == 0 ? '线下收费' : '¥' + item?.price}
|
||||
</div>
|
||||
</div>
|
||||
<Divider/>
|
||||
</>
|
||||
}
|
||||
<div className={'flex px-3 items-center gap-2'}>
|
||||
<PickedUp size={14}/>
|
||||
<div
|
||||
className={'text-sm font-bold'}>已捐款人数 {bmUsers}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<Divider/>
|
||||
<div className={'flex px-3 items-center gap-2'}>
|
||||
<Coupon size={14}/>
|
||||
<div
|
||||
className={'text-sm font-bold'}>活动详情
|
||||
</div>
|
||||
</div>
|
||||
<View className={'content text-gray-700 text-sm relative content-bg'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
<Line height={44}/>
|
||||
</div>
|
||||
<AddCartBar/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Detail
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我要捐款',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,212 +0,0 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import Taro, {getCurrentInstance} from '@tarojs/taro'
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Input,
|
||||
RadioGroup,
|
||||
Radio
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {navigateTo} from '@tarojs/taro'
|
||||
import {addBszxPay,myPageBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
import {TenantId} from "@/utils/config";
|
||||
import {myPageBszxBm} from "@/api/bszx/bszxBm";
|
||||
|
||||
const Bm = () => {
|
||||
const { router } = getCurrentInstance();
|
||||
const [formId, setFormId] = useState<number>()
|
||||
const [gradeName, setGradeName] = useState('')
|
||||
const [className, setClassName] = useState('')
|
||||
const [FormData, setFormData] = useState<BszxPay>(
|
||||
{
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
phone: undefined,
|
||||
className: undefined,
|
||||
gradeName: undefined,
|
||||
address: undefined,
|
||||
workUnit: undefined,
|
||||
position: undefined,
|
||||
present: undefined,
|
||||
formId: undefined
|
||||
}
|
||||
)
|
||||
|
||||
// 提交表单
|
||||
const submitSucceed = (values: any) => {
|
||||
// if(values.price < 10){
|
||||
// Taro.showToast({
|
||||
// title: '捐款金额不能低于10元',
|
||||
// icon: 'none'
|
||||
// })
|
||||
// return false;
|
||||
// }
|
||||
Taro.request({
|
||||
url: 'https://cms-api.websoft.top/api/shop/shop-order',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
'Authorization': Taro.getStorageSync('access_token'),
|
||||
TenantId
|
||||
},
|
||||
data: {
|
||||
totalPrice: values.price,
|
||||
payPrice: values.price,
|
||||
tenantId: TenantId,
|
||||
payType: 1,
|
||||
comments: values.comments,
|
||||
name: values.name || FormData.name,
|
||||
sex: values.sex || FormData.sex,
|
||||
phone: values.phone || FormData.phone,
|
||||
className: className || FormData.className,
|
||||
gradeName: gradeName || FormData.gradeName,
|
||||
address: values.address || FormData.address,
|
||||
workUnit: values.workUnit || FormData.workUnit,
|
||||
position: values.position || FormData.position,
|
||||
formId
|
||||
},
|
||||
success: function (res) {
|
||||
const data = res.data.data
|
||||
if(data){
|
||||
Taro.requestPayment({
|
||||
timeStamp: data.timeStamp,
|
||||
nonceStr: data.nonceStr,
|
||||
package: data.package,
|
||||
signType: data.signType,
|
||||
paySign: data.paySign,
|
||||
success: function (res) {
|
||||
if(res.errMsg == "requestPayment:ok"){
|
||||
console.log('捐款成功')
|
||||
addBszxPay({
|
||||
formId,
|
||||
orderNo: data.orderNo,
|
||||
name: values.name || FormData.name,
|
||||
sex: values.sex || FormData.sex,
|
||||
phone: values.phone || FormData.phone,
|
||||
className: className || FormData.className,
|
||||
gradeName: gradeName || FormData.gradeName,
|
||||
address: values.address || FormData.address,
|
||||
workUnit: values.workUnit || FormData.workUnit,
|
||||
position: values.position || FormData.position,
|
||||
present: values.present === '能',
|
||||
price: values.price || FormData.price,
|
||||
comments: values.comments,
|
||||
}).then((data) => {
|
||||
console.log(data,'payRes')
|
||||
console.log('跳转证书页面')
|
||||
setTimeout(() => {
|
||||
navigateTo({url: `/bszx/pay-cert/pay-cert?id=${data?.id}&orderNo=${data?.orderNo}`});
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
console.log(err,'errr....')
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const submitFailed = (error: any) => {
|
||||
console.log(error,'err...')
|
||||
// Taro.showToast({ title: error[0].message, icon: 'error' })
|
||||
}
|
||||
const reload = () => {
|
||||
myPageBszxBm().then(res => {
|
||||
if(res.count == 0){
|
||||
Taro.showToast({
|
||||
title: '请先完善个人资料',
|
||||
icon: 'error'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({
|
||||
url: '/user/profile/profile'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
myPageBszxPay({limit: 1}).then(res => {
|
||||
const item = res.list[0];
|
||||
if(item){
|
||||
setFormData(item)
|
||||
if(item.gradeName){
|
||||
setGradeName(item.gradeName)
|
||||
}
|
||||
if(item.className){
|
||||
setClassName(item.className)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = router?.params.id as number | undefined;
|
||||
setFormId(id)
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'p-4'}>
|
||||
<Form
|
||||
divider
|
||||
initialValues={FormData}
|
||||
labelPosition="left"
|
||||
onFinish={(values) => submitSucceed(values)}
|
||||
onFinishFailed={(errors) => submitFailed(errors)}
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button nativeType="submit" block type="primary">
|
||||
提交
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
name="price"
|
||||
initialValue={FormData.price}
|
||||
rules={[{message: '请选您要择捐款的金额'}]}
|
||||
>
|
||||
<RadioGroup value={FormData.price} direction="horizontal">
|
||||
<Radio shape="button" value="100"><span className={'font-bold text-sm'}>100元</span></Radio>
|
||||
<Radio shape="button" value="200"><span className={'font-bold text-sm'}>200元</span></Radio>
|
||||
<Radio shape="button" value="300"><span className={'font-bold text-sm'}>300元</span></Radio>
|
||||
<Radio shape="button" value="500"><span className={'font-bold text-sm'}>500元</span></Radio>
|
||||
<Radio shape="button" value="1000"><span className={'font-bold text-sm'}>1000元</span></Radio>
|
||||
<Radio shape="button" value="2000"><span className={'font-bold text-sm'}>2000元</span></Radio>
|
||||
<Radio shape="button" value="3000"><span className={'font-bold text-sm'}>3000元</span></Radio>
|
||||
<Radio shape="button" value="5000"><span className={'font-bold text-sm'}>5000元</span></Radio>
|
||||
<Radio shape="button" value="10000"><span className={'font-bold text-sm'}>10000元</span></Radio>
|
||||
</RadioGroup>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="捐款金额"
|
||||
name="price"
|
||||
required
|
||||
rules={[{ required: true,message: '请输入捐款数额' }]}
|
||||
>
|
||||
<Input placeholder="请输入捐款数额" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="您的心愿"
|
||||
name="comments"
|
||||
rules={[{ message: '请填写您的心意' }]}
|
||||
>
|
||||
<Input placeholder="请填写您的心意" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bm
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '文章详情',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
.content{
|
||||
padding: 32px;
|
||||
line-height: 2.4rem;
|
||||
}
|
||||
|
||||
.nr-bg{
|
||||
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import {Eye} from '@nutui/icons-react-taro'
|
||||
// 显示html富文本
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import './pdf.scss'
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticle} from "@/api/cms/cmsArticle";
|
||||
|
||||
function Item() {
|
||||
const {params} = useRouter();
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
// 浏览量
|
||||
const [views, setViews] = useState<number>()
|
||||
|
||||
const reload = () => {
|
||||
getCmsArticle(Number(params.id)).then(data => {
|
||||
if(data){
|
||||
data.content = wxParse(data.content)
|
||||
setItem(data)
|
||||
setViews(data.actualViews)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={'bg-white nr-bg'}>
|
||||
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
|
||||
<div className={'flex justify-between px-3'}>
|
||||
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
|
||||
</div>
|
||||
<View className={'content text-gray-700 text-sm'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Item
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Avatar, Cell, Space, Tabs, Button, TabPane} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import dayjs from "dayjs";
|
||||
@@ -50,29 +51,55 @@ function OrderList(props: any) {
|
||||
const [tapIndex, setTapIndex] = useState<string | number>('0')
|
||||
|
||||
console.log(props.statusBarHeight, 'ppp')
|
||||
const reload = async () => {
|
||||
pageShopOrder({page}).then(res => {
|
||||
let newList: BszxPay[] | undefined = []
|
||||
|
||||
const getOrderStatusParams = (index: string | number) => {
|
||||
let params: { payStatus?: number; deliveryStatus?: number; orderStatus?: number } = {};
|
||||
switch (index) {
|
||||
case '1': // 待付款
|
||||
params.payStatus = 0;
|
||||
break;
|
||||
case '2': // 待发货
|
||||
params.payStatus = 1;
|
||||
params.deliveryStatus = 10;
|
||||
break;
|
||||
case '3': // 已收货
|
||||
params.deliveryStatus = 30;
|
||||
break;
|
||||
case '4': // 已完成
|
||||
params.orderStatus = 1;
|
||||
break;
|
||||
case '0': // 全部
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return params;
|
||||
};
|
||||
|
||||
const reload = async (resetPage = false) => {
|
||||
const currentPage = resetPage ? 1 : page;
|
||||
const params = getOrderStatusParams(tapIndex);
|
||||
pageShopOrder({ page: currentPage, ...params }).then(res => {
|
||||
let newList: ShopOrder[] | undefined = [];
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
newList = list?.concat(res.list)
|
||||
setHasMore(true)
|
||||
newList = resetPage ? res.list : list?.concat(res.list);
|
||||
setHasMore(true);
|
||||
} else {
|
||||
newList = res?.list
|
||||
setHasMore(false)
|
||||
newList = res?.list;
|
||||
setHasMore(false);
|
||||
}
|
||||
setList(newList || []);
|
||||
})
|
||||
}
|
||||
setPage(currentPage);
|
||||
});
|
||||
};
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1)
|
||||
setPage(page + 1);
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPage(2)
|
||||
reload()
|
||||
}, [])
|
||||
reload(true); // 首次加载或tab切换时重置页码
|
||||
}, [tapIndex]); // 监听tapIndex变化
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -116,12 +143,12 @@ function OrderList(props: any) {
|
||||
>
|
||||
{list?.map(item => {
|
||||
return (
|
||||
<Cell style={{padding: '16px'}}>
|
||||
<Cell style={{padding: '16px'}} onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
|
||||
<Space direction={'vertical'} className={'w-full flex flex-col'}>
|
||||
<div className={'order-no flex justify-between'}>
|
||||
<span className={'text-gray-700 font-bold text-sm'}
|
||||
onClick={() => copyText(`${item.orderNo}`)}>{item.orderNo}</span>
|
||||
<span className={'text-orange-500'}>待付款</span>
|
||||
onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</span>
|
||||
<span className={'text-orange-500'}>待付款</span> {/* 这里可以根据item.orderStatus显示不同的状态 */}
|
||||
</div>
|
||||
<div
|
||||
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import {useEffect} from "react";
|
||||
import {Image, Space} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
|
||||
const BestSellers = (props: any) => {
|
||||
const reload = () => {
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-2 mb-4'}>
|
||||
<div className={'flex flex-col justify-between items-center rounded-lg px-3'}>
|
||||
{props.data?.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={'flex bg-white rounded-lg w-full p-3 mb-3'}
|
||||
onClick={() => Taro.navigateTo({url: '/hjm/query?id=' + item.code})}>
|
||||
{ item.image && (
|
||||
<Image src={JSON.parse(item.image)[0].url} mode={'scaleToFill'}
|
||||
radius="10%" width="80" height="80"/>
|
||||
)}
|
||||
<div className={'mx-3 flex flex-col'}>
|
||||
<Space direction={'vertical'}>
|
||||
<div className={'car-no text-lg font-bold'}>{item.code}</div>
|
||||
<div className={'flex text-xs text-gray-500'}>快递公司:<span
|
||||
className={'text-gray-700'}>{item.parentOrganization}</span></div>
|
||||
<div className={'flex text-xs text-gray-500'}>所属站点:<span
|
||||
className={'text-gray-700'}>{item.organization}</span></div>
|
||||
<div className={'flex text-xs text-gray-500'}>保险状态:<span className={'text-green-600'}>{item.insuranceStatus}</span>
|
||||
</div>
|
||||
<div className={'flex text-xs text-gray-500'}>绑定操作员:<span
|
||||
className={'text-gray-700'}>{item.driver}</span></div>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div style={{height: '170px'}}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default BestSellers
|
||||
@@ -1,202 +0,0 @@
|
||||
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
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '一键报险'
|
||||
})
|
||||
@@ -1,549 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {
|
||||
Button,
|
||||
TextArea,
|
||||
Cell,
|
||||
Loading,
|
||||
Space
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Truck} from '@nutui/icons-react-taro'
|
||||
import {addHjmBxLog} from "@/api/hjm/hjmBxLog";
|
||||
import {pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {HjmBxLog} from "@/api/hjm/hjmBxLog/model";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||
import {CmsWebsiteField} from "@/api/cms/cmsWebsiteField/model";
|
||||
import {TenantId} from "@/utils/config";
|
||||
|
||||
// 图片数据接口
|
||||
interface UploadedImageData {
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键报险 - 添加报险记录页面
|
||||
*/
|
||||
function BxAdd() {
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [bxFiled, setBxFiled] = useState<CmsWebsiteField>()
|
||||
const [bxFiled2, setBxFiled2] = useState<CmsWebsiteField>()
|
||||
const [carInfo, setCarInfo] = useState<HjmCar | null>(null)
|
||||
const [fileList, setFileList] = useState<any[]>([]) // 图片文件列表
|
||||
const [lastSubmitTime, setLastSubmitTime] = useState<number>(0) // 最后提交时间
|
||||
const [formData, setFormData] = useState<HjmBxLog>({
|
||||
carId: undefined,
|
||||
accidentType: undefined,
|
||||
image: undefined,
|
||||
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 {
|
||||
listCmsWebsiteField({}).then(data => {
|
||||
const bxPhone = data.find(item => item.name === 'bxPhone');
|
||||
const bxPhone2 = data.find(item => item.name === 'bxPhone2');
|
||||
if (bxPhone) {
|
||||
setBxFiled(bxPhone);
|
||||
}
|
||||
if (bxPhone2) {
|
||||
setBxFiled2(bxPhone2);
|
||||
}
|
||||
})
|
||||
pageHjmCar({driverId: Taro.getStorageSync('UserId')}).then(res => {
|
||||
const car = res?.list[0];
|
||||
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 handleChooseImage = () => {
|
||||
if (fileList.length >= 5) {
|
||||
Taro.showToast({
|
||||
title: '最多只能上传5张图片',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
Taro.chooseImage({
|
||||
count: 5 - fileList.length, // 剩余可选择的数量
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
console.log('选择图片成功:', res)
|
||||
|
||||
// 逐个上传选中的图片
|
||||
res.tempFilePaths.forEach((filePath, index) => {
|
||||
uploadSingleImage(filePath, index)
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败:', err)
|
||||
Taro.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 上传单张图片
|
||||
const uploadSingleImage = (filePath: string, index: number) => {
|
||||
|
||||
Taro.uploadFile({
|
||||
url: 'https://server.gxwebsoft.com/api/oss/upload',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
TenantId
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
const data = JSON.parse(res.data);
|
||||
console.log('上传成功', data)
|
||||
if (data.code === 0) {
|
||||
// 更新文件列表
|
||||
const newFile = {
|
||||
name: `图片${Date.now()}_${index}`,
|
||||
url: data.data.url,
|
||||
status: 'success',
|
||||
message: '上传成功',
|
||||
type: 'image',
|
||||
uid: `${Date.now()}_${index}`,
|
||||
}
|
||||
|
||||
setFileList(prev => {
|
||||
const newList = [...prev, newFile]
|
||||
// 同时更新表单数据 - 使用JSON格式存储
|
||||
const imageData: UploadedImageData[] = newList.map(f => ({
|
||||
url: f.url
|
||||
}))
|
||||
setFormData(prevForm => ({
|
||||
...prevForm,
|
||||
image: JSON.stringify(imageData)
|
||||
}))
|
||||
return newList
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '上传成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: data.message || '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析响应失败:', error)
|
||||
Taro.showToast({
|
||||
title: '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('上传请求失败', err);
|
||||
Taro.showToast({
|
||||
title: '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理文件删除
|
||||
const handleFileRemove = (file: any) => {
|
||||
console.log('删除文件:', file)
|
||||
const newFileList = fileList.filter(f => f.uid !== file.uid)
|
||||
setFileList(newFileList)
|
||||
|
||||
// 更新表单数据 - 使用JSON格式存储
|
||||
if (newFileList.length === 0) {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
image: undefined
|
||||
}))
|
||||
} else {
|
||||
const imageData: UploadedImageData[] = newFileList.map(f => ({
|
||||
url: f.url
|
||||
}))
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
image: JSON.stringify(imageData)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 防止重复提交 - 检查loading状态
|
||||
if (loading) {
|
||||
Taro.showToast({
|
||||
title: '正在提交中,请稍候...',
|
||||
icon: 'loading'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 防止快速连续点击 - 2秒内不允许重复提交
|
||||
const now = Date.now()
|
||||
if (now - lastSubmitTime < 2000) {
|
||||
Taro.showToast({
|
||||
title: '请勿频繁提交',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
setLastSubmitTime(now)
|
||||
|
||||
// 表单验证
|
||||
if (!formData.carId) {
|
||||
Taro.showToast({
|
||||
title: '请选择车辆',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!accidentType) {
|
||||
Taro.showToast({
|
||||
title: '请选择事故类型',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.image || fileList.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '请上传现场照片',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
// 构建提交数据
|
||||
const submitData: HjmBxLog = {
|
||||
...formData,
|
||||
accidentType: accidentType,
|
||||
comments: `${accidentDescription || '无'}`
|
||||
}
|
||||
|
||||
console.log('提交的图片数据:', formData.image)
|
||||
console.log('完整提交数据:', submitData)
|
||||
|
||||
// 解析JSON格式的图片数据示例
|
||||
if (formData.image) {
|
||||
try {
|
||||
const parsedImages: UploadedImageData[] = JSON.parse(formData.image)
|
||||
console.log('解析后的图片数据:', parsedImages)
|
||||
console.log('图片数量:', parsedImages.length)
|
||||
parsedImages.forEach((img, index) => {
|
||||
console.log(`图片${index + 1}:`, {
|
||||
url: img.url
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('解析图片数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
await addHjmBxLog(submitData)
|
||||
|
||||
Taro.showToast({
|
||||
title: '报险提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
formData.image = ''
|
||||
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
}, 2000)
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
Taro.showToast({
|
||||
title: '提交失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initPageData().then(r => {
|
||||
console.log(r,'rr')
|
||||
})
|
||||
}, [])
|
||||
|
||||
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'
|
||||
}}>
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
margin: '16px',
|
||||
borderRadius: '12px',
|
||||
padding: '16px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
|
||||
}}>
|
||||
{bxFiled && (<div>{bxFiled.comments}:{bxFiled.value}</div>)}
|
||||
{bxFiled2 && (<div>{bxFiled2.comments}:{bxFiled2.value}</div>)}
|
||||
</div>
|
||||
{/* 车辆信息卡片 */}
|
||||
{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">
|
||||
<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: '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'}}>*必填(最多5张)</span>
|
||||
{fileList.length > 0 && (
|
||||
<span style={{color: '#52c41a', fontSize: '12px', marginLeft: '8px'}}>
|
||||
已上传{fileList.length}张
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '12px'
|
||||
}}>
|
||||
{/* 显示已上传的图片 */}
|
||||
{fileList.map((file) => (
|
||||
<div key={file.uid} style={{
|
||||
position: 'relative',
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
border: '1px solid #d9d9d9'
|
||||
}}>
|
||||
<img
|
||||
src={file.url}
|
||||
alt={file.name}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="default"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-8px',
|
||||
right: '-8px',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
borderRadius: '12px',
|
||||
fontSize: '12px',
|
||||
minWidth: '24px',
|
||||
padding: 0
|
||||
}}
|
||||
onClick={() => handleFileRemove(file)}
|
||||
>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 添加图片按钮 */}
|
||||
{fileList.length < 5 && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleChooseImage}
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '2px dashed #d9d9d9',
|
||||
backgroundColor: '#fafafa'
|
||||
}}
|
||||
>
|
||||
<span style={{fontSize: '24px', color: '#d9d9d9'}}>+</span>
|
||||
<span style={{fontSize: '12px', marginTop: '4px', color: '#666'}}>
|
||||
添加图片
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</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.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={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: '#fff',
|
||||
padding: '16px',
|
||||
borderTop: '1px solid #f0f0f0'
|
||||
}}>
|
||||
<Button
|
||||
type="primary"
|
||||
block
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{loading ? '提交中...' : '提交报险申请'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BxAdd
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '报险记录'
|
||||
})
|
||||
@@ -1,292 +0,0 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {
|
||||
Loading,
|
||||
Empty,
|
||||
Button,
|
||||
Input,
|
||||
Tag,
|
||||
Image,
|
||||
Space
|
||||
} 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 Bx: React.FC = () => {
|
||||
const [list, setList] = useState<HjmBxLog[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
console.log(refreshing)
|
||||
// 获取状态显示
|
||||
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(),
|
||||
userId: Taro.getStorageSync('UserId')
|
||||
})
|
||||
|
||||
setList(res?.list.map(d => {
|
||||
console.log(d,'ddd')
|
||||
d.image = JSON.parse(d.image);
|
||||
console.log(d)
|
||||
return d;
|
||||
}) || [])
|
||||
} 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().then()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 搜索栏 */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '20px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: "none",
|
||||
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: '10px',
|
||||
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">
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}>
|
||||
<Truck size={14} color="#8c8c8c"/>
|
||||
<span style={{fontSize: '13px', color: '#8c8c8c'}}>
|
||||
车辆编号:{item.carNo}
|
||||
</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'}} className={'flex gap-2'}>
|
||||
{item.image.map((image) => (
|
||||
<Image
|
||||
src={image.url}
|
||||
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"*/}
|
||||
{/* size="large"*/}
|
||||
{/* onClick={onAddInsurance}*/}
|
||||
{/* >*/}
|
||||
{/* 一键报险*/}
|
||||
{/* </Button>*/}
|
||||
{/*</div>*/}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bx
|
||||
@@ -1,131 +0,0 @@
|
||||
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
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '开始考试'
|
||||
})
|
||||
@@ -1,608 +0,0 @@
|
||||
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
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '位置明细',
|
||||
navigationBarBackgroundColor: '#ffe0e0'
|
||||
})
|
||||
@@ -1,91 +0,0 @@
|
||||
import {Cell, Space} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {pageHjmGpsLog} from "@/api/hjm/hjmGpsLog";
|
||||
import {HjmGpsLog} from "@/api/hjm/hjmGpsLog/model";
|
||||
|
||||
const InfiniteUlStyle: CSSProperties = {
|
||||
height: '80vh',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}
|
||||
|
||||
function PayRecord() {
|
||||
const {params} = useRouter();
|
||||
const [list, setList] = useState<HjmGpsLog[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const reload = async () => {
|
||||
pageHjmGpsLog({page, gpsNo: params.id}).then(res => {
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
const newList = list?.concat(res.list)
|
||||
setList(newList);
|
||||
setHasMore(true)
|
||||
} else {
|
||||
setHasMore(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1)
|
||||
reload();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setPage(2)
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Cell>
|
||||
<ul style={InfiniteUlStyle} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
hasMore={hasMore}
|
||||
onLoadMore={reloadMore}
|
||||
onScroll={() => {
|
||||
console.log('onScroll')
|
||||
}}
|
||||
onScrollToUpper={() => {
|
||||
console.log('onScrollToUpper')
|
||||
}}
|
||||
loadingText={
|
||||
<>
|
||||
加载中
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
<>
|
||||
没有更多了
|
||||
</>
|
||||
}
|
||||
>
|
||||
{list?.map(item => {
|
||||
return (
|
||||
<Cell style={{padding: '0'}}>
|
||||
<div className={'flex w-full justify-between items-center'}>
|
||||
<div className={'flex'}>
|
||||
<Space>
|
||||
<div className={'flex flex-col'}>
|
||||
<div className={'real-name py-1'}>
|
||||
时间:{item.createTime}
|
||||
</div>
|
||||
<div className={'text-gray-400 py-1'}>速度:{item.speed} km/h,经纬度:{item.latitude},{item.longitude}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
)
|
||||
})}
|
||||
</InfiniteLoading>
|
||||
</ul>
|
||||
</Cell>
|
||||
)
|
||||
}
|
||||
|
||||
export default PayRecord
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '车辆列表'
|
||||
})
|
||||
@@ -1,89 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Search} from '@nutui/icons-react-taro'
|
||||
import {Button, Input, InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import {pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import Taro from '@tarojs/taro'
|
||||
import './location.scss'
|
||||
import BestSellers from "./BestSellers";
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
*/
|
||||
const List = () => {
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
const [list, setList] = useState<HjmCar[]>([])
|
||||
|
||||
const onKeywords = (keywords: string) => {
|
||||
setKeywords(keywords)
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
// 搜索条件
|
||||
const where = {status: 1,deleted: 0, keywords}
|
||||
// 判断身份
|
||||
const roleCode = Taro.getStorageSync('RoleCode');
|
||||
if(roleCode == 'kuaidiyuan'){
|
||||
// @ts-ignore
|
||||
where.driverId = Taro.getStorageSync('UserId')
|
||||
}
|
||||
if(roleCode == 'zhandian'){
|
||||
// @ts-ignore
|
||||
where.organizationId = Taro.getStorageSync('OrganizationId');
|
||||
}
|
||||
if(roleCode == 'kuaidi'){
|
||||
// @ts-ignore
|
||||
where.organizationParentId = Taro.getStorageSync('OrganizationParentId');
|
||||
}
|
||||
|
||||
// 获取车辆列表
|
||||
pageHjmCar(where).then(res => {
|
||||
setList(res?.list || [])
|
||||
})
|
||||
}
|
||||
|
||||
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}
|
||||
onConfirm={reload}
|
||||
/>
|
||||
<div
|
||||
className={'flex items-center'}
|
||||
>
|
||||
<Button type="warning" onClick={reload}>
|
||||
查询
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<InfiniteLoading
|
||||
className={'w-full fixed left-0 top-20'}
|
||||
>
|
||||
<BestSellers data={list}/>
|
||||
</InfiniteLoading>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default List
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '定位查询'
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
page {
|
||||
//background: url("https://oss.wsdns.cn/20250414/58cac36d806a40e298def726bcd9e44b.jpeg");
|
||||
//background-size: cover;
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Map} from '@tarojs/components'
|
||||
import {Search} from '@nutui/icons-react-taro'
|
||||
import {Button, Input} from '@nutui/nutui-react-taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {getHjmCarByCode} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import './location.scss'
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
*/
|
||||
const Location = () => {
|
||||
const {params} = useRouter();
|
||||
const [keywords, setKeywords] = useState<string>()
|
||||
const [item, setItem] = useState<HjmCar>()
|
||||
const [longitude, setLongitude] = useState<any>()
|
||||
const [latitude, setLatitude] = useState<any>()
|
||||
const [scale, setScale] = useState<any>(16)
|
||||
const [showCircles, setShowCircles] = useState<boolean>(false)
|
||||
const [points, setPoints] = useState<any[]>([])
|
||||
|
||||
const onKeywords = (keywords: string) => {
|
||||
setKeywords(keywords)
|
||||
}
|
||||
|
||||
// 通用的坐标字符串转数组函数
|
||||
const parseCoordinateString = (coordStr: string) => {
|
||||
if (!coordStr) return {points: []};
|
||||
|
||||
try {
|
||||
// 分割坐标点
|
||||
const coordPairs = coordStr.split(';');
|
||||
|
||||
// 转为多边形点数组
|
||||
const points = coordPairs.map(coord => {
|
||||
const [lat, lng] = coord.split(',');
|
||||
return {
|
||||
latitude: parseFloat(lat),
|
||||
longitude: parseFloat(lng)
|
||||
}
|
||||
});
|
||||
|
||||
return {points};
|
||||
} catch (error) {
|
||||
console.error('解析坐标字符串失败:', error);
|
||||
return {points: []};
|
||||
}
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
const code = params.id;
|
||||
setScale(14)
|
||||
// 执行搜索
|
||||
if (keywords) {
|
||||
getHjmCarByCode(keywords).then(data => {
|
||||
console.log('执行搜索', data)
|
||||
setItem(data)
|
||||
setLatitude(data.latitude)
|
||||
setLongitude(data.longitude)
|
||||
setKeywords(data.code)
|
||||
if (data.fence) {
|
||||
// 方法2:使用实际的 fence 数据(如果是字符串格式)
|
||||
const coordStr = data.fence.points || '';
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setPoints(points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
// 获取车辆信息
|
||||
if (code) {
|
||||
getHjmCarByCode(code).then(data => {
|
||||
setItem(data)
|
||||
setLatitude(data.latitude)
|
||||
setLongitude(data.longitude)
|
||||
setKeywords(data.code)
|
||||
if (data.fence) {
|
||||
// 方法2:使用实际的 fence 数据(如果是字符串格式)
|
||||
const coordStr = data.fence.points || '';
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setPoints(points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
|
||||
// pushSubscriptionMessages({
|
||||
// toUser: 'ozH7Z7VioC3_-5m_z0DfD9Rd7kM4',
|
||||
// templateId: 'oMckHaNgNT-ivInYF5DtCcqyd9O-i1hP_G0jQALsx54',
|
||||
// url: '/pages/index/index',
|
||||
// miniprogramState: 'trial',
|
||||
// data: {
|
||||
// phrase6: {
|
||||
// value: data?.driver ?? '未知',
|
||||
// color: '#333'
|
||||
// },
|
||||
// time4: {
|
||||
// value: data?.createTime ?? '未知',
|
||||
// color: '#333'
|
||||
// },
|
||||
// phrase7: {
|
||||
// value: '离开围栏',
|
||||
// color: '#333'
|
||||
// },
|
||||
// thing11: {
|
||||
// value: data?.fenceName ?? '未知',
|
||||
// color: '#333'
|
||||
// },
|
||||
// car_number12: {
|
||||
// value: data.code,
|
||||
// color: '#333'
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .then(res => {
|
||||
// console.log('订阅消息发送成功', res)
|
||||
// })
|
||||
// .catch(err => {
|
||||
// console.error('订阅消息发送失败', err)
|
||||
// })
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
// 监听圈圈数据变化
|
||||
useEffect(() => {
|
||||
}, [showCircles])
|
||||
|
||||
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 fixed bottom-0 left-0 w-full z-20 bg-white p-4'}>
|
||||
<div className={'car-info-item'}>
|
||||
<div className={'car-info-item-content'}>
|
||||
操作人员:{item?.driver}
|
||||
</div>
|
||||
<div className={'car-info-item-content pr-6'}>
|
||||
电子围栏:{item?.fenceName} ({item?.inFence ? '围栏内' : '围栏外'})
|
||||
</div>
|
||||
<div className={'car-info-item-content pr-6'}>
|
||||
当前车速:{item?.speed}
|
||||
</div>
|
||||
<div className={'car-info-item-title'}>
|
||||
更新时间:{item?.updateTime}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : ''}
|
||||
|
||||
<Map
|
||||
id="map"
|
||||
longitude={longitude}
|
||||
latitude={latitude}
|
||||
scale={scale}
|
||||
markers={[{
|
||||
id: 1,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
label: {
|
||||
content: `${item?.code}`,
|
||||
color: '#000000',
|
||||
fontSize: 12,
|
||||
borderRadius: 5,
|
||||
bgColor: '#FFFFFF',
|
||||
// @ts-ignore
|
||||
padding: '5px 5px',
|
||||
borderWidth: 1
|
||||
},
|
||||
}]}
|
||||
polygons={points.length > 0 ? [
|
||||
{
|
||||
points: points,
|
||||
color: '#ff0000',
|
||||
strokeWidth: 3
|
||||
}
|
||||
] : []}
|
||||
onTap={() => {
|
||||
console.log('map tap')
|
||||
}}
|
||||
style={{width: '100%', height: '100vh'}}
|
||||
/>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Location
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '车辆查询'
|
||||
})
|
||||
@@ -1,773 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro, {useRouter} from '@tarojs/taro'
|
||||
import {getHjmCarByCode, pageHjmCar, updateHjmCar} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import './location.scss';
|
||||
import {Swiper} from '@nutui/nutui-react-taro'
|
||||
import {copyText} from "@/utils/common";
|
||||
import {View} from '@tarojs/components'
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Input,
|
||||
Radio,
|
||||
Cell,
|
||||
Image
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {ImagePreview} from '@nutui/nutui-react-taro'
|
||||
import {Scan} from '@nutui/icons-react-taro'
|
||||
import {pageDictData} from "@/api/system/dict-data";
|
||||
import {DictData} from "@/api/system/dict-data/model";
|
||||
import {myUserVerify} from "@/api/system/userVerify";
|
||||
import {listUserRole} from "@/api/system/userRole";
|
||||
import {TenantId} from "@/utils/config";
|
||||
|
||||
// 图片数据接口
|
||||
interface UploadedImageData {
|
||||
url?: string;
|
||||
src?: string;
|
||||
name?: string;
|
||||
uid?: string;
|
||||
message?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
*/
|
||||
const Query = () => {
|
||||
const {params} = useRouter();
|
||||
const [keywords, setKeywords] = useState<string>()
|
||||
const [dict, setDict] = useState<DictData[]>([])
|
||||
const [claimVehicle, setClaimVehicle] = useState<boolean>(false)
|
||||
const [showPreview, setShowPreview] = useState(false)
|
||||
const [disabled, setDisabled] = useState<boolean>(false)
|
||||
const [fileList, setFileList] = useState<UploadedImageData[]>([]) // 图片文件列表
|
||||
const [FormData, setFormData] = useState<HjmCar>(
|
||||
{
|
||||
// 自增ID
|
||||
id: undefined,
|
||||
// 车辆名称
|
||||
name: undefined,
|
||||
// 车辆图片
|
||||
image: undefined,
|
||||
// 类型 0汽车 1其他车
|
||||
type: undefined,
|
||||
// 快递公司
|
||||
kuaidi: undefined,
|
||||
// 管理负责人
|
||||
kuaidiAdmin: undefined,
|
||||
organization: undefined,
|
||||
organizationParentId: undefined,
|
||||
parentOrganization: undefined,
|
||||
parentOrganizationAdmin: undefined,
|
||||
// 车辆编号
|
||||
code: undefined,
|
||||
// 操作员
|
||||
driver: undefined,
|
||||
// 保险状态
|
||||
insuranceStatus: undefined,
|
||||
// GPS设备编号
|
||||
gpsNo: undefined,
|
||||
// 电子围栏ID
|
||||
fenceId: undefined,
|
||||
// 电子围栏名称
|
||||
fenceName: undefined,
|
||||
// 电子围栏
|
||||
fence: undefined,
|
||||
// 位置
|
||||
location: undefined,
|
||||
// 经度
|
||||
longitude: undefined,
|
||||
// 纬度
|
||||
latitude: undefined,
|
||||
// 地址
|
||||
address: undefined,
|
||||
// 用户ID
|
||||
userId: undefined,
|
||||
// 排序(数字越小越靠前)
|
||||
sortNumber: undefined,
|
||||
// 备注
|
||||
comments: undefined,
|
||||
// 状态, 0正常, 1冻结
|
||||
status: undefined,
|
||||
// 是否删除, 0否, 1是
|
||||
deleted: undefined,
|
||||
// 租户id
|
||||
tenantId: undefined,
|
||||
// 创建时间
|
||||
createTime: undefined,
|
||||
// 更新时间
|
||||
updateTime: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
// 提交表单
|
||||
const submitSucceed = (values: any) => {
|
||||
// 禁用按钮
|
||||
if (disabled) {
|
||||
return false;
|
||||
}
|
||||
console.log(values)
|
||||
if (FormData.image == '[]' || !FormData.image) {
|
||||
Taro.showToast({
|
||||
title: '请上传车辆图片',
|
||||
icon: 'error'
|
||||
});
|
||||
return false
|
||||
}
|
||||
if (!FormData.gpsNo) {
|
||||
Taro.showToast({
|
||||
title: '请绑定GPS',
|
||||
icon: 'error'
|
||||
});
|
||||
return false
|
||||
}
|
||||
|
||||
// 安装车辆
|
||||
updateHjmCar({
|
||||
...FormData,
|
||||
status: 1
|
||||
}).then(() => {
|
||||
Taro.showToast({title: `安装成功`, icon: 'success'})
|
||||
setTimeout(() => {
|
||||
return Taro.navigateBack()
|
||||
}, 1000)
|
||||
}).catch((err) => {
|
||||
Taro.showToast({
|
||||
title: err.message,
|
||||
icon: 'none'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const submitFailed = (error: any) => {
|
||||
console.log(error, 'err...')
|
||||
}
|
||||
|
||||
const saveGpsNo = () => {
|
||||
Taro.scanCode({
|
||||
onlyFromCamera: true,
|
||||
scanType: ['barCode', 'qrCode'],
|
||||
success: (res) => {
|
||||
// 更新表单数据
|
||||
setFormData({
|
||||
...FormData,
|
||||
gpsNo: res.result
|
||||
});
|
||||
Taro.showToast({
|
||||
title: res.result,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('扫码失败', err);
|
||||
Taro.showToast({
|
||||
title: '扫码失败',
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 选择并上传图片
|
||||
const handleChooseImage = () => {
|
||||
if (disabled) {
|
||||
Taro.showToast({
|
||||
title: '您不是安装人员',
|
||||
icon: 'error'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (fileList.length >= 5) {
|
||||
Taro.showToast({
|
||||
title: '最多只能上传5张图片',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
Taro.chooseImage({
|
||||
count: 5 - fileList.length, // 剩余可选择的数量
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
console.log('选择图片成功:', res)
|
||||
|
||||
// 逐个上传选中的图片
|
||||
res.tempFilePaths.forEach((filePath, index) => {
|
||||
uploadSingleImage(filePath, index)
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败:', err)
|
||||
Taro.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 上传单张图片
|
||||
const uploadSingleImage = (filePath: any, index: number) => {
|
||||
|
||||
Taro.uploadFile({
|
||||
url: 'https://server.gxwebsoft.com/api/oss/upload',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
TenantId
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
const data = JSON.parse(res.data);
|
||||
console.log('上传成功', data)
|
||||
if (data.code === 0) {
|
||||
// 更新文件列表
|
||||
const newFile = {
|
||||
name: `图片${Date.now()}_${index}`,
|
||||
url: data.data.url,
|
||||
status: 'success',
|
||||
message: '上传成功',
|
||||
type: 'image',
|
||||
uid: `${Date.now()}_${index}`,
|
||||
}
|
||||
|
||||
setFileList(prev => {
|
||||
const newList = [...prev, newFile]
|
||||
// 同时更新表单数据 - 使用JSON格式存储
|
||||
const imageData: UploadedImageData[] = newList.map(f => ({
|
||||
url: f.url,
|
||||
name: f.name,
|
||||
uid: f.uid
|
||||
}))
|
||||
setFormData(prevForm => ({
|
||||
...prevForm,
|
||||
image: JSON.stringify(imageData)
|
||||
}))
|
||||
return newList
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '上传成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: data.message || '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析响应失败:', error)
|
||||
Taro.showToast({
|
||||
title: '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('上传请求失败', err);
|
||||
Taro.showToast({
|
||||
title: '上传失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理文件删除
|
||||
const handleFileRemove = (file: any) => {
|
||||
console.log('删除文件:', file)
|
||||
const newFileList = fileList.filter(f => f.uid !== file.uid)
|
||||
setFileList(newFileList)
|
||||
|
||||
// 更新表单数据 - 使用JSON格式存储
|
||||
if (newFileList.length === 0) {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
image: undefined
|
||||
}))
|
||||
} else {
|
||||
const imageData: UploadedImageData[] = newFileList.map(f => ({
|
||||
url: f.url,
|
||||
src: f.url,
|
||||
name: f.name,
|
||||
uid: f.uid
|
||||
}))
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
image: JSON.stringify(imageData)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// 认领车辆
|
||||
const onClaimVehicle = () => {
|
||||
updateHjmCar({
|
||||
...FormData,
|
||||
status: 2,
|
||||
driverId: Taro.getStorageSync('UserId'),
|
||||
driverName: Taro.getStorageSync('RealName')
|
||||
}).then(() => {
|
||||
Taro.showToast({title: `认领成功`, icon: 'success'})
|
||||
setTimeout(() => {
|
||||
return Taro.navigateBack()
|
||||
}, 1000)
|
||||
}).catch((err) => {
|
||||
Taro.showToast({
|
||||
title: err.message,
|
||||
icon: 'none'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
|
||||
// 1.判断是否登录
|
||||
if (!Taro.getStorageSync('UserId')) {
|
||||
Taro.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'error'
|
||||
})
|
||||
setDisabled(true);
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
}, 2000)
|
||||
return false
|
||||
}
|
||||
|
||||
// 2.获取数据字典
|
||||
const dict = await pageDictData({dictCode: 'InsuranceStatus'})
|
||||
setDict(dict?.list || [])
|
||||
|
||||
// 3.检查是否已实名
|
||||
const verify = await myUserVerify({status: 1})
|
||||
if (!verify) {
|
||||
Taro.showToast({
|
||||
title: '未实名认证',
|
||||
icon: 'error'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({
|
||||
url: '/user/userVerify/index'
|
||||
})
|
||||
}, 1000)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// 4.查询角色
|
||||
const role = await listUserRole({userId: Taro.getStorageSync('UserId')})
|
||||
const roleCode = role[0].roleCode;
|
||||
|
||||
// 5.获取车辆信息
|
||||
const code = params.id;
|
||||
if (code) {
|
||||
const carInfo = await getHjmCarByCode(code);
|
||||
if (carInfo) {
|
||||
// 赋值车辆信息
|
||||
setFormData(carInfo)
|
||||
setKeywords(carInfo.code)
|
||||
|
||||
if (carInfo.image) {
|
||||
try {
|
||||
const parsedImages: UploadedImageData[] = JSON.parse(carInfo.image)
|
||||
setFileList(parsedImages.map((img) => ({
|
||||
url: img.url,
|
||||
src: img.url
|
||||
})))
|
||||
} catch (error) {
|
||||
// 如果解析失败,可能是旧格式的单个URL
|
||||
if (carInfo.image && carInfo.image.trim()) {
|
||||
setFileList([{
|
||||
src: carInfo.image,
|
||||
url: carInfo.image,
|
||||
message: '上传成功',
|
||||
type: 'image',
|
||||
uid: `legacy_${Date.now()}`,
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1.符合条件则由安装人员安装车辆,否则提示无权限
|
||||
console.log(roleCode,'roleCode..')
|
||||
if (carInfo.status == 0 && roleCode != 'Installer') {
|
||||
Taro.setNavigationBarTitle({
|
||||
title: '安装设备'
|
||||
})
|
||||
Taro.showToast({
|
||||
title: '您不是安装人员',
|
||||
icon: 'error'
|
||||
})
|
||||
setDisabled(true)
|
||||
return false
|
||||
}
|
||||
// 2.如果已安装,则判断是否已认领车辆
|
||||
if (carInfo.status == 1 && roleCode == 'kuaidiyuan') {
|
||||
// 2.1先查询名下有多少辆车
|
||||
const carCount = await pageHjmCar({driverId: Taro.getStorageSync('UserId')})
|
||||
if (carCount?.count && carCount?.count == 0) {
|
||||
// 2.2无车辆则认领
|
||||
setClaimVehicle(true)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: '认领车辆'
|
||||
})
|
||||
} else {
|
||||
// 2.3存在车辆则展示车辆信息
|
||||
setClaimVehicle(false)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: '车辆信息'
|
||||
})
|
||||
if(Taro.getStorageSync('UserId') != carInfo.driverId){
|
||||
Taro.showToast({
|
||||
title: '暂无权限',
|
||||
icon: 'error'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
},2000)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行搜索
|
||||
if (keywords) {
|
||||
pageHjmCar({keywords}).then(res => {
|
||||
if (res?.list && res?.list?.length > 0) {
|
||||
const data = res?.list[0];
|
||||
// setFormData(data)
|
||||
setKeywords(data.code)
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
console.log('初始化完成')
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 未安装 */}
|
||||
{FormData?.status == 0 ? (
|
||||
<div className={'car-info w-full bg-white'}>
|
||||
<div className={'px-0'}>
|
||||
<Form
|
||||
divider
|
||||
initialValues={FormData}
|
||||
labelPosition="left"
|
||||
onFinish={(values) => submitSucceed(values)}
|
||||
onFinishFailed={(errors) => submitFailed(errors)}
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<Button nativeType="submit" block type="info" disabled={disabled}>
|
||||
提交
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label={'车辆编号'}
|
||||
name="code"
|
||||
rules={[{message: '请输入车辆编号'}]}
|
||||
>
|
||||
<View onClick={() => copyText(`${FormData?.code}`)}>{FormData?.code}</View>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={'快递公司品牌'}
|
||||
name="parentOrganization"
|
||||
rules={[{message: '快递公司品牌'}]}
|
||||
>
|
||||
<Input placeholder="快递公司品牌" disabled type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={'管理责任人'}
|
||||
name="parentOrganizationAdmin"
|
||||
rules={[{message: '管理责任人'}]}
|
||||
>
|
||||
<Input placeholder="管理责任人" disabled type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={'电子围栏'}
|
||||
name="fenceName"
|
||||
rules={[{message: '电子围栏'}]}
|
||||
>
|
||||
<Input placeholder="电子围栏" type="text"/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="保险状态"
|
||||
name="insuranceStatus"
|
||||
rules={[
|
||||
{message: '保险状态'}
|
||||
]}
|
||||
>
|
||||
<Radio.Group value={FormData.insuranceStatus} disabled direction="horizontal">
|
||||
{
|
||||
dict?.map((item, index) => (
|
||||
<Radio key={index} value={item.dictDataCode}>
|
||||
{item.dictDataName}
|
||||
</Radio>
|
||||
))
|
||||
}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={'GPS编号'}
|
||||
name="gpsNo"
|
||||
required
|
||||
rules={[{message: 'GPS编号'}]}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: '#fff',
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
placeholder="请填入GPS设备编号"
|
||||
value={FormData.gpsNo}
|
||||
disabled={disabled}
|
||||
onChange={(value) => setFormData({...FormData, gpsNo: value})}
|
||||
/>
|
||||
<div
|
||||
className="right"
|
||||
style={{display: 'flex', alignItems: 'center'}}
|
||||
>
|
||||
<Scan onClick={saveGpsNo}/>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={'拍照上传'}
|
||||
name="image"
|
||||
required
|
||||
rules={[{message: '请上传照片'}]}
|
||||
>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '12px',
|
||||
padding: '8px 0'
|
||||
}}>
|
||||
{/* 显示已上传的图片 */}
|
||||
{fileList.map((file) => (
|
||||
<div key={file.uid} style={{
|
||||
position: 'relative',
|
||||
width: '80px',
|
||||
height: '80px',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
border: '1px solid #d9d9d9'
|
||||
}}>
|
||||
<img
|
||||
src={file.url}
|
||||
alt={file.name}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="default"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-8px',
|
||||
right: '-8px',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '10px',
|
||||
fontSize: '12px',
|
||||
minWidth: '20px',
|
||||
padding: 0,
|
||||
lineHeight: '20px'
|
||||
}}
|
||||
onClick={() => handleFileRemove(file)}
|
||||
>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 添加图片按钮 */}
|
||||
{fileList.length < 5 && (
|
||||
<div
|
||||
onClick={handleChooseImage}
|
||||
style={{
|
||||
width: '80px',
|
||||
height: '80px',
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '2px dashed #d9d9d9',
|
||||
backgroundColor: '#fafafa',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<span style={{fontSize: '20px', color: '#d9d9d9'}}>+</span>
|
||||
<span style={{fontSize: '10px', marginTop: '2px', color: '#666'}}>
|
||||
添加图片
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 显示上传数量提示 */}
|
||||
{fileList.length > 0 && (
|
||||
<div style={{
|
||||
width: '100%',
|
||||
fontSize: '12px',
|
||||
color: '#52c41a',
|
||||
textAlign: 'center',
|
||||
marginTop: '4px'
|
||||
}}>
|
||||
已上传{fileList.length}张图片(最多5张)
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={'操作员'}
|
||||
name="driver"
|
||||
rules={[{message: '操作员'}]}
|
||||
>
|
||||
<Input placeholder="操作员" type="text"/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
) : ''}
|
||||
|
||||
{/* 已安装 */}
|
||||
{FormData?.status != 0 ? (
|
||||
<div className={'car-info w-full bg-white'}>
|
||||
{/* 显示多张图片 */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '8px',
|
||||
padding: '16px',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<ImagePreview
|
||||
autoPlay
|
||||
// @ts-ignore
|
||||
images={fileList}
|
||||
visible={showPreview}
|
||||
onClose={() => setShowPreview(false)}
|
||||
/>
|
||||
<Swiper defaultValue={1} autoPlay indicator>
|
||||
{fileList.map((item) => (
|
||||
// @ts-ignore
|
||||
<Swiper.Item key={item}>
|
||||
<Image
|
||||
width="100%"
|
||||
height="100%"
|
||||
mode={'aspectFit'}
|
||||
onClick={() => setShowPreview(true)}
|
||||
src={item.url}
|
||||
/>
|
||||
</Swiper.Item>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
<div className={'px-2'}>
|
||||
<Cell className={'car-info-item-title'} onClick={() => copyText(`${FormData?.code}`)}>
|
||||
车辆编号:{FormData?.code}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
快递公司品牌:{FormData?.parentOrganization}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
所属站点:{FormData?.organization}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-title'}>
|
||||
管理责任人:{FormData?.parentOrganizationAdmin}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
保险状态:{FormData?.insuranceStatus}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
GPS编号:{FormData?.gpsNo}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
电子围栏:{FormData.fenceName}
|
||||
</Cell>
|
||||
<Cell className={'car-info-item-content'}>
|
||||
操作员:{FormData.status == 2 ? FormData.driver : '-'}
|
||||
</Cell>
|
||||
{
|
||||
// 认领车辆
|
||||
claimVehicle && (
|
||||
<div className={'flex justify-around py-4'}>
|
||||
<Button nativeType="submit" type="danger" onClick={onClaimVehicle}>
|
||||
认领车辆
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
// 展示车辆信息
|
||||
!claimVehicle && !disabled && (
|
||||
<div className={'flex justify-around py-4'}>
|
||||
<Button nativeType="submit" type="info" onClick={
|
||||
() => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/location?id=${FormData?.code}`
|
||||
})
|
||||
}
|
||||
}>
|
||||
查看定位
|
||||
</Button>
|
||||
<Button nativeType="submit" type="warning" onClick={
|
||||
() => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/trajectory/trajectory?id=${FormData?.code}`
|
||||
})
|
||||
}
|
||||
}>
|
||||
播放轨迹
|
||||
</Button>
|
||||
<Button nativeType="submit" type="default" onClick={
|
||||
() => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/gps-log/gps-log?id=${FormData?.gpsNo}`
|
||||
})
|
||||
}
|
||||
}>
|
||||
位置信息
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
) : ''}
|
||||
</>
|
||||
)
|
||||
}
|
||||
// @ts-ignore
|
||||
export default Query
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '查看轨迹'
|
||||
})
|
||||
@@ -1,4 +0,0 @@
|
||||
page {
|
||||
//background: url("https://oss.wsdns.cn/20250414/58cac36d806a40e298def726bcd9e44b.jpeg");
|
||||
//background-size: cover;
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Map} from '@tarojs/components'
|
||||
import Taro, {useRouter} from '@tarojs/taro'
|
||||
import {DatePicker, Button, type PickerOption} from '@nutui/nutui-react-taro'
|
||||
import {getHjmCarByCode} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
import './trajectory.scss'
|
||||
import {pageHjmGpsLog} from "@/api/hjm/hjmGpsLog";
|
||||
import {formatCurrentDate, getCurrentHour} from "@/utils/time";
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
*/
|
||||
const Location = () => {
|
||||
const {params} = useRouter();
|
||||
const [keywords, setKeywords] = useState<string>()
|
||||
const [item, setItem] = useState<HjmCar>()
|
||||
const [longitude, setLongitude] = useState<any>()
|
||||
const [latitude, setLatitude] = useState<any>()
|
||||
const [scale, setScale] = useState<any>(16)
|
||||
const [showCircles, setShowCircles] = useState<boolean>(false)
|
||||
// const [points, setPoints] = useState<any[]>([])
|
||||
const [hjmGpsLog, setHjmGpsLog] = useState<any[]>([])
|
||||
const [ddmmyy, setDdmmyy] = useState<string>(formatCurrentDate())
|
||||
const [hhmmss, setHhmmss] = useState<string>(getCurrentHour())
|
||||
const [show1, setShow1] = useState(false)
|
||||
const defaultValue = new Date()
|
||||
const defaultDescription = `${defaultValue.getFullYear()}年${defaultValue.getMonth() + 1}月${defaultValue.getDate()}日`
|
||||
const [desc1, setDesc1] = useState(defaultDescription)
|
||||
// 选择时分秒
|
||||
const startDate = new Date(2025, 0, 1)
|
||||
const endDate = new Date(2030, 10, 1)
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
// 通用的坐标字符串转数组函数
|
||||
const parseCoordinateString = (coordStr: string) => {
|
||||
if (!coordStr) return {points: []};
|
||||
|
||||
try {
|
||||
// 分割坐标点
|
||||
const coordPairs = coordStr.split(';');
|
||||
|
||||
// 转为多边形点数组
|
||||
const points = coordPairs.map(coord => {
|
||||
const [lat, lng] = coord.split(',');
|
||||
return {
|
||||
latitude: parseFloat(lat),
|
||||
longitude: parseFloat(lng)
|
||||
}
|
||||
});
|
||||
|
||||
return {points};
|
||||
} catch (error) {
|
||||
console.error('解析坐标字符串失败:', error);
|
||||
return {points: []};
|
||||
}
|
||||
}
|
||||
|
||||
// const onKeywords = (keywords: string) => {
|
||||
// setKeywords(keywords)
|
||||
// }
|
||||
|
||||
// @ts-ignore
|
||||
const confirm = (values: any, options: PickerOption[]) => {
|
||||
// 截取字符串后2位
|
||||
console.log(`${String(values[2]).padStart(2, '0')}${String(values[1]).padStart(2, '0')}${String(values[0]).slice(-2)}`, 'options')
|
||||
setDdmmyy(`${String(values[2]).padStart(2, '0')}${String(values[1]).padStart(2, '0')}${String(values[0]).slice(-2)}`);
|
||||
setDesc1(options.map((option) => option.text).join(' '))
|
||||
// const v = values.join('-')
|
||||
// setValue(v)
|
||||
getLocationRecord(item)
|
||||
}
|
||||
|
||||
const onAfter = () => {
|
||||
setHhmmss(`${String(Number(hhmmss) + 1).padStart(2, '0')}`)
|
||||
getLocationRecord(item)
|
||||
}
|
||||
const onBefore = () => {
|
||||
setHhmmss(`${String(Number(hhmmss) - 1).padStart(2, '0')}`)
|
||||
getLocationRecord(item)
|
||||
}
|
||||
// const change = (options: PickerOption[], values: (string | number)[]) => {
|
||||
// // console.log(values,'values')
|
||||
// // 截取字符串后2位
|
||||
// // console.log(`${String(values[2]).padStart(2, '0')}${String(values[1]).padStart(2, '0')}${String(values[0]).slice(-2)}`,'options')
|
||||
// setDdmmyy(`${String(values[2]).padStart(2, '0')}${String(values[1]).padStart(2, '0')}${String(values[0]).slice(-2)}`);
|
||||
// // const v = values.join('-')
|
||||
// // if (v) {
|
||||
// // setValue(v)
|
||||
// // }
|
||||
// // setDesc1(options.map((option) => option.text).join(''))
|
||||
// }
|
||||
|
||||
const getLocationRecord = (data?: HjmCar) => {
|
||||
const where = {
|
||||
gpsNo: data?.gpsNo,
|
||||
ddmmyy: ddmmyy,
|
||||
hhmmss: hhmmss,
|
||||
limit: 200
|
||||
}
|
||||
Taro.showLoading({title: '加载中...'});
|
||||
pageHjmGpsLog(where).then(res => {
|
||||
console.log(res?.list, 'list')
|
||||
// setPoints(res?.list.map(item => {
|
||||
// console.log(item, 'item.')
|
||||
// return {
|
||||
// latitude: item.latitude,
|
||||
// longitude: item.longitude
|
||||
// }
|
||||
// }) || [])
|
||||
setHjmGpsLog(res?.list || []);
|
||||
}).finally(() => {
|
||||
Taro.hideLoading();
|
||||
})
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
const code = params.id;
|
||||
setScale(16)
|
||||
// 执行搜索
|
||||
if (keywords) {
|
||||
getHjmCarByCode(keywords).then(data => {
|
||||
console.log('执行搜索', data)
|
||||
setItem(data)
|
||||
setLatitude(data.latitude)
|
||||
setLongitude(data.longitude)
|
||||
setKeywords(data.code)
|
||||
// 获取车辆轨迹信息
|
||||
getLocationRecord(data);
|
||||
if (data.fence) {
|
||||
// 方法2:使用实际的 fence 数据(如果是字符串格式)
|
||||
const coordStr = data.fence.points || '';
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
})
|
||||
return false;
|
||||
}
|
||||
// 获取车辆信息
|
||||
if (code) {
|
||||
getHjmCarByCode(code).then(data => {
|
||||
setItem(data)
|
||||
setLatitude(data.latitude)
|
||||
setLongitude(data.longitude)
|
||||
setKeywords(data.code)
|
||||
// 获取车辆轨迹信息
|
||||
getLocationRecord(data);
|
||||
if (data.fence) {
|
||||
// 方法2:使用实际的 fence 数据(如果是字符串格式)
|
||||
const coordStr = data.fence.points || '';
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
// 监听圈圈数据变化
|
||||
useEffect(() => {
|
||||
}, [showCircles])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'fixed z-20 top-0 left-0 w-full'}>
|
||||
<div className={'px-0'}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: '#ffffff',
|
||||
borderTop: '1px solid #f7f7f7',
|
||||
borderBottom: '1px solid #f7f7f7',
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
title="日期选择"
|
||||
visible={show1}
|
||||
value={defaultValue}
|
||||
showChinese
|
||||
defaultValue={new Date(`${defaultDescription}`)}
|
||||
onClose={() => setShow1(false)}
|
||||
threeDimensional={false}
|
||||
onConfirm={(options, values) => confirm(values, options)}
|
||||
/>
|
||||
<div className={'flex w-full justify-between items-center p-2'}>
|
||||
<Button type="info" size={'mini'} onClick={onBefore}>
|
||||
前一小时
|
||||
</Button>
|
||||
<div className={'gap-1 flex'}>
|
||||
<span onClick={() => setShow1(true)}>{desc1}</span>
|
||||
<span onClick={() => setShow(true)}>{(Number(hhmmss) + 8) + ':00'}</span>
|
||||
</div>
|
||||
<Button type="info" size={'mini'} onClick={onAfter}>
|
||||
后一小时
|
||||
</Button>
|
||||
</div>
|
||||
<DatePicker
|
||||
title="时间选择"
|
||||
type="hour-minutes"
|
||||
defaultValue={new Date(`${defaultDescription}`)}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
visible={show}
|
||||
onClose={() => setShow(false)}
|
||||
// @ts-ignore
|
||||
onConfirm={(options, values) => confirm(options, values)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/*<Map polyline={{hjmGpsLog}}></Map>*/}
|
||||
<Map
|
||||
id="map"
|
||||
longitude={longitude}
|
||||
latitude={latitude}
|
||||
scale={scale}
|
||||
markers={[{
|
||||
id: 1,
|
||||
title: '车辆编号XXX',
|
||||
label: {
|
||||
content: `${item?.code}`,
|
||||
color: '#000000',
|
||||
fontSize: 12,
|
||||
borderRadius: 5,
|
||||
bgColor: '#FFFFFF',
|
||||
// @ts-ignore
|
||||
padding: '5px 5px',
|
||||
borderWidth: 1
|
||||
},
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
// @ts-ignore
|
||||
name: '位置'
|
||||
}]}
|
||||
polyline={[
|
||||
{
|
||||
points: hjmGpsLog,
|
||||
color: '#FF0000',
|
||||
width: 2,
|
||||
dottedLine: false
|
||||
}
|
||||
]}
|
||||
onTap={() => {
|
||||
console.log('map tap')
|
||||
}}
|
||||
style={{width: '100%', height: '100vh'}}
|
||||
/>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Location
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '观看视频'
|
||||
})
|
||||
@@ -1,69 +0,0 @@
|
||||
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
|
||||
@@ -2,9 +2,7 @@ import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
import {pageCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
import {checkMonthTaskCompleted} from "@/api/hjm/hjmExamLog";
|
||||
import {NavBar, Space} from '@nutui/nutui-react-taro'
|
||||
import {getWebsiteField} from "@/api/system/website/field";
|
||||
import {NavBar} from '@nutui/nutui-react-taro'
|
||||
import './find.scss'
|
||||
|
||||
/**
|
||||
@@ -13,26 +11,15 @@ import './find.scss'
|
||||
*/
|
||||
const Find = () => {
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>()
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false)
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [list, setList] = useState<CmsArticle[]>()
|
||||
const [monthTaskCompleted, setMonthTaskCompleted] = useState<boolean>(false)
|
||||
|
||||
const reload = async () => {
|
||||
setLoading(true)
|
||||
const field = await getWebsiteField(15524);
|
||||
if (field.value == '0') {
|
||||
setIsAdmin(true)
|
||||
}else {
|
||||
setIsAdmin(false)
|
||||
}
|
||||
const article = await pageCmsArticle({categoryId: 4289, status: 0})
|
||||
if(article){
|
||||
setList(article?.list)
|
||||
}
|
||||
const promise = await checkMonthTaskCompleted();
|
||||
if(promise){
|
||||
setMonthTaskCompleted(true)
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +36,7 @@ const Find = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && (<div>暂无数据</div>)}
|
||||
<NavBar
|
||||
fixed={true}
|
||||
style={{marginTop: `${statusBarHeight}px`, backgroundColor: 'transparent'}}
|
||||
@@ -69,6 +57,10 @@ const Find = () => {
|
||||
>
|
||||
<span>发现</span>
|
||||
</NavBar>
|
||||
{list && (
|
||||
<>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
.buy-btn{
|
||||
height: 70px;
|
||||
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
||||
border-radius: 100px;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
.cart-icon{
|
||||
background: linear-gradient(to bottom, #bbe094, #4ee265);
|
||||
border-radius: 100px 0 0 100px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,168 +6,6 @@ import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {pageShopGoods} from "@/api/shop/shopGoods";
|
||||
import './BestSellers.scss'
|
||||
|
||||
const goodsList = [
|
||||
{
|
||||
goodsId: 1,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 2,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 3,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 4,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 5,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 6,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 7,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 8,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 9,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 10,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 11,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 12,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 13,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 14,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 15,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 16,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 17,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 18,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 19,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
},
|
||||
{
|
||||
goodsId: 20,
|
||||
code: '123456',
|
||||
goodsName: '【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋',
|
||||
unitName: '测试单位',
|
||||
sales: '1.2万',
|
||||
image: 'https://oss.wsdns.cn/20250622/84f4a3e981584b1fb8e4b3898aa07cb7.jpg'
|
||||
}
|
||||
]
|
||||
|
||||
const BestSellers = () => {
|
||||
const [list, setList] = useState<ShopGoods[]>([])
|
||||
@@ -185,15 +23,15 @@ const BestSellers = () => {
|
||||
return (
|
||||
<div className={'py-0'}>
|
||||
<div className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
||||
{goodsList?.map((item, index) => {
|
||||
{list?.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}
|
||||
onClick={() => Taro.navigateTo({url: '/hjm/location?id=' + item.goodsId})}>
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>
|
||||
<Image src={item.image} mode={'scaleToFill'}
|
||||
radius="10px 10px 0 0" height="180"/>
|
||||
<div className={'flex flex-col p-2 rounded-lg'}>
|
||||
<div>
|
||||
<div className={'car-no text-sm'}>{item.goodsName} #{item.goodsId}</div>
|
||||
<div className={'car-no text-sm'}>{item.name} #{item.goodsId}</div>
|
||||
<div className={'flex justify-between text-xs py-1'}>
|
||||
<span className={'text-orange-500'}>丰江桥佛手瓜面,非油炸,Q弹爽口,营养丰</span>
|
||||
<span className={'text-gray-400'}>已售 {item.sales}</span>
|
||||
|
||||
4
src/shop/goodsDetail/index.config.ts
Normal file
4
src/shop/goodsDetail/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '商品详情',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
5
src/shop/goodsDetail/index.scss
Normal file
5
src/shop/goodsDetail/index.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.cart-icon{
|
||||
background: linear-gradient(to bottom, #bbe094, #4ee265);
|
||||
border-radius: 100px 0 0 100px;
|
||||
height: 70px;
|
||||
}
|
||||
73
src/shop/goodsDetail/index.tsx
Normal file
73
src/shop/goodsDetail/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Image, Space, Tag, Divider} from '@nutui/nutui-react-taro'
|
||||
import {Cart} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
||||
import './index.scss'
|
||||
|
||||
const GoodsDetail = () => {
|
||||
const [goods, setGoods] = useState<ShopGoods | null>(null);
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const goodsId = router?.params?.id;
|
||||
|
||||
useEffect(() => {
|
||||
if (goodsId) {
|
||||
getShopGoods(Number(goodsId)).then(res => {
|
||||
setGoods(res);
|
||||
}).catch(error => {
|
||||
console.error("Failed to fetch goods detail:", error);
|
||||
});
|
||||
}
|
||||
}, [goodsId]);
|
||||
|
||||
if (!goods) {
|
||||
return <div>加载中...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'py-0'}>
|
||||
<div className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
||||
<div className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
|
||||
<Image src={goods.image} mode={'scaleToFill'}
|
||||
radius="10px 10px 0 0" height="300"/>
|
||||
<div className={'flex flex-col p-2 rounded-lg'}>
|
||||
<div>
|
||||
<div className={'car-no text-sm'}>{goods.name} #{goods.goodsId}</div>
|
||||
<div className={'flex justify-between text-xs py-1'}>
|
||||
<span className={'text-orange-500'}>丰江桥佛手瓜面,非油炸,Q弹爽口,营养丰</span>
|
||||
<span className={'text-gray-400'}>已售 {goods.sales}</span>
|
||||
</div>
|
||||
<Space>
|
||||
<Tag plain round background={'#ff0000'}>会客厅循环劵<Divider direction={'vertical'} style={{
|
||||
borderStyle: 'dashed', padding: '0',
|
||||
borderColor: '#fd8989', margin: '0 5px'
|
||||
}}/>每拍2减10元</Tag>
|
||||
<Tag plain round background={'#ff0000'}>会客厅专享<Divider direction={'vertical'} style={{
|
||||
borderStyle: 'dashed', padding: '0',
|
||||
borderColor: '#fd8989', margin: '0 5px'
|
||||
}}/>拍1减3元</Tag>
|
||||
</Space>
|
||||
<div className={'flex justify-between items-center py-2'}>
|
||||
<div className={'flex text-red-500 text-xl items-baseline'}>
|
||||
<span className={'text-xs'}>¥</span>
|
||||
<span className={'font-bold text-2xl'}>{goods.price}</span>
|
||||
</div>
|
||||
<div className={'buy-btn'} onClick={() => Taro.navigateTo({url: '/shop/orderConfirm/index?goodsId=' + goods.goodsId})}>
|
||||
<div className={'cart-icon'}><Cart size={20} className={'mx-4 mt-2'} /></div>
|
||||
<div className={'text-white pl-4 pr-5'}>购买</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'py-2'}>
|
||||
<h3>商品详情</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: goods.content || '' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoodsDetail;
|
||||
@@ -1,118 +0,0 @@
|
||||
import {Avatar, Cell, Space} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {BszxPay} from "@/api/bszx/bszxPay/model";
|
||||
import {getCount, pageBszxPay} from "@/api/bszx/bszxPay";
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const InfiniteUlStyle: CSSProperties = {
|
||||
height: '70vh',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}
|
||||
function PayRecord() {
|
||||
const [list, setList] = useState<BszxPay[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [totalMoney, setTotalMoney] = useState()
|
||||
const [numbers, setNumbers] = useState()
|
||||
const reload = async () => {
|
||||
pageBszxPay({page}).then(res => {
|
||||
let newList: BszxPay[] | undefined = []
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
newList = list?.concat(res.list)
|
||||
setHasMore(true)
|
||||
} else {
|
||||
newList = res?.list
|
||||
setHasMore(false)
|
||||
}
|
||||
setList(newList || []);
|
||||
})
|
||||
getCount().then(res => {
|
||||
setNumbers(res.numbers);
|
||||
setTotalMoney(res.totalMoney);
|
||||
})
|
||||
}
|
||||
|
||||
const reloadMore = async () => {
|
||||
setPage(page + 1)
|
||||
reload();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setPage(2)
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-2'}>
|
||||
<Cell>
|
||||
<div className={'flex w-full text-center justify-around'}>
|
||||
<div className={'item py-1'}>
|
||||
<span className={'text-gray-400'}>已筹资金(元)</span>
|
||||
<span className={'text-xl py-1 font-bold'}>¥{totalMoney}元</span>
|
||||
</div>
|
||||
<div className={'item py-1'}>
|
||||
<span className={'text-gray-400'}>爱心人次</span>
|
||||
<span className={'text-xl py-1 font-bold'}>{numbers}次</span>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
<Cell>
|
||||
<ul style={InfiniteUlStyle} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
hasMore={hasMore}
|
||||
onLoadMore={reloadMore}
|
||||
onScroll={() => {
|
||||
console.log('onScroll')
|
||||
}}
|
||||
onScrollToUpper={() => {
|
||||
console.log('onScrollToUpper')
|
||||
}}
|
||||
loadingText={
|
||||
<>
|
||||
加载中
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
<>
|
||||
没有更多了
|
||||
</>
|
||||
}
|
||||
>
|
||||
{list?.map(item => {
|
||||
return (
|
||||
<Cell style={{padding: '0'}}>
|
||||
<div className={'flex w-full justify-between items-center'}>
|
||||
<div className={'flex'}>
|
||||
<Space>
|
||||
<Avatar
|
||||
src={item.avatar}
|
||||
/>
|
||||
<div className={'flex flex-col'}>
|
||||
<div className={'real-name text-lg'}>
|
||||
{item.name || '匿名'}
|
||||
</div>
|
||||
<div style={{maxWidth: '240px'}} className={'text-gray-400'}>{item.formName},{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}</div>
|
||||
<div className={'text-green-600 my-1'}>心愿:{item.comments}</div>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
<div className={'price text-red-500 text-xl font-bold'}>
|
||||
¥{item.price}
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
)
|
||||
})}
|
||||
</InfiniteLoading>
|
||||
</ul>
|
||||
</Cell>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PayRecord
|
||||
4
src/shop/orderConfirm/index.config.ts
Normal file
4
src/shop/orderConfirm/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '订单确认',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
27
src/shop/orderConfirm/index.scss
Normal file
27
src/shop/orderConfirm/index.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
.order-confirm-page {
|
||||
padding-bottom: 100px; // 留出底部固定按钮的空间
|
||||
|
||||
.fixed-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.total-price {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/shop/orderConfirm/index.tsx
Normal file
69
src/shop/orderConfirm/index.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Image, Button, Cell, CellGroup, Input, TextArea} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
||||
import './index.scss'
|
||||
|
||||
const OrderConfirm = () => {
|
||||
const [goods, setGoods] = useState<ShopGoods | null>(null);
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const goodsId = router?.params?.goodsId;
|
||||
|
||||
useEffect(() => {
|
||||
if (goodsId) {
|
||||
getShopGoods(Number(goodsId)).then(res => {
|
||||
setGoods(res);
|
||||
}).catch(error => {
|
||||
console.error("Failed to fetch goods detail:", error);
|
||||
});
|
||||
}
|
||||
}, [goodsId]);
|
||||
|
||||
if (!goods) {
|
||||
return <div>加载中...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'order-confirm-page'}>
|
||||
<CellGroup title="商品信息">
|
||||
<Cell>
|
||||
<div className={'flex items-center'}>
|
||||
<Image src={goods.image} width="80" height="80" />
|
||||
<div className={'ml-2'}>
|
||||
<div className={'text-sm font-bold'}>{goods.name}</div>
|
||||
<div className={'text-red-500 text-lg'}>¥{goods.price}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup title="收货信息">
|
||||
<Cell title="收货人">
|
||||
<Input placeholder="请输入收货人姓名" />
|
||||
</Cell>
|
||||
<Cell title="手机号">
|
||||
<Input placeholder="请输入手机号" type="tel" />
|
||||
</Cell>
|
||||
<Cell title="收货地址">
|
||||
<TextArea placeholder="请输入详细收货地址" />
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup title="订单备注">
|
||||
<Cell>
|
||||
<TextArea placeholder="请输入订单备注" />
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
<div className={'fixed-bottom'}>
|
||||
<div className={'total-price'}>
|
||||
合计:<span className={'text-red-500 text-xl font-bold'}>¥{goods.price}</span>
|
||||
</div>
|
||||
<Button type="primary" size="large" className={'submit-btn'}>提交订单</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderConfirm;
|
||||
4
src/shop/orderDetail/index.config.ts
Normal file
4
src/shop/orderDetail/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '订单详情',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
27
src/shop/orderDetail/index.scss
Normal file
27
src/shop/orderDetail/index.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
.order-detail-page {
|
||||
padding-bottom: 80px; // 留出底部固定按钮的空间
|
||||
|
||||
.nut-cell-group__title {
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.fixed-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.nut-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/shop/orderDetail/index.tsx
Normal file
128
src/shop/orderDetail/index.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {ShopOrder} from "@/api/shop/shopOrder/model";
|
||||
import {getShopOrder} from "@/api/shop/shopOrder";
|
||||
import {listOrderGoods} from "@/api/system/orderGoods";
|
||||
import {OrderGoods} from "@/api/system/orderGoods/model";
|
||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
||||
import dayjs from "dayjs";
|
||||
import './index.scss'
|
||||
|
||||
interface OrderGoodsDetail extends OrderGoods {
|
||||
goodsName?: string;
|
||||
goodsImage?: string;
|
||||
}
|
||||
|
||||
const OrderDetail = () => {
|
||||
const [order, setOrder] = useState<ShopOrder | null>(null);
|
||||
const [orderGoodsList, setOrderGoodsList] = useState<OrderGoodsDetail[]>([]);
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const orderId = router?.params?.orderId;
|
||||
|
||||
const getOrderStatusText = (orderStatus?: number, payStatus?: number) => {
|
||||
if (payStatus === 0) return '待付款';
|
||||
switch (orderStatus) {
|
||||
case 0: return '未使用';
|
||||
case 1: return '已完成';
|
||||
case 2: return '已取消';
|
||||
case 3: return '取消中';
|
||||
case 4: return '退款申请中';
|
||||
case 5: return '退款被拒绝';
|
||||
case 6: return '退款成功';
|
||||
case 7: return '客户端申请退款';
|
||||
default: return '未知状态';
|
||||
}
|
||||
};
|
||||
|
||||
const getPayTypeText = (payType?: number) => {
|
||||
switch (payType) {
|
||||
case 0: return '余额支付';
|
||||
case 1: return '微信支付';
|
||||
case 102: return '微信Native';
|
||||
case 2: return '会员卡支付';
|
||||
case 3: return '支付宝';
|
||||
case 4: return '现金';
|
||||
case 5: return 'POS机';
|
||||
default: return '未知支付方式';
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (orderId) {
|
||||
console.log('shop-goods',orderId)
|
||||
getShopOrder(Number(orderId)).then(async (res) => {
|
||||
setOrder(res);
|
||||
|
||||
// 获取订单商品列表
|
||||
const goodsRes = await listOrderGoods({ orderId: Number(orderId) });
|
||||
if (goodsRes && goodsRes.length > 0) {
|
||||
const goodsDetailsPromises = goodsRes.map(async (item) => {
|
||||
console.log(item,'item.>>>')
|
||||
const shopGoods = await getShopGoods(Number(item.goodsId));
|
||||
return {
|
||||
...item,
|
||||
goodsName: shopGoods?.name,
|
||||
goodsImage: shopGoods?.image,
|
||||
};
|
||||
});
|
||||
const detailedGoodsList = await Promise.all(goodsDetailsPromises);
|
||||
setOrderGoodsList(detailedGoodsList);
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("Failed to fetch order detail:", error);
|
||||
});
|
||||
}
|
||||
}, [orderId]);
|
||||
|
||||
if (!order) {
|
||||
return <div>加载中...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'order-detail-page'}>
|
||||
<CellGroup title="订单信息">
|
||||
<Cell title="订单编号" description={order.orderNo} />
|
||||
<Cell title="下单时间" description={dayjs(order.createTime).format('YYYY-MM-DD HH:mm:ss')} />
|
||||
<Cell title="订单状态" description={getOrderStatusText(order.orderStatus, order.payStatus)} />
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup title="商品信息">
|
||||
{orderGoodsList.map((item, index) => (
|
||||
<Cell key={index}>
|
||||
<div className={'flex items-center'}>
|
||||
<Image src={item.goodsImage} width="80" height="80" />
|
||||
<div className={'ml-2'}>
|
||||
<div className={'text-sm font-bold'}>{item.goodsName}</div>
|
||||
<div className={'text-gray-500 text-xs'}>数量:{item.totalNum}</div>
|
||||
<div className={'text-red-500 text-lg'}>¥{item.payPrice}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
))}
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup title="收货信息">
|
||||
<Cell title="收货人" description={order.realName} />
|
||||
<Cell title="手机号" description={order.phone} />
|
||||
<Cell title="收货地址" description={order.address} />
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup title="支付信息">
|
||||
<Cell title="支付方式" description={getPayTypeText(order.payType)} />
|
||||
<Cell title="实付金额" description={`¥${order.payPrice}`} />
|
||||
</CellGroup>
|
||||
|
||||
<div className={'fixed-bottom'}>
|
||||
<Space>
|
||||
{order.payStatus === 0 && <Button size="small" onClick={() => console.log('取消订单')}>取消订单</Button>}
|
||||
{order.payStatus === 0 && <Button size="small" type="primary" onClick={() => console.log('立即支付')}>立即支付</Button>}
|
||||
{order.orderStatus === 1 && <Button size="small" onClick={() => console.log('申请退款')}>申请退款</Button>}
|
||||
{order.deliveryStatus === 20 && <Button size="small" type="primary" onClick={() => console.log('确认收货')}>确认收货</Button>}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderDetail;
|
||||
Reference in New Issue
Block a user