Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/H5/shehong-vehicle-supervision
| | |
| | | <template> |
| | | <div> |
| | | <video id="video" width="100%" height="100%" muted controls></video> |
| | | <div style="height: 100%;"> |
| | | <video id="video" style="height: 100%;width: 100%;" muted controls></video> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | <script> |
| | | import { getComplainList } from './service' |
| | | import { exportExcell } from '@/utils/utils' |
| | | import moment from 'moment' |
| | | |
| | | export default { |
| | | data() { |
| | |
| | | let obj = { ...this.searchForm } |
| | | delete obj.total |
| | | if (obj.selectTime) { |
| | | obj.startTime = moment(obj.selectTime[0]).format('YYYY-MM-DD HH-mm-ss') |
| | | obj.endTime = moment(obj.selectTime[1]).format('YYYY-MM-DD HH-mm-ss') |
| | | obj.startTime = moment(obj.selectTime[0]).format('YYYY-MM-DD') + ' 00:00:00' |
| | | obj.endTime = moment(obj.selectTime[1]).format('YYYY-MM-DD') + ' 23:59:59' |
| | | delete obj.selectTime |
| | | } |
| | | exportExcell('投诉记录导出', obj, '/system/complain/exportComplainList') |
| | |
| | | let obj = { ...this.searchForm } |
| | | delete obj.total |
| | | if (obj.selectTime) { |
| | | obj.startTime = moment(obj.selectTime[0]).format('YYYY-MM-DD HH-mm-ss') |
| | | obj.endTime = moment(obj.selectTime[1]).format('YYYY-MM-DD HH-mm-ss') |
| | | obj.startTime = moment(obj.selectTime[0]).format('YYYY-MM-DD') + ' 00:00:00' |
| | | obj.endTime = moment(obj.selectTime[1]).format('YYYY-MM-DD') + ' 23:59:59' |
| | | delete obj.selectTime |
| | | } |
| | | getComplainList(obj).then(res => { |
| | |
| | | },
|
| | | handleSizeChange(e) {
|
| | | this.queryForm.pageSize = e
|
| | | this.fetchData()
|
| | |
|
| | | },
|
| | | handleCurrentChange(e) {
|
| | | this.queryForm.page = e
|
| | | this.queryForm.pageCurr = e
|
| | | this.fetchData()
|
| | |
|
| | | },
|
| | | }
|
| | | }
|
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog title="订单详情" :visible.sync="dialogVisible" width="50%" :modal-append-to-body="false" |
| | | @close="closeClick"> |
| | | :close-on-press-escape="false" :close-on-click-modal="false" @close="closeClick"> |
| | | <el-radio-group v-model="tabPosition" style="margin-bottom: 30px;"> |
| | | <el-radio-button label="order">订单信息</el-radio-button> |
| | | <el-radio-button label="track">行程轨迹</el-radio-button> |
| | |
| | | ], |
| | | }) |
| | | .then((AMap) => { |
| | | this.map = new AMap.Map("mapContainer", { |
| | | center: [this.travelData[this.travelData.length / 2].longitude, this.travelData[this.travelData.length / 2].latitude], |
| | | zoom: 12, |
| | | }); |
| | | this.map.addControl(new AMap.ToolBar()); |
| | | let path = this.travelData.map(item => { |
| | | return new AMap.LngLat(item.longitude, item.latitude) |
| | | }) |
| | | const content = `<div class="custom-content-marker"> |
| | | 起点 |
| | | </div>`; |
| | | // 转换 travelData 中的坐标 |
| | | const wgs84Path = this.travelData.map(item => [item.longitude, item.latitude]); |
| | | const batchSize = 40; // 每次转换 40 对坐标 |
| | | const batches = []; |
| | | |
| | | const contentTwo = `<div class="custom-content-marker-two"> |
| | | 终点 |
| | | </div>`; |
| | | const marker = [ |
| | | new AMap.Marker({ |
| | | content: content, //自定义点标记覆盖物内容 |
| | | position: [this.travelData[0].longitude, this.travelData[0].latitude], //基点位置 |
| | | offset: new AMap.Pixel(-35, -25), //相对于基点的偏移位置 |
| | | }), |
| | | new AMap.Marker({ |
| | | content: contentTwo, //自定义点标记覆盖物内容 |
| | | position: [this.travelData[this.travelData.length - 1].longitude, this.travelData[this.travelData.length - 1].latitude], //基点位置 |
| | | offset: new AMap.Pixel(-35, -25), //相对于基点的偏移位置 |
| | | // 分批处理 |
| | | for (let i = 0; i < wgs84Path.length; i += batchSize) { |
| | | batches.push(wgs84Path.slice(i, i + batchSize)); |
| | | } |
| | | |
| | | const gcj02Path = []; |
| | | const promises = batches.map(batch => { |
| | | return new Promise((resolve, reject) => { |
| | | AMap.convertFrom(batch, 'gps', (status, result) => { |
| | | if (status === 'complete' && result.locations) { |
| | | resolve(result.locations); |
| | | } else { |
| | | reject(result); |
| | | } |
| | | }); |
| | | }); |
| | | }); |
| | | |
| | | // 等待所有批次转换完成 |
| | | Promise.all(promises) |
| | | .then(results => { |
| | | results.forEach(batchResult => { |
| | | gcj02Path.push(...batchResult); |
| | | }); |
| | | |
| | | // 开始绘制地图 |
| | | this.map = new AMap.Map("mapContainer", { |
| | | center: gcj02Path[Math.floor(gcj02Path.length / 2)], // 使用转换后的中点坐标 |
| | | zoom: 12, |
| | | }); |
| | | this.map.addControl(new AMap.ToolBar()); |
| | | |
| | | // 添加起点和终点标记 |
| | | const marker = [ |
| | | new AMap.Marker({ |
| | | content: `<div class="custom-content-marker">起点</div>`, |
| | | position: gcj02Path[0], |
| | | offset: new AMap.Pixel(-35, -25), |
| | | }), |
| | | new AMap.Marker({ |
| | | content: `<div class="custom-content-marker-two">终点</div>`, |
| | | position: gcj02Path[gcj02Path.length - 1], |
| | | offset: new AMap.Pixel(-35, -25), |
| | | }), |
| | | ]; |
| | | this.map.add(marker); |
| | | |
| | | // 绘制路径 |
| | | const polyline = new AMap.Polyline({ |
| | | path: gcj02Path, |
| | | strokeWeight: 3, |
| | | strokeColor: "red", |
| | | lineJoin: "round", |
| | | }); |
| | | this.map.add(polyline); |
| | | |
| | | // 强制刷新地图 |
| | | this.$nextTick(() => { |
| | | this.map.resize(); |
| | | }); |
| | | }) |
| | | ] |
| | | this.map.add(marker); |
| | | let polyline = new AMap.Polyline({ |
| | | path: path, |
| | | strokeWeight: 3, //线条宽度 |
| | | strokeColor: "red", //线条颜色 |
| | | lineJoin: "round", //折线拐点连接处样式 |
| | | }); |
| | | this.map.add(polyline); |
| | | // 强制刷新地图 |
| | | this.$nextTick(() => { |
| | | this.map.resize(); |
| | | }); |
| | | .catch(error => { |
| | | console.error('坐标转换失败', error); |
| | | }); |
| | | }) |
| | | .catch((e) => { |
| | | console.log(e); |
| | | }); |
| | | }, |
| | | closeClick() { |
| | |
| | | }, |
| | | showDetail(row) { |
| | | this.loading = true |
| | | Promise.all([getOrderInfo(row.id), getOrderMonitoring({ id: row.id }), getOrderTravel({ id: row.id })]).then(res => { |
| | | // Promise.all([getOrderInfo(row.id), getOrderTravel({ id: row.id })]).then(res => { |
| | | this.$refs.detailModal.initData(res[0], res[1], res[2]) |
| | | this.loading = false |
| | | Promise.all([getOrderInfo(row.id), getOrderTravel({ id: row.id })]).then(res => { |
| | | getOrderMonitoring({ id: row.id }).then(resp => { |
| | | this.$refs.detailModal.initData(res[0], resp, res[1]) |
| | | this.loading = false |
| | | }).catch(err => { |
| | | this.$refs.detailModal.initData(res[0], {}, res[1]) |
| | | this.loading = false |
| | | }) |
| | | }).catch(err => { |
| | | this.loading = false |
| | | }) |
| | |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <div class="grid-content"> |
| | | <!-- <video ref="videoPlayer" controls autoplay style="width: 100%; height: 100%;"> |
| | | <source :src="videoUrl" type="video/mp4"> |
| | | 您的浏览器不支持视频播放 |
| | | </video> --> |
| | | <video controls src="http://192.168.110.85/test.m3u8" ref="videoPlayer" crossorigin="anonymous" autoplay style="width: 100%; height: 100%;"> |
| | | <source src="http://192.168.110.85/test.m3u8" type="application/x-mpegURL"> |
| | | </video> |
| | | |
| | | <div class="grid-content" style=""> |
| | | <PlayLive :serverPort="serverPort" :serverIp="serverIp" :carId="$route.query.id"/> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import moment from 'moment'; |
| | | import { getDetail, getPlaybackVideo } from './service' |
| | | import PlayLive from '@/components/PlayLive' |
| | | |
| | | export default { |
| | | components: { |
| | | PlayLive |
| | | }, |
| | | data() { |
| | | return { |
| | | info: {}, |
| | |
| | | disabledDate(time) { |
| | | return time.getTime() > Date.now(); |
| | | } |
| | | } |
| | | }, |
| | | serverIp: '', |
| | | serverPort: '' |
| | | } |
| | | }, |
| | | created() { |
| | |
| | | } |
| | | } |
| | | }, |
| | | initVideoPlayer(url) { |
| | | const video = this.$refs.videoPlayer; |
| | | video.src = url; |
| | | |
| | | video.onerror = (e) => { |
| | | console.error('视频播放错误:', e); |
| | | this.$message.error('视频播放失败,请检查网络连接或视频源'); |
| | | }; |
| | | |
| | | video.oncanplay = () => { |
| | | video.play().catch(err => { |
| | | console.error('播放错误:', err); |
| | | this.$message.error('视频播放失败,请检查网络连接或视频源'); |
| | | }); |
| | | }; |
| | | }, |
| | | search() { |
| | | if (!this.searchForm.startTime || !this.searchForm.endTime) { |
| | | this.$message.warning('请选择开始时间和结束时间'); |
| | |
| | | endTime: new Date(this.searchForm.endTime).getTime(), |
| | | id: this.$route.query.id |
| | | }).then(res => { |
| | | if (res && res.url) { |
| | | if (res && res.serverIp && res.serverPort) { |
| | | // 构建完整的视频流地址 |
| | | const videoUrl = res.url; |
| | | this.initVideoPlayer(videoUrl); |
| | | this.serverIp = res.serverIp; |
| | | this.serverPort = res.serverPort |
| | | } else { |
| | | this.$message.error('未获取到视频地址'); |
| | | } |
| | |
| | | flex: 1; |
| | | position: relative; |
| | | overflow: hidden; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | video { |
| | |
| | | }, |
| | | showDetail(row) { |
| | | getDriverInfo(row.id).then(res=>{ |
| | | |
| | | this.$refs.detailModal.initData(res) |
| | | }) |
| | | }, |
| | | } |
| | |
| | | |
| | | // 获取司机详情 |
| | | export const getDriverInfo = (id) => { |
| | | return axios.get(`/system/driver/getDriverInfo/{id}/${id}`) |
| | | return axios.get(`/system/driver/getDriverInfo/${id}`) |
| | | } |
| | | |
| | | // 获取司机列表 |