This commit is contained in:
2025-09-06 19:20:55 +08:00
5 changed files with 555 additions and 67 deletions

View File

@ -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: {
modelMover = new ModelPathMover(window.Earth3.viewer, {
// modelUrl: './air.glb', // 模型URL
positions: positions,
width: 5,
material: Cesium.Color.RED,
clampToGround: true
}
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;

View 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;
}
}

View File

@ -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) => {
const handleBindUser = async (row: ExtendedEquipmentVO) => {
try {
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: undefined,
id: deviceId,
projectId: undefined,
userId: undefined,
clientId: row?.clientId,
deviceName: undefined
clientId: row.clientId,
deviceName: row.deviceName || '未知设备'
});
bindUserFormRef.value?.resetFields();
userList.value = [];
const _id = row?.id || ids.value[0];
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 (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();

View File

@ -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>

View File

@ -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: [
{