删除无用代码

This commit is contained in:
2025-06-26 16:40:55 +08:00
parent d75fb55eec
commit cf1c69b6d6
74 changed files with 438 additions and 5506 deletions

View File

@@ -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',
{
'/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',
{
'/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;

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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;
}
}

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '邀请函',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '我的报名',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '我要报名',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -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;
}

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '捐款凭证',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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;

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '烟花特效',
navigationBarBackgroundColor: '#000000'
})

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '文章详情',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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);
}

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '捐款凭证',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '捐款记录',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '善款明细',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -1,6 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '捐款详情',
enableShareTimeline: true,
enableShareAppMessage: true,
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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);
}

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '我要捐款',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '文章详情',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -1,8 +0,0 @@
.content{
padding: 32px;
line-height: 2.4rem;
}
.nr-bg{
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '一键报险'
})

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '报险记录'
})

View File

@@ -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

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '开始考试'
})

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '电子围栏'
})

View File

@@ -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

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '位置明细',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '车辆列表'
})

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '定位查询'
})

View File

@@ -1,4 +0,0 @@
page {
//background: url("https://oss.wsdns.cn/20250414/58cac36d806a40e298def726bcd9e44b.jpeg");
//background-size: cover;
}

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '车辆查询'
})

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '查看轨迹'
})

View File

@@ -1,4 +0,0 @@
page {
//background: url("https://oss.wsdns.cn/20250414/58cac36d806a40e298def726bcd9e44b.jpeg");
//background-size: cover;
}

View File

@@ -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

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '观看视频'
})

View File

@@ -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

View File

@@ -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 && (
<>
</>
)}
</>
)
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '商品详情',
navigationBarTextStyle: 'black'
})

View File

@@ -0,0 +1,5 @@
.cart-icon{
background: linear-gradient(to bottom, #bbe094, #4ee265);
border-radius: 100px 0 0 100px;
height: 70px;
}

View 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'
}}/>210</Tag>
<Tag plain round background={'#ff0000'}><Divider direction={'vertical'} style={{
borderStyle: 'dashed', padding: '0',
borderColor: '#fd8989', margin: '0 5px'
}}/>13</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;

View File

@@ -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

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '订单确认',
navigationBarTextStyle: 'black'
})

View 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;
}
}
}

View 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;

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '订单详情',
navigationBarTextStyle: 'black'
})

View 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;
}
}
}

View 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;