无人机管理后台前端(已迁走)
rain
2025-06-12 0082fa417fb2af9add3406bf05ab3e3e45890c34
src/views/tickets/orderLog.vue
@@ -15,45 +15,78 @@
        <div class="tab-content">
          <!-- 查询条件筛选栏 -->
          <div class="filter-bar">
            <el-input style="width: 250px" v-model="filters.key_word" placeholder="输入工单编号/名称/内容/姓名" class="filter-item"
              clearable @keyup.enter="handleSearch" />
            <div class="search-bar-box">
              <div class="search-bar-box-item">
                <el-input v-model="filters.key_word" placeholder="输入工单编号/名称/内容/姓名" clearable
                  @keyup.enter="handleSearch" />
              </div>
            <el-select placeholder="请选择所属单位" v-model="filters.create_dept" @change="handleDepartmentChange"
              class="filter-item" clearable>
              <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" />
            </el-select>
              <div class="search-bar-box-item">
                <el-select placeholder="请选择所属单位" v-model="filters.create_dept" clearable>
                  <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" />
                </el-select>
              </div>
            <el-date-picker v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
              end-placeholder="结束日期" class="filter-item" style="width: 100px" :default-value="datePickerDefaultVal" />
            <el-select v-model="filters.file_id" placeholder="请选择关联航线" class="filter-item frequency" clearable>
              <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name"
                :value="item.wayline_id" />
            </el-select>
              <div class="search-bar-box-item">
                <el-date-picker v-model="filters.dateRange" type="daterange" range-separator="至"
                  start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" />
              </div>
            <el-select v-model="filters.ai_types" placeholder="关联算法" class="filter-item" clearable>
              <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
            </el-select>
              <div class="search-bar-box-item">
                <el-select v-model="filters.file_id" placeholder="请选择关联航线" clearable>
                  <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name"
                    :value="item.wayline_id" />
                </el-select>
              </div>
            <el-select v-model="filters.type" placeholder="请选择工单类型" class="filter-item" clearable>
              <el-option v-for="item in types" :key="item.dictValue" :label="item.dictValue" :value="item.dictKey" />
            </el-select>
              <div class="search-bar-box-item">
                <el-select v-model="filters.ai_types" placeholder="关联算法" clearable>
                  <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue"
                    :value="item.dictKey" />
                </el-select>
              </div>
            <el-select v-model="filters.status" placeholder="请选择工单状态" class="filter-item" clearable>
              <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" />
            </el-select>
            <el-date-picker v-model="filters.cycleDateRange" type="daterange" range-separator="至"
              start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" class="filter-item"
              :default-value="datePickerDefaultVal" style="width: 220px !important" />
            <el-select v-model="filters.rep_fre_type" placeholder="请选择频次" class="filter-item frequency">
              <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
            </el-select>
            <el-time-picker style="width: 150px" v-model="filters.deal_time" placeholder="请选择执行时间" prop="deal_time"
              value-format="HH:mm" :picker-options="{
                selectableRange: '00:00 - 23:59',
              }" />
            <el-col :span="8"> </el-col>
            <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
            <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
              <div class="search-bar-box-item">
                <el-select v-model="filters.type" placeholder="请选择工单类型" clearable>
                  <el-option v-for="item in types" :key="item.dictValue" :label="item.dictValue"
                    :value="item.dictKey" />
                </el-select>
              </div>
              <div class="search-bar-box-item">
                <el-select v-model="filters.status" placeholder="请选择工单状态" clearable>
                  <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" />
                </el-select>
              </div>
            </div>
            <div class="search-bar-box">
              <div class="search-bar-box-item flex-2">
                <el-date-picker v-model="filters.cycleDateRange" type="daterange" range-separator="至"
                  start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" :default-value="datePickerDefaultVal" />
              </div>
              <div class="search-bar-box-item">
                <el-select v-model="filters.rep_fre_type" placeholder="请选择频次">
                  <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
                </el-select>
              </div>
              <div class="search-bar-box-item">
                <el-time-picker v-model="filters.deal_time" placeholder="请选择执行时间" prop="deal_time" value-format="HH:mm"
                  :picker-options="{
                    selectableRange: '00:00 - 23:59',
                  }" />
              </div>
              <div class="search-bar-box-item"></div>
              <div class="search-bar-box-item"></div>
              <div class="search-bar-box-item search-btn">
                <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
                <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button>
              </div>
            </div>
          </div>
          <!-- 表格部分 -->
@@ -68,37 +101,33 @@
            <template #menu="{ row }">
              <div class="menu-custom-box">
                <div v-if="row.status == 1">
                  <el-button class="audit-btn" v-if="hasPaddingBtnPermission()" type="text" icon="el-icon-view"
                <template v-if="row.status == 1">
                  <el-button v-if="hasPaddingBtnPermission()" type="text" icon="el-icon-view"
                    @click="handleCheckDetail(row)">审核</el-button>
                </div>
                <div v-if="
                  (userInfo.user_id == row.create_user || hasRecallPaddingBtnPermission()) &&
                  row.status == 1
                ">
                </template>
                <!-- v-if="
                    (userInfo.user_id == row.create_user || hasRecallPaddingBtnPermission()) &&
                    row.status == 1
                  " -->
                <template v-if="row.status == 1">
                  <!--待审核状态-->
                  <el-button class="withdraw-btn" type="text" icon="el-icon-warning"
                    @click="orderLogRecall(row.id)">撤回</el-button>
                </div>
                  <el-button type="text" icon="el-icon-warning" @click="orderLogRecall(row.id)">撤回</el-button>
                </template>
                <!--已驳回-->
                <div v-if="row.status == 2">
                  <el-button class="reject-reason-btn" type="text" icon="el-icon-warning"
                    @click="rejectDetail(row.id)">驳回原因</el-button>
                </div>
                <template v-if="row.status == 2">
                  <el-button type="text" icon="el-icon-warning" @click="rejectDetail(row.id)">驳回原因</el-button>
                  <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button>
                </template>
                <!-- 已通过 -->
                <template v-if="row.status == 3">
                  <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button>
                </template>
                <!--草稿-->
                <div>
                  <el-button class="edit-btn" type="text" icon="el-icon-edit"
                    @click="handleViewDetail(row)">编辑</el-button>
                  <el-button class="publish-btn" type="text" icon="el-icon-position"
                    @click="userPublishPush(row.id)">发布</el-button>
                  <el-button class="delete-btn" type="text" icon="el-icon-delete"
                    @click="deleteOrderLog(row.id)">删除</el-button>
                </div>
                <div v-if="row.status == 3 || row.status == 1 || row.status == 2">
                  <el-button class="detail-btn" type="text" icon="el-icon-view"
                    @click="handleViewDetail(row)">详情</el-button>
                </div>
                <template v-if="row.status == 0">
                  <el-button type="text" icon="el-icon-edit" @click="handleViewDetail(row)">编辑</el-button>
                  <el-button type="text" icon="el-icon-position" @click="userPublishPush(row.id)">发布</el-button>
                  <el-button type="text" icon="el-icon-delete" @click="deleteOrderLog(row.id)">删除</el-button>
                </template>
              </div>
            </template>
            <template #status="{ row }">
@@ -161,7 +190,7 @@
          <el-col :span="8">
            <el-form-item label="周期频次" prop="date_range">
              <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期"
                end-placeholder="结束日期" class="date-picker" />
                end-placeholder="结束日期" />
            </el-form-item>
          </el-col>
          <el-col :span="4">
@@ -217,16 +246,7 @@
                <span class="step-description" style="position: relative; display: inline-block">
                  {{ record.user_name }}
                </span>
                <span style="
                    position: absolute;
                    left: 80%;
                    top: 50%;
                    transform: translateY(-50%);
                    width: 100px;
                    margin-left: 4px;
                    color: #666;
                    font-size: 12px;
                  ">
                <span class="step-timer ">
                  {{ record.interval_time_str }}
                </span>
                <div class="step-description">
@@ -280,7 +300,7 @@
          <el-col :span="6">
            <el-form-item label="周期频次" prop="date_range">
              <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期"
                end-placeholder="结束日期" class="date-picker" />
                end-placeholder="结束日期" />
            </el-form-item>
          </el-col>
@@ -350,16 +370,7 @@
                <span class="step-description" style="position: relative; display: inline-block">
                  {{ record.user_name }}
                </span>
                <span style="
                    position: absolute;
                    left: 80%;
                    top: 50%;
                    transform: translateY(-50%);
                    width: 100px;
                    margin-left: 4px;
                    color: #666;
                    font-size: 12px;
                  ">
                <span class="step-timer ">
                  {{ record.interval_time_str }}
                </span>
                <div class="step-description">
@@ -414,7 +425,7 @@
          <el-col :span="6">
            <el-form-item label="周期频次" prop="date_range">
              <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期"
                end-placeholder="结束日期" class="date-picker" :disabled="true" />
                end-placeholder="结束日期" :disabled="true" />
            </el-form-item>
          </el-col>
          <el-col :span="3">
@@ -561,6 +572,7 @@
          { label: '工单状态', prop: 'status', width: 88 },
          { label: '所属单位', prop: 'dept_name', width: 100, ellipsis: true },
          { label: '发起时间', prop: 'create_time', width: 144, ellipsis: true },
          { label: '已执行次数', prop: 'job_num', width: 96, ellipsis: true },
          { label: '工单内容', prop: 'content', ellipsis: true, overHidden: true },
          { label: '关联航线', prop: 'wayline_name', width: 100, ellipsis: true, overHidden: true },
          { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true },
@@ -629,6 +641,13 @@
  },
  mounted () {
    this.fetchTableData()
    const id = this.$route.query.id
    if (id) {
      // 确保 id 存在
      this.handleViewDetail({ id })
    } else {
      console.error('工单ID不存在!')
    }
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
@@ -651,7 +670,7 @@
  methods: {
    searchChange (params, done) {
      console.log('searchChange')
      // console.log('searchChange')
      this.query = params
      this.parentId = ''
      this.page.currentPage = 1
@@ -693,7 +712,7 @@
      this.loading = true
      try {
        let params = this.getQueryParam()
        console.log('发送的参数:', params)
        // console.log('发送的参数:', params)
        const response = await getList(params, this.page.currentPage, this.page.pageSize)
        if (!response?.data?.data?.records) {
          throw new Error('接口返回数据格式不正确')
@@ -704,11 +723,11 @@
          return item
        })
        console.log('权限检查:', this.permission)
        // console.log('权限检查:', this.permission)
        this.page.total = total || 0
        this.updateGlobalCounts()
      } catch (error) {
        console.error('获取数据失败:', error)
        // console.error('获取数据失败:', error)
        this.$message.error(error.message || '获取数据失败')
        this.tableData = []
        this.page.total = 0
@@ -720,10 +739,10 @@
    getQueryParam () {
      const currentTab = this.tabs.find(tab => tab.name === this.activeTab)
      if (this.filters.dateRange) {
        console.log(
          'this.formatDate(this.filters.dateRange[0])',
          this.formatDate(this.filters.dateRange[0])
        )
        // console.log(
        //   'this.formatDate(this.filters.dateRange[0])',
        //   this.formatDate(this.filters.dateRange[0])
        // )
      }
      const params = {
@@ -736,7 +755,7 @@
        file_id: this.filters.file_id || undefined,
        status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value,
        dept_id: this.filters.department || undefined,
        creat_start_date: this.filters.dateRange?.[0]
        create_start_date: this.filters.dateRange?.[0]
          ? this.formatDate(this.filters.dateRange[0])
          : undefined,
        create_end_date: this.filters.dateRange?.[1]
@@ -763,7 +782,7 @@
      this.$refs.testform.validate(async valid => {
        if (valid) {
          let dateRange = this.form.date_range
          console.log('dateRange' + dateRange)
          // console.log('dateRange' + dateRange)
          this.form.begin_time = this.formatDate(dateRange[0])
          this.form.end_time = this.formatDate(dateRange[1])
@@ -867,7 +886,7 @@
        PASS: 0,
      }
      var reponse = await jobStatusNum()
      console.log('统计' + reponse.data.data)
      // console.log('统计' + reponse.data.data)
      reponse.data.data.forEach(item => {
        const tab = this.tabs.find(t => t.name === item.dict_key)
        if (tab) {
@@ -908,7 +927,6 @@
    async handleViewDetail (row) {
      const response = await orderLogDetails(row.id)
      const data = response.data.data
      this.form = {
        ...data,
      }
@@ -923,16 +941,42 @@
      this.detailVisible = true
      // 更新航线列表,追加 wayline_file_region_vo 数据
      if (data.wayline_file_region_vo) {
        const newWayline = data.wayline_file_region_vo
        // 检查是否已经存在于 this.wayLineList 中
        const isDuplicate = this.wayLineList.some(
          (item) => item.wayline_id === newWayline.wayline_id
        )
        if (!isDuplicate) {
          this.wayLineList.push(newWayline)
        }
      }
      this.initMapLine(data.device_map_infos)
    },
    async handleCheckDetail (row) {
      const response = await orderLogDetails(row.id)
      const data = response.data.data
      this.form = {
        ...data,
      }
      // 更新航线列表,追加 wayline_file_region_vo 数据
      // 更新航线列表,追加 wayline_file_region_vo 数据
      if (data.wayline_file_region_vo) {
        const newWayline = data.wayline_file_region_vo
        // 检查是否已经存在于 this.wayLineList 中
        const isDuplicate = this.wayLineList.some(
          (item) => item.wayline_id === newWayline.wayline_id
        )
        if (!isDuplicate) {
          this.wayLineList.push(newWayline)
        }
      }
      // 更新机巢列表
      this.device_sns = data.device_list
      this.detailVisibleCopy = true
@@ -955,23 +999,24 @@
    },
    hasAddBtnPermission () {
      // undefined 或 false 都返回 false,只有 true 返回 true
      console.log('this.permission.order_log_add :', this.permission.order_log_add)
      // console.log('this.permission.order_log_add :', this.permission.order_log_add)
      return this.permission && this.permission.order_log_add === true
    },
    hasPaddingBtnPermission () {
      // undefined 或 false 都返回 false,只有 true 返回 true
      console.log('权限检查:', this.permission)
      // console.log('权限检查:', this.permission)
      return this.permission && this.permission.order_log_review === true
    },
    hasRecallPaddingBtnPermission () {
      // undefined 或 false 都返回 false,只有 true 返回 true
      console.log('权限检查:', this.permission)
      // console.log('权限检查:', this.permission)
      // 智飞工单撤回
      return this.permission && this.permission.order_log_recall === true
    },
    //驳回按钮权限
    hasRejectionBtnPermission () {
      // undefined 或 false 都返回 false,只有 true 返回 true
      console.log('权限检查:', this.permission)
      // console.log('权限检查:', this.permission)
      return this.permission && this.permission.rejection_btn === true
    },
    //自己点发布
@@ -1010,60 +1055,40 @@
      this.initMapLine()
    },
    initMapLine (infos = {}) {
    initMapLine (cb = () => { }) {
      let currentLine = this.wayLineList.find(item => item.wayline_id == this.form.file_id)
      if (!currentLine) return
      // 异步解析kmz文件
      const analysis = async url => {
        return new Promise(async resolve => {
          const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
          const templateXML = await res.fileInfoObj['wpmz/template.kml']
          const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
          const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
          resolve(templateXMLObj)
        })
      }
      const drawLine = async () => {
        let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key)
        const res = await analysis(prexUrl.value)
        if (!res.Placemark.length) return
        renderingLine(res)
      }
      const renderingLine = lineObj => {
        const positions = lineObj.Placemark.map(item => {
          return item.Point.coordinates.split(',')
        })
        if (JSON.stringify(infos) != '{}') positions.unshift([
          infos[0].longitude,
          infos[0].latitude,
        ])
        this.$nextTick(() => {
          if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
            this.$refs.MapContainer.initAddEntity('polyline', positions)
          }
        })
      }
      drawLine()
      this.$nextTick(() => {
        if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
          this.$refs.MapContainer.initAddEntity('polyline', {
            url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key}?_t=${new Date().getTime()}`,
            type: currentLine.wayline_type,
            cb
          })
        }
      })
    },
    //可飞行机巢列表
    async getFlyingNestBy (waylineId) {
      this.initMapLine()
      const that = this
      //按照航线来
      const params = {
        type: 0,
        waylineId: waylineId,
      }
      var wayLineListResponse = await getFlyingNestBy(params)
      this.device_sns = wayLineListResponse.data.data
      that.device_sns = []
      this.initMapLine(async (polygon) => {
        const currentLine = that.wayLineList.find(item => item.wayline_id === waylineId)
        //按照航线来
        const params = {
          type: currentLine.wayline_type,
          wayline_id: waylineId,
          polygon
        }
        var wayLineListResponse = await getFlyingNestBy(params)
        that.device_sns = wayLineListResponse.data.data
      })
    },
    //撤回
@@ -1115,8 +1140,32 @@
  },
}
</script>
<style></style>
<style lang="scss" scoped>
:deep() {
  .avue-crud__body {
    .el-form {
      height: 625px;
      overflow: auto;
    }
  }
}
:deep(.el-pagination .el-select) {
  width: 128px !important;
}
.step-timer {
  position: absolute;
  left: 80%;
  top: 50%;
  transform: translateY(-50%);
  width: 100px;
  margin-left: 4px;
  color: #666;
  font-size: 12px;
}
:deep(.el-textarea__inner) {
  padding: 8px 12px;
}
@@ -1132,23 +1181,32 @@
}
.filter-bar {
  display: flex;
  // align-items: center;
  margin-bottom: 15px;
  flex-wrap: wrap;
  .filter-item {
    margin-right: 20px;
    margin-bottom: 10px;
    width: 200px;
  }
  .search-bar-box {
    margin-bottom: 15px;
    display: flex;
    justify-content: space-between;
    gap: 8px;
  .frequency {
    margin-left: 20px;
  }
    &-item {
      flex: 1;
  .date-picker {
    width: 240px;
      &> ::v-deep(.el-date-editor) {
        width: 100%;
        box-sizing: border-box;
      }
    }
    .search-btn {
      flex: 1;
      display: flex;
      justify-content: flex-end;
    }
    .flex-2 {
      min-width: 454px;
    }
  }
}
@@ -1265,37 +1323,16 @@
  .menu-custom-box {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    &>div {
    &> ::v-deep(.el-button) {
      flex: 1;
    }
      max-width: 44px;
      margin-right: 10px;
    .audit-btn {
      color: #1ba0ff;
    }
    .withdraw-btn {
      color: #ff5d9e;
    }
    .reject-reason-btn {
      color: #ff6a00;
    }
    .edit-btn {
      color: #00b187;
    }
    .publish-btn {
      color: #5e00ff;
    }
    .delete-btn {
      color: #fe0202;
    }
    .detail-btn {
      color: #1f4aff;
      &:nth-child(4n) {
        margin-left: 0;
      }
    }
  }
}