zmj před 6 měsíci
rodič
revize
08038cdad4

+ 2 - 0
package.json

@@ -39,7 +39,9 @@
     "@amap/amap-jsapi-loader": "^1.0.1",
     "@riophae/vue-treeselect": "0.4.0",
     "axios": "0.28.1",
+    "cesium": "^1.95.0",
     "clipboard": "2.0.8",
+    "copy-webpack-plugin": "^5.1.1",
     "core-js": "3.37.1",
     "echarts": "^5.4.0",
     "element-china-area-data": "^5.0.2",

+ 3 - 0
public/index.html

@@ -6,6 +6,7 @@
     <meta name="renderer" content="webkit">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+   <!--  <link rel="stylesheet" href="./Cesium/Widgets/widgets.css"> -->
     <title><%= webpackConfig.name %></title>
     <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
     <!-- 视频播放器库 -->
@@ -261,5 +262,7 @@
 		    <div class="load_title">正在加载系统资源,请耐心等待</div>
         </div>
 	</div>
+
+ <!--  <script type="text/javascript" src="./Cesium/Cesium.js"></script> -->
   </body>
 </html>

+ 17 - 0
src/api/base/tasks.js

@@ -9,6 +9,23 @@ export function listTasks(query) {
   })
 }
 
+// 查询农事任务统计
+export function listTasksStatistics(query) {
+  return request({
+    url: '/base/tasks/listStatistics',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function listTasksTodoData() {
+  return request({
+    url: '/base/tasks/listTasksTodoData',
+    method: 'get',
+  })
+}
+
 // 查询农事任务详细
 export function getTasks(id) {
   return request({

+ 0 - 3
src/main.js

@@ -37,11 +37,9 @@ import DictTag from '@/components/DictTag'
 import VueMeta from 'vue-meta'
 // 字典数据组件
 import DictData from '@/components/DictData'
-
 // 地图组件
 import VueAMap from 'vue-amap';
 import * as echarts from 'echarts'
-
 Vue.use(VueAMap)
 Vue.prototype.$echarts = echarts
 VueAMap.initAMapApiLoader({
@@ -81,7 +79,6 @@ Vue.prototype.selectDictLabel = selectDictLabel
 Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.download = download
 Vue.prototype.handleTree = handleTree
-
 // 全局组件挂载
 Vue.component('DictTag', DictTag)
 Vue.component('Pagination', Pagination)

+ 1 - 1
src/permission.js

@@ -9,7 +9,7 @@ import { isRelogin } from '@/utils/request'
 
 NProgress.configure({ showSpinner: false })
 
-const whiteList = ['/login', '/register']
+const whiteList = ['/login', '/register','/map/map']
 
 const isWhiteList = (path) => {
   return whiteList.some(pattern => isPathMatch(pattern, path))

+ 6 - 1
src/router/index.js

@@ -132,7 +132,12 @@ export const constantRoutes = [
     name: 'AgriDigitalDashboard',
     hidden: true,
     meta: { title: '农业数字化决策大屏' }
-  }
+  },
+  {
+    path: '/map/map',//浏览器路由
+    component: () => import('@/views/base/map/index.vue'),//vue文件路由
+    hidden: true
+  },
 ]
 
 // 动态路由,基于用户权限动态去加载

+ 4 - 4
src/views/base/device/device-monitor.vue

@@ -981,17 +981,17 @@ export default {
         this.cameraList = [
           { 
             id: 1, 
-            name: '东区一号摄像头',
+            name: '合和智行',
             status: '在线', 
-            location: '东区1号地块', 
+            location: '智慧农场六号', 
             previewUrl: 'http://121.4.16.100:28080/#/play/wasm/' + encodeURIComponent('ws://121.4.16.100:6080/rtp/34020000001110000001_34020000001320000012.live.flv'),
             domId: 'camera-1'
           },
           { 
             id: 2, 
-            name: '东区2号摄像头', 
+            name: '葡萄园一号摄像头', 
             status: '在线', 
-            location: '东区2号地块', 
+            location: '智慧农场六号', 
             previewUrl: 'http://121.4.16.100:28080/#/play/wasm/' + encodeURIComponent('ws://121.4.16.100:6080/rtp/34020000001320000001.live.flv'),
             domId: 'camera-2'
           },

+ 35 - 81
src/views/base/machineWorkRecords/index.vue

@@ -148,7 +148,7 @@
       @pagination="getList" />
 
     <!-- 添加或修改农机作业记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body @opened="queryMachineNameFieldUserTe()">
       <el-form ref="form" :model="form" :rules="rules" label-width="100px">
         <!-- 编辑时显示作业单号 -->
         <el-row :gutter="20" v-if="title.indexOf('修改') !== -1">
@@ -162,11 +162,7 @@
         <!-- 第一步:选择所属农场(必填) -->
         <el-row :gutter="20">
           <el-col :span="24">
-            <!-- <el-form-item label="所属农场" prop="farmName">
-              <el-select v-model="form.farmName" placeholder="请先选择所属农场" style="width: 100%" @change="handleFarmChange">
-                <el-option v-for="farm in farmOptions" :key="farm.value" :label="farm.label" :value="farm.value" />
-              </el-select>
-            </el-form-item> -->
+        
             <el-form-item label="所属农场" prop="farmId">
               <treeselect v-model="form.farmId" :options="enabledDeptOptions" :show-count="true"
                 @input="queryMachineNameFieldUser(form.farmId)" placeholder="请先选择所属农场" />
@@ -178,12 +174,6 @@
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="农机名称" prop="machineId">
-              <!-- <el-select v-model="form.machineName" placeholder="请选择农机" style="width: 100%"
-                @change="handleMachineNameChange" :disabled="!form.farmName">
-                <el-option v-for="machine in filteredMachineOptions" :key="machine.value" :label="machine.label"
-                  :value="machine.value" />
-              </el-select> -->
-
               <el-select v-model="form.machineId" placeholder="请选择农机" :disabled="!form.farmId">
                 <el-option v-for="item in machinesList" :key="item.id" :label="item.machineName" :value="item.id">
                 </el-option>
@@ -192,11 +182,6 @@
           </el-col>
           <el-col :span="12">
             <el-form-item label="作业地块" prop="fieldId">
-              <!-- <el-select v-model="form.fieldName" placeholder="请选择作业地块" style="width: 100%" :disabled="!form.farmName">
-                <el-option v-for="field in filteredFieldOptions" :key="field.value" :label="field.label"
-                  :value="field.value" />
-              </el-select> -->
-
               <el-select v-model="form.fieldId" placeholder="请选择作业地块" :disabled="!form.farmId">
                 <el-option v-for="item in fields" :key="item.id" :label="item.fieldName" :value="item.id">
                 </el-option>
@@ -208,12 +193,6 @@
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="操作员" prop="operatorId">
-              <!-- <el-select v-model="form.operatorName" placeholder="请选择操作员" filterable style="width: 100%"
-                :disabled="!form.farmName">
-                <el-option v-for="operator in filteredOperatorOptions" :key="operator.value" :label="operator.label"
-                  :value="operator.value" />
-              </el-select> -->
-
               <el-select v-model="form.operatorId" placeholder="请选择操作员" :disabled="!form.farmId">
                 <el-option v-for="item in userName" :key="item.userId" :label="item.nickName" :value="item.userId">
                 </el-option>
@@ -280,7 +259,7 @@
 </template>
 
 <script>
-import { listMachineWorkRecords, getMachineWorkRecords, delMachineWorkRecords, addMachineWorkRecords, updateMachineWorkRecords, getMachineNameFieldUser } from "@/api/base/machineWorkRecords"
+import { listMachineWorkRecords, getMachineWorkRecords, delMachineWorkRecords, addMachineWorkRecords, updateMachineWorkRecords, getMachineNameFieldUser,MachineNameFieldUserText } from "@/api/base/machineWorkRecords"
 import { deptTreeSelect } from "@/api/system/user";
 import { listFieldName } from "@/api/base/field";
 import Treeselect from "@riophae/vue-treeselect";
@@ -399,9 +378,7 @@ export default {
         startTime: [
           { required: true, message: "开始时间不能为空", trigger: "change" }
         ],
-        endTime: [
-          { required: true, message: "结束时间不能为空", trigger: "change" }
-        ],
+
         operatorId: [
           { required: true, message: "操作员不能为空", trigger: "change" }
         ]
@@ -421,23 +398,14 @@ export default {
   },
   methods: {
 
-    queryMachineNameFieldUserTe() {
-      const id = this.form.id;
-      if (id != undefined) {
-        console.log("queryMachineNameFieldUserTe")
-        getTasks(id).then(response => {
-          this.form.fieldId = response.data.plotId
-          this.form.operatorId = response.data.assigneeId
-        })
-      }
-    },
-    
+
 
     queryMachineNameFieldUser(farmId) {
       console.log(this.form)
       console.log("queryMachineNameFieldUser")
-      this.form.plotId = null;
-      this.form.assigneeId = null;
+      this.form.fieldId = null;
+      this.form.machineId = null;
+      this.form.operatorId = null;
       if (farmId != undefined) {
         const data = {
           deptId: farmId
@@ -450,6 +418,21 @@ export default {
       }
     },
 
+    queryMachineNameFieldUserTe() {
+      const id = this.form.id;
+      if (id != undefined) {
+        console.log("queryMachineNameFieldUserTe")
+        getMachineWorkRecords(id).then(response => {
+          this.form.fieldId = response.data.fieldId
+          this.form.machineId = response.data.machineId
+          this.form.operatorId = response.data.operatorId
+        })
+      }
+    },
+    
+
+    
+
     /** 查询地块名称 **/
     getFieldNameList() {
       listFieldName().then(res => {
@@ -714,54 +697,25 @@ export default {
 
     /** 修改按钮操作 */
     handleUpdate(row) {
-      this.reset()
-
-      // 直接使用行数据进行回显(因为目前使用模拟数据)
-      this.form = { ...row }
-
-      // 确保所有字段都正确回显
-      this.form.workOrderId = row.workOrderId || row.id
-      this.form.machineCode = row.machineCode
-      this.form.machineName = row.machineName
-      this.form.machineType = row.machineType
-      this.form.farmName = row.farmName
-      this.form.fieldName = row.fieldName
-      this.form.taskName = row.taskName
-      this.form.taskType = row.taskType
-      this.form.status = row.status
-      this.form.startTime = row.startTime
-      this.form.endTime = row.endTime
-      this.form.operatorName = row.operatorName
-      this.form.remark = row.remark
-
-      // 编辑时根据农场初始化过滤选项(传递初始化标志,保持原有值)
-      if (this.form.farmName) {
-        this.handleFarmChange(this.form.farmName, true)
+     
+      if (row.farmId != undefined) {
+        const data = {
+          deptId: row.farmId
+        }
+        MachineNameFieldUserText(data).then(response => {
+          this.fields = response.data.fields
+          this.machinesList = response.data.machinesList
+          this.userName = response.data.users
+        })
       }
-
-      this.open = true
-      this.title = "修改农机作业记录"
-
-      // 如果将来需要使用API,可以使用以下代码
-      /*
+       this.reset()
       const id = row.id || this.ids
       getMachineWorkRecords(id).then(response => {
         this.form = response.data
-        // 确保保留原始作业单号
-        if (!this.form.workOrderId) {
-          this.form.workOrderId = row.workOrderId
-        }
-        // 编辑时根据农场初始化过滤选项
-        if (this.form.farmName) {
-          this.handleFarmChange(this.form.farmName)
-        }
         this.open = true
         this.title = "修改农机作业记录"
-      }).catch(error => {
-        console.warn('API调用失败,使用当前行数据:', error)
-        // 回退到使用行数据的逻辑
       })
-      */
+     
     },
     /** 提交按钮 */
     submitForm() {

+ 115 - 20
src/views/base/tasks/stats/index.vue

@@ -68,11 +68,16 @@
           </el-select>
         </el-form-item>
 
-        <el-form-item label="任务类型" prop="taskTypes">
+        <!-- <el-form-item label="任务类型" prop="taskTypes">
           <el-select v-model="filters.taskTypes" placeholder="请选择任务类型" multiple collapse-tags style="width: 180px">
             <el-option v-for="item in taskTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
-        </el-form-item>
+        </el-form-item> -->
+        <el-form-item label="任务类型" prop="typeName">
+        <el-select v-model="filters.typeName" placeholder="请选择任务类型" clearable>
+          <el-option v-for="dict in dict.type.task_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
 
         <el-form-item label="执行人" prop="executors">
           <el-select v-model="filters.executors" placeholder="请选择执行人" multiple collapse-tags style="width: 180px">
@@ -80,11 +85,16 @@
           </el-select>
         </el-form-item>
 
-        <el-form-item label="状态" prop="statuses">
+        <!-- <el-form-item label="状态" prop="statuses">
           <el-select v-model="filters.statuses" placeholder="请选择状态" multiple collapse-tags style="width: 180px">
             <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
-        </el-form-item>
+        </el-form-item> -->
+        <el-form-item label="任务状态" prop="taskStatus">
+        <el-select v-model="filters.taskStatus" placeholder="请选择任务状态" clearable>
+          <el-option v-for="dict in dict.type.task_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
 
         <el-form-item>
           <div class="action-buttons">
@@ -102,7 +112,7 @@
     <!-- KPI 卡片区域 -->
     <div class="kpi-section stats-card">
       <div class="kpi-grid">
-        <div v-for="(kpi, index) in kpiData" :key="index" class="kpi-card" @click="handleKpiClick(kpi.key)">
+        <div v-for="(kpi, index) in kpiData" :key="index" class="kpi-card" >
           <div class="kpi-card__icon" :style="{ backgroundColor: kpi.iconBg }">
             <i :class="kpi.icon" :style="{ color: kpi.color }"></i>
           </div>
@@ -117,7 +127,7 @@
               <span v-if="kpi.unit" class="kpi-unit">{{ kpi.unit }}</span>
             </div>
           </div>
-          <el-dropdown trigger="click" @command="handleKpiAction" class="kpi-card__menu">
+          <!-- <el-dropdown trigger="click" @command="handleKpiAction" class="kpi-card__menu">
             <span class="kpi-menu-trigger">
               <i class="el-icon-more"></i>
             </span>
@@ -129,7 +139,7 @@
                 <i class="el-icon-view"></i> 查看明细
               </el-dropdown-item>
             </el-dropdown-menu>
-          </el-dropdown>
+          </el-dropdown> -->
         </div>
       </div>
     </div>
@@ -365,6 +375,7 @@ import seedrandom from 'seedrandom'
 import CountTo from 'vue-count-to'
 import { deptTreeSelect } from "@/api/system/user";
 import { listFieldName } from "@/api/base/field"
+import { listTasksStatistics} from "@/api/base/tasks"
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 
@@ -374,6 +385,7 @@ export default {
     CountTo,
     Treeselect
   },
+  dicts: ['task_type', 'task_status', 'field_soil_type', 'sys_field_type'],
   data() {
     return {
       // 部门树选项
@@ -427,9 +439,9 @@ export default {
         farms: [],
         plots: [],
         crops: [],
-        taskTypes: [],
+        typeName: [],
         executors: [],
-        statuses: []
+        taskStatus: []
       },
 
       // 下拉选项
@@ -525,6 +537,13 @@ export default {
     this.getDeptTree();
     this.getFieldNameList();
     // this.initOptions()
+    /* const queryParams = {
+
+    }
+    listTasksStatistics(queryParams).then(response => {
+        console.log(response)
+        console.log(response.data)
+      }) */
   },
 
   mounted() {
@@ -603,9 +622,9 @@ export default {
     // 加载所有数据
     loadAllData() {
       this.loadKpiData()
-      this.loadTrendData()
-      this.loadDimensionData()
-      this.loadTodoData()
+      this.loadTrendData() // 加载趋势数据
+      this.loadDimensionData() // 加载维度对比数据
+      this.loadTodoData() // 加载待办数据
     },
 
     // 初始化图表
@@ -639,6 +658,7 @@ export default {
         this.formatDate(start),
         this.formatDate(end)
       ]
+      console.log(this.filters.dateRange);
       this.loadAllData()
     },
 
@@ -701,8 +721,87 @@ export default {
 
     // 加载KPI数据
     loadKpiData() {
-      const kpi = this.mockKpi(this.filters)
-      this.kpiData = [
+      console.log(this.filters)
+      /* const kpi = this.mockKpi(this.filters) */
+      this.filters.params = {}
+      if (null != this.filters.dateRange && '' != this.filters.dateRange) {
+        this.filters.params["beginCreateTime"] = this.filters.dateRange[0];
+        this.filters.params["endCreateTime"] = this.filters.dateRange[1];
+      }
+      listTasksStatistics(this.filters).then(response => {
+        console.log("加载KPI数据")
+        console.log(response)
+        console.log(response.data)
+        const kpi = response.data
+
+        this.kpiData = [
+        {
+          title: '任务总数',
+          value: kpi.taskCount,
+          type: 'number',
+          color: '#1F2937',
+          key: 'total',
+          icon: 'el-icon-s-data',
+          iconBg: 'rgba(74, 144, 226, 0.12)'
+        },
+        {
+          title: '已完成',
+          value: kpi.completedCount,
+          type: 'number',
+          color: '#3BB44A',
+          key: 'completed',
+          icon: 'el-icon-circle-check',
+          iconBg: 'rgba(59, 180, 74, 0.12)'
+        },
+        {
+          title: '待完成',
+          value: kpi.pendingCount,
+          type: 'number',
+          color: '#3BB44A',
+          key: 'completion_rate',
+          icon: 'el-icon-pie-chart',
+          iconBg: 'rgba(59, 180, 74, 0.12)'
+        },
+        {
+          title: '逾期数',
+          value: kpi.overdueCount,
+          type: 'number',
+          color: '#4A90E2',
+          key: 'ontime_rate',
+          icon: 'el-icon-timer',
+          iconBg: 'rgba(74, 144, 226, 0.12)'
+        },
+        {
+          title: '完成率',
+          value: kpi.completionRate * 100,
+          type: 'percent',
+          color: '#20B2AA',
+          key: 'avg_duration',
+          icon: 'el-icon-stopwatch',
+          iconBg: 'rgba(32, 178, 170, 0.12)',
+          unit: kpi.durationUnit
+        },
+        {
+          title: '准时率',
+          value: kpi.onTimeRate * 100,
+          type: 'percent',
+          color: '#E85D75',
+          key: 'overdue',
+          icon: 'el-icon-warning',
+          iconBg: 'rgba(232, 93, 117, 0.12)'
+        },
+        {
+          title: '平均完成时长',
+          value: kpi.avgCompletionDuration,
+          type: 'number',
+          color: '#4A90E2',
+          key: 'in_progress',
+          icon: 'el-icon-loading',
+          iconBg: 'rgba(74, 144, 226, 0.12)'
+        }
+      ]
+      })
+      /* this.kpiData = [
         {
           title: '任务总数',
           value: kpi.total,
@@ -776,19 +875,15 @@ export default {
           icon: 'el-icon-time',
           iconBg: 'rgba(139, 148, 158, 0.12)'
         }
-      ]
+      ] */
+    
     },
 
     // 加载趋势数据
     loadTrendData() {
       this.trendLoading = true
       setTimeout(() => {
-      console.log("进入mockTrend的条件")
-      console.log(this.filters)
-      console.log(this.trendMetric)
         const data = this.mockTrend(this.filters, this.trendMetric)
-        console.log("loadTrendData加载趋势数据")
-        console.log(data)
         this.updateTrendChart(data)
         this.trendLoading = false
       }, 500)

+ 21 - 3
vue.config.js

@@ -7,6 +7,8 @@ function resolve(dir) {
 
 const CompressionPlugin = require('compression-webpack-plugin')
 
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+
 const name = process.env.VUE_APP_TITLE || '爱智农智慧管理平台' // 网页标题
 
 const port = process.env.port || process.env.npm_config_port || 80 // 端口
@@ -36,7 +38,8 @@ module.exports = {
     proxy: {
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
-        target: `http://localhost:8080`,
+        target: `http://121.4.16.100:8080`,
+        // target: `http://localhost:8080`,
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''
@@ -56,7 +59,8 @@ module.exports = {
     name: name,
     resolve: {
       alias: {
-        '@': resolve('src')
+        '@': resolve('src'),
+        cesium: 'cesium',
       }
     },
     plugins: [
@@ -68,7 +72,13 @@ module.exports = {
         algorithm: 'gzip',                             // 使用gzip压缩
         minRatio: 0.8,                                 // 压缩比例,小于 80% 的文件不会被压缩
         deleteOriginalAssets: false                    // 压缩后删除原文件
-      })
+      }),
+      new CopyWebpackPlugin([
+        { from: 'node_modules/cesium/Build/Cesium/Workers', to: 'cesium/Workers' },
+        { from: 'node_modules/cesium/Build/Cesium/ThirdParty', to: 'cesium/ThirdParty' },
+        { from: 'node_modules/cesium/Build/Cesium/Assets', to: 'cesium/Assets' },
+        { from: 'node_modules/cesium/Build/Cesium/Widgets', to: 'cesium/Widgets' }
+      ]),
     ],
   },
   chainWebpack(config) {
@@ -91,6 +101,14 @@ module.exports = {
         symbolId: 'icon-[name]'
       })
       .end()
+       config.module
+      .rule('cesium')
+      .test(/\.js$/)
+      .include.add(/cesium/)
+      .end()
+      .use('babel')
+      .loader('babel-loader')
+      .end();
 
     config.when(process.env.NODE_ENV !== 'development', config => {
           config