当前位置: 首页 > news >正文

vue实现用户动态权限登录

一、使用vue+elementUI搭登录框架,主要就是1、2、3、4
在这里插入图片描述
配置:
①vue.config.js

'use strict'
const path = require('path')function resolve(dir) {return path.join(__dirname, dir)
}// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {publicPath: '/',outputDir: 'dist',assetsDir: 'static',lintOnSave: false, // 是否校验语法productionSourceMap: false,devServer: {port: 8888,open: true,},configureWebpack: {resolve: {alias: {'@': resolve('src')}}}
}

②main.js

import Vue from "vue"
import App from "./App.vue"
import router from "./router"
import store from "./store"
import ElementUI from "element-ui"
import 'element-ui/lib/theme-chalk/index.css'
import "./router/router-config"  // 路由守卫,做动态路由的地方Vue.config.productionTip = false
Vue.use(ElementUI)new Vue({router,store,render: (h) => h(App),
}).$mount("#app")

二、代码部分

  1. layout目录:
  • layout/index.vue
<template><el-container><el-header><header-temp></header-temp></el-header><el-container><el-aside width="200px"><sidebar class="sidebar-container"></sidebar></el-aside><el-main><app-main></app-main></el-main></el-container></el-container>
</template>
<script>
import AppMain  from './appMain'      // 页面布局的右侧区域
import sidebar  from './sideBar'      // 页面布局的左侧菜单
import headerTemp from "./headerTemp" // 页面布局的header菜单
export default {name: 'layout',components: { sidebar, AppMain, headerTemp }
}
</script>
<style>
.el-header{padding: 0!important;margin-left: 180px;}
</style>

①appMain/index.vue

<template><section class="app-main"><transition name="fade" mode="out-in"><router-view></router-view></transition></section>
</template><script>
export default { name: 'AppMain' }
</script>

②headerTemp/index.vue

<template><div class="header-temp-container"><div class="userInfo"><el-image :src="userInfo.avatar" class="eImage"></el-image><el-dropdown @command="handleLogout"><div class="detail user-link"><span>{{ userInfo.name }}</span><span>{{ userInfo.desc }}</span><i class="el-icon--right"></i></div><template #dropdown><el-dropdown-menu><el-dropdown-item command="logout">退出</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></div>
</template>
<script>
import { Message } from "element-ui"
export default {name: "header-temp-container",data() {return {userInfo: JSON.parse(window.localStorage.getItem("userInfo"))}},methods: {// 退出登录handleLogout(key) {if(key == "logout") {window.localStorage.removeItem("userInfo")Message({ type: 'success', message: "退出登录", showClose: true, duration: 3000 })this.$router.replace({ path: "/login" })location.reload()}}}
}
</script>
<style scoped>
.header-temp-container{border-bottom: 1px solid #ddd; width: 100%;height: 60px;}
.userInfo{display: flex;flex-direction: row;align-items: center;justify-content: flex-end;height: 100%;margin-right: 20px;}
.eImage{width: 40px;height: 40px;border-radius: 50%;margin-right: 10px;}
.detail{display: flex;flex-direction: column;align-items: flex-start;justify-content: space-around;}
</style>

③sideBar/index.vue

<template><el-menumode="vertical"unique-opened:default-active="$route.path"background-color="#304156"text-color="#fff"active-text-color="#409EFF"><sidebar-item :routes="routes"></sidebar-item></el-menu>
</template><script>
import sidebarItem from "./sidebarItem";export default {components: { sidebarItem },computed: {routes() {return this.$router.options.routes;},},
};
</script>
<style scoped>
.sidebar-container {transition: width 0.28s;width: 180px !important;height: 100%;position: fixed;top: 0;bottom: 0;left: 0;z-index: 1001;
}
.sidebar-container a {display: inline-block;width: 100%;
}
.sidebar-container .svg-icon {margin-right: 16px;
}
.sidebar-container .el-menu {border: none;width: 100%;
}
</style>
<style>
a{text-decoration: none;}
</style>

④sideBar/sidebarItem.vue

<template><div class="menu-wrapper"><template v-for="item in routes" v-if="!item.hidden && item.children"><router-linkv-if="item.children.length === 1 &&!item.children[0].children &&!item.alwaysShow":to="item.children[0].path":key="item.children[0].name"><el-menu-item:index="item.children[0].path":class="{ 'submenu-title-noDropdown': !isNest }"><span v-if="item.children[0].meta && item.children[0].meta.title">{{item.children[0].meta.title}}</span></el-menu-item></router-link><el-submenu v-else :index="item.name || item.path" :key="item.name"><template slot="title"><span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span></template><template v-for="child in item.children" v-if="!child.hidden"><sidebar-item:is-nest="true"class="nest-menu"v-if="child.children && child.children.length > 0":routes="[child]":key="child.path"></sidebar-item><router-link v-else :to="child.path" :key="child.name"><el-menu-item :index="child.path"><span v-if="child.meta && child.meta.title">{{child.meta.title}}</span></el-menu-item></router-link></template></el-submenu></template></div>
</template>
<script>
export default {name: "sidebarItem",props: {routes: { type: Array },isNest: {type: Boolean,default: false,},},
};
</script>
<style scoped>
.nest-menu .el-submenu > .el-submenu__title,
.el-submenu .el-menu-item {min-width: 180px !important;background-color: #1f2d3d !important;
}
.nest-menu .el-submenu > .el-submenu__title,
.el-submenu .el-menu-item :hover {background-color: #001528 !important;
}
.el-menu--collapse .el-menu .el-submenu {min-width: 180px !important;
}
</style>
  1. 路由配置
    ①router/index.js
import Vue from "vue"
import VueRouter from "vue-router"
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)return originalPush.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
const routes = [{ name: "login", path: "/login", meta: { title: "login" },component: () => import("../views/login/index"), hidden: true }
]
const router = new VueRouter({ routes })
export default router

②router/router-config.js

import router from "./index"
import Layout from "../layout/index"
import NProgress from 'nprogress' // progress barNProgress.configure({ showSpinner: false }) // NProgress Configurationconst filterRoutes = ["/login"]   // 需要过滤掉的路由
router.beforeEach((to, from, next) => {// start progress barNProgress.start()// 获取路由 meta 中的title,并设置给页面标题document.title = "动态路由(" + to.meta.title + ")"// 判断路由指向是否在需要过滤的路由地址数组里// 如果在,则直接跳进页面,无需判断if(filterRoutes.indexOf(to.path) !== -1) {next()return false}if(router.options.routes.length == 1) {// 获取token和原始路由数组const userInfo = JSON.parse(window.localStorage.getItem('userInfo')) ?? ""// 当token和原始路由都存在的时候if(userInfo.token && userInfo.routes) {onFilterRoutes(to, next, userInfo.routes)  // 执行路由过滤和跳转}else {next({ path: "/login", replace: true })}} else next()
})router.afterEach(() => {// finish progress barNProgress.done()
})// 路由拼接
function loadView(view) {return () => import(`@/views/${ view }`)
}// 路由过滤和跳转
async function onFilterRoutes(to, next, e) {const routes = await filterASyncRoutes(e)    // 路由过滤routes.sort((a, b) => a['id'] - b['id'])routes.forEach(item => {router.options.routes.push(item)router.addRoute(item)})next({ ...to, replace: true })
}// 路由过滤   遍历路由 转换为组件对象和路径
function filterASyncRoutes(data) {const routes = data.filter(item => {if(item["component"] === "Layout") item.component = Layoutelse item["component"] = loadView(item["component"])// 路由递归,转换组件对象和路径if(item["children"] && item["children"].length > 0) item["children"] = filterASyncRoutes(item.children)return true})return routes
}
  1. 登录(views/login/index.vue)
<template><div class="login-wrapper"><div class="modal"><el-form :model="user" status-icon :rules="rules" ref="userForm"><div class="title">动态路由</div><el-form-item prop="username"><el-input type="text" prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="user.username" /></el-form-item><el-form-item prop="password"><el-input type="password" prefix-icon="el-icon-view" placeholder="请输入密码" v-model="user.password" /></el-form-item><el-form-item><el-button type="primary" class="btn-login" @click="login">登录</el-button></el-form-item><div class="toast"><span>管理员账号:admin </span><span>密码:654321</span></div><div class="toast"><span>普通人员账号:people</span><span>密码:123456</span></div></el-form></div></div>
</template>
<script>
import dynamicUser from "../../mock"
import { Message } from "element-ui"export default {name: 'login',data() {return {user: {username: "",password: ""},rules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' }]}}},methods: {login() {this.$refs.userForm.validate(( valid ) => {if(valid) {let flag = !1window.localStorage.removeItem("userInfo")dynamicUser.forEach(item => {if(item["username"] == this.user['username'] && item["password"] == this.user['password']) {flag = !0Message({ type: 'success', message: "登录成功", showClose: true, duration: 3000 })window.localStorage.setItem("userInfo", JSON.stringify(item))this.$router.replace({ path: "/" })}})if(!flag) Message({ type: 'warning', message: "账号密码错误,请重试!", showClose: true, duration: 3000 })} else return false})}}
}
</script>
<style scoped>
.login-wrapper {display: flex;flex-direction: row;align-items: center;justify-content: center;background-color: #fff;width: 100vw;height: 100vh;
}
.modal {width: 360px;height: 380px;box-shadow: 0 0 10px 5px #ddd;padding: 50px;border-radius: 5px;
}
.title {width: 100%;text-align: center;line-height: 1.5;font-size: 50px;margin-bottom: 30px;
}
.btn-login {width: 100%;
}
.toast{width: 100%;display: flex;flex-direction: row;align-items: center;justify-content: space-between;height: 50px;
}
</style>
  1. 动态返回路由
    使用mock.js造了一条路由,后端返回格式类似于下列这种样式:
const dynamicUser = [{name: "管理员",avatar: "https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image",desc: "管理员 - admin",username: "admin",password: "654321",token: "rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 3, name: "/example", path: "/example", component: "Layout", redirect: "/example/tree", meta: { title: "example" }, hidden: false, children: [{ name: "/tree", path: "/example/tree", meta: { title: "tree" }, component: "tree/index" },{ name: "/copy", path: "/example/copy", meta: { title: "copy" }, component: "tree/copy" }] },{ id: 4, name: "/table", path: "/table", component: "Layout", redirect: "/table/index", hidden: false, children: [{ name: "/table/index", path: "/table/index", meta: { title: "table" }, component: "table/index" }] },{ id: 5, name: "/admin", path: "/admin", component: "Layout", redirect: "/admin/index", hidden: false, children: [{ name: "/admin/index", path: "/admin/index", meta: { title: "admin" }, component: "admin/index" }] },{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]},{name: "普通用户",avatar: "https://img0.baidu.com/it/u=2097980764,1024880469&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=300",desc: "普通用户 - people",username: "people",password: "123456",token: "4es8eyDwznXrCX3b3439EmTFnIkrBYWh",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]}
]export default dynamicUser

相关文章:

vue实现用户动态权限登录

一、使用vueelementUI搭登录框架&#xff0c;主要就是1、2、3、4 配置&#xff1a; ①vue.config.js use strict const path require(path)function resolve(dir) {return path.join(__dirname, dir) }// All configuration item explanations can be find in https://cli.v…...

ONNX模型修改为自定义节点

参考一 首先&#xff0c;需要将ONNX模型中的节点修改为自定义节点。要实现这一点&#xff0c;您需要了解自定义节点的定义和如何在ONNX中使用它们。ONNX定义了一个自定义运算符的接口&#xff0c;您可以使用该接口定义自己的运算符&#xff0c;并将其编译为ONNX模型可以识别的…...

内存对齐原则

struct &#xff08;1&#xff09;结构体第一个数据成员放在offset为0的地方&#xff0c;后面每个成员相对于结构体首地址的偏移量&#xff08;offset&#xff09;都是成员大小&#xff08;该变量类型所占字节&#xff09;的整数倍&#xff0c;如有需要编译器会在成员之间加上填…...

Java SPI 一 之SPI(Service Provider Interface)进阶 AutoService

​ 一、SPI&#xff08;Service Provider Interface&#xff09; 1.1 介绍 SPI&#xff08;Service Provider Interface&#xff09;&#xff0c;是JDK内置的一种 服务提供发现机制(为某个接口寻找服务实现的机制)&#xff0c;可以用来启用框架扩展和替换组件&#xff0c;其…...

C++ list类成员函数介绍

目录 &#x1f914;list模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;list内存结构图解&#xff1a; &#x1f914; list的成员函数&#xff1a; &#x1f60a;list构造函数&#xff1a; &#x1f50d;代码示例&#xff1a; &#x1f50d;运行结果&…...

【服务器】本地搭建PHP简单Imagewheel私人云图床

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…...

第四十二回:DateRangePickerDialog Widget

文章目录 概念介绍使用方法示例代码 我们在上一章回中介绍了DatePickerDialog Widget相关的内容,本章回中将介绍 DateRangePickerDialog Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在这里说的DateRangePickerDialog是一种弹出窗口&#xff0c;只不…...

【C++系列P3】‘类与对象‘-三部曲——[基础知识](1/3)

前言 大家好吖&#xff0c;欢迎来到 YY 滴 C系列 &#xff0c;热烈欢迎&#xff01; 【 类与对象-三部曲】的大纲主要内容如下&#xff1a; 如标题所示&#xff0c;本章是【 类与对象-三部曲】三章中的第一章节——基础知识章节&#xff0c;主要内容如下&#xff1a; 目录 一.…...

Android UEvent事件分析之Kernel上报电量

kernel-4.4\drivers\power\power_supply_core.c 当电量信息需要更新的时候,kernel会调用power_supply_changed_work这个工作队列,使用kobject_uevent函数往上发送uevent事件,action是KOBJ_CHANGE; static void power_supply_changed_work(struct work_struct *work) {uns…...

C++ vector模板和deque的简单应用

目录 &#x1f914;vector模板和deque的简单介绍: &#x1f914;vector和deque的主要不同之处&#xff1a; &#x1f914;今天我们用vector模板和deque模板实现以下简单的功能&#xff1a; 代码实现&#xff1a; &#x1f914;讲解&#xff1a; &#x1f914;vector模板和d…...

声明式事务控制

声明式事务控制 编程式事务控制相关对象 PlatformTransactionManager PlatformTransactionManager接口是spring的事务管理器&#xff0c;它里面提供了常用的操作事务的方法 方法说明TransactionStatus getTransaction(TransactionDefaultion defination)获取事务的状态信息…...

cisp pte模拟题

1.信息搜集 本题共三个key 端口 1433 27689 存活ip 192.168.85.137 2.访问网站27689进行信息搜集 一个登录框&#xff0c;sql注入失败&#xff0c;暴力破解失败 扫描目录 发现三个文件robots.txt ,web.config 除了robots.txt,其他都访问不了 访问robots.txt,发现一个file参数…...

Docker容器 和 Kubernetes容器集群管理系统

一、快速了解Docker 1. 什么是Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;基于Go语言并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以…...

港联证券|资金疯狂涌入AI,这一板块涨幅超90%!万亿巨头继续狂飙

国内外资金继续加码AI概念股。 当前&#xff0c;国内政策层面对于通用人工智能和算力中心的创新发展扶持政策不断推出&#xff0c;资本市场对于AI&#xff08;人工智能&#xff09;热情不断升温。在AI下游应用中&#xff0c;游戏板块成为最被看好的投资标的&#xff0c;资金流入…...

短视频矩阵系统源码-开源开发php语言搭建

短视频矩阵系统源码---------- php源码是什么&#xff1f; PHP源码指的就是PHP源代码&#xff0c;源代码是用特定编程语言编写的人类可读文本&#xff0c;源代码的目标是为可以转换为机器语言的计算机设置准确的规则和规范。因此&#xff0c;源代码是程序和网站的基础。 PHP…...

PFEA112-20 3BSE050091R20 张力控制器

您的张力测量系统包括&#xff1a; 张力电子PFEA111或PFEA112–PFEA111是一款经济高效的紧凑型用户友好型张力电子产品&#xff0c;提供 来自两个称重传感器的精确可靠的快速模拟SUM信号&#xff0c;用于控制 和/或监测。显示器可以显示SUM、单个A&B和差异信号小尺寸和DIN导…...

Java springboot+vue生成报纸排版页面的新闻官网

实现方案&#xff0c;可以作为您开始开发的参考&#xff1a; 后端&#xff1a; 使用Spring Boot框架构建Java Web应用&#xff1b;使用MyBatis Plus进行ORM映射&#xff0c;管理MySQL数据库&#xff1b;按照需求定义新闻数据表、类别数据表和用户数据表&#xff0c;使用默认的…...

Terra-Luna归零一年后:信任重建、加密未来路在何方?

本月既是Terra-Luna归零的一周年&#xff0c;也是FTX崩溃的第六个月&#xff0c;而这两个事件分别代表着2022年加密市场连环爆的开始与高潮&#xff0c;引发了加密行业15年历史上最可怕的生存危机。 尽管今年市场行情有所回暖&#xff0c;比特币开年至今涨幅70%&#xff0c;以太…...

Android 12.0 手动安装Persistent app失败的解决方案

1.概述 在12.0的系统产品开发中,对于一些安装app的失败问题,需要看日志 和抛出异常来判断问题所在,在最近的一些app安装失败抛出了关于Presistent app安装失败的问题,就需要从PMS安装的过程中看异常抛出的原因解决问题所在 2.手动安装Persistent app失败的解决方案的核心类…...

Unity3D安装:从命令行安装 Unity

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 从命令行安装 Unity 如果要在组织中自动部署 Unity&#xff0c;可以从命令行安装 Editor 和其他组件。这些组件是普通的安装程序可执行程序和软件包&#xff0c;可以给用来自动部署…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...