修改弹窗报错,添加图例,添加名称搜索

This commit is contained in:
xwj
2026-02-02 13:21:12 +08:00
parent 3cd0eeb9f9
commit db958614c4
9 changed files with 245 additions and 140 deletions

View File

@@ -30,7 +30,7 @@
"vue-property-decorator": "^9.1.2", "vue-property-decorator": "^9.1.2",
"vue-router": "^3.5.1", "vue-router": "^3.5.1",
"vuex": "^3.6.2", "vuex": "^3.6.2",
"vxe-table": "^3.5.9", "vxe-table": "^3.8.11",
"weatherv": "^0.1.12", "weatherv": "^0.1.12",
"xe-utils": "^3.5.6", "xe-utils": "^3.5.6",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"

View File

@@ -40,10 +40,15 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { VxeModal } from 'vxe-table' // 引入单个组件
import 'vxe-table/lib/style.css' // 引入样式
import {Component, Vue} from 'vue-property-decorator' import {Component, Vue} from 'vue-property-decorator'
import DialogUtil from '@/util/new-dialog/index' import DialogUtil from '@/util/new-dialog/index'
@Component({ @Component({
components: {
VxeModal
},
props: { props: {
show: { show: {
type: Boolean, type: Boolean,

View File

@@ -10,13 +10,14 @@
<!-- 筛选搜索弹窗 --> <!-- 筛选搜索弹窗 -->
<div v-if="isShowFilter" class="filter-popup"> <div v-if="isShowFilter" class="filter-popup">
<div class="filter-item"> <div class="filter-item">
<el-checkbox v-model="checkedTypes.ship" @change="handleTypeChange('ship')"> <!-- 船舶图例 -->
<!-- {{ language === 'zh' ? '船舶' : 'Ship' }} --> <div class="legend-circle ship-legend"></div>
</el-checkbox>
<div class="filter-title">{{ language === 'zh' ? '船舶' : 'Ship' }}</div> <div class="filter-title">{{ language === 'zh' ? '船舶' : 'Ship' }}</div>
<el-checkbox v-model="checkedTypes.ship" @change="handleTypeChange('ship')">
</el-checkbox>
<el-select <el-select
v-model="searchValues.ship" v-model="searchValues.ship"
:placeholder="language === 'zh' ? '搜索MMSI' : 'Search Ship Name/MMSI'" :placeholder="language === 'zh' ? '搜索船名/MMSI' : 'Search Ship Name'"
:disabled="!checkedTypes.ship" :disabled="!checkedTypes.ship"
filterable filterable
clearable clearable
@@ -30,21 +31,24 @@
<el-option <el-option
v-for="ship in shipOptions" v-for="ship in shipOptions"
:key="ship.shipId" :key="ship.shipId"
:label="ship.mmsi" :label="`${ship.shipnameEn || '未知船名'} (${ship.mmsi})`"
:value="ship.mmsi" :value="ship.shipnameEn || ship.mmsi"
> >
<div class="ship-option-content"> <div class="ship-option-content">
<div class="ship-name">{{ ship.shipnameEn || '未知船名' }}</div>
<div class="ship-mmsi">MMSI: {{ ship.mmsi }}</div> <div class="ship-mmsi">MMSI: {{ ship.mmsi }}</div>
</div> </div>
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
<div class="filter-item"> <div class="filter-item">
<el-checkbox v-model="checkedTypes.car" @change="handleTypeChange('car')"> <!-- 车辆图例 -->
<!-- {{ language === 'zh' ? '车辆' : 'Car' }} --> <div class="legend-car">
</el-checkbox> <img src="/zero2/img/car.svg" alt="车辆" class="car-icon">
</div>
<div class="filter-title"> {{ language === 'zh' ? '车辆' : 'Car' }}</div> <div class="filter-title"> {{ language === 'zh' ? '车辆' : 'Car' }}</div>
<el-checkbox v-model="checkedTypes.car" @change="handleTypeChange('car')">
</el-checkbox>
<el-select <el-select
style="width: 300px" style="width: 300px"
v-model="searchValues.car" v-model="searchValues.car"
@@ -73,11 +77,12 @@
</el-select> </el-select>
</div> </div>
<div class="filter-item"> <div class="filter-item">
<!-- 游艇图例 -->
<div class="legend-circle yacht-legend"></div>
<div class="filter-title"> {{ language === 'zh' ? '游艇' : 'Yacht' }}</div>
<el-checkbox v-model="checkedTypes.yacht" @change="handleTypeChange('yacht')"> <el-checkbox v-model="checkedTypes.yacht" @change="handleTypeChange('yacht')">
<!-- {{ language === 'zh' ? '游艇' : 'Yacht' }} --> <!-- {{ language === 'zh' ? '游艇' : 'Yacht' }} -->
</el-checkbox> </el-checkbox>
<div class="filter-title"> {{ language === 'zh' ? '游艇' : 'Yacht' }}</div>
<el-select <el-select
v-model="searchValues.yacht" v-model="searchValues.yacht"
:placeholder="language === 'zh' ? '搜索游艇名/MMSI' : 'Search Yacht Name/MMSI'" :placeholder="language === 'zh' ? '搜索游艇名/MMSI' : 'Search Yacht Name/MMSI'"
@@ -94,11 +99,12 @@
<el-option <el-option
v-for="yacht in yachtOptions" v-for="yacht in yachtOptions"
:key="yacht.shipId" :key="yacht.shipId"
:label="yacht.mmsi" :label="`${yacht.shipnameEn || '未知游艇'} (${yacht.mmsi})`"
:value="yacht.mmsi" :value="yacht.shipnameEn || yacht.mmsi"
> >
<div class="yacht-option-content"> <div class="ship-option-content">
<div class="yacht-mmsi">MMSI: {{ yacht.mmsi }}</div> <div class="ship-name">{{ yacht.shipnameEn || '未知游艇' }}</div>
<div class="ship-mmsi">MMSI: {{ yacht.mmsi }}</div>
</div> </div>
</el-option> </el-option>
</el-select> </el-select>
@@ -225,11 +231,11 @@ export default class MapBottomShipType extends Vue {
const shipData = this.shipData.filter((ship: any) => { const shipData = this.shipData.filter((ship: any) => {
return ship.shipTypeName !== '其他' return ship.shipTypeName !== '其他'
}) })
console.log(shipData,'船舶数据') // 模糊搜索船舶数据(匹配船名)
// 模糊搜索船舶数据匹配船名或MMSI
this.shipOptions = shipData.filter((ship: any) => { this.shipOptions = shipData.filter((ship: any) => {
const shipName = ship.shipnameEn || ''
const mmsi = ship.mmsi || '' const mmsi = ship.mmsi || ''
return mmsi.includes(query) return shipName.toLowerCase().includes(query.toLowerCase()) || mmsi.includes(query)
}) })
} }
@@ -245,11 +251,11 @@ export default class MapBottomShipType extends Vue {
const yachtData = this.shipData.filter((ship: any) => { const yachtData = this.shipData.filter((ship: any) => {
return ship.shipTypeName === '其他' return ship.shipTypeName === '其他'
}) })
console.log(yachtData,'游艇数据')
// 模糊搜索游艇数据匹配船名或MMSI // 模糊搜索游艇数据匹配船名或MMSI
this.yachtOptions = yachtData.filter((yacht: any) => { this.yachtOptions = yachtData.filter((yacht: any) => {
const shipName = yacht.shipnameEn || ''
const mmsi = yacht.mmsi || '' const mmsi = yacht.mmsi || ''
return mmsi.includes(query) return shipName.toLowerCase().includes(query.toLowerCase()) || mmsi.includes(query)
}) })
} }
@@ -311,6 +317,54 @@ export default class MapBottomShipType extends Vue {
font-weight: 600; font-weight: 600;
} }
/* 图例样式 */
.legend-circle {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 0.6rem;
margin-left: .14rem;
flex-shrink: 0;
}
/* 船舶图例 - 黄色圆形 */
.ship-legend {
background-color: rgb(180, 39, 74);
}
/* 游艇图例 - 红色圆形 */
.yacht-legend {
background-color: rgb(255, 188, 67);
}
/* 船舶选项样式 */
.ship-option-content {
display: flex;
}
.ship-name {
font-weight: 600;
margin-bottom: 4px;
margin-right: 0.5rem;
}
.ship-mmsi {
font-size: 12px;
color: #666;
}
/* 车辆图例 */
.legend-car {
margin-right: 0.5rem;
flex-shrink: 0;
}
.car-icon {
width: 16px;
height: 16px;
display: block;
}
.filter-popup { .filter-popup {
position: absolute; position: absolute;
bottom: 3rem; bottom: 3rem;

View File

@@ -4,7 +4,7 @@ import router from './router'
import store from './store' import store from './store'
import 'xe-utils' import 'xe-utils'
import '@/css/index.css' import '@/css/index.css'
import VXETable from 'vxe-table' import VXETable, { VxeModal } from 'vxe-table'
import "@/css/vxe-table/source-index.css" import "@/css/vxe-table/source-index.css"
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/index.css'
@@ -17,6 +17,7 @@ Vue.prototype.$myFleet = []
const config = require('../public/config/sys-config.json') const config = require('../public/config/sys-config.json')
window.localStorage.setItem('pgName', config.projectName) window.localStorage.setItem('pgName', config.projectName)
Vue.use(VXETable) Vue.use(VXETable)
Vue.component('vxe-modal', VxeModal)
Vue.use(ElementUI) Vue.use(ElementUI)
Vue.config.productionTip = false Vue.config.productionTip = false

View File

@@ -1418,8 +1418,10 @@ shipFun.getShipFeaturesByIds = (shipIds) => {
shipFun.getCarFeaturesByIds = (carIds) => { //根据shipId找到对应的feature shipFun.getCarFeaturesByIds = (carIds) => { //根据shipId找到对应的feature
const features = [] const features = []
carIds.forEach(id => { carIds.forEach(id => {
// if (id !== undefined) {
let f = shipMap.sourceList.carSource.getFeatureById(id) let f = shipMap.sourceList.carSource.getFeatureById(id)
features.push(f) features.push(f)
// }
}) })
return features return features
} }

View File

@@ -29,7 +29,7 @@ class DialogUtil {
} }
} }
vm.$mount(document.createElement('div')); vm.$mount(document.createElement('div'));
(document.getElementById('ship-type-box') as HTMLElement).append(vm.$el) document.body.append(vm.$el)
showComponents.set(vueName, vm) showComponents.set(vueName, vm)
} else { } else {
compent.$props.tempData = data compent.$props.tempData = data
@@ -112,7 +112,10 @@ class DialogUtil {
MiniUtil.delMini(vueName) MiniUtil.delMini(vueName)
} }
const el = component.$el const el = component.$el
// 检查$refs[vueName]是否存在,避免报错
if (component.$refs[vueName]) {
component.$refs[vueName].cancel() component.$refs[vueName].cancel()
}
el.parentElement.removeChild(el) el.parentElement.removeChild(el)
component.$destroy() component.$destroy()
showComponents.delete(vueName) showComponents.delete(vueName)

View File

@@ -198,7 +198,6 @@ export default class Main extends Vue {
} }
// 更新车辆数据 // 更新车辆数据
this.carData = carData this.carData = carData
console.log(carData,'carData')
// 将有效的车辆数据(有经纬度)添加到地图 // 将有效的车辆数据(有经纬度)添加到地图
ShipFun.addCar(carData.filter((item: any) => { ShipFun.addCar(carData.filter((item: any) => {
return item.latitude && item.longitude return item.latitude && item.longitude
@@ -417,6 +416,215 @@ export default class Main extends Vue {
}) })
// 添加地图点击事件监听 // 添加地图点击事件监听
ShipFun.addClick(async (res: any) => { ShipFun.addClick(async (res: any) => {
// 调用地图点击事件处理逻辑
await this.handleMapClick(res)
})
// 监听地图容器大小变化,更新地图大小
elementResizeDetectorMaker().listenTo(document.getElementById('map'), () => {
this.map.updateSize()
})
// 获取初始船舶数据
this.getShips()
// 获取初始车辆数据
this.getCars()
// 监听全局事件,重新显示所有数据
bus.$on('showAllData', () => {
this.showAllData()
})
// 初始化筛选和搜索事件监听
this.initFilterEvent()
}
/**
* 初始化筛选和搜索事件
*/
private initFilterEvent() {
// 类型筛选事件
bus.$on('filterTypeChange', (data: any) => {
this.handleFilterTypeChange(data)
})
// 搜索事件
bus.$on('filterSearch', (data: any) => {
this.handleFilterSearch(data)
})
}
/**
* 处理类型筛选变化
* @param {Object} data - 筛选数据
* @param {string} data.type - 类型ship, car, yacht
* @param {boolean} data.isChecked - 是否勾选
*/
private handleFilterTypeChange(data: any) {
const { type, isChecked } = data
switch (type) {
case 'ship':
if (isChecked) {
// 显示船舶
const filteredShips = this.shipData.filter((ship: any) => {
return ship.shipTypeName !== '其他'
})
this.jiexiRanks(filteredShips)
} else {
// 只隐藏船舶,保留游艇
const shipIds = this.shipData
.filter((ship: any) => ship.shipTypeName !== '其他')
.map((ship: any) => ship.shipId)
ShipFun.hideShips(shipIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
case 'car':
if (isChecked) {
// 显示车辆
ShipFun.addCar(this.carData.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
} else {
// 隐藏车辆
ShipFun.clearAllCar(this.map)
DialogUtil.closeCom(DialogType.CAR_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
ShipFun.delPath()
}
break
case 'yacht':
if (isChecked) {
// 显示游艇
const yachts = this.shipData.filter((ship: any) => {
return ship.shipTypeName === '其他'
})
this.jiexiRanks(yachts)
} else {
const yachtIds = this.shipData
.filter((ship: any) => ship.shipTypeName === '其他')
.map((ship: any) => ship.shipId)
ShipFun.hideShips(yachtIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
}
}
/**
* 处理搜索
* @param {Object} data - 搜索数据
* @param {string} data.type - 类型ship, car, yacht
* @param {string} data.value - 搜索值
*/
private async handleFilterSearch(data: any) {
const { type, value } = data
if (!value) {
// 搜索值为空,显示所有数据
this.handleFilterTypeChange({ type, isChecked: true })
return
}
switch (type) {
case 'ship': {
// 船舶搜索
const allShips = this.shipData.filter((ship: any) => {
const isShip = ship.shipTypeName !== '其他'
return isShip
})
// 再根据shipnameEn包含value筛选
const filteredaShips = allShips.filter((ship: any) => {
const shipName = ship.shipnameEn || ''
const mmsi = ship.mmsi || ''
const isMatch = shipName.toLowerCase().includes(value.toLowerCase()) || mmsi.includes(value)
return isMatch
})
// // 保存游艇数据,避免被清除
// const yachts = this.shipData.filter((ship: any) => {
// return ship.shipTypeName === '其他'
// })
// // 清空现有船舶数据
// ShipFun.clearallship(this.map)
// // 重新添加游艇数据
// if (yachts.length > 0) {
// this.jiexiRanks(yachts)
// }
if (filteredaShips.length > 0) {
// 触发弹窗展示匹配到的第一个船舶信息
const ship: any = filteredaShips[0]
// 构造点击事件的响应对象
const res = {
isType: 'ship',
shipId: ship.shipId
}
// 调用地图点击事件处理逻辑
await this.handleMapClick(res)
} else {
console.log('没有匹配到船舶数据')
}
break
}
case 'car': {
// 车辆搜索
const filteredCars = this.carData.filter((car: any) => {
const carNum = car.carNum || ''
return carNum.includes(value)
})
if (filteredCars.length > 0) {
// 触发弹窗展示匹配到的第一个车辆信息
const car: any = filteredCars[0]
// 构造点击事件的响应对象
const res = {
isType: 'car',
carNum: car.carNum,
...(car as object)
}
// 调用地图点击事件处理逻辑
await this.handleMapClick(res)
} else {
console.log('没有匹配到车辆数据')
}
break
}
case 'yacht': {
// 游艇搜索 - 先过滤游艇数据再根据mmsi包含value筛选
// 先过滤出游艇数据
const allYachts = this.shipData.filter((ship: any) => {
const isYacht = ship.shipTypeName === '其他'
return isYacht
})
// 再根据mmsi包含value筛选
const filteredYachts = allYachts.filter((yacht: any) => {
const mmsi = yacht.mmsi || ''
const shipName = yacht.shipnameEn || ''
const isMatch = mmsi.includes(value) || shipName.toLowerCase().includes(value.toLowerCase())
return isMatch
})
if (filteredYachts.length > 0) {
// 触发弹窗展示匹配到的第一个游艇信息
const yacht: any = filteredYachts[0]
// 构造点击事件的响应对象
const res = {
isType: 'ship',
shipId: yacht.shipId
}
// 调用地图点击事件处理逻辑
await this.handleMapClick(res)
} else {
console.log('没有匹配到游艇数据')
}
break
}
}
}
/**
* 处理地图点击事件
* @param {Object} res - 点击事件响应对象
*/
private async handleMapClick(res: any) {
// 处理港口点击 // 处理港口点击
if (res.isType === 'port') { if (res.isType === 'port') {
let port: any = {} let port: any = {}
@@ -500,175 +708,6 @@ export default class Main extends Vue {
show: true show: true
}) })
} }
})
// 监听地图容器大小变化,更新地图大小
elementResizeDetectorMaker().listenTo(document.getElementById('map'), () => {
this.map.updateSize()
})
// 获取初始船舶数据
this.getShips()
// 获取初始车辆数据
this.getCars()
// 监听全局事件,重新显示所有数据
bus.$on('showAllData', () => {
this.showAllData()
})
// 初始化筛选和搜索事件监听
this.initFilterEvent()
}
/**
* 初始化筛选和搜索事件
*/
private initFilterEvent() {
// 类型筛选事件
bus.$on('filterTypeChange', (data: any) => {
this.handleFilterTypeChange(data)
})
// 搜索事件
bus.$on('filterSearch', (data: any) => {
this.handleFilterSearch(data)
})
}
/**
* 处理类型筛选变化
* @param {Object} data - 筛选数据
* @param {string} data.type - 类型ship, car, yacht
* @param {boolean} data.isChecked - 是否勾选
*/
private handleFilterTypeChange(data: any) {
const { type, isChecked } = data
switch (type) {
case 'ship':
if (isChecked) {
// 显示船舶
const filteredShips = this.shipData.filter((ship: any) => {
return ship.shipTypeName !== '其他'
})
this.jiexiRanks(filteredShips)
} else {
// 只隐藏船舶,保留游艇
const shipIds = this.shipData
.filter((ship: any) => ship.shipTypeName !== '其他')
.map((ship: any) => ship.shipId)
console.log(shipIds,'shipIds')
ShipFun.hideShips(shipIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
case 'car':
if (isChecked) {
// 显示车辆
ShipFun.addCar(this.carData.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
} else {
// 隐藏车辆
ShipFun.clearAllCar(this.map)
DialogUtil.closeCom(DialogType.CAR_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
ShipFun.delPath()
}
break
case 'yacht':
if (isChecked) {
// 显示游艇
const yachts = this.shipData.filter((ship: any) => {
return ship.shipTypeName === '其他'
})
this.jiexiRanks(yachts)
} else {
// 只隐藏游艇,保留船舶
const yachtIds = this.shipData
.filter((ship: any) => ship.shipTypeName === '其他')
.map((ship: any) => ship.shipId)
console.log(yachtIds,'yachtIds')
ShipFun.hideShips(yachtIds)
DialogUtil.closeCom(DialogType.SHIP_INFO)
DialogUtil.closeCom(DialogType2.TRACK_RECORD)
}
break
}
}
/**
* 处理搜索
* @param {Object} data - 搜索数据
* @param {string} data.type - 类型ship, car, yacht
* @param {string} data.value - 搜索值
*/
private handleFilterSearch(data: any) {
const { type, value } = data
if (!value) {
// 搜索值为空,显示所有数据
this.handleFilterTypeChange({ type, isChecked: true })
return
}
switch (type) {
case 'ship': {
// 船舶搜索
const allShips = this.shipData.filter((ship: any) => {
const isShip = ship.shipTypeName !== '其他'
return isShip
})
console.log(allShips,'所有的船舶数据')
console.log(value,'搜索值')
// 再根据mmsi包含value筛选
const filteredaShips = allShips.filter((ship: any) => {
const mmsi = ship.mmsi || ''
const isMatch = mmsi.includes(value)
return isMatch
})
// 清空现有船舶数据
console.log(filteredaShips,'筛选后的船舶数据')
ShipFun.clearallship(this.map)
if (filteredaShips.length > 0) {
this.jiexiRanks(filteredaShips)
} else {
console.log('没有匹配到船舶数据')
}
break
}
case 'car': {
// 车辆搜索
const filteredCars = this.carData.filter((car: any) => {
const carNum = car.carNum || ''
return carNum.includes(value)
})
ShipFun.clearAllCar(this.map)
ShipFun.addCar(filteredCars.filter((item: any) => {
return item.latitude && item.longitude
}), this.map)
break
}
case 'yacht': {
// 游艇搜索 - 先过滤游艇数据再根据mmsi包含value筛选
// 先过滤出游艇数据
const allYachts = this.shipData.filter((ship: any) => {
const isYacht = ship.shipTypeName === '其他'
return isYacht
})
// 再根据mmsi包含value筛选
const filteredYachts = allYachts.filter((ship: any) => {
const mmsi = ship.mmsi || ''
const isMatch = mmsi.includes(value)
return isMatch
})
// 清空现有船舶数据
ShipFun.clearallship(this.map)
if (filteredYachts.length > 0) {
// 直接调用jiexiRanks方法处理数据并添加到地图
this.jiexiRanks(filteredYachts)
} else {
console.log('没有匹配到游艇数据')
}
break
}
}
} }
} }
</script> </script>

View File

@@ -33,13 +33,13 @@ import CarInfo from '@/views/on-map-view/CarInfo.vue'
} }
} }
}) })
export default class ShipInfoTemp extends Vue { export default class CarInfoTemp extends Vue {
private dialogConfig = { private dialogConfig = {
width: '500px', width: '500px',
height: '31.25rem', height: '31.25rem',
header: { header: {
title: '船舶历史轨迹', title: '车辆信息',
hasAfterIcon: true, hasAfterIcon: false,
hasIcon: false, hasIcon: false,
iconSrc: '', iconSrc: '',
hasMin: true, hasMin: true,
@@ -49,7 +49,7 @@ export default class ShipInfoTemp extends Vue {
top: '9.375rem', top: '9.375rem',
left: '16.25rem' left: '16.25rem'
}, },
vueName: '' vueName: 'CarInfoTemp'
} }
@Watch('tempData', { @Watch('tempData', {

View File

@@ -62,8 +62,9 @@ export default class CarInfo extends Vue {
}) })
carChange(data: any) { carChange(data: any) {
const flagDom: any = document.getElementById('ship-flag-url'); const flagDom: any = document.getElementById('ship-flag-url');
if (flagDom) {
flagDom.setAttribute('src', `/zero2/country-img/CHN.png`) flagDom.setAttribute('src', `/zero2/country-img/CHN.png`)
this.carData = data }
} }
private cancel() { private cancel() {