合并
This commit is contained in:
@ -1,17 +1,63 @@
|
||||
<template>
|
||||
<div class="p5" style="width: 100%; height: calc(100vh - 84px)" v-loading="loading">
|
||||
<!-- 返回按钮区域 -->
|
||||
<div style="position: absolute; top: 20px; left: 20px; z-index: 100; display: flex; gap: 10px">
|
||||
<el-button type="primary" icon="ArrowLeft" @click="handleBack">返回</el-button>
|
||||
<el-button type="success" icon="Play" @click="handleStart">开始</el-button>
|
||||
<el-button type="warning" icon="Pause" @click="handleStop">停止</el-button>
|
||||
</div>
|
||||
<div id="TrajectoryEarth" style="width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="equipmentGPS">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { ElButton } from 'element-plus';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router'; // 补充导入useRouter
|
||||
import { getFootNote } from '@/api/equipment/index';
|
||||
import { ModelPathMover } from './index.js';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter(); // 初始化router
|
||||
const loading = ref(true);
|
||||
let modelMover = null;
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.back();
|
||||
};
|
||||
|
||||
// 开始轨迹运动
|
||||
const handleStart = () => {
|
||||
if (modelMover && typeof modelMover.start === 'function') {
|
||||
try {
|
||||
modelMover.start();
|
||||
ElMessage.success('轨迹已开始播放');
|
||||
} catch (error) {
|
||||
console.error('启动轨迹失败:', error);
|
||||
ElMessage.error('启动轨迹失败');
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning('轨迹未初始化,请稍后再试');
|
||||
}
|
||||
};
|
||||
|
||||
// 停止轨迹运动
|
||||
const handleStop = () => {
|
||||
if (modelMover && typeof modelMover.stop === 'function') {
|
||||
try {
|
||||
modelMover.stop();
|
||||
ElMessage.success('轨迹已停止');
|
||||
} catch (error) {
|
||||
console.error('停止轨迹失败:', error);
|
||||
ElMessage.error('停止轨迹失败');
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning('轨迹未初始化,请稍后再试');
|
||||
}
|
||||
};
|
||||
|
||||
let earthInstance = null;
|
||||
let data = [
|
||||
{
|
||||
@ -146,34 +192,39 @@ const renderRange = (data) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const positions = data.map((point) => {
|
||||
return Cesium.Cartesian3.fromDegrees(point.locLongitude, point.locLatitude, point.locAltitude || 0);
|
||||
const positions = data.map((item) => {
|
||||
return {
|
||||
lon: item.locLongitude,
|
||||
lat: item.locLatitude,
|
||||
height: item.locAltitude || 0,
|
||||
name: item.name || ''
|
||||
};
|
||||
});
|
||||
|
||||
const entity = earthInstance.viewer.entities.add({
|
||||
polyline: {
|
||||
positions: positions,
|
||||
width: 5,
|
||||
material: Cesium.Color.RED,
|
||||
clampToGround: true
|
||||
}
|
||||
modelMover = new ModelPathMover(window.Earth3.viewer, {
|
||||
// modelUrl: './air.glb', // 模型URL
|
||||
positions: positions,
|
||||
speed: 50, // 移动速度
|
||||
loop: true, // 循环运动
|
||||
iconUrl: '/image/Foot.png', // 模型上方图标
|
||||
baseHeight: 10 // 外部传入的统一高度(米)
|
||||
});
|
||||
|
||||
// 调整视角以适应轨迹
|
||||
earthInstance.viewer.flyTo(entity);
|
||||
window.modelMover = modelMover;
|
||||
} catch (err) {
|
||||
console.error('渲染轨迹失败:', err);
|
||||
ElMessage.error('渲染轨迹失败');
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
onMounted(() => {
|
||||
createEarth();
|
||||
});
|
||||
|
||||
//
|
||||
onUnmounted(() => {
|
||||
if (modelMover) {
|
||||
modelMover.stop(); // 组件卸载时停止轨迹
|
||||
modelMover = null;
|
||||
}
|
||||
if (earthInstance) {
|
||||
earthInstance.destroy();
|
||||
earthInstance = null;
|
||||
|
392
src/views/equipment/index.js
Normal file
392
src/views/equipment/index.js
Normal file
@ -0,0 +1,392 @@
|
||||
export class ModelPathMover {
|
||||
/**
|
||||
* 构造函数
|
||||
* @param {Cesium.Viewer} viewer - Cesium viewer实例
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {Number} [options.baseHeight=0] - 外部传入的统一高度(米),将叠加到所有位置点上
|
||||
*/
|
||||
constructor(viewer, options) {
|
||||
this.viewer = viewer;
|
||||
this.modelUrl = options.modelUrl;
|
||||
this.positions = options.positions || [];
|
||||
this.speed = options.speed || 10; // 米/秒
|
||||
this.loop = options.loop !== undefined ? options.loop : true;
|
||||
this.pathStyle = options.pathStyle || {
|
||||
color: Cesium.Color.YELLOW,
|
||||
width: 3
|
||||
};
|
||||
this.markerStyle = options.markerStyle || {
|
||||
color: Cesium.Color.RED,
|
||||
pixelSize: 10,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2
|
||||
};
|
||||
this.iconUrl = options.iconUrl;
|
||||
this.iconSize = options.iconSize || 32;
|
||||
this.iconOffset = options.iconOffset || 0;
|
||||
this.headingOffset = options.headingOffset || 0;
|
||||
// 新增:接收外部传入的统一高度(默认0米)
|
||||
this.baseHeight = options.baseHeight || 0;
|
||||
|
||||
// 内部状态(保持不变)
|
||||
this.modelEntity = null;
|
||||
this.pathEntity = null;
|
||||
this.markerEntities = [];
|
||||
this.iconEntity = null;
|
||||
this.currentIndex = 0;
|
||||
this.isMoving = false;
|
||||
this.lastTime = 0;
|
||||
this.animationFrameId = null;
|
||||
this.currentPosition = null;
|
||||
this.progress = 0;
|
||||
this.modelHeight = 0;
|
||||
|
||||
// 初始化
|
||||
this.init();
|
||||
|
||||
// 调试信息(增加baseHeight打印)
|
||||
console.log('ModelPathMover初始化完成', {
|
||||
positionCount: this.positions.length,
|
||||
speed: this.speed,
|
||||
loop: this.loop,
|
||||
baseHeight: this.baseHeight // 打印传入的统一高度
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化(保持不变)
|
||||
*/
|
||||
init() {
|
||||
if (this.positions.length < 2) {
|
||||
console.warn('至少需要2个位置点才能进行移动');
|
||||
}
|
||||
this.createPath();
|
||||
this.createMarkers();
|
||||
this.createModel();
|
||||
if (this.iconUrl) {
|
||||
this.createIcon();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模型(修改:叠加baseHeight)
|
||||
*/
|
||||
createModel() {
|
||||
if (this.positions.length === 0) return;
|
||||
|
||||
const firstPos = this.positions[0];
|
||||
// 关键修改:在原始高度基础上叠加baseHeight
|
||||
const finalHeight = (firstPos.height || 0) + this.baseHeight;
|
||||
this.currentPosition = Cesium.Cartesian3.fromDegrees(
|
||||
firstPos.lon,
|
||||
firstPos.lat,
|
||||
finalHeight // 使用叠加后的高度
|
||||
);
|
||||
|
||||
// 初始朝向计算(保持不变)
|
||||
let initialOrientation = Cesium.Transforms.headingPitchRollQuaternion(this.currentPosition, new Cesium.HeadingPitchRoll(0, 0, 0));
|
||||
|
||||
if (this.positions.length > 1) {
|
||||
const nextPos = this.positions[1];
|
||||
// 计算下一个点时同样叠加baseHeight
|
||||
const nextFinalHeight = (nextPos.height || 0) + this.baseHeight;
|
||||
const nextCartesian = Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, nextFinalHeight);
|
||||
const heading = this.calculateHeading(this.currentPosition, nextCartesian);
|
||||
initialOrientation = Cesium.Transforms.headingPitchRollQuaternion(this.currentPosition, new Cesium.HeadingPitchRoll(heading, 0, 0));
|
||||
}
|
||||
|
||||
this.modelEntity = this.viewer.entities.add({
|
||||
name: 'Moving Model',
|
||||
position: this.currentPosition,
|
||||
orientation: initialOrientation,
|
||||
model: {
|
||||
uri: this.modelUrl,
|
||||
minimumPixelSize: 64,
|
||||
readyPromise: (model) => {
|
||||
const boundingSphere = model.boundingSphere;
|
||||
if (boundingSphere) {
|
||||
this.modelHeight = boundingSphere.radius * 2;
|
||||
console.log('模型高度检测完成:', this.modelHeight, '米');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建路径(修改:叠加baseHeight)
|
||||
*/
|
||||
createPath() {
|
||||
if (this.positions.length < 2) return;
|
||||
|
||||
// 关键修改:遍历所有点时,给每个点的高度叠加baseHeight
|
||||
const pathPositions = this.positions.map((pos) => {
|
||||
const finalHeight = (pos.height || 0) + this.baseHeight;
|
||||
return Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, finalHeight);
|
||||
});
|
||||
|
||||
this.pathEntity = this.viewer.entities.add({
|
||||
name: 'Model Path',
|
||||
polyline: {
|
||||
positions: pathPositions,
|
||||
width: this.pathStyle.width,
|
||||
material: this.pathStyle.color,
|
||||
clampToGround: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建位置标记(修改:叠加baseHeight)
|
||||
*/
|
||||
createMarkers() {
|
||||
this.markerEntities = this.positions.map((pos, index) => {
|
||||
// 关键修改:标记点高度同样叠加baseHeight
|
||||
const finalHeight = (pos.height || 0) + this.baseHeight;
|
||||
const position = Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, finalHeight);
|
||||
|
||||
const marker = this.viewer.entities.add({
|
||||
name: `Position Marker ${index}`,
|
||||
position: position,
|
||||
point: {
|
||||
color: this.markerStyle.color,
|
||||
pixelSize: this.markerStyle.pixelSize,
|
||||
outlineColor: this.markerStyle.outlineColor,
|
||||
outlineWidth: this.markerStyle.outlineWidth
|
||||
},
|
||||
label: {
|
||||
text: pos.name || `Point ${index + 1}`,
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -20),
|
||||
fillColor: Cesium.Color.WHITE
|
||||
}
|
||||
});
|
||||
|
||||
return marker;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模型上方的图标(保持不变)
|
||||
*/
|
||||
createIcon() {
|
||||
this.iconEntity = this.viewer.entities.add({
|
||||
name: 'Model Icon',
|
||||
position: new Cesium.CallbackProperty(() => {
|
||||
return this.adjustCartesianHeight(this.currentPosition, 0.3);
|
||||
}, false),
|
||||
billboard: {
|
||||
image: this.iconUrl,
|
||||
width: this.iconSize,
|
||||
height: this.iconSize,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
|
||||
},
|
||||
label: {
|
||||
text: 'Model Icon',
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -40),
|
||||
fillColor: Cesium.Color.WHITE
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整笛卡尔坐标的高度(在原有高度基础上叠加指定值)
|
||||
* @param {Cesium.Cartesian3} cartesian - 原始笛卡尔坐标
|
||||
* @param {Number} heightOffset - 要增加的高度值(米,可为负数)
|
||||
* @param {Cesium.Ellipsoid} [ellipsoid=Cesium.Ellipsoid.WGS84] - 参考椭球体
|
||||
* @returns {Cesium.Cartesian3|null} 调整高度后的笛卡尔坐标(失败返回null)
|
||||
*/
|
||||
adjustCartesianHeight(cartesian, heightOffset, ellipsoid = Cesium.Ellipsoid.WGS84) {
|
||||
// 1. 校验输入的笛卡尔坐标是否有效
|
||||
// if (!Cesium.Cartesian3.isValid(cartesian)) {
|
||||
// console.error("无效的笛卡尔坐标,无法调整高度");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// 2. 笛卡尔坐标 → Cartographic(地理坐标,含弧度经纬度和高度)
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(cartesian, ellipsoid);
|
||||
if (!cartographic) {
|
||||
console.error('笛卡尔坐标转换为Cartographic失败');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 调整高度(在原有高度上叠加offset,可为负数)
|
||||
cartographic.height += heightOffset;
|
||||
// 可选:确保高度不低于0(根据需求决定是否保留)
|
||||
// cartographic.height = Math.max(cartographic.height, 0);
|
||||
|
||||
// 4. Cartographic → 笛卡尔坐标(使用弧度经纬度转换)
|
||||
const adjustedCartesian = Cesium.Cartesian3.fromRadians(
|
||||
cartographic.longitude, // 经度(弧度)
|
||||
cartographic.latitude, // 纬度(弧度)
|
||||
cartographic.height, // 调整后的高度(米)
|
||||
ellipsoid
|
||||
);
|
||||
|
||||
return adjustedCartesian;
|
||||
}
|
||||
/**
|
||||
* 开始移动(保持不变)
|
||||
*/
|
||||
start() {
|
||||
if (this.isMoving) {
|
||||
console.log('模型已经在移动中');
|
||||
return;
|
||||
}
|
||||
if (this.positions.length < 2) {
|
||||
console.error('无法移动:位置点数量不足(至少需要2个)');
|
||||
return;
|
||||
}
|
||||
this.isMoving = true;
|
||||
this.lastTime = performance.now();
|
||||
this.progress = 0;
|
||||
console.log('开始移动,速度:', this.speed, '米/秒');
|
||||
this.animate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 动画循环函数(保持不变)
|
||||
*/
|
||||
animate() {
|
||||
if (!this.isMoving) return;
|
||||
const currentTime = performance.now();
|
||||
const timeDeltaMs = currentTime - this.lastTime;
|
||||
this.lastTime = currentTime;
|
||||
const timeDelta = timeDeltaMs / 1000;
|
||||
this.updatePosition(timeDelta);
|
||||
this.animationFrameId = requestAnimationFrame(() => this.animate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止移动(保持不变)
|
||||
*/
|
||||
stop() {
|
||||
if (!this.isMoving) return;
|
||||
this.isMoving = false;
|
||||
console.log('停止移动');
|
||||
if (this.animationFrameId) {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
this.animationFrameId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新模型位置(修改:叠加baseHeight)
|
||||
*/
|
||||
updatePosition(timeDelta) {
|
||||
if (!this.isMoving) return;
|
||||
const distanceToMove = this.speed * timeDelta;
|
||||
const currentPos = this.positions[this.currentIndex];
|
||||
const nextIndex = (this.currentIndex + 1) % this.positions.length;
|
||||
const nextPos = this.positions[nextIndex];
|
||||
|
||||
// 关键修改:计算当前点和下一点时,均叠加baseHeight
|
||||
const currentFinalHeight = (currentPos.height || 0) + this.baseHeight;
|
||||
const nextFinalHeight = (nextPos.height || 0) + this.baseHeight;
|
||||
|
||||
const currentCartesian = Cesium.Cartesian3.fromDegrees(currentPos.lon, currentPos.lat, currentFinalHeight);
|
||||
const nextCartesian = Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, nextFinalHeight);
|
||||
|
||||
// 后续逻辑保持不变
|
||||
const totalDistance = Cesium.Cartesian3.distance(currentCartesian, nextCartesian);
|
||||
if (totalDistance < 0.001) {
|
||||
this.currentIndex = nextIndex;
|
||||
this.progress = 0;
|
||||
this.setModelPosition(nextCartesian, this.getNextPositionCartesian(nextIndex));
|
||||
return;
|
||||
}
|
||||
this.progress += distanceToMove / totalDistance;
|
||||
if (this.progress >= 1.0) {
|
||||
const remainingDistance = (this.progress - 1.0) * totalDistance;
|
||||
this.currentIndex = nextIndex;
|
||||
if (!this.loop && this.currentIndex === this.positions.length - 1) {
|
||||
this.setModelPosition(nextCartesian, null);
|
||||
this.stop();
|
||||
console.log('已到达终点,停止移动');
|
||||
return;
|
||||
}
|
||||
const newTotalDistance = Cesium.Cartesian3.distance(nextCartesian, this.getNextPositionCartesian(this.currentIndex));
|
||||
this.progress = newTotalDistance > 0.001 ? remainingDistance / newTotalDistance : 0;
|
||||
this.setModelPosition(nextCartesian, this.getNextPositionCartesian(this.currentIndex));
|
||||
} else {
|
||||
const newPosition = Cesium.Cartesian3.lerp(currentCartesian, nextCartesian, this.progress, new Cesium.Cartesian3());
|
||||
this.setModelPosition(newPosition, nextCartesian);
|
||||
}
|
||||
this.viewer.scene.requestRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个位置的笛卡尔坐标(修改:叠加baseHeight)
|
||||
*/
|
||||
getNextPositionCartesian(currentIndex) {
|
||||
const nextIndex = (currentIndex + 1) % this.positions.length;
|
||||
const nextPos = this.positions[nextIndex];
|
||||
// 关键修改:返回下一点时叠加baseHeight
|
||||
const finalHeight = (nextPos.height || 0) + this.baseHeight;
|
||||
return Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, finalHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模型位置和方向(保持不变)
|
||||
*/
|
||||
setModelPosition(position, targetPosition) {
|
||||
if (!this.modelEntity) return;
|
||||
this.currentPosition = position;
|
||||
this.modelEntity.position.setValue(position);
|
||||
if (targetPosition) {
|
||||
const heading = this.calculateHeading(position, targetPosition);
|
||||
this.modelEntity.orientation.setValue(Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(heading, 0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的朝向(保持不变)
|
||||
*/
|
||||
calculateHeading(from, to) {
|
||||
const fromCartographic = Cesium.Cartographic.fromCartesian(from);
|
||||
const toCartographic = Cesium.Cartographic.fromCartesian(to);
|
||||
const startLat = fromCartographic.latitude;
|
||||
const startLon = fromCartographic.longitude;
|
||||
const endLat = toCartographic.latitude;
|
||||
const endLon = toCartographic.longitude;
|
||||
const dLon = endLon - startLon;
|
||||
const y = Math.sin(dLon) * Math.cos(endLat);
|
||||
const x = Math.cos(startLat) * Math.sin(endLat) - Math.sin(startLat) * Math.cos(endLat) * Math.cos(dLon);
|
||||
let heading = Math.atan2(y, x);
|
||||
heading = (heading + Cesium.Math.TWO_PI) % Cesium.Math.TWO_PI;
|
||||
heading = (heading + this.headingOffset) % Cesium.Math.TWO_PI;
|
||||
return heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置模型高度(保持不变)
|
||||
*/
|
||||
setModelHeight(height) {
|
||||
this.modelHeight = height;
|
||||
console.log('手动设置模型高度:', height, '米');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置航向角偏移(保持不变)
|
||||
*/
|
||||
setHeadingOffset(offsetDegrees) {
|
||||
this.headingOffset = Cesium.Math.toRadians(offsetDegrees);
|
||||
console.log('设置航向角偏移:', offsetDegrees, '度');
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁所有资源(保持不变)
|
||||
*/
|
||||
destroy() {
|
||||
this.stop();
|
||||
if (this.modelEntity) this.viewer.entities.remove(this.modelEntity);
|
||||
if (this.pathEntity) this.viewer.entities.remove(this.pathEntity);
|
||||
this.markerEntities.forEach((marker) => this.viewer.entities.remove(marker));
|
||||
if (this.iconEntity) this.viewer.entities.remove(this.iconEntity);
|
||||
this.modelEntity = null;
|
||||
this.pathEntity = null;
|
||||
this.markerEntities = [];
|
||||
this.iconEntity = null;
|
||||
}
|
||||
}
|
@ -34,13 +34,7 @@
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.8">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="User"
|
||||
:disabled="single"
|
||||
@click="handleBindUser()"
|
||||
v-hasPermi="['gps:equipment:unbindManmachine', 'gps:equipment:bindManmachine']"
|
||||
<el-button type="primary" plain icon="User" :disabled="single" @click="handleBindUser()" v-hasPermi="['gps:equipment:bindManmachine']"
|
||||
>绑定用户</el-button
|
||||
>
|
||||
</el-col>
|
||||
@ -97,11 +91,11 @@
|
||||
type="primary"
|
||||
icon="User"
|
||||
@click="scope.row.type === 1 ? handleUnbindUser(scope.row) : handleBindUser(scope.row)"
|
||||
v-hasPermi="['gps:equipment:unbindManmachine', 'gps:equipment:bindManmachine']"
|
||||
v-hasPermi="scope.row.type === 1 ? ['gps:equipment:unbindManmachine'] : ['gps:equipment:bindManmachine']"
|
||||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<!-- 新增:跳转空页面按钮 -->
|
||||
|
||||
<el-tooltip content="足迹" placement="top">
|
||||
<el-button
|
||||
link
|
||||
@ -113,7 +107,14 @@
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="历史记录" placement="top">
|
||||
<el-button link type="primary" icon="Clock" @click="handleOpenHistoryUser(scope.row.clientId, scope.row.userId)"> </el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Clock"
|
||||
@click="handleOpenHistoryUser(scope.row.clientId, scope.row.userId)"
|
||||
v-hasPermi="['gps:equipment:getUserList']"
|
||||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -146,9 +147,6 @@
|
||||
<el-option v-for="project in projectList" :key="project.projectId" :label="project.projectName" :value="project.projectId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称" prop="deviceName">
|
||||
<el-input v-model="bindForm.deviceName" disabled placeholder="设备名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择用户" prop="userId" required>
|
||||
<el-select v-model="bindForm.userId" placeholder="请选择用户" clearable>
|
||||
<el-option v-for="user in userList" :key="user.sysUserId" :label="user.userName" :value="user.sysUserId" />
|
||||
@ -175,6 +173,7 @@
|
||||
>
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="用户名" align="center" prop="userName" />
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="状态" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.type === 0 ? 'success' : 'info'" size="small">
|
||||
@ -182,6 +181,19 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="足迹" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Location"
|
||||
v-hasPermi="['gps:equipmentSon:getList']"
|
||||
@click="handleGoToEmptyPage(scope.row.userId, scope.row.projectId, currentHistoryClientId)"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="!historyUserLoading && historyUserList.length === 0" style="text-align: center; padding: 50px 0">
|
||||
@ -479,6 +491,8 @@ const handleViewAll = () => {
|
||||
};
|
||||
|
||||
const handleGoToEmptyPage = (userId: any, projectId: any, clientId: any) => {
|
||||
console.log('userId:', userId, 'projectId:', projectId, 'clientId:', clientId);
|
||||
|
||||
router.push({
|
||||
path: './equipmentGPS',
|
||||
query: {
|
||||
@ -578,41 +592,53 @@ const handleDelete = async (row?: ExtendedEquipmentVO) => {
|
||||
};
|
||||
|
||||
/** 绑定用户按钮操作 */
|
||||
const handleBindUser = async (row?: EquipmentVO) => {
|
||||
Object.assign(bindForm, {
|
||||
id: undefined,
|
||||
projectId: undefined,
|
||||
userId: undefined,
|
||||
clientId: row?.clientId,
|
||||
deviceName: undefined
|
||||
});
|
||||
bindUserFormRef.value?.resetFields();
|
||||
userList.value = [];
|
||||
|
||||
const _id = row?.id || ids.value[0];
|
||||
const handleBindUser = async (row: ExtendedEquipmentVO) => {
|
||||
try {
|
||||
const res = await getEquipment(_id);
|
||||
const equipmentData = res.data;
|
||||
bindForm.id = equipmentData.id;
|
||||
bindForm.deviceName = equipmentData.deviceName;
|
||||
bindForm.clientId = row?.clientId || equipmentData.clientId;
|
||||
if (!row) {
|
||||
proxy?.$modal.msgWarning('未获取到设备信息');
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.id === undefined || row.id === null || row.id === '') {
|
||||
proxy?.$modal.msgWarning('设备ID不能为空');
|
||||
return;
|
||||
}
|
||||
const deviceId = Number(row.id);
|
||||
if (isNaN(deviceId) || deviceId <= 0) {
|
||||
proxy?.$modal.msgWarning(`设备ID格式错误,必须是正整数,当前值: ${row.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!row.clientId || typeof row.clientId !== 'string' || row.clientId.trim() === '') {
|
||||
proxy?.$modal.msgWarning(`设备标识clientId格式错误,必须是非空字符串,当前值: ${row.clientId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化表单
|
||||
Object.assign(bindForm, {
|
||||
id: deviceId,
|
||||
projectId: undefined,
|
||||
userId: undefined,
|
||||
clientId: row.clientId,
|
||||
deviceName: row.deviceName || '未知设备'
|
||||
});
|
||||
bindUserFormRef.value?.resetFields();
|
||||
userList.value = [];
|
||||
|
||||
// 加载项目和用户数据
|
||||
if (projectList.value.length === 0) {
|
||||
await getProjects();
|
||||
}
|
||||
|
||||
if (equipmentData.projectId) {
|
||||
bindForm.projectId = equipmentData.projectId;
|
||||
await getUsersByProjectId(equipmentData.projectId);
|
||||
} else if (currentProject.value?.id) {
|
||||
if (currentProject.value?.id) {
|
||||
bindForm.projectId = currentProject.value.id;
|
||||
await getUsersByProjectId(currentProject.value.id);
|
||||
}
|
||||
|
||||
bindDialogVisible.value = true;
|
||||
} catch (error) {
|
||||
console.error('获取绑定用户信息失败:', error);
|
||||
proxy?.$modal.msgError('获取数据失败,请重试');
|
||||
console.error('打开绑定用户对话框失败:', error);
|
||||
proxy?.$modal.msgError('操作失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
@ -628,17 +654,9 @@ const submitBindUser = () => {
|
||||
|
||||
bindButtonLoading.value = true;
|
||||
try {
|
||||
const bindData = {
|
||||
id: bindForm.id,
|
||||
projectId: bindForm.projectId,
|
||||
userId: bindForm.userId,
|
||||
clientId: bindForm.clientId,
|
||||
deviceName: bindForm.deviceName
|
||||
};
|
||||
|
||||
console.log('提交绑定用户参数:', bindData);
|
||||
|
||||
await bindUser(bindData);
|
||||
// 直接使用bindForm数据调用bindUser接口
|
||||
console.log('提交绑定用户参数:', bindForm);
|
||||
await bindUser(bindForm);
|
||||
proxy?.$modal.msgSuccess('用户绑定成功');
|
||||
bindDialogVisible.value = false;
|
||||
await getList();
|
||||
@ -718,6 +736,8 @@ const handleOpenHistoryUser = async (clientId: string | number | undefined, curr
|
||||
historyUserDialogVisible.value = true;
|
||||
historyUserLoading.value = true;
|
||||
historyUserList.value = [];
|
||||
// 保存当前clientId用于足迹操作
|
||||
currentHistoryClientId.value = clientId;
|
||||
|
||||
try {
|
||||
const res = await gethistroyUser({
|
||||
@ -742,6 +762,27 @@ const handleOpenHistoryUser = async (clientId: string | number | undefined, curr
|
||||
}
|
||||
};
|
||||
|
||||
/** 历史弹窗中的足迹操作 */
|
||||
const handleFootprintOperation = () => {
|
||||
if (!historyUserList.value || historyUserList.value.length === 0) {
|
||||
proxy?.$modal.msgWarning('暂无用户数据,无法查看足迹');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取第一个用户的数据(通常是当前绑定用户)
|
||||
const firstUser = historyUserList.value[0];
|
||||
if (!firstUser.sysUserId) {
|
||||
proxy?.$modal.msgWarning('用户ID不存在,无法查看足迹');
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳转到足迹页面
|
||||
handleGoToEmptyPage(firstUser.sysUserId, firstUser.projectId || currentProject.value?.id, currentHistoryClientId.value);
|
||||
};
|
||||
|
||||
// 当前历史弹窗的clientId
|
||||
const currentHistoryClientId = ref('');
|
||||
|
||||
/** 页面挂载时初始化 */
|
||||
onMounted(() => {
|
||||
getList();
|
||||
|
@ -10,7 +10,7 @@
|
||||
<el-form-item label="地块名称" prop="landName">
|
||||
<el-input v-model="queryParams.landName" placeholder="请输入地块名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属村委会" prop="villageCommittee">
|
||||
<!-- <el-form-item label="所属村委会" prop="villageCommittee">
|
||||
<el-input v-model="queryParams.villageCommittee" placeholder="请输入所属村委会" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设计面积(亩)" prop="designArea">
|
||||
@ -21,7 +21,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="农户数(户)" prop="farmerCount">
|
||||
<el-input v-model="queryParams.farmerCount" type="number" placeholder="请输入农户数" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
|
@ -667,16 +667,20 @@ const data = reactive<PageData<LandTransferLedgerForm, LandTransferLedgerQuery>>
|
||||
},
|
||||
rules: {
|
||||
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
|
||||
landBlockId: [{ required: true, message: '请选择对应地块', trigger: 'change' }],
|
||||
landType: [{ required: true, message: '土地类型不能为空', trigger: 'change' }],
|
||||
transferStatus: [{ required: true, message: '请选择流转台账状态', trigger: 'change' }],
|
||||
designArea: [{ required: true, message: '请输入设计面积', trigger: 'blur' }],
|
||||
responsiblePerson: [{ required: true, message: '请输责任人', trigger: 'blur' }],
|
||||
expectedFinishDate: [{ required: true, message: '请选择预计完成时间', trigger: 'change' }],
|
||||
transferRatio: [
|
||||
// 动态校验:仅已流转状态下必填
|
||||
{
|
||||
required: true,
|
||||
message: '流转比例不能为空',
|
||||
trigger: ['blur', 'change'],
|
||||
validator: (rule, value, callback) => {
|
||||
if (data.form.transferStatus !== '1') {
|
||||
callback(); // 非已流转状态跳过校验
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (value === undefined || value === null || value === '') {
|
||||
@ -686,7 +690,6 @@ const data = reactive<PageData<LandTransferLedgerForm, LandTransferLedgerQuery>>
|
||||
}
|
||||
}
|
||||
},
|
||||
// 比例范围校验(0-100)
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value < 0 || value > 100) {
|
||||
@ -746,6 +749,7 @@ const sonSummaryInfo = computed(() => {
|
||||
const lastSelectedParent = ref<LandTransferLedgerVO | null>(null);
|
||||
const sonRules = {
|
||||
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
|
||||
parentId: [{ required: true, message: '父级ID不能为空', trigger: 'blur' }],
|
||||
landType: [{ required: true, message: '土地类型不能为空', trigger: 'change' }],
|
||||
transferRatio: [
|
||||
{
|
||||
|
Reference in New Issue
Block a user