无人机管理后台前端(已迁走)
rain
2025-04-12 8aeea2893e1f4b703f04e2211cf7ea2322c1931b
src/views/tickets/orderLog.vue
@@ -17,26 +17,42 @@
              clearable
              @keyup.enter="handleSearch"
            />
            <el-select
              v-model="filters.create_dept"
              placeholder="请选择所属单位"
              v-model="filters.create_dept"
              @change="handleDepartmentChange"
              class="filter-item"
              clearable
            >
              <el-option
                v-for="item in departments"
                :key="item.value"
                :label="item.label"
                :value="item.value"
                v-for="dept in departments"
                :key="dept.value"
                :label="dept.label"
                :value="dept.value"
              />
              <el-date-picker
                v-model="filters.dateRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                class="date-picker"
                value-format="yyyy-MM-dd"
            </el-select>
            <el-date-picker
              v-model="filters.dateRange"
              type="datetimerange"
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              class="filter-item"
              style="width: 100px"
            />
            <el-select
              v-model="filters.file_id"
              placeholder="请选择关联航线"
              class="filter-item"
              clearable
            >
              <el-option
                v-for="item in wayLineList"
                :key="item.wayline_id"
                :label="item.name"
                :value="item.wayline_id"
              />
            </el-select>
@@ -62,9 +78,9 @@
            >
              <el-option
                v-for="item in types"
                :key="item.value"
                :label="item.label"
                :value="item.value"
                :key="item.dictValue"
                :label="item.dictValue"
                :value="item.dictKey"
              />
            </el-select>
@@ -89,15 +105,14 @@
          <avue-crud
            :data="tableData"
            :option="option"
            :page.sync="pagination"
            v-model:page="page"
            ref="crud"
            :table-loading="loading"
            @current-change="handlePaginationChange"
            @size-change="handlePaginationChange"
            @current-change="currentChange"
            @refresh-change="refreshChange"
            @on-load="onLoad"
            @search-change="searchChange"
            @size-change="sizeChange"
          >
            <template #menu-left>
              <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建工单</el-button>
@@ -161,13 +176,12 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="关联航线" prop="file_id">
              <el-select v-model="form.file_id" placeholder="请选择航线"             @change="getFlyingNestBy" >
              <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
                <el-option
                  v-for="item in wayLineList"
                  :key="item.wayline_id"
                  :label="item.name"
                  :value="item.wayline_id"
                />
              </el-select>
            </el-form-item>
@@ -235,6 +249,7 @@
          <el-col :span="3">
            <el-time-picker
              style="width: 100px"
              v-model="form.deal_time"
              prop="deal_time"
              value-format="HH:mm"
@@ -256,6 +271,34 @@
      @close="resetForm"
    >
      <el-form :model="form" ref="form" label-width="100px">
        <div class="custom-steps-container">
          <!-- 标题行 -->
          <div class="steps-titles">
            <div
              v-for="(record, index) in form.record_list"
              :class="{ active: record.user_id >= 0 }"
              :key="index"
              class="step-title"
            >
              {{ record.status_str }}
            </div>
          </div>
          <!-- Element Steps 组件 -->
          <el-steps :active="form.active" align-center class="custom-steps">
            <el-step v-for="(record, index) in form.record_list" :key="index">
              <template #description>
                <div class="step-description">
                  {{ record.user_name }}
                </div>
                <div class="step-description">
                  {{ record.create_time_str }}
                </div>
              </template>
            </el-step>
          </el-steps>
        </div>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="工单名称" prop="name">
@@ -263,14 +306,13 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
           <el-form-item label="关联航线" prop="file_id">
              <el-select v-model="form.file_id" placeholder="请选择航线"             @change="getFlyingNestBy" >
            <el-form-item label="关联航线" prop="file_id">
              <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
                <el-option
                  v-for="item in wayLineList"
                  :key="item.wayline_id"
                  :label="item.name"
                  :value="item.wayline_id"
                />
              </el-select>
            </el-form-item>
@@ -278,7 +320,7 @@
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
             <el-form-item label="关联机巢" prop="device_sns">
            <el-form-item label="关联机巢" prop="device_sns">
              <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple>
                <el-option
                  v-for="item in device_sns"
@@ -329,7 +371,7 @@
            <el-button
              type="danger"
              v-if="form.status == 0|| form.status == 2 && userInfo.user_id == form.create_user"
              v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)"
              @click="submitForm(1)"
              >发布</el-button
            >
@@ -354,6 +396,7 @@
          <el-col :span="3">
            <el-time-picker
              style="width: 100px"
              v-model="form.deal_time"
              prop="deal_time"
              value-format="HH:mm"
@@ -376,12 +419,17 @@
  orderLogRecall,
  orderLogReject,
  orderLogPass,
  orderLogExport,
} from '@/api/tickets/orderLog';
import { getTicketInfo } from '@/api/tickets/ticket';
import { getDictionary } from '@/api/system/dictbiz';
import{getWaylineFileListByArea} from '@/api/resource/wayline';
import { getWaylineFileListByArea } from '@/api/resource/wayline';
import { export_json_to_excel } from '@/utils/exportExcel';
import {getFlyingNestBy} from '@/api/device/device';
import { getFlyingNestBy } from '@/api/device/device';
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import { downloadXls } from '@/utils/util';
import 'nprogress/nprogress.css';
export default {
  name: 'TicketPage',
@@ -404,21 +452,14 @@
        file_id: '',
        ai_types: [],
        status: '',
        dateRange: null,
        dateRange: [],
      },
      departments: [],
      types: [],
      device_sns:[],
      device_sns: [],
      ai_types: [],
      wayLineList:[],
      handlers: [
        { label: '处理人A', value: 'handlerA' },
        { label: '处理人B', value: 'handlerB' },
      ],
      algorithms: [
        { label: '算法A', value: 'algorithmA' },
        { label: '算法B', value: 'algorithmB' },
      ],
      wayLineList: [],
      statuses: [
        { label: '草稿', value: '0' },
        { label: '待审核', value: '1' },
@@ -453,6 +494,8 @@
          { label: '工单内容', prop: 'content', width: 160 },
          { label: '关联航线', prop: 'wayline_name', width: 160 },
          { label: '关联算法', prop: 'ai_type_str', width: 180 },
          { label: '关联机巢', prop: 'device_names', width: 180 },
          { label: '创建人', prop: 'creator_name', width: 180 },
          { label: '关联机巢', prop: 'device_names', width: 108 },
          {
@@ -464,11 +507,7 @@
          { label: '工单状态', prop: 'status', width: 60 },
        ],
      },
      pagination: {
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      page: {
        pageSize: 10,
        currentPage: 1,
@@ -494,18 +533,24 @@
    };
  },
  async created() {
    this.loadAMapScripts();
    this.fetchDropdownData();
    var response = await getDictionary({ code: 'SF' });
    var word_order_typeResponse = await getDictionary({ code: 'WORK_ORDER_TYPE' });
    this.ai_types = response.data.data;
    this.types = word_order_typeResponse.data.data;
    //获取航线
    this.asyncgetWaylineFileListByArea();
       const response2 = await getTicketInfo();
      const { dept_data, event_type, ai_type } = response2.data.data;
      this.departments = dept_data.map(item => ({
        label: item.dept_name,
        value: item.id,
      }));
  },
  mounted() {
    this.fetchTableData();
  },
  computed: {
    ...mapGetters(['userInfo']),
  },
  methods: {
@@ -517,13 +562,20 @@
      this.onLoad(this.page, params);
      done();
    },
    onLoad(page, params = {}) {
    async onLoad(page, params = {}) {
      this.loading = true;
      getList(null, Object.assign(params, this.query)).then(res => {
      getList(
        null,
        this.page.currentPage,
        this.page.pageSize,
        Object.assign(params, this.query)
      ).then(res => {
        this.tableData = res.data.data;
        this.loading = false;
        this.selectionClear();
      });
    },
    selectionClear() {
      this.selectionList = [];
@@ -542,56 +594,13 @@
    formatCycleTime(row) {
      return row.begin_time + '-' + row.end_time + ' ' + row.rep_rule_val;
    },
    async fetchDropdownData() {
      try {
        const response = await orderLogDetails();
        console.log('接口返回数据:', response.data.data);
        const { dept_data, event_type } = response.data.data;
        this.departments = dept_data.map(item => ({
          label: item.dept_name,
          value: item.id,
        }));
        this.types = Object.entries(event_type).map(([key, value]) => ({
          label: value,
          value: key,
        }));
        console.log('departments:', this.departments);
        console.log('types:', this.types);
      } catch (error) {
        console.error('获取下拉框数据失败:', error);
        this.$message.error('加载下拉框数据失败');
      }
    },
    async fetchTableData() {
      this.loading = true;
      try {
        const currentTab = this.tabs.find(tab => tab.name === this.activeTab);
        const params = {
          key_word: this.filters.key_word || undefined,
          create_dept: this.filters.create_dept || undefined,
          ai_types:
            this.filters.ai_types && this.filters.ai_types.length > 0
              ? [this.filters.ai_types]
              : null,
          file_id: this.filters.file_id || undefined,
          status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value,
          dept_id: this.filters.department || undefined,
          start_date: this.filters.dateRange?.[0]
            ? this.formatDate(this.filters.dateRange[0])
            : undefined,
          end_date: this.filters.dateRange?.[1]
            ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59')
            : undefined,
          current: this.pagination.currentPage,
          size: this.pagination.pageSize,
        };
        let params = this.getQueryParam();
        console.log('发送的参数:', params);
        const response = await getList(params);
        const response = await getList(params, this.page.currentPage, this.page.pageSize);
        if (!response?.data?.data?.records) {
          throw new Error('接口返回数据格式不正确');
        }
@@ -605,69 +614,51 @@
          this.updateGlobalCounts(records, total);
        }
        this.pagination.total = total || 0;
        this.page.total = total || 0;
        this.updateTabCounts();
      } catch (error) {
        console.error('获取数据失败:', error);
        this.$message.error(error.message || '获取数据失败');
        this.tableData = [];
        this.pagination.total = 0;
        this.page.total = 0;
      } finally {
        this.loading = false;
      }
    },
    beforeUpload(file) {
      const isImage = file.type.includes('image');
      const isLt5M = file.size / 1024 / 1024 < 5;
      if (!isImage) {
        this.$message.error('只能上传图片文件!');
        return false;
    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])
        );
      }
      if (!isLt5M) {
        this.$message.error('图片大小不能超过5MB!');
        return false;
      }
      return true;
    },
    handleUploadPreview(file) {
      this.$message.info(`预览图片:${file.name}`);
      const params = {
        key_word: this.filters.key_word || undefined,
        create_dept: this.filters.create_dept || undefined,
        ai_types:
          this.filters.ai_types && this.filters.ai_types.length > 0
            ? [this.filters.ai_types]
            : null,
        file_id: this.filters.file_id || undefined,
        status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value,
        dept_id: this.filters.department || undefined,
        start_date: this.filters.dateRange?.[0]
          ? this.formatDate(this.filters.dateRange[0])
          : undefined,
        end_date: this.filters.dateRange?.[1]
          ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59')
          : undefined,
        current: this.page.currentPage,
        size: this.page.pageSize,
      };
      return params;
    },
    handleUploadRemove(file, fileList) {
      this.form.photos = fileList;
      this.$message.success(`已移除图片:${file.name}`);
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },
    handleUploadSuccess(response, file, fileList) {
      this.form.photos = fileList;
      this.$message.success(`图片上传成功:${file.name}`);
    },
    handleUploadError(err, file) {
      this.$message.error(`图片上传失败:${file.name}`);
      console.error('上传失败:', err);
    },
    async uploadFiles(files) {
      const uploadPromises = files.map(file => {
        return new Promise(resolve => {
          const formData = new FormData();
          formData.append('file', file.raw);
          setTimeout(() => {
            resolve({
              url: URL.createObjectURL(file.raw),
              name: file.name,
            });
          }, 1000);
        });
      });
      return Promise.all(uploadPromises);
    },
    //取消
    async submitForm(status) {
      this.$refs.form.validate(async valid => {
        if (valid) {
@@ -691,28 +682,24 @@
          }
          this.dialogVisible = false;
          this.detailVisible = false;
          this.device_sns=[],
          this.wayLineList=[],
          this.fetchTableData();
          (this.device_sns = []), (this.wayLineList = []), this.fetchTableData();
        }
      });
    },
    //驳回原因显示
   async rejectDetail(id){
    const response = await orderLogDetails(id);
    let data=response.data.data;
    async rejectDetail(id) {
      const response = await orderLogDetails(id);
      let data = response.data.data;
      this.$confirm(data.remark, '驳回原因', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
           this.form = {
        ...response.data.data,
      };
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        this.form = {
          ...response.data.data,
        };
        this.detailVisible = true;
        })
      });
    },
    formatDate(date) {
      if (!date) return undefined;
@@ -746,12 +733,12 @@
    handleTabChange(tab) {
      this.activeTab = tab.props?.name || tab.name;
      this.filters.status = '';
      this.pagination.currentPage = 1;
      this.page.currentPage = 1;
      this.fetchTableData();
    },
    handleSearch() {
      this.pagination.currentPage = 1;
      this.page.currentPage = 1;
      this.fetchTableData();
    },
@@ -763,14 +750,12 @@
        dateRange: [],
        status: '',
      };
      this.pagination.currentPage = 1;
      this.page.currentPage = 1;
      this.fetchTableData();
    },
    handlePaginationChange(val) {
      this.pagination.currentPage = val.currentPage;
      this.pagination.pageSize = val.pageSize;
      this.fetchTableData();
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    },
    updateGlobalCounts(records, total) {
@@ -813,7 +798,7 @@
    handleAdd() {
      this.form = {};
      this.dialogVisible = true;
          //航线列表
      //航线列表
      this.asyncgetWaylineFileListByArea();
    },
@@ -848,41 +833,42 @@
      this.detailVisible = true;
      //航线列表
      this.asyncgetWaylineFileListByArea();
      this.device_sns=response.data.data.deviceList;
      this.device_sns = response.data.data.deviceList;
    },
    openMap() {
      this.$message.info('地图选址功能暂未实现');
    },
    handlePreview(file) {
      this.$message.info(`预览图片:${file.name}`);
    },
    handleRemove(file) {
      this.$message.info(`移除图片:${file.name}`);
    //导出
    async exportData() {
      this.$confirm('是否智飞工单数据?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        NProgress.start();
        let params = this.getQueryParam();
        orderLogExport(params).then(res => {
          downloadXls(res.data, `智飞工单${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);
          NProgress.done();
        });
      });
    },
    refreshChange() {
      this.fetchTableData();
    },
    //获取航线列表
 async asyncgetWaylineFileListByArea(name){
      var wayLineListResponse= await getWaylineFileListByArea(this.userInfo.detail.areaCode);
    this.wayLineList=wayLineListResponse.data.data;
},
  //可飞行机巢列表
 async getFlyingNestBy(waylineId){
  //按照航线来
 const params = {
      type:0,
      waylineId:waylineId,
 }
   var wayLineListResponse= await getFlyingNestBy(params);
   this.device_sns=wayLineListResponse.data.data;
},
    async asyncgetWaylineFileListByArea(name) {
      var wayLineListResponse = await getWaylineFileListByArea(this.userInfo.detail.areaCode);
      this.wayLineList = wayLineListResponse.data.data;
    },
    //可飞行机巢列表
    async getFlyingNestBy(waylineId) {
      //按照航线来
      const params = {
        type: 0,
        waylineId: waylineId,
      };
      var wayLineListResponse = await getFlyingNestBy(params);
      this.device_sns = wayLineListResponse.data.data;
    },
    //撤回
    async orderLogRecall(id) {
@@ -911,78 +897,18 @@
     * 驳回
     */
    async orderLogReject(id) {
        this.$prompt('','驳回原因',{
         confirmButtonText: '确定',
          cancelButtonText: '取消',
        }).then(async({ value }) => {
      let response = await orderLogReject(id, value);
      let data = response.data.data;
      this.$message.success('驳回成果');
      this.detailVisible = false;
       });
    },
    async exportData() {
      try {
        this.loading = true;
        const params = {
          keyword: this.filters.keyword || undefined,
          word_order_type: this.filters.type || undefined,
          status: this.filters.status || undefined,
          dept_id: this.filters.department || undefined,
          start_date: this.filters.dateRange?.[0]
            ? this.formatDate(this.filters.dateRange[0])
            : undefined,
          end_date: this.filters.dateRange?.[1]
            ? this.formatDate(this.filters.dateRange[1])
            : undefined,
        };
        const response = await getList(params);
        if (!response?.data?.data?.records) {
          throw new Error('接口返回数据格式不正确');
        }
        const records = response.data.data.records;
        const exportData = records.map(item => ({
          工单编号: item.wayline_job_id || '',
          工单名称: item.event_name || '',
          所属单位: this.departments.find(d => d.value === item.deptId)?.label || '未知单位',
          发起时间: item.create_time || '',
          工单内容: item.ai_types || '',
          工单类型: this.types.find(t => t.value === item.event_dict_key)?.label || '未知类型',
          经纬度: `${item.longitude || '未知'}, ${item.latitude || '未知'}`,
          详细地址: item.address || '暂无地址信息',
          创建人: item.create_user || '未知创建人',
          处理人: item.update_user || '未分配',
          工单状态: this.mapStatus(Number(item.status || 0)),
        }));
        export_json_to_excel(
          [
            '工单编号',
            '工单名称',
            '所属单位',
            '发起时间',
            '工单内容',
            '工单类型',
            '经纬度',
            '详细地址',
            '创建人',
            '处理人',
            '工单状态',
          ],
          exportData,
          '工单数据'
        );
        this.$message.success('数据导出成功');
      } catch (error) {
        console.error('导出失败:', error);
        this.$message.error('数据导出失败');
      } finally {
        this.loading = false;
      }
      this.$prompt('', '驳回原因', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }).then(async ({ value }) => {
        let response = await orderLogReject(id, value);
        let data = response.data.data;
        this.$message.success('驳回成果');
        this.detailVisible = false;
      });
    },
  },
  watch: {
    tableData: {
      handler() {
@@ -1070,4 +996,49 @@
  color: #909399;
  margin-top: 7px;
}
.custom-steps-container {
  width: 100%;
  margin: 20px 0;
}
.steps-titles {
  display: flex;
  justify-content: space-between;
  margin-bottom: 16px;
  position: relative;
}
.step-title {
  text-align: center;
  flex: 1;
  font-size: 16px;
  color: #999;
  position: relative;
  padding-bottom: 10px;
}
.step-title.active {
  color: #409eff;
  font-weight: bold;
}
.custom-steps {
  margin-top: -20px; /* 向上移动与标题重叠 */
}
.el-step__head {
  margin-top: 0;
}
.el-step__description {
  margin-top: 8px;
  padding: 0 20px;
}
.step-description {
  font-size: 14px;
  color: #666;
  line-height: 1.5;
}
</style>