Files
mp-10550/src/hjm/query.tsx
2025-06-26 11:41:12 +08:00

774 lines
23 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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