774 lines
23 KiB
TypeScript
774 lines
23 KiB
TypeScript
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
|