提交代码

This commit is contained in:
2025-06-26 11:41:12 +08:00
commit d75fb55eec
396 changed files with 42172 additions and 0 deletions

773
src/hjm/query.tsx Normal file
View File

@@ -0,0 +1,773 @@
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