无人机管理后台前端(已迁走)
shuishen
2025-06-27 dc24abd66181bd413d286e6398d068f337258495
Merge branch 'all_setting_styles'
53 files modified
3 files added
8939 ■■■■ changed files
src/components/basic-container/main.vue 42 ●●●● patch | view | raw | blame | history
src/components/basic-main-content/main.vue 49 ●●●●● patch | view | raw | blame | history
src/components/wel-container/main.vue 84 ●●●●● patch | view | raw | blame | history
src/main.js 11 ●●●●● patch | view | raw | blame | history
src/option/job/jobinfo.js 20 ●●●●● patch | view | raw | blame | history
src/option/job/jobserver.js 15 ●●●●● patch | view | raw | blame | history
src/option/system/dict.js 8 ●●●● patch | view | raw | blame | history
src/option/system/dictbiz.js 8 ●●●● patch | view | raw | blame | history
src/page/index/index.vue 51 ●●●● patch | view | raw | blame | history
src/page/index/top/index.vue 1 ●●●● patch | view | raw | blame | history
src/styles/base.scss 111 ●●●●● patch | view | raw | blame | history
src/styles/common.scss 20 ●●●●● patch | view | raw | blame | history
src/styles/variables.scss 4 ●●●● patch | view | raw | blame | history
src/views/algorithmRepository/algorithmRepository.vue 12 ●●●●● patch | view | raw | blame | history
src/views/authority/apiscope.vue 368 ●●●●● patch | view | raw | blame | history
src/views/authority/datascope.vue 427 ●●●●● patch | view | raw | blame | history
src/views/authority/role.vue 263 ●●●●● patch | view | raw | blame | history
src/views/base/region.vue 307 ●●●● patch | view | raw | blame | history
src/views/dataCenter/dataCenter.vue 967 ●●●● patch | view | raw | blame | history
src/views/device/addDevice.vue 254 ●●●● patch | view | raw | blame | history
src/views/device/airport.vue 11 ●●●●● patch | view | raw | blame | history
src/views/device/components/firmwareManage.vue 14 ●●●● patch | view | raw | blame | history
src/views/device/devicePerMenu.vue 179 ●●●●● patch | view | raw | blame | history
src/views/device/fly.vue 303 ●●●●● patch | view | raw | blame | history
src/views/device/index.vue 80 ●●●●● patch | view | raw | blame | history
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 99 ●●●●● patch | view | raw | blame | history
src/views/job/components/TaskTop/TaskEvent.vue 9 ●●●●● patch | view | raw | blame | history
src/views/job/components/TaskTop/TaskIndustry.vue 8 ●●●●● patch | view | raw | blame | history
src/views/job/components/TaskTop/TaskTime.vue 4 ●●● patch | view | raw | blame | history
src/views/job/components/TaskTop/TaskTop.vue 22 ●●●●● patch | view | raw | blame | history
src/views/job/jobinfo.vue 258 ●●●●● patch | view | raw | blame | history
src/views/job/jobserver.vue 216 ●●●●● patch | view | raw | blame | history
src/views/job/jobstatistics.vue 11 ●●●●● patch | view | raw | blame | history
src/views/license/license.vue 176 ●●●●● patch | view | raw | blame | history
src/views/monitor/log/api.vue 113 ●●●●● patch | view | raw | blame | history
src/views/monitor/log/error.vue 109 ●●●●● patch | view | raw | blame | history
src/views/odm/task.vue 277 ●●●●● patch | view | raw | blame | history
src/views/resource/attach.vue 219 ●●●●● patch | view | raw | blame | history
src/views/resource/media.vue 346 ●●●● patch | view | raw | blame | history
src/views/resource/oss.vue 277 ●●●●● patch | view | raw | blame | history
src/views/resource/wayline.vue 241 ●●●● patch | view | raw | blame | history
src/views/resource/waylineFile.vue 189 ●●●● patch | view | raw | blame | history
src/views/system/client.vue 188 ●●●●● patch | view | raw | blame | history
src/views/system/dept.vue 403 ●●●● patch | view | raw | blame | history
src/views/system/dict.vue 8 ●●●●● patch | view | raw | blame | history
src/views/system/menu.vue 261 ●●●●● patch | view | raw | blame | history
src/views/system/sysConfig.vue 220 ●●●●● patch | view | raw | blame | history
src/views/system/user.vue 614 ●●●● patch | view | raw | blame | history
src/views/tickets/orderLog.vue 117 ●●●● patch | view | raw | blame | history
src/views/tickets/ticket.vue 111 ●●●● patch | view | raw | blame | history
src/views/wel/components/backlog.vue 222 ●●●● patch | view | raw | blame | history
src/views/wel/components/calendarBox.vue 139 ●●●● patch | view | raw | blame | history
src/views/wel/components/flyratio.vue 3 ●●●● patch | view | raw | blame | history
src/views/wel/components/statistics.vue 9 ●●●●● patch | view | raw | blame | history
src/views/wel/index.vue 455 ●●●●● patch | view | raw | blame | history
vite.config.mjs 6 ●●●●● patch | view | raw | blame | history
src/components/basic-container/main.vue
@@ -1,3 +1,13 @@
<!--
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-27 14:34:02
 * @FilePath     : \src\components\basic-container\main.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-14 15:19:16
-->
<template>
  <div class="basic-container" :style="styleName" :class="{ 'basic-container--block': block }">
    <el-card class="basic-container__card">
@@ -23,25 +33,41 @@
    },
  },
  computed: {
    styleName() {
    styleName () {
      return {
        borderRadius: `${this.radius}px`,
        background: this.background,
      };
      }
    },
  },
};
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.basic-container {
  padding: 0 10px;
  box-sizing: border-box;
  height: 0;
  flex: 1;
  padding: 10px;
  // box-sizing: border-box;
  // height: 100%;
  .basic-container__card{
  .basic-container__card {
    height: 100% !important;
    overflow: auto;
    overflow: hidden;
    .el-card__body {
      display: flex;
      flex-direction: column;
      height: 100% !important;
      overflow: hidden;
      box-sizing: border-box;
      &>div {
        height: 0;
        flex: 1;
      }
    }
  }
  &--block {
src/components/basic-main-content/main.vue
New file
@@ -0,0 +1,49 @@
<!--
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-27 14:33:15
 * @FilePath     : \src\components\basic-main-content\main.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-14 15:19:16
-->
<template>
  <div class="basic-main-content">
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: 'basicMainContent',
  props: {
    radius: {
      type: [String, Number],
      default: 10,
    },
    background: {
      type: String,
    },
    block: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    styleName () {
      return {
        borderRadius: `${this.radius}px`,
        background: this.background,
      }
    },
  },
}
</script>
<style lang="scss" scoped>
.basic-main-content {
  height: 0;
  flex: 1;
}
</style>
src/components/wel-container/main.vue
New file
@@ -0,0 +1,84 @@
<template>
  <div class="wel-container" :style="styleName" :class="{ 'wel-container--block': block }">
    <el-card class="wel-container__card">
      <slot></slot>
    </el-card>
  </div>
</template>
<script>
export default {
  name: 'welContainer',
  props: {
    radius: {
      type: [String, Number],
      default: 10,
    },
    background: {
      type: String,
    },
    block: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    styleName () {
      return {
        borderRadius: `${this.radius}px`,
        background: this.background,
      }
    },
  },
}
</script>
<style lang="scss" scoped>
.wel-container {
  height: 0;
  flex: 1;
  padding-top: 0 !important;
  // box-sizing: border-box;
  // height: 100%;
  .el-card {
    background-color: transparent !important;
  }
  .wel-container__card {
    height: 100% !important;
    overflow: hidden;
    :deep(.el-card__body) {
      display: flex;
      flex-direction: column;
      height: 100% !important;
      overflow: hidden;
      box-sizing: border-box;
      padding: 10px !important;
      padding-top: 0 !important;
      &>div {
        height: 0;
        flex: 1;
      }
    }
  }
  &--block {
    height: 100%;
    .wel-container__card {
      height: 100%;
    }
  }
  &__card {
    width: 100%;
  }
  &:first-child {
    padding-top: 0;
  }
}
</style>
src/main.js
@@ -30,6 +30,9 @@
import avueUeditor from 'avue-plugin-ueditor'
import basicBlock from 'components/basic-block/main.vue'
import basicContainer from 'components/basic-container/main.vue'
import welContainer from 'components/wel-container/main.vue'
import basicMainContent from 'components/basic-main-content/main.vue'
import thirdRegister from './components/third-register/main.vue'
import NfDesignBase from '@saber/nf-design-base-elp'
import flowDesign from './components/flow-design/main.vue'
@@ -49,7 +52,7 @@
window.DC = Object.create(DC)
import { onResize } from '@/utils/rem'
onResize()
const { VITE_APP_BASE,VITE_APP_CESIUM_TOKEN } = import.meta.env
const { VITE_APP_BASE, VITE_APP_CESIUM_TOKEN } = import.meta.env
window.CESIUM_BASE_URL = `${VITE_APP_BASE}/cesiumPu`
Cesium.Ion.defaultAccessToken = VITE_APP_CESIUM_TOKEN
@@ -86,6 +89,9 @@
    app.component(key, component)
}
app.component('basicContainer', basicContainer)
app.component('welContainer', welContainer)
app.component('basicMainContent', basicMainContent)
app.component('basicBlock', basicBlock)
app.component('thirdRegister', thirdRegister)
app.component('flowDesign', flowDesign)
@@ -95,6 +101,9 @@
app.config.globalProperties.$dayjs = dayjs
app.config.globalProperties.website = website
app.config.globalProperties.getScreen = getScreen
app.config.globalProperties.pxToVh = height => height / 10.8 + 'vh'
app.config.globalProperties.pxToRem = width => width * 0.1 + 'rem'
app.use(error)
app.use(i18n)
app.use(store)
src/option/job/jobinfo.js
@@ -19,7 +19,7 @@
    label: '工作流',
    value: 5,
  },
];
]
export const executeTypeDic = [
  {
@@ -34,7 +34,7 @@
    label: 'MapReduce',
    value: 3,
  },
];
]
export const processorTypeDic = [
  {
@@ -45,7 +45,7 @@
    label: '外部处理器(动态加载)',
    value: 4,
  },
];
]
export const dispatchStrategyDic = [
  {
@@ -56,7 +56,7 @@
    label: 'RANDOM',
    value: 2,
  },
];
]
export const enableDic = [
  {
@@ -67,7 +67,7 @@
    label: '启用',
    value: 1,
  },
];
]
export const logTypeDic = [
  {
@@ -90,7 +90,7 @@
    label: 'NULL',
    value: 999,
  },
];
]
export const logLevelDic = [
  {
@@ -113,7 +113,7 @@
    label: 'OFF',
    value: 99,
  },
];
]
export default {
  tip: false,
@@ -129,6 +129,10 @@
  dialogWidth: 1200,
  dialogClickModal: false,
  tabs: true,
  height: 'auto',
  calcHeight: 20,
  column: [
    {
      label: '任务应用',
@@ -510,4 +514,4 @@
      ],
    },
  ],
};
}
src/option/job/jobserver.js
@@ -1,3 +1,13 @@
/*
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-26 17:24:02
 * @FilePath     : \src\option\job\jobserver.js
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-14 15:19:16
 */
export default {
  tip: false,
  searchShow: true,
@@ -10,6 +20,9 @@
  labelWidth: 100,
  menuWidth: 350,
  dialogClickModal: false,
  height: 'auto',
  calcHeight: 20,
  column: [
    {
      label: '服务名称',
@@ -73,4 +86,4 @@
      span: 24,
    },
  ],
};
}
src/option/system/dict.js
@@ -9,6 +9,10 @@
  menuWidth: 250,
  dialogWidth: 880,
  dialogClickModal: false,
  height: 'auto',
  calcHeight: 20,
  column: [
    {
      label: '字典编号',
@@ -83,7 +87,7 @@
      hide: true,
    },
  ],
};
}
export const optionChild = {
  tip: false,
@@ -203,4 +207,4 @@
      hide: true,
    },
  ],
};
}
src/option/system/dictbiz.js
@@ -9,6 +9,10 @@
  menuWidth: 250,
  dialogWidth: 880,
  dialogClickModal: false,
  height: 'auto',
  calcHeight: 20,
  column: [
    {
      label: '字典编号',
@@ -84,7 +88,7 @@
      hide: true,
    },
  ],
};
}
export const optionChild = {
  tip: false,
@@ -204,4 +208,4 @@
      hide: true,
    },
  ],
};
}
src/page/index/index.vue
@@ -13,9 +13,9 @@
        <div class="tags-box">
          <tags />
        </div>
        <search class="avue-view" v-show="isSearch"></search>
        <search class="main-avue-view-container" v-show="isSearch"></search>
        <!-- 主体视图层 -->
        <div id="avue-view" v-show="!isSearch" v-if="isRefresh">
        <div class="main-avue-view-container" v-show="!isSearch" v-if="isRefresh">
          <router-view #="{ Component }">
            <keep-alive :include="$store.getters.tagsKeep">
              <component :is="Component" />
@@ -29,15 +29,15 @@
</template>
<script>
import index from '@/mixins/index';
import wechat from './wechat.vue';
import index from '@/mixins/index'
import wechat from './wechat.vue'
//import { validatenull } from 'utils/validate';
import { mapGetters } from 'vuex';
import tags from './tags.vue';
import search from './search.vue';
import logo from './logo.vue';
import top from './top/index.vue';
import sidebar from './sidebar/index.vue';
import { mapGetters } from 'vuex'
import tags from './tags.vue'
import search from './search.vue'
import logo from './logo.vue'
import top from './top/index.vue'
import sidebar from './sidebar/index.vue'
export default {
  mixins: [index],
@@ -50,10 +50,10 @@
    wechat,
  },
  name: 'index',
  provide() {
  provide () {
    return {
      index: this,
    };
    }
  },
  computed: {
    ...mapGetters([
@@ -65,27 +65,27 @@
      'menu',
      'setting',
    ]),
    validSidebar() {
    validSidebar () {
      return !(
        (this.$route.meta || {}).menu === false || (this.$route.query || {}).menu === 'false'
      );
      )
    },
  },
  props: [],
  methods: {
    //打开菜单
    openMenu(item = {}) {
    openMenu (item = {}) {
      this.$store.dispatch('GetMenu', item.id).then(data => {
        if (data.length !== 0) {
          this.$router.$avueRouter.formatRoutes(data, true)
          // 获取url里面的redirect
          const redirect = decodeURIComponent(this.$route.query.redirect || '');
          if (redirect){
            console.log('redirect',redirect);
            const [path, queryString] = redirect.split('?');
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {};
            this.$router.push({ path,query})
          const redirect = decodeURIComponent(this.$route.query.redirect || '')
          if (redirect) {
            console.log('redirect', redirect)
            const [path, queryString] = redirect.split('?')
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {}
            this.$router.push({ path, query })
          }
        }
        //当点击顶部菜单后默认打开第一个菜单
@@ -109,15 +109,13 @@
            }, itemActive.meta)
          });
        }*/
      });
      })
    },
  },
};
}
</script>
<style lang="scss" scoped>
:deep(.basic-container){
height: none !important;}
// #avue-view{
//   :deep(){
//     .avue-crud__body{
@@ -129,9 +127,10 @@
//   }
// }
.tags-box{
.tags-box {
  background: transparent;
  padding: 0 10px;
  --el-color-primary: rgba(20, 65, 255, 1);
    height: pxToVh(55);
}
</style>
src/page/index/top/index.vue
@@ -133,6 +133,7 @@
  flex: 1;
  display: flex;
  align-items: center;
  height: pxToVh(80);
}
.top-bar__right {
src/styles/base.scss
New file
@@ -0,0 +1,111 @@
.manage-flex {
    display: flex;
}
.manage-f-d-c {
    flex-direction: column;
}
.manage-flex-1 {
    flex: 1;
}
.manage-flex-2 {
    flex: 2;
}
.manage-flex-3 {
    flex: 3;
}
.manage-flex-4 {
    flex: 4;
}
.manage-flex-5 {
    flex: 5;
}
.manage-flex-6 {
    flex: 6;
}
.manage-flex-7 {
    flex: 7;
}
.manage-flex-8 {
    flex: 8;
}
.manage-flex-9 {
    flex: 9;
}
.manage-w-0 {
    width: 0;
}
.manage-w-100 {
    width: 100%;
}
.manage-h-0 {
    height: 0;
}
.manage-h-100 {
    height: 100%;
}
.manage-m-all-10 {
    margin: 10px;
}
.manage-m-t-0 {
    margin-top: 0px;
}
.manage-m-t-10 {
    margin-top: 10px;
}
.manage-m-t-20 {
    margin-top: 20px;
}
.manage-p-all-10 {
    padding: 10px;
}
.manage-p-all-16 {
    padding: 16px;
}
.manage-p-all-20 {
    padding: 20px;
}
.manage-p-t-0 {
    padding-top: 0;
}
.manage-p-b-0 {
    padding-bottom: 0;
}
.manage-b-c-w {
    background-color: white;
}
.manage-b-r-5 {
    border-radius: 5px;
}
.manage-overflow-h {
    overflow: hidden;
}
.manage-box-s-b-b {
    box-sizing: border-box;
}
src/styles/common.scss
@@ -1,5 +1,6 @@
// 全局变量
@import './variables.scss';
@import './base.scss';
a {
  text-decoration: none;
@@ -20,9 +21,9 @@
.avue-layout {
  display: flex;
  height: 1080px;
  width: 1920px;
  background: url("@/assets/images/layoutBg.png")  no-repeat center / 100% 100%;
  width: 100%;
  height: 100%;
  background: url("@/assets/images/layoutBg.png") no-repeat center / 100% 100%;
  overflow: hidden;
  &--horizontal {
@@ -85,9 +86,12 @@
  margin-bottom: 0;
}
.avue-view {
  width: 100%;
  box-sizing: border-box;
.main-avue-view-container {
  height: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.avue-footer {
@@ -155,7 +159,7 @@
  }
}
.ztzf-cesium{
.ztzf-cesium {
  .cesium-viewer {
    height: 100%;
    overflow: hidden;
@@ -179,4 +183,4 @@
  .cesium-viewer-bottom {
    display: none;
  }
}
}
src/styles/variables.scss
@@ -1,3 +1,7 @@
$sidebar_width: 230px;
$sidebar_collapse: 60px;
$top_height: 50px;
@function pxToVh($px) {
  @return calc($px / 1080) * 100vh;
}
src/views/algorithmRepository/algorithmRepository.vue
@@ -204,15 +204,19 @@
</script>
<style scoped lang="scss">
.algorithContainer {
height: 100%;
overflow: auto;
}
.algorithItem {
  padding: 20px;
  // padding: 20px;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 53px;
  // gap: 13px;
  text-align: center;
  vertical-align: middle;
  .item {
    height: 300px;
    height: 267px;
    display: flex;
    flex-direction: column;
    justify-content: center;
@@ -227,7 +231,7 @@
    .imgicon {
      width: 100px;
      height: 100px;
      margin-bottom: 47px;
      margin-bottom: 17px;
    }
    &:hover {
      background: url('/src/assets/images/ht-sfbg-hover.png') no-repeat center;
src/views/authority/apiscope.vue
@@ -1,39 +1,17 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      @row-del="rowDel"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
      @tree-load="treeLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" @row-del="rowDel" @row-update="rowUpdate"
      @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad"
      @tree-load="treeLoad">
      <template #menu="{ row }">
        <el-button
          type="primary"
          text
          icon="el-icon-setting"
          v-if="permission.api_scope_setting"
          plain
          style="border: 0; background-color: transparent !important"
          @click.stop="handleDataScope(row)"
          >权限配置
        <el-button type="primary" text icon="el-icon-setting" v-if="permission.api_scope_setting" plain
          style="border: 0; background-color: transparent !important" @click.stop="handleDataScope(row)">权限配置
        </el-button>
      </template>
      <template #name="{ row }">
        <i :class="row.source" class="namei"/>
        <i :class="row.source" class="namei" />
        <span>{{ row.name }}</span>
      </template>
      <template #source="{ row }">
@@ -42,36 +20,16 @@
        </div>
      </template>
    </avue-crud>
    <el-drawer
      :title="`[${scopeMenuName}] 接口权限配置`"
      v-model="drawerVisible"
      :direction="direction"
      append-to-body
      :before-close="handleDrawerClose"
      size="1000px"
    >
    <el-drawer :title="`[${scopeMenuName}] 接口权限配置`" v-model="drawerVisible" :direction="direction" append-to-body
      :before-close="handleDrawerClose" size="1000px">
      <basic-container>
        <avue-crud
          :option="optionScope"
          :data="dataScope"
          :page="pageScope"
          v-model="formScope"
          :table-loading="scopeLoading"
          ref="crudScope"
          @row-del="rowDelScope"
          @row-update="rowUpdateScope"
          @row-save="rowSaveScope"
          :before-open="beforeOpenScope"
          @search-change="searchChangeScope"
          @search-reset="searchResetScope"
          @selection-change="selectionChangeScope"
          @current-change="currentChangeScope"
          @size-change="sizeChangeScope"
          @on-load="onLoadScope"
        >
        <avue-crud :option="optionScope" :data="dataScope" :page="pageScope" v-model="formScope"
          :table-loading="scopeLoading" ref="crudScope" @row-del="rowDelScope" @row-update="rowUpdateScope"
          @row-save="rowSaveScope" :before-open="beforeOpenScope" @search-change="searchChangeScope"
          @search-reset="searchResetScope" @selection-change="selectionChangeScope" @current-change="currentChangeScope"
          @size-change="sizeChangeScope" @on-load="onLoadScope">
          <template #menu-left>
            <el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope"
              >删 除
            <el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope">删 除
            </el-button>
          </template>
          <template #scopeType="{ row }">
@@ -84,19 +42,19 @@
</template>
<script>
import { add, remove, update, getLazyMenuList, getMenu } from '@/api/system/menu';
import { add, remove, update, getLazyMenuList, getMenu } from '@/api/system/menu'
import {
  addApiScope,
  removeApiScope,
  updateApiScope,
  getListApiScope,
  getMenuApiScope,
} from '@/api/system/scope';
import { mapGetters } from 'vuex';
import iconList from '@/config/iconList';
} from '@/api/system/scope'
import { mapGetters } from 'vuex'
import iconList from '@/config/iconList'
export default {
  data() {
  data () {
    return {
      form: {},
      selectionList: [],
@@ -131,6 +89,10 @@
        delBtn: false,
        menuWidth: 150,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '菜单名称',
@@ -386,108 +348,108 @@
        ],
      },
      dataScope: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.menu_add, false),
        viewBtn: this.validData(this.permission.menu_view, false),
        delBtn: this.validData(this.permission.menu_delete, false),
        editBtn: this.validData(this.permission.menu_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
    scopeIds() {
      let ids = [];
    scopeIds () {
      let ids = []
      this.selectionListScope.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    // 菜单管理模块
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.parentId = 0;
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.parentId = 0
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.parentId = '';
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.parentId = ''
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -495,119 +457,119 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getMenu(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getLazyMenuList(this.parentId, Object.assign(params, this.query)).then(res => {
        this.data = res.data.data;
        this.loading = false;
        this.selectionClear();
      });
        this.data = res.data.data
        this.loading = false
        this.selectionClear()
      })
    },
    treeLoad(tree, treeNode, resolve) {
      const parentId = tree.id;
    treeLoad (tree, treeNode, resolve) {
      const parentId = tree.id
      getLazyMenuList(parentId).then(res => {
        resolve(res.data.data);
      });
        resolve(res.data.data)
      })
    },
    // 数据权限模块
    handleDataScope(row) {
      this.drawerVisible = true;
      this.scopeMenuId = row.id;
      this.scopeMenuName = row.name;
      this.onLoadScope(this.pageScope);
    handleDataScope (row) {
      this.drawerVisible = true
      this.scopeMenuId = row.id
      this.scopeMenuName = row.name
      this.onLoadScope(this.pageScope)
    },
    handleDrawerClose(hide) {
      hide();
    handleDrawerClose (hide) {
      hide()
    },
    rowSaveScope(row, done, loading) {
    rowSaveScope (row, done, loading) {
      row = {
        ...row,
        menuId: this.scopeMenuId,
      };
      }
      addApiScope(row).then(
        () => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdateScope(row, index, done, loading) {
    rowUpdateScope (row, index, done, loading) {
      row = {
        ...row,
        menuId: this.scopeMenuId,
      };
      }
      updateApiScope(row).then(
        () => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDelScope(row) {
    rowDelScope (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return removeApiScope(row.id);
          return removeApiScope(row.id)
        })
        .then(() => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleDeleteScope() {
    handleDeleteScope () {
      if (this.selectionListScope.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -615,71 +577,63 @@
        type: 'warning',
      })
        .then(() => {
          return removeApiScope(this.scopeIds);
          return removeApiScope(this.scopeIds)
        })
        .then(() => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crudScope.toggleSelection();
        });
          })
          this.$refs.crudScope.toggleSelection()
        })
    },
    beforeOpenScope(done, type) {
    beforeOpenScope (done, type) {
      if (['edit', 'view'].includes(type)) {
        getMenuApiScope(this.formScope.id).then(res => {
          this.formScope = res.data.data;
        });
          this.formScope = res.data.data
        })
      }
      done();
      done()
    },
    searchResetScope() {
      this.onLoadScope(this.pageScope);
    searchResetScope () {
      this.onLoadScope(this.pageScope)
    },
    searchChangeScope(params, done) {
      this.onLoadScope(this.pageScope, params);
      done();
    searchChangeScope (params, done) {
      this.onLoadScope(this.pageScope, params)
      done()
    },
    selectionChangeScope(list) {
      this.selectionListScope = list;
    selectionChangeScope (list) {
      this.selectionListScope = list
    },
    currentChangeScope(currentPage) {
      this.pageScope.currentPage = currentPage;
    currentChangeScope (currentPage) {
      this.pageScope.currentPage = currentPage
    },
    sizeChangeScope(pageSize) {
      this.pageScope.pageSize = pageSize;
    sizeChangeScope (pageSize) {
      this.pageScope.pageSize = pageSize
    },
    onLoadScope(page, params = {}) {
      this.scopeLoading = true;
    onLoadScope (page, params = {}) {
      this.scopeLoading = true
      const values = {
        ...params,
        menuId: this.scopeMenuId,
      };
      }
      getListApiScope(page.currentPage, page.pageSize, Object.assign(values, this.query)).then(
        res => {
          const data = res.data.data;
          this.pageScope.total = data.total;
          this.dataScope = data.records;
          this.selectionListScope = [];
          this.scopeLoading = false;
          const data = res.data.data
          this.pageScope.total = data.total
          this.dataScope = data.records
          this.selectionListScope = []
          this.scopeLoading = false
        }
      );
      )
    },
  },
};
}
</script>
<style scoped lang="scss">
.namei {
  margin-right: 5px;
}
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
src/views/authority/datascope.vue
@@ -1,40 +1,17 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      :before-close="beforeClose"
      @row-del="rowDel"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
      @tree-load="treeLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" :before-close="beforeClose" @row-del="rowDel"
      @row-update="rowUpdate" @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad" @tree-load="treeLoad">
      <template #menu="{ row }">
        <el-button
          type="primary"
          text
          icon="el-icon-setting"
          v-if="permission.data_scope_setting"
          plain
          style="border: 0; background-color: transparent !important"
          @click.stop="handleDataScope(row)"
          >权限配置
        <el-button type="primary" text icon="el-icon-setting" v-if="permission.data_scope_setting" plain
          style="border: 0; background-color: transparent !important" @click.stop="handleDataScope(row)">权限配置
        </el-button>
      </template>
      <template #name="{ row }">
        <i :class="row.source" class="namei"  />
        <i :class="row.source" class="namei" />
        <span>{{ row.name }}</span>
      </template>
      <template #source="{ row }">
@@ -43,36 +20,16 @@
        </div>
      </template>
    </avue-crud>
    <el-drawer
      :title="`[${scopeMenuName}] 数据权限配置`"
      v-model="drawerVisible"
      :direction="direction"
      append-to-body
      :before-close="handleDrawerClose"
      size="1000px"
    >
    <el-drawer :title="`[${scopeMenuName}] 数据权限配置`" v-model="drawerVisible" :direction="direction" append-to-body
      :before-close="handleDrawerClose" size="1000px">
      <basic-container>
        <avue-crud
          :option="optionScope"
          :data="dataScope"
          :page="pageScope"
          v-model="formScope"
          :table-loading="scopeLoading"
          ref="crudScope"
          @row-del="rowDelScope"
          @row-update="rowUpdateScope"
          @row-save="rowSaveScope"
          :before-open="beforeOpenScope"
          @search-change="searchChangeScope"
          @search-reset="searchResetScope"
          @selection-change="selectionChangeScope"
          @current-change="currentChangeScope"
          @size-change="sizeChangeScope"
          @on-load="onLoadScope"
        >
        <avue-crud :option="optionScope" :data="dataScope" :page="pageScope" v-model="formScope"
          :table-loading="scopeLoading" ref="crudScope" @row-del="rowDelScope" @row-update="rowUpdateScope"
          @row-save="rowSaveScope" :before-open="beforeOpenScope" @search-change="searchChangeScope"
          @search-reset="searchResetScope" @selection-change="selectionChangeScope" @current-change="currentChangeScope"
          @size-change="sizeChangeScope" @on-load="onLoadScope">
          <template #menu-left>
            <el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope"
              >删 除
            <el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope">删 除
            </el-button>
          </template>
          <template #scopeType="{ row }">
@@ -85,20 +42,20 @@
</template>
<script>
import { add, remove, update, getLazyMenuList, getMenu } from '@/api/system/menu';
import { add, remove, update, getLazyMenuList, getMenu } from '@/api/system/menu'
import {
  addDataScope,
  removeDataScope,
  updateDataScope,
  getListDataScope,
  getMenuDataScope,
} from '@/api/system/scope';
import { mapGetters } from 'vuex';
import iconList from '@/config/iconList';
import func from '@/utils/func';
} from '@/api/system/scope'
import { mapGetters } from 'vuex'
import iconList from '@/config/iconList'
import func from '@/utils/func'
export default {
  data() {
  data () {
    return {
      form: {},
      selectionList: [],
@@ -135,6 +92,10 @@
        delBtn: false,
        menuWidth: 150,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '菜单名称',
@@ -427,150 +388,150 @@
        ],
      },
      dataScope: [],
    };
    }
  },
  watch: {
    'formScope.scopeType'() {
      this.initScope();
    'formScope.scopeType' () {
      this.initScope()
    },
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.menu_add, false),
        viewBtn: this.validData(this.permission.menu_view, false),
        delBtn: this.validData(this.permission.menu_delete, false),
        editBtn: this.validData(this.permission.menu_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
    scopeIds() {
      let ids = [];
    scopeIds () {
      let ids = []
      this.selectionListScope.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    initScope() {
      const scopeType = func.toInt(this.formScope.scopeType);
      const watchMode = this.watchMode;
    initScope () {
      const scopeType = func.toInt(this.formScope.scopeType)
      const watchMode = this.watchMode
      let column = '-',
        name = '暂无';
        name = '暂无'
      if (scopeType === 1) {
        column = '-';
        name = '全部可见';
        column = '-'
        name = '全部可见'
      } else if (scopeType === 2) {
        column = 'create_user';
        name = '本人可见';
        column = 'create_user'
        name = '本人可见'
      } else if (scopeType === 3) {
        column = 'create_dept';
        name = '所在机构可见';
        column = 'create_dept'
        name = '所在机构可见'
      } else if (scopeType === 4) {
        column = 'create_dept';
        name = '所在机构可见及子级可见';
        column = 'create_dept'
        name = '所在机构可见及子级可见'
      } else if (scopeType === 5) {
        column = '';
        name = '自定义';
        column = ''
        name = '自定义'
      }
      this.$refs.crudScope.option.column.filter(item => {
        if (watchMode) {
          if (item.prop === 'scopeName') {
            this.formScope.scopeName = `${this.scopeMenuName} [${name}]`;
            this.formScope.scopeName = `${this.scopeMenuName} [${name}]`
          }
          if (item.prop === 'resourceCode') {
            this.formScope.resourceCode = this.scopeMenuCode;
            this.formScope.resourceCode = this.scopeMenuCode
          }
          if (item.prop === 'scopeColumn') {
            this.formScope.scopeColumn = column;
            this.formScope.scopeColumn = column
          }
        }
        if (item.prop === 'scopeValue') {
          item.display = scopeType === 5;
          item.display = scopeType === 5
        }
      });
      })
    },
    // 菜单管理模块
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.parentId = 0;
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.parentId = 0
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.parentId = '';
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.parentId = ''
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -578,124 +539,124 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getMenu(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    beforeClose(done) {
      this.formScope = {};
      done();
    beforeClose (done) {
      this.formScope = {}
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getLazyMenuList(this.parentId, Object.assign(params, this.query)).then(res => {
        this.data = res.data.data;
        this.loading = false;
        this.selectionClear();
      });
        this.data = res.data.data
        this.loading = false
        this.selectionClear()
      })
    },
    treeLoad(tree, treeNode, resolve) {
      const parentId = tree.id;
    treeLoad (tree, treeNode, resolve) {
      const parentId = tree.id
      getLazyMenuList(parentId).then(res => {
        resolve(res.data.data);
      });
        resolve(res.data.data)
      })
    },
    // 数据权限模块
    handleDataScope(row) {
      this.drawerVisible = true;
      this.scopeMenuId = row.id;
      this.scopeMenuCode = row.code;
      this.scopeMenuName = row.name;
      this.onLoadScope(this.pageScope);
    handleDataScope (row) {
      this.drawerVisible = true
      this.scopeMenuId = row.id
      this.scopeMenuCode = row.code
      this.scopeMenuName = row.name
      this.onLoadScope(this.pageScope)
    },
    handleDrawerClose(hide) {
      hide();
    handleDrawerClose (hide) {
      hide()
    },
    rowSaveScope(row, done, loading) {
    rowSaveScope (row, done, loading) {
      row = {
        ...row,
        menuId: this.scopeMenuId,
      };
      }
      addDataScope(row).then(
        () => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdateScope(row, index, done, loading) {
    rowUpdateScope (row, index, done, loading) {
      row = {
        ...row,
        menuId: this.scopeMenuId,
      };
      }
      updateDataScope(row).then(
        () => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDelScope(row) {
    rowDelScope (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return removeDataScope(row.id);
          return removeDataScope(row.id)
        })
        .then(() => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleDeleteScope() {
    handleDeleteScope () {
      if (this.selectionListScope.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -703,76 +664,68 @@
        type: 'warning',
      })
        .then(() => {
          return removeDataScope(this.scopeIds);
          return removeDataScope(this.scopeIds)
        })
        .then(() => {
          this.onLoadScope(this.pageScope);
          this.onLoadScope(this.pageScope)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crudScope.toggleSelection();
        });
          })
          this.$refs.crudScope.toggleSelection()
        })
    },
    beforeOpenScope(done, type) {
    beforeOpenScope (done, type) {
      if (['add'].includes(type)) {
        this.watchMode = true;
        this.initScope();
        this.watchMode = true
        this.initScope()
      }
      if (['edit', 'view'].includes(type)) {
        this.watchMode = false;
        this.watchMode = false
        getMenuDataScope(this.formScope.id).then(res => {
          this.formScope = res.data.data;
        });
          this.formScope = res.data.data
        })
      }
      done();
      done()
    },
    searchResetScope() {
      this.onLoadScope(this.pageScope);
    searchResetScope () {
      this.onLoadScope(this.pageScope)
    },
    searchChangeScope(params, done) {
      this.onLoadScope(this.pageScope, params);
      done();
    searchChangeScope (params, done) {
      this.onLoadScope(this.pageScope, params)
      done()
    },
    selectionChangeScope(list) {
      this.selectionListScope = list;
    selectionChangeScope (list) {
      this.selectionListScope = list
    },
    currentChangeScope(currentPage) {
      this.pageScope.currentPage = currentPage;
    currentChangeScope (currentPage) {
      this.pageScope.currentPage = currentPage
    },
    sizeChangeScope(pageSize) {
      this.pageScope.pageSize = pageSize;
    sizeChangeScope (pageSize) {
      this.pageScope.pageSize = pageSize
    },
    onLoadScope(page, params = {}) {
      this.scopeLoading = true;
    onLoadScope (page, params = {}) {
      this.scopeLoading = true
      const values = {
        ...params,
        menuId: this.scopeMenuId,
      };
      }
      getListDataScope(page.currentPage, page.pageSize, Object.assign(values, this.query)).then(
        res => {
          const data = res.data.data;
          this.pageScope.total = data.total;
          this.dataScope = data.records;
          this.selectionListScope = [];
          this.scopeLoading = false;
          const data = res.data.data
          this.pageScope.total = data.total
          this.dataScope = data.records
          this.selectionListScope = []
          this.scopeLoading = false
        }
      );
      )
    },
  },
};
}
</script>
<style scoped lang="scss">
.namei {
  margin-right: 5px;
}
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
src/views/authority/role.vue
@@ -1,39 +1,13 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      @row-del="rowDel"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" @row-del="rowDel" @row-update="rowUpdate"
      @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          v-if="permission.role_delete"
          plain
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" v-if="permission.role_delete" plain @click="handleDelete">删 除
        </el-button>
        <el-button
          icon="el-icon-setting"
          v-if="userInfo.role_name.includes('admin')"
          @click="handleRole"
          plain
          >权限设置
        <el-button icon="el-icon-setting" v-if="userInfo.role_name.includes('admin')" @click="handleRole" plain>权限设置
        </el-button>
        <!--  -->
      </template>
@@ -41,14 +15,8 @@
    <el-dialog title="角色权限配置" append-to-body v-model="box" width="345px">
      <el-tabs type="border-card">
        <el-tab-pane label="菜单权限">
          <el-tree
            :data="menuGrantList"
            show-checkbox
            node-key="id"
            ref="treeMenu"
            :default-checked-keys="menuTreeObj"
            :props="props"
          >
          <el-tree :data="menuGrantList" show-checkbox node-key="id" ref="treeMenu" :default-checked-keys="menuTreeObj"
            :props="props">
          </el-tree>
        </el-tab-pane>
        <!-- <el-tab-pane label="数据权限">
@@ -94,12 +62,12 @@
  grantTree,
  remove,
  update,
} from '@/api/system/role';
import { mapGetters } from 'vuex';
import website from '@/config/website';
} from '@/api/system/role'
import { mapGetters } from 'vuex'
import website from '@/config/website'
export default {
  data() {
  data () {
    return {
      form: {},
      box: false,
@@ -134,6 +102,10 @@
        viewBtn: true,
        dialogWidth: 900,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '角色名称',
@@ -218,152 +190,152 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.role_add, false),
        viewBtn: this.validData(this.permission.role_view, false),
        delBtn: this.validData(this.permission.role_delete, false),
        editBtn: this.validData(this.permission.role_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
    idsArray() {
      let ids = [];
    idsArray () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids;
        ids.push(ele.id)
      })
      return ids
    },
  },
  methods: {
    initData(roleId) {
    initData (roleId) {
      getRoleTreeById(roleId).then(res => {
        const column = this.findObject(this.option.column, 'parentId');
        column.dicData = res.data.data;
      });
        const column = this.findObject(this.option.column, 'parentId')
        column.dicData = res.data.data
      })
    },
    submit() {
      const menuList = this.$refs.treeMenu.getCheckedKeys();
    submit () {
      const menuList = this.$refs.treeMenu.getCheckedKeys()
      // const dataScopeList = this.$refs.treeDataScope.getCheckedKeys();
      const dataScopeList = [];
      const dataScopeList = []
      // const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
      const apiScopeList = [];
      const apiScopeList = []
      grant(this.idsArray, menuList, dataScopeList, apiScopeList).then(() => {
        this.box = false;
        this.box = false
        this.$message({
          type: 'success',
          message: '操作成功!',
        });
        this.onLoad(this.page);
      });
        })
        this.onLoad(this.page)
      })
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['add', 'edit'].includes(type)) {
        this.initData(this.form.id);
        this.initData(this.form.id)
      }
      done();
      done()
    },
    handleRole() {
    handleRole () {
      if (this.selectionList.length !== 1) {
        this.$message.warning('只能选择一条数据');
        return;
        this.$message.warning('只能选择一条数据')
        return
      }
      this.menuTreeObj = [];
      this.dataScopeTreeObj = [];
      this.apiScopeTreeObj = [];
      this.menuTreeObj = []
      this.dataScopeTreeObj = []
      this.apiScopeTreeObj = []
      grantTree().then(res => {
        this.menuGrantList = res.data.data.menu;
        this.dataScopeGrantList = res.data.data.dataScope;
        this.apiScopeGrantList = res.data.data.apiScope;
        this.menuGrantList = res.data.data.menu
        this.dataScopeGrantList = res.data.data.dataScope
        this.apiScopeGrantList = res.data.data.apiScope
        getRole(this.ids).then(res => {
          this.menuTreeObj = res.data.data.menu;
          this.dataScopeTreeObj = res.data.data.dataScope;
          this.apiScopeTreeObj = res.data.data.apiScope;
          this.box = true;
        });
      });
          this.menuTreeObj = res.data.data.menu
          this.dataScopeTreeObj = res.data.data.dataScope
          this.apiScopeTreeObj = res.data.data.apiScope
          this.box = true
        })
      })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -371,44 +343,35 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        this.data = res.data.data;
        this.loading = false;
        this.selectionClear();
      });
        this.data = res.data.data
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/base/region.vue
@@ -1,112 +1,93 @@
<template>
  <el-row>
    <el-col :span="9">
      <div class="box">
  <div class="manage-p-all-10 manage-p-t-0 manage-h-0 manage-flex-1 manage-flex">
    <div
      class="manage-p-all-20 manage-flex-2 manage-h-100 manage-b-r-5 manage-box-s-b-b manage-overflow-h manage-b-c-w">
      <div class="box ">
        <el-scrollbar>
          <basic-container>
          <div class="manage-w-100 manage-h-100">
            <avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick" />
          </basic-container>
          </div>
        </el-scrollbar>
      </div>
    </el-col>
    <el-col :span="15">
      <basic-container>
        <el-button-group>
          <el-button
            v-if="permission.region_add"
            type="primary"
            icon="el-icon-plus"
            @click="addChildren"
            >新增下级
          </el-button>
          <el-button
            v-if="permission.region_delete"
            type="primary"
            icon="el-icon-delete"
            @click="handleDelete"
            >删除
          </el-button>
          <el-button
            v-if="permission.region_import"
            type="primary"
            icon="el-icon-upload"
            @click="handleImport"
            >导入
          </el-button>
          <el-button
            v-if="permission.region_export"
            type="primary"
            icon="el-icon-download"
            @click="handleExport"
            >导出
          </el-button>
          <el-button
            v-if="permission.region_debug"
            type="primary"
            icon="el-icon-video-play"
            @click="handleDebug"
            >调试
          </el-button>
        </el-button-group>
      </basic-container>
      <basic-container>
        <avue-form ref="form" :option="regionOption" v-model="regionForm" @submit="handleSubmit">
          <template #code="{}">
            <el-input placeholder="请输入 区划子编号" v-model="regionForm.subCode">
              <template #prepend>{{ regionForm.parentCode }}</template>
            </el-input>
          </template>
        </avue-form>
        <el-dialog title="行政区划数据导入" append-to-body v-model="excelBox" width="555px">
          <avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
            <template #excelTemplate>
              <el-button type="primary" @click="handleTemplate">
                点击下载<i class="el-icon-download el-icon--right"></i>
              </el-button>
    </div>
    <div
      class="manage-p-all-10 manage-p-t-0 manage-p-b-0 manage-flex-4 manage-h-100 manage-flex manage-box-s-b-b manage-f-d-c">
      <div class="manage-p-all-20 manage-flex manage-f-d-c manage-h-100 manage-box-s-b-b manage-b-c-w manage-b-r-5">
        <div>
          <el-button-group>
            <el-button v-if="permission.region_add" type="primary" icon="el-icon-plus" @click="addChildren">新增下级
            </el-button>
            <el-button v-if="permission.region_delete" type="primary" icon="el-icon-delete" @click="handleDelete">删除
            </el-button>
            <el-button v-if="permission.region_import" type="primary" icon="el-icon-upload" @click="handleImport">导入
            </el-button>
            <el-button v-if="permission.region_export" type="primary" icon="el-icon-download" @click="handleExport">导出
            </el-button>
            <el-button v-if="permission.region_debug" type="primary" icon="el-icon-video-play" @click="handleDebug">调试
            </el-button>
          </el-button-group>
        </div>
        <div class="manage-m-t-20 manage-h-0 manage-flex-1 ">
          <avue-form ref="form" :option="regionOption" v-model="regionForm" @submit="handleSubmit">
            <template #code="{ }">
              <el-input placeholder="请输入 区划子编号" v-model="regionForm.subCode">
                <template #prepend>{{ regionForm.parentCode }}</template>
              </el-input>
            </template>
          </avue-form>
        </el-dialog>
        <el-dialog title="行政区划数据调试" append-to-body v-model="debugBox" width="350px">
          <avue-form :option="debugOption" v-model="debugForm" />
        </el-dialog>
      </basic-container>
    </el-col>
  </el-row>
          <el-dialog title="行政区划数据导入" append-to-body v-model="excelBox" width="555px">
            <avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
              <template #excelTemplate>
                <el-button type="primary" @click="handleTemplate">
                  点击下载<i class="el-icon-download el-icon--right"></i>
                </el-button>
              </template>
            </avue-form>
          </el-dialog>
          <el-dialog title="行政区划数据调试" append-to-body v-model="debugBox" width="350px">
            <avue-form :option="debugOption" v-model="debugForm" />
          </el-dialog>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { getLazyTree, getDetail, submit, remove } from '@/api/base/region';
import { exportBlob } from '@/api/common';
import { mapGetters } from 'vuex';
import { validatenull } from '@/utils/validate';
import { downloadXls } from '@/utils/util';
import { getToken } from '@/utils/auth';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { getLazyTree, getDetail, submit, remove } from '@/api/base/region'
import { exportBlob } from '@/api/common'
import { mapGetters } from 'vuex'
import { validatenull } from '@/utils/validate'
import { downloadXls } from '@/utils/util'
import { getToken } from '@/utils/auth'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
export default {
  data() {
  data () {
    return {
      topCode: '00',
      treeCode: '',
      treeParentCode: '',
      loading:null,
      loading: null,
      treeData: [],
      treeOption: {
        nodeKey: 'id',
        lazy: true,
        treeLoad: function (node, resolve) {
          const parentCode = node.level === 0 ? '000000000000' : node.data.id;
          const parentCode = node.level === 0 ? '000000000000' : node.data.id
          getLazyTree(parentCode).then(res => {
            resolve(
              res.data.data.map(item => {
                return {
                  ...item,
                  leaf: !item.hasChildren,
                };
                }
              })
            );
          });
            )
          })
        },
        addBtn: false,
        menu: false,
@@ -311,99 +292,99 @@
          },
        ],
      },
    };
    }
  },
  watch: {
    'regionForm.subCode'() {
      this.regionForm.code = this.regionForm.parentCode + this.regionForm.subCode;
    'regionForm.subCode' () {
      this.regionForm.code = this.regionForm.parentCode + this.regionForm.subCode
    },
    'excelForm.isCovered'() {
    'excelForm.isCovered' () {
      if (this.excelForm.isCovered !== '') {
        const column = this.findObject(this.excelOption.column, 'excelFile');
        column.action = `/blade-system/region/import-region?isCovered=${this.excelForm.isCovered}`;
        const column = this.findObject(this.excelOption.column, 'excelFile')
        column.action = `/blade-system/region/import-region?isCovered=${this.excelForm.isCovered}`
      }
    },
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.region_add, false),
        viewBtn: this.validData(this.permission.region_view, false),
        delBtn: this.validData(this.permission.region_delete, false),
        editBtn: this.validData(this.permission.region_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    initTree() {
      this.treeData = [];
    initTree () {
      this.treeData = []
      getLazyTree(this.topCode).then(res => {
        this.treeData = res.data.data.map(item => {
          return {
            ...item,
            leaf: !item.hasChildren,
          };
        });
      });
          }
        })
      })
    },
    nodeClick(data) {
      const column = this.findObject(this.regionOption.column, 'parentCode');
      column.disabled = true;
      this.treeCode = data.id;
      this.treeParentCode = data.parentId;
    nodeClick (data) {
      const column = this.findObject(this.regionOption.column, 'parentCode')
      column.disabled = true
      this.treeCode = data.id
      this.treeParentCode = data.parentId
      getDetail(this.treeCode).then(res => {
        this.regionForm = res.data.data;
        this.regionForm.subCode = this.regionForm.code.replace(this.regionForm.parentCode, '');
      });
        this.regionForm = res.data.data
        this.regionForm.subCode = this.regionForm.code.replace(this.regionForm.parentCode, '')
      })
    },
    addChildren() {
    addChildren () {
      if (validatenull(this.regionForm.code) || validatenull(this.regionForm.name)) {
        this.$message.warning('请先选择一项区划');
        return;
        this.$message.warning('请先选择一项区划')
        return
      }
      this.regionForm.parentCode = this.regionForm.code;
      this.regionForm.parentName = this.regionForm.name;
      this.regionForm.code = '';
      this.regionForm.subCode = '';
      this.regionForm.name = '';
      this.regionForm.parentCode = this.regionForm.code
      this.regionForm.parentName = this.regionForm.name
      this.regionForm.code = ''
      this.regionForm.subCode = ''
      this.regionForm.name = ''
      this.regionForm.regionLevel =
        this.regionForm.regionLevel === 5 ? 5 : this.regionForm.regionLevel + 1;
        this.regionForm.regionLevel === 5 ? 5 : this.regionForm.regionLevel + 1
    },
    handleSubmit(form, done, loading) {
      const parentCode = form.parentCode === this.topCode ? '' : form.parentCode;
    handleSubmit (form, done, loading) {
      const parentCode = form.parentCode === this.topCode ? '' : form.parentCode
      // form.code = parentCode + form.subCode;
      form.code = form.subCode;
      form.code = form.subCode
      submit(form).then(
        () => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.initTree();
          this.regionForm.subCode = '';
          this.$refs.form.resetForm();
          done();
          console.log('成功');
          })
          this.initTree()
          this.regionForm.subCode = ''
          this.$refs.form.resetForm()
          done()
          console.log('成功')
        },
        error => {
          // loading();
          window.console.log(error);
          window.console.log(error)
        }
      );
      )
    },
    handleDelete() {
    handleDelete () {
      if (validatenull(this.regionForm.code)) {
        this.$message.warning('请先选择一项区划');
        return;
        this.$message.warning('请先选择一项区划')
        return
      }
      this.$confirm(`确定将 [${this.regionForm.name}] 数据删除?`, {
        confirmButtonText: '确定',
@@ -411,66 +392,66 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.treeCode);
          return remove(this.treeCode)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.initTree();
          this.regionForm.subCode = '';
          this.$refs.form.resetForm();
        });
          })
          this.initTree()
          this.regionForm.subCode = ''
          this.$refs.form.resetForm()
        })
    },
    uploadAfter(res, done, loading, column) {
      window.console.log(column);
      this.excelBox = false;
      this.initTree();
      done();
    uploadAfter (res, done, loading, column) {
      window.console.log(column)
      this.excelBox = false
      this.initTree()
      done()
    },
    handleDebug() {
      this.debugBox = true;
    handleDebug () {
      this.debugBox = true
    },
    handleImport() {
      this.excelBox = true;
    handleImport () {
      this.excelBox = true
    },
    handleExport() {
    handleExport () {
      this.$confirm('是否导出行政区划数据?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        NProgress.start();
        NProgress.start()
        exportBlob(
          `/blade-system/region/export-region?${this.website.tokenHeader}=${getToken()}`
        ).then(res => {
          downloadXls(res.data, `行政区划数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
          NProgress.done();
        });
      });
          downloadXls(res.data, `行政区划数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`)
          NProgress.done()
        })
      })
    },
    handleTemplate() {
    handleTemplate () {
      exportBlob(
        `/blade-system/region/export-template?${this.website.tokenHeader}=${getToken()}`
      ).then(res => {
        downloadXls(res.data, '行政区划模板.xlsx');
      });
        downloadXls(res.data, '行政区划模板.xlsx')
      })
    },
  },
};
}
</script>
<style>
<style lang="scss" scoped>
.box {
  height: 800px;
}
.el-scrollbar {
  height: 100%;
}
.box .el-scrollbar__wrap {
  overflow: scroll;
::v-deep(.el-scrollbar) {
  height: 100%;
  .el-scrollbar__wrap {
    overflow: auto;
  }
}
</style>
src/views/dataCenter/dataCenter.vue
@@ -1,286 +1,203 @@
<template>
  <div class="dataCenter-table">
    <searchData
      @search="searchClick"
      @downFun="downloadFile"
      @allDownFun="aLLDownloadFile"
    ></searchData>
    <!-- 表格部分 -->
    <div class="dataTable">
      <el-table
        v-loading="loadings"
        element-loading-text="加载中"
        stripe
        :data="tableData"
        class="custom-header"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="55" />
        <el-table-column label="序号" type="index" width="60">
          <template #default="{ $index }">
            {{
              ($index + 1 + (jobListParams.current - 1) * jobListParams.size)
                .toString()
                .padStart(2, '0')
            }}
          </template>
        </el-table-column>
        <el-table-column prop="regionName" label="所属区域" v-if="!isDistrictLevel">
          <template #default="scope">
            <span>{{ processAddress(scope.row.regionName) }}</span>
          </template>
        </el-table-column>
        <el-table-column property="nestName" label="所属机巢" />
        <el-table-column property="jobName" label="任务名称" show-overflow-tooltip />
        <el-table-column prop="nickName" label="文件名称" show-overflow-tooltip />
        <el-table-column property="link" label="缩图" width="120">
          <template #default="scope">
            <img
              class="quanjing"
              @click="clickpanorama(scope.row)"
              v-if="scope.row?.resultType === 5"
              :src="scope.row?.smallUrl"
              alt=""
            />
            <img
              v-else-if="scope.row?.resultType === 1"
              :src="convertVideoUrlToThumbnail(scope.row?.link)"
              alt=""
              class="imageBox"
              @click="enterFullScreen(scope.row)"
            />
            <!-- 正射 -->
            <img
              v-else-if="scope.row?.resultType === 4"
              :src="getzsSmallImg(scope.row?.link)"
              alt=""
              class="imageBox"
            />
            <el-image
              v-else
              :src="scope.row?.smallUrl"
              :preview-src-list="[scope.row?.showUrl]"
              fit="cover"
              preview-teleported
            />
          </template>
        </el-table-column>
        <el-table-column prop="jobTime" label="任务时间" />
        <el-table-column property="photoType" label="文件类别">
          <template #default="scope">
            <span>{{ photoTypeMap[scope.row.photoType] }}</span>
          </template>
        </el-table-column>
        <el-table-column property="resultType" label="文件格式">
          <template #default="{ row }">
            <span>{{ resultTypeMap[row?.resultType] }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="150" align="center">
          <template #default="scope">
            <span class="look" @click="lookDetail(scope.row)">查看</span>
            <span class="delete" @click="deleteDetail(scope.row)" v-if="scope.row.resultType !== 2"
              >删除</span
            >
  <div
    class="manage-m-all-10 manage-m-t-0 manage-p-all-20 manage-h-0 manage-flex-1 manage-flex manage-f-d-c manage-b-r-5 manage-b-c-w">
    <div class="dataCenter-table">
      <searchData @search="searchClick" @downFun="downloadFile" @allDownFun="aLLDownloadFile"></searchData>
      <!-- 表格部分 -->
      <div class="dataTable">
        <el-table v-loading="loadings" element-loading-text="加载中" stripe :data="tableData" class="custom-header"
          @selection-change="handleSelectionChange">
          <el-table-column type="selection" width="55" />
          <el-table-column label="序号" type="index" width="60">
            <template #default="{ $index }">
              {{
                ($index + 1 + (jobListParams.current - 1) * jobListParams.size)
                  .toString()
                  .padStart(2, '0')
              }}
            </template>
          </el-table-column>
          <el-table-column prop="regionName" label="所属区域" v-if="!isDistrictLevel">
            <template #default="scope">
              <span>{{ processAddress(scope.row.regionName) }}</span>
            </template>
          </el-table-column>
          <el-table-column property="nestName" label="所属机巢" />
          <el-table-column property="jobName" label="任务名称" show-overflow-tooltip />
          <el-table-column prop="nickName" label="文件名称" show-overflow-tooltip />
          <el-table-column property="link" label="缩图" width="120">
            <template #default="scope">
              <img class="quanjing" @click="clickpanorama(scope.row)" v-if="scope.row?.resultType === 5"
                :src="scope.row?.smallUrl" alt="" />
              <img v-else-if="scope.row?.resultType === 1" :src="convertVideoUrlToThumbnail(scope.row?.link)" alt=""
                class="imageBox" @click="enterFullScreen(scope.row)" />
              <!-- 正射 -->
              <img v-else-if="scope.row?.resultType === 4" :src="getzsSmallImg(scope.row?.link)" alt=""
                class="imageBox" />
              <el-image v-else :src="scope.row?.smallUrl" :preview-src-list="[scope.row?.showUrl]" fit="cover"
                preview-teleported />
            </template>
          </el-table-column>
          <el-table-column prop="jobTime" label="任务时间" />
          <el-table-column property="photoType" label="文件类别">
            <template #default="scope">
              <span>{{ photoTypeMap[scope.row.photoType] }}</span>
            </template>
          </el-table-column>
          <el-table-column property="resultType" label="文件格式">
            <template #default="{ row }">
              <span>{{ resultTypeMap[row?.resultType] }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150" align="center">
            <template #default="scope">
              <span class="look" @click="lookDetail(scope.row)">查看</span>
              <span class="delete" @click="deleteDetail(scope.row)" v-if="scope.row.resultType !== 2">删除</span>
            <span
              class="location"
              @click="positionDetail(scope.row)"
              v-if="shouldShowLocation(scope.row)"
              >定位</span
            >
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- 分页 -->
    <div class="pagination">
      <el-pagination
        v-model:current-page="jobListParams.current"
        v-model:page-size="jobListParams.size"
        :page-sizes="[10, 20, 30, 40]"
        background
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
    <!-- 查看弹框 -->
    <el-dialog v-model="dialogVisible" width="60%" append-to-body @close="dialogClose">
      <template #header="{ titleId, titleClass }">
        <div class="my-header">
          <h4 :id="titleId" :class="titleClass">{{ detailTitle }}</h4>
        </div>
      </template>
      <div class="detailContainer">
        <div class="leftImg">
          <!-- <img
              <span class="location" @click="positionDetail(scope.row)" v-if="shouldShowLocation(scope.row)">定位</span>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <!-- 分页 -->
      <div class="pagination">
        <el-pagination v-model:current-page="jobListParams.current" v-model:page-size="jobListParams.size"
          :page-sizes="[10, 20, 30, 40]" background layout="total, sizes, prev, pager, next, jumper" :total="total"
          @size-change="handleSizeChange" @current-change="handleCurrentChange" />
      </div>
      <!-- 查看弹框 -->
      <el-dialog v-model="dialogVisible" width="60%" append-to-body @close="dialogClose">
        <template #header="{ titleId, titleClass }">
          <div class="my-header">
            <h4 :id="titleId" :class="titleClass">{{ detailTitle }}</h4>
          </div>
        </template>
        <div class="detailContainer">
          <div class="leftImg">
            <!-- <img
            v-if="dialogDetailList?.resultType === 1"
            :src="convertVideoUrlToThumbnail(dialogDetailList?.link)"
            alt=""
            class="imageBox"
          /> -->
          <!-- 视频 -->
          <video
            v-if="dialogDetailList?.resultType === 1"
            style="width: 100%"
            class="imageBox"
            ref="videoRefs"
            controls
            autoplay
            :src="dialogDetailList.link"
          ></video>
          <!-- 全景 -->
           <!-- <img
              class="quanjing"
              @click="clickpanorama(dialogDetailList)"
              v-else-if="dialogDetailList?.resultType === 5"
              :src="dialogDetailList?.resultType?.smallUrl"
              alt=""
            /> -->
          <!-- 地图 -->
          <div
            v-else-if="dialogDetailList?.resultType === 4"
            id="detaildataCenterMap"
            class="ztzf-cesium"
          ></div>
          <img v-else :src="getSmallImg(dialogDetailList?.link)" alt="" />
        </div>
        <div class="rightDetail">
          <div class="title">
            <div class="inputEdit">
              文件名称:<span class="fileTitle" v-if="!dialogDetailList?.checkedinput">{{
                dialogDetailList?.nickName ? dialogDetailList?.nickName : '--'
              }}</span>
              <el-input
                v-else
                v-model="dialogDetailList.nickName"
                @keyup.enter="saveTitle()"
                class="title-input"
                clearable
              />
            </div>
            <div class="editname">
              <span v-if="!dialogDetailList?.checkedinput" @click="editTitle(dialogDetailList)"
                ><el-icon><Edit /></el-icon
              ></span>
              <div v-else class="suffixBoxEdit">
                <div class="editText" @click="submitEditSuffix(dialogDetailList)">✔</div>
                <div class="editText" @click="cancelEditSuffix(dialogDetailList)">✖</div>
            <!-- 视频 -->
            <video v-if="dialogDetailList?.resultType === 1" style="width: 100%" class="imageBox" ref="videoRefs"
              controls autoplay :src="dialogDetailList.link"></video>
            <!-- 全景 -->
            <!-- <img
             class="quanjing"
             @click="clickpanorama(dialogDetailList)"
             v-else-if="dialogDetailList?.resultType === 5"
             :src="dialogDetailList?.resultType?.smallUrl"
             alt=""
           /> -->
            <!-- 地图 -->
            <div v-else-if="dialogDetailList?.resultType === 4" id="detaildataCenterMap" class="ztzf-cesium"></div>
            <img v-else :src="getSmallImg(dialogDetailList?.link)" alt="" />
          </div>
          <div class="rightDetail">
            <div class="title">
              <div class="inputEdit">
                文件名称:<span class="fileTitle" v-if="!dialogDetailList?.checkedinput">{{
                  dialogDetailList?.nickName ? dialogDetailList?.nickName : '--'
                }}</span>
                <el-input v-else v-model="dialogDetailList.nickName" @keyup.enter="saveTitle()" class="title-input"
                  clearable />
              </div>
              <div class="editname">
                <span v-if="!dialogDetailList?.checkedinput" @click="editTitle(dialogDetailList)"><el-icon>
                    <Edit />
                  </el-icon></span>
                <div v-else class="suffixBoxEdit">
                  <div class="editText" @click="submitEditSuffix(dialogDetailList)">✔</div>
                  <div class="editText" @click="cancelEditSuffix(dialogDetailList)">✖</div>
                </div>
              </div>
            </div>
          </div>
          <div>任务名称:{{ dialogDetailList?.jobName ? dialogDetailList?.jobName : '--' }}</div>
          <div>
            所属区域:{{ dialogDetailList?.regionName ? dialogDetailList?.regionName : '--' }}
          </div>
          <div>拍摄机巢:{{ dialogDetailList?.nestName ? dialogDetailList?.nestName : '--' }}</div>
          <div>
            照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{
              _.round(dialogDetailList?.latitude, 3)
            }}
          </div>
          <div>任务时间:{{ dialogDetailList?.jobTime ? dialogDetailList?.jobTime : '--' }}</div>
          <div>
            拍摄时间:{{ dialogDetailList?.createTime ? dialogDetailList?.createTime : '--' }}
          </div>
          <div>
            文件类型:{{
              photoTypeMap[dialogDetailList?.photoType]
                ? photoTypeMap[dialogDetailList?.photoType]
                : '--'
            }}
          </div>
          <div>
            文件格式:{{
              resultTypeMap[dialogDetailList?.resultType]
                ? resultTypeMap[dialogDetailList?.resultType]
                : '--'
            }}
          </div>
          <div>
            照片文件大小:{{
              bytesToMB(dialogDetailList?.attachSize)
                ? bytesToMB(dialogDetailList?.attachSize)
                : '--'
            }}
          </div>
          <div>
            <el-button
              type="success"
              plain
              icon="el-icon-download"
              @click="detailDownLoad(dialogDetailList)"
              >下载</el-button
            >
            <div>任务名称:{{ dialogDetailList?.jobName ? dialogDetailList?.jobName : '--' }}</div>
            <div>
              所属区域:{{ dialogDetailList?.regionName ? dialogDetailList?.regionName : '--' }}
            </div>
            <div>拍摄机巢:{{ dialogDetailList?.nestName ? dialogDetailList?.nestName : '--' }}</div>
            <div>
              照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{
                _.round(dialogDetailList?.latitude, 3)
              }}
            </div>
            <div>任务时间:{{ dialogDetailList?.jobTime ? dialogDetailList?.jobTime : '--' }}</div>
            <div>
              拍摄时间:{{ dialogDetailList?.createTime ? dialogDetailList?.createTime : '--' }}
            </div>
            <div>
              文件类型:{{
                photoTypeMap[dialogDetailList?.photoType]
                  ? photoTypeMap[dialogDetailList?.photoType]
                  : '--'
              }}
            </div>
            <div>
              文件格式:{{
                resultTypeMap[dialogDetailList?.resultType]
                  ? resultTypeMap[dialogDetailList?.resultType]
                  : '--'
              }}
            </div>
            <div>
              照片文件大小:{{
                bytesToMB(dialogDetailList?.attachSize)
                  ? bytesToMB(dialogDetailList?.attachSize)
                  : '--'
              }}
            </div>
            <div>
              <el-button type="success" plain icon="el-icon-download"
                @click="detailDownLoad(dialogDetailList)">下载</el-button>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
    <!-- 全景预览 -->
    <PanoramaPopup
      v-model:panoramaParamsShow="panoramaParamsShow"
      v-model:panoramaParamsUrl="panoramaParamsUrl"
    ></PanoramaPopup>
    <!-- 视频预览 -->
    <el-dialog
      :title="currentVideoTitle"
      modal-class="videoDialog"
      append-to-body
      width="54%"
      v-model="VideoShow"
      :close-on-click-modal="false"
      :destroy-on-close="true"
      @close="currentVideoIndex = -1"
    >
      <div class="video-container">
        <video
          style="width: 100%"
          class="videoBox"
          ref="videoRefs"
          controls
          autoplay
          :src="currentVideoUrl"
        ></video>
      </div>
    </el-dialog>
    <!-- 地图弹框 -->
    <dataCenterMap
      ref="mapComponent"
      v-model:show="dataCenterMapVisible"
      :jobId="jobId"
      @lookDetail="lookDetail"
      :dotData="mapList"
    ></dataCenterMap>
      </el-dialog>
      <!-- 全景预览 -->
      <PanoramaPopup v-model:panoramaParamsShow="panoramaParamsShow" v-model:panoramaParamsUrl="panoramaParamsUrl">
      </PanoramaPopup>
      <!-- 视频预览 -->
      <el-dialog :title="currentVideoTitle" modal-class="videoDialog" append-to-body width="54%" v-model="VideoShow"
        :close-on-click-modal="false" :destroy-on-close="true" @close="currentVideoIndex = -1">
        <div class="video-container">
          <video style="width: 100%" class="videoBox" ref="videoRefs" controls autoplay :src="currentVideoUrl"></video>
        </div>
      </el-dialog>
      <!-- 地图弹框 -->
      <dataCenterMap ref="mapComponent" v-model:show="dataCenterMapVisible" :jobId="jobId" @lookDetail="lookDetail"
        :dotData="mapList"></dataCenterMap>
    </div>
  </div>
</template>
<script setup>
import { useStore } from 'vuex';
import { PublicCesium } from '@/utils/cesium/publicCesium';
import { Cartesian3 } from 'cesium';
import * as Cesium from 'cesium';
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue';
import { nextTick, render } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png';
const userInfo = computed(() => store.getters.userInfo);
const isShow = defineModel('show');
const viewerRef = shallowRef(null);
let viewer = null;
import { useStore } from 'vuex'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import { Cartesian3 } from 'cesium'
import * as Cesium from 'cesium'
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
import { nextTick, render } from 'vue'
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png'
const userInfo = computed(() => store.getters.userInfo)
const isShow = defineModel('show')
const viewerRef = shallowRef(null)
let viewer = null
const viewInstance = shallowRef(null);
const store = useStore();
const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 });
let handler = null;
const viewInstance = shallowRef(null)
const store = useStore()
const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 })
let handler = null
import EventBus from '@/utils/eventBus';
import dataCenterMap from '@/views/dataCenter/components/dataCenterMap.vue';
import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'; //全景
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import searchData from '@/views/dataCenter/components/searchData.vue';
import fy1 from '@/assets/images/dataCenter/1.jpeg';
import _ from 'lodash';
import EventBus from '@/utils/eventBus'
import dataCenterMap from '@/views/dataCenter/components/dataCenterMap.vue'
import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue' //全景
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import searchData from '@/views/dataCenter/components/searchData.vue'
import fy1 from '@/assets/images/dataCenter/1.jpeg'
import _ from 'lodash'
import {
  getaiImagesPageAPI,
  getAttachInfoAPI,
@@ -288,24 +205,24 @@
  downloadApi,
  updataTitleApi,
  getOrthoimageInfo,
} from '@/api/dataCenter/dataCenter';
import { getShowImg, getSmallImg, getzsSmallImg } from '@/utils/util';
import { onMounted, watch } from 'vue';
import dayjs from 'dayjs';
} from '@/api/dataCenter/dataCenter'
import { getShowImg, getSmallImg, getzsSmallImg } from '@/utils/util'
import { onMounted, watch } from 'vue'
import dayjs from 'dayjs'
function bytesToMB(bytes, decimalPlaces = 2) {
  if (typeof bytes !== 'number' || bytes < 0) return '无效输入';
  return (bytes / 1048576).toFixed(decimalPlaces) + ' MB';
function bytesToMB (bytes, decimalPlaces = 2) {
  if (typeof bytes !== 'number' || bytes < 0) return '无效输入'
  return (bytes / 1048576).toFixed(decimalPlaces) + ' MB'
}
// 视频一帧
function convertVideoUrlToThumbnail(videoUrl) {
function convertVideoUrlToThumbnail (videoUrl) {
  // 检查是否是有效的视频URL
  if (!videoUrl || typeof videoUrl !== 'string') {
    return videoUrl;
    return videoUrl
  }
  // 替换文件扩展名
  const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg');
  return thumbnailUrl;
  const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg')
  return thumbnailUrl
}
const resultTypeMap = {
  0: '照片',
@@ -313,84 +230,84 @@
  2: 'AI识别',
  5: '全景',
  4: '正射',
};
}
const photoTypeMap = {
  visible: '可见光',
  ir: '红外',
};
}
// 判断省市区
const areaCode = userInfo.value?.detail?.areaCode || '';
const areaCode = userInfo.value?.detail?.areaCode || ''
// 判断账号级别
const isProvinceLevel = computed(() => areaCode.endsWith('00000000')); // 省级:后8位为0
const isCityLevel = computed(() => !isProvinceLevel.value && areaCode.endsWith('000000')); // 市级:后6位为0
const isDistrictLevel = computed(() => !isProvinceLevel.value && !isCityLevel.value); // 区县级
const isProvinceLevel = computed(() => areaCode.endsWith('00000000')) // 省级:后8位为0
const isCityLevel = computed(() => !isProvinceLevel.value && areaCode.endsWith('000000')) // 市级:后6位为0
const isDistrictLevel = computed(() => !isProvinceLevel.value && !isCityLevel.value) // 区县级
// 处理区域名称的函数
function processAddress(str) {
  if (!str) return '';
  const parts = str.split(',');
function processAddress (str) {
  if (!str) return ''
  const parts = str.split(',')
  // 省级账号:显示市和区(去除省级)
  if (isProvinceLevel.value) {
    return parts.filter(part => !part.endsWith('省')).join(',');
    return parts.filter(part => !part.endsWith('省')).join(',')
  }
  // 市级账号:只显示区(去除省和市)
  if (isCityLevel.value) {
    return parts.filter(part => !part.endsWith('省') && !part.endsWith('市')).join(',');
    return parts.filter(part => !part.endsWith('省') && !part.endsWith('市')).join(',')
  }
  // 区县级账号:不显示区域信息(在模板中已隐藏该列)
  return '';
  return ''
}
const showRegionColumn = computed(() => {
  // 是否显示列
  return tableData.value.some(row => {
    const processed = processAddress(row.regionName || '');
    return processed !== '';
  });
});
const loadings = ref(true);
let loading;
const total = ref(0);
const startTime = dayjs().subtract(6, 'day').startOf('day');
const endTime = dayjs().endOf('day');
const timeRange = [startTime.format('YYYY-MM-DD HH:mm:ss'), endTime.format('YYYY-MM-DD HH:mm:ss')];
    const processed = processAddress(row.regionName || '')
    return processed !== ''
  })
})
const loadings = ref(true)
let loading
const total = ref(0)
const startTime = dayjs().subtract(6, 'day').startOf('day')
const endTime = dayjs().endOf('day')
const timeRange = [startTime.format('YYYY-MM-DD HH:mm:ss'), endTime.format('YYYY-MM-DD HH:mm:ss')]
// 全景预览
const panoramaParamsShow = ref(false);
const panoramaParamsUrl = ref(null);
const panoramaParamsShow = ref(false)
const panoramaParamsUrl = ref(null)
const clickpanorama = val => {
  panoramaParamsShow.value = true;
  panoramaParamsUrl.value = val.link;
};
  panoramaParamsShow.value = true
  panoramaParamsUrl.value = val.link
}
// 视频
const currentVideoTitle = ref('');
const VideoShow = ref(false);
const currentVideoUrl = ref(null);
const currentVideoTitle = ref('')
const VideoShow = ref(false)
const currentVideoUrl = ref(null)
const enterFullScreen = val => {
  currentVideoTitle.value = val?.nickName;
  currentVideoUrl.value = val?.link;
  VideoShow.value = true;
};
  currentVideoTitle.value = val?.nickName
  currentVideoUrl.value = val?.link
  VideoShow.value = true
}
const jobListParams = reactive({
  current: 1,
  size: 10,
  orderByCreateTime: true,
  searchParams: { startTime: timeRange[0], endTime: timeRange[1] },
});
const tableData = ref([]);
})
const tableData = ref([])
// 获取列表数据
const getaiImagesPage = () => {
  loadings.value = true;
  loadings.value = true
  const params = {
    orderByCreateTime: jobListParams.orderByCreateTime,
    ...jobListParams.searchParams,
  };
  getaiImagesPageAPI(params, {
    current: jobListParams.current,
    size: jobListParams.size
  }
  getaiImagesPageAPI(params, {
    current: jobListParams.current,
    size: jobListParams.size
  })
    .then(res => {
      total.value = res.data.data.total;
      total.value = res.data.data.total
      tableData.value = res.data.data.records.map(i => ({
        ...i,
        checked: false,
@@ -398,41 +315,41 @@
        smallUrl: getSmallImg(i?.link),
        showUrl: getShowImg(i?.link),
        file_name: i.name.split('/').pop(),
      }));
      }))
    })
    .catch(error => {
      // 可选:这里可以添加错误处理逻辑
      console.error("获取数据失败:", error);
      console.error("获取数据失败:", error)
    })
    .finally(() => {
      loadings.value = false; // 无论成功失败都会执行
    });
};
      loadings.value = false // 无论成功失败都会执行
    })
}
// 查询
const searchClick = params => {
  jobListParams.current = 1;
  jobListParams.size = 10;
  jobListParams.searchParams = params;
  getaiImagesPage();
};
  jobListParams.current = 1
  jobListParams.size = 10
  jobListParams.searchParams = params
  getaiImagesPage()
}
const handleSizeChange = val => {
  jobListParams.size = val;
  getaiImagesPage();
};
  jobListParams.size = val
  getaiImagesPage()
}
const handleCurrentChange = val => {
  jobListParams.current = val;
  getaiImagesPage();
};
  jobListParams.current = val
  getaiImagesPage()
}
// 多选
const selectedRows = ref([]);
const selectedRows = ref([])
const handleSelectionChange = val => {
  // 更新选中状态
  tableData.value.forEach(item => {
    item.checked = val.some(selected => selected.id === item.id);
  });
    item.checked = val.some(selected => selected.id === item.id)
  })
  selectedRows.value = val;
};
  selectedRows.value = val
}
// 删除
const deleteDetail = val => {
  ElMessageBox.confirm('您确定删除吗?', '提示', {
@@ -443,36 +360,36 @@
    .then(() => {
      deleteFileMultipleApi(val.id)
        .then(res => {
          ElMessage.success('删除成功');
          getaiImagesPage();
          ElMessage.success('删除成功')
          getaiImagesPage()
        })
        .catch(error => {
          ElMessage.error('删除失败');
        });
          ElMessage.error('删除失败')
        })
    })
    .catch(() => {});
};
    .catch(() => { })
}
// url下载
function aLinkDownload(url, name) {
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = name;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
function aLinkDownload (url, name) {
  const a = document.createElement('a')
  a.style.display = 'none'
  a.href = url
  a.download = name
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
}
const fileDownload = () => {
  const list = selectedRows.value.filter(i => i.checked);
  if (!list?.length) return ElMessage.warning('请选择文件');
     if (list.length === 1) {
        list.forEach((item, index) => {
            const suffix = item.url.split('.').pop()
            aLinkDownload(item.url, item.nickName + '.' + suffix)
        })
    } else {
    loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' });
    const fileIds = list.map(i => i.id);
  const list = selectedRows.value.filter(i => i.checked)
  if (!list?.length) return ElMessage.warning('请选择文件')
  if (list.length === 1) {
    list.forEach((item, index) => {
      const suffix = item.url.split('.').pop()
      aLinkDownload(item.url, item.nickName + '.' + suffix)
    })
  } else {
    loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' })
    const fileIds = list.map(i => i.id)
    let aaa = {
      areaCode: '',
      attachIds: fileIds,
@@ -484,274 +401,281 @@
      resultType: '',
      startTime: '',
      wayLineJobIds: [],
    };
    }
    downloadApi(aaa).then(res => {
      aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`);
      loading.close();
    });
      aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`)
      loading.close()
    })
  }
};
}
// 下载
const downloadFile = () => {
  fileDownload();
};
  fileDownload()
}
const detailDownLoad = val => {
    const suffix = val.url?.split('.').pop()
  aLinkDownload(val.link, val?.nickName + '.' + suffix);
};
  const suffix = val.url?.split('.').pop()
  aLinkDownload(val.link, val?.nickName + '.' + suffix)
}
// 全部下载
const aLLDownloadFile = () => {
  loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' });
  loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' })
  const params = {
    ...jobListParams.searchParams,
  };
  params.areaCode = '';
  }
  params.areaCode = ''
  downloadApi(params).then(res => {
    aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`);
    loading.close();
  });
};
    aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`)
    loading.close()
  })
}
// 地图弹框
const mapComponent = ref(null); // 创建子组件引用
const mapList = ref(null);
const dataCenterMapVisible = ref(false);
const jobId = ref('');
const statusType = ref(null);
const mapComponent = ref(null) // 创建子组件引用
const mapList = ref(null)
const dataCenterMapVisible = ref(false)
const jobId = ref('')
const statusType = ref(null)
const positionDetail = val => {
  jobId.value = val.wayLineJobId;
  mapList.value = val;
  dataCenterMapVisible.value = true;
  jobId.value = val.wayLineJobId
  mapList.value = val
  dataCenterMapVisible.value = true
  // 确保地图组件加载完成
  nextTick(() => {
    if (mapComponent.value) {
      // 调用子组件方法并传递数据
      mapComponent.value.initEntityOrPopup(val);
      mapComponent.value.initEntityOrPopup(val)
    }
  });
};
  })
}
const fileNameedit = ref('');
const fileNameedit = ref('')
// 编辑文件名
const editTitle = val => {
  val.checkedinput = true;
  fileNameedit.value = val?.nickName;
};
  val.checkedinput = true
  fileNameedit.value = val?.nickName
}
// 取消
const cancelEditSuffix = item => {
  item.nickName = fileNameedit.value;
  item.checkedinput = false;
};
  item.nickName = fileNameedit.value
  item.checkedinput = false
}
const submitEditSuffix = item => {
  saveTitle(item);
};
  saveTitle(item)
}
// 通用空值检查函数
const validateNickname = (name, fieldName) => {
  if (!name || name.trim() === '') {
    ElMessage.warning(`${fieldName}不能为空`);
    return false;
    ElMessage.warning(`${fieldName}不能为空`)
    return false
  }
  if (name.length > 50) {
    ElMessage.warning(`${fieldName}不能超过50个字符`);
    return false;
    ElMessage.warning(`${fieldName}不能超过50个字符`)
    return false
  }
  return true;
};
  return true
}
// 保存文件名
const saveTitle = item => {
  const updateparams = {
    id: item.id,
    nickName: `${item.nickName.trim()}`,
  };
  }
  // 验证并提示
  if (!validateNickname(updateparams.nickName, '名称')) return;
  item.checkedinput = false;
  detailTitle.value = item.nickName;
  if (!validateNickname(updateparams.nickName, '名称')) return
  item.checkedinput = false
  detailTitle.value = item.nickName
  updataTitleApi(updateparams)
    .then(res => {
      if (res.status === 200) {
        ElMessage.success('修改成功');
        getaiImagesPage();
        ElMessage.success('修改成功')
        getaiImagesPage()
      } else {
        ElMessage.error(res.data.message || '修改失败');
        ElMessage.error(res.data.message || '修改失败')
      }
    })
    .catch(error => {
      ElMessage.error('请求失败,请稍后重试');
      console.error('API error:', error);
    });
};
      ElMessage.error('请求失败,请稍后重试')
      console.error('API error:', error)
    })
}
// 正射地图
const yxShowBox = ref(false);
const yxShowBox = ref(false)
const removeHandler = () => {
  handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  handler?.destroy();
  handler = null;
};
const isMapInitialized = ref(false); //地图加载
  handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
  handler?.destroy()
  handler = null
}
const isMapInitialized = ref(false) //地图加载
const initMap = () => {
  if (viewer || isMapInitialized.value) return;
  if (viewer || isMapInitialized.value) return
  const container = document.getElementById('detaildataCenterMap');
  const container = document.getElementById('detaildataCenterMap')
  if (!container) {
    console.error('地图容器未找到');
    return;
    console.error('地图容器未找到')
    return
  }
  viewInstance.value = new PublicCesium({
    dom: 'detaildataCenterMap',
    flatMode: false,
    terrain: false,
    mapFilter: true,
  });
  viewer = viewInstance.value.getViewer();
  viewerRef.value = viewer;
  })
  viewer = viewInstance.value.getViewer()
  viewerRef.value = viewer
  isMapInitialized.value = true;
  isMapInitialized.value = true
  // console.log('地图初始化完成');
};
}
// 判断是否是正射
function isTifFile(filename) {
  const lastDot = filename.lastIndexOf('.');
  if (lastDot === -1) return false; // 无后缀
  return filename.slice(lastDot + 1).toLowerCase() === 'tif';
function isTifFile (filename) {
  const lastDot = filename.lastIndexOf('.')
  if (lastDot === -1) return false // 无后缀
  return filename.slice(lastDot + 1).toLowerCase() === 'tif'
}
const shouldShowLocation = row => {
  return row.resultType !== 1 && row.resultType !== 4;
};
  return row.resultType !== 1 && row.resultType !== 4
}
// 查看弹框
const dialogVisible = ref(false);
const dialogDetailList = ref(null);
const detailTitle = ref('');
let curCustomImageryProvider = null;
const orthoimageApi = ref('');
const positiongeom = ref('');
const odmToken = ref(null);
const dialogVisible = ref(false)
const dialogDetailList = ref(null)
const detailTitle = ref('')
let curCustomImageryProvider = null
const orthoimageApi = ref('')
const positiongeom = ref('')
const odmToken = ref(null)
const lookDetail = val => {
  if (val.resultType === 4) {
    // 正射
    getOrthoimageInfo(val.wayLineJobId).then(async res => {
      dialogVisible.value = true;
      detailTitle.value = val.nickName;
      dialogDetailList.value = val;
      console.log('3333', detailTitle.value, dialogDetailList.value);
      dialogVisible.value = true
      detailTitle.value = val.nickName
      dialogDetailList.value = val
      console.log('3333', detailTitle.value, dialogDetailList.value)
      await nextTick();
      initMap();
      positiongeom.value = res.data.data.geom;
      orthoimageApi.value = res.data.data.orthoimageApi;
      odmToken.value = res.data.data.odmToken;
      await nextTick()
      initMap()
      positiongeom.value = res.data.data.geom
      orthoimageApi.value = res.data.data.orthoimageApi
      odmToken.value = res.data.data.odmToken
      if (res.data.data != null && isMapInitialized.value === true) {
        curCustomImageryProvider = viewInstance.value.addCustomImageryProviderDataSource(
          new Cesium.UrlTemplateImageryProvider({
            url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${res.data.data.orthoimageApi}?jwt=${
              res.data.data.odmToken.split(' ')[1]
            }`,
            url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${res.data.data.orthoimageApi}?jwt=${res.data.data.odmToken.split(' ')[1]
              }`,
            // maximumLevel: 12,
          })
        );
        yxShowBox.value = true;
        )
        yxShowBox.value = true
        // 添加定位
        if (positiongeom.value) {
          // 解析WKT格式的多边形坐标
          const wktString = positiongeom.value;
          const coordinates = parseWKTCoordinates(wktString);
          const wktString = positiongeom.value
          const coordinates = parseWKTCoordinates(wktString)
          if (coordinates.length > 0) {
            // 创建多边形边界
            const positions = coordinates.map(coord =>
              Cesium.Cartesian3.fromDegrees(coord[0], coord[1])
            );
            )
            // 计算多边形的包围球
            const boundingSphere = Cesium.BoundingSphere.fromPoints(positions);
            const viewer = viewInstance.value.getViewer();
            const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
            const viewer = viewInstance.value.getViewer()
            if (viewer && viewer.camera) {
              viewer.camera.flyToBoundingSphere(boundingSphere, {
                duration: 0, // 飞行动画持续时间(秒)
                offset: new Cesium.HeadingPitchRange(0, -0.5, boundingSphere.radius * 1.5),
              });
              })
            } else {
              console.error('无法获取有效的 Viewer 或 camera 对象');
              console.error('无法获取有效的 Viewer 或 camera 对象')
            }
          }
        }
      }
    });
    })
  } else {
    getAttachInfoAPI(val.id).then(res => {
      detailTitle.value = res.data.data.nickName.split('.jpeg')[0];
      detailTitle.value = res.data.data.nickName.split('.jpeg')[0]
      dialogDetailList.value = {
        ...res.data.data,
        checkedinput: false,
        editName: res.data.data?.nickName?.split('.jpeg')[0],
      };
      dialogVisible.value = true;
    });
      }
      dialogVisible.value = true
    })
  }
};
// WKT坐标解析函数
function parseWKTCoordinates(wktString) {
  // 解析POLYGON格式的WKT字符串
  const regex = /POLYGON\s*\(\((.*?)\)\)/i;
  const match = wktString.match(regex);
  if (!match || !match[1]) return [];
  // 分割坐标对
  const coordinatePairs = match[1].split(/,\s*/);
  return coordinatePairs.map(pair => {
    const [lon, lat] = pair.trim().split(/\s+/).map(Number);
    return [lon, lat];
  });
}
function dialogClose() {
// WKT坐标解析函数
function parseWKTCoordinates (wktString) {
  // 解析POLYGON格式的WKT字符串
  const regex = /POLYGON\s*\(\((.*?)\)\)/i
  const match = wktString.match(regex)
  if (!match || !match[1]) return []
  // 分割坐标对
  const coordinatePairs = match[1].split(/,\s*/)
  return coordinatePairs.map(pair => {
    const [lon, lat] = pair.trim().split(/\s+/).map(Number)
    return [lon, lat]
  })
}
function dialogClose () {
  // 对话框关闭时清理资源
  viewInstance.value?.viewerDestroy();
  viewer = null;
  isMapInitialized.value = false;
  viewInstance.value?.viewerDestroy()
  viewer = null
  isMapInitialized.value = false
}
onMounted(() => {
  getaiImagesPage();
  getaiImagesPage()
  // 监听打开全景事件
  EventBus.on('open-panorama', params => {
    panoramaParamsShow.value = params.show;
    panoramaParamsUrl.value = params.url;
  });
});
    panoramaParamsShow.value = params.show
    panoramaParamsUrl.value = params.url
  })
})
onBeforeUnmount(() => {
  // 组件卸载时移除事件监听,防止内存泄漏
  EventBus.off('open-panorama');
  viewInstance.value?.removeAllCustomImageryProviderDataSource(curCustomImageryProvider);
  viewInstance.value?.viewerDestroy();
});
  EventBus.off('open-panorama')
  viewInstance.value?.removeAllCustomImageryProviderDataSource(curCustomImageryProvider)
  viewInstance.value?.viewerDestroy()
})
</script>
<style scoped lang="scss">
.dataCenter-table {
  margin: 0 18px 16px 10px;
  background-color: #ffffff;
  padding: 14px 18px;
  height: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  .dataTable {
    height: 700px;
    height: 0;
    flex: 1;
    overflow: auto;
    .look {
      color: #1c5cff;
      cursor: pointer;
      margin-right: 10px;
    }
    .delete {
      color: #ff241c;
      margin-right: 10px;
      cursor: pointer;
    }
    .location {
      color: #19876d;
      cursor: pointer;
    }
  }
  .pagination {
    display: flex;
    justify-content: end;
    margin-top: 20px;
  }
  .quanjing,
  .el-image,
  .imageBox {
@@ -759,54 +683,70 @@
    width: 76px;
    height: 72px;
  }
  .videoDialog :deep(.el-dialog) {
    height: 600px;
    width: 54%;
  }
  .video-container {
    width: 100%;
    aspect-ratio: 16/9; /* 按视频比例设置(如16:9) */
    overflow: hidden; /* 隐藏溢出部分 */
    aspect-ratio: 16/9;
    /* 按视频比例设置(如16:9) */
    overflow: hidden;
    /* 隐藏溢出部分 */
  }
  .videoBox {
    width: 100%;
    height: 100%;
    object-fit: contain; /* 保持比例完整显示 */
    object-fit: contain;
    /* 保持比例完整显示 */
    display: block;
  }
}
:deep(.custom-header th.el-table__cell) {
  color: rgba(0, 0, 0, 0.85);
}
// 弹框
.detailContainer {
  display: flex;
  justify-content: space-between;
  .leftImg {
    width: 70%;
    height: 500px;
    img {
      width: 100%;
      height: 100%;
    }
    #detaildataCenterMap {
      width: 100%;
      height: 100%;
    }
  }
  .rightDetail {
    width: 30%;
    padding-left: 40px;
    .title {
      display: flex;
      margin: 0 !important;
      .editname {
        cursor: pointer;
      }
      .inputEdit {
        display: flex;
        align-items: center;
        width: 100%;
        .fileTitle {
          width: 70%;
          white-space: nowrap;
@@ -815,49 +755,60 @@
        }
      }
    }
    div {
      margin-bottom: 20px;
    }
  }
}
.my-header :deep(.el-dialog__title) {
  margin: 0 !important;
  height: 19px;
}
.my-header {
  display: flex;
  align-items: center;
  .el-button {
    margin-left: 20px;
  }
}
.title-input {
  margin-bottom: 0 !important;
  width: 70%;
}
.editname {
  margin-bottom: 0 !important;
}
.suffixBoxEdit {
  display: flex;
  // align-items: center;
  justify-content: space-between;
  margin-bottom: 0 !important;
  font-size: 16px;
  > .editText {
  >.editText {
    cursor: pointer;
    margin-right: 6px;
    &:hover {
      transform: scale(1.2);
    }
  }
  .editimg {
    cursor: pointer;
    width: 15px;
    height: 15px;
    &:hover {
      transform: scale(1.2);
    }
  }
}
</style>
</style>
src/views/device/addDevice.vue
@@ -1,20 +1,20 @@
<template>
    <basic-container>
        <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel"
            v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave"
        <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud"
            @row-del="rowDel" v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave"
            :before-open="beforeOpen" @search-change="searchChange" @search-reset="searchReset"
            @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
            @refresh-change="refreshChange" @on-load="onLoad">
            <template #menu="scope">
                <el-button type="primary" text icon="el-icon-copy-document" @click.stop="copyInfo(scope.row, scope.index)"
                    v-if="userInfo.role_name.includes('admin')">复制
                <el-button type="primary" text icon="el-icon-copy-document"
                    @click.stop="copyInfo(scope.row, scope.index)" v-if="userInfo.role_name.includes('admin')">复制
                </el-button>
            </template>
        </avue-crud>
    </basic-container>
</template>
<script>
import {
    getList,
@@ -22,21 +22,21 @@
    update,
    add,
    getDetail,
} from '@/api/device/workspaces';
} from '@/api/device/workspaces'
import { getLazyTree } from '@/api/base/region';
import { getLazyTree } from '@/api/base/region'
import { mapGetters } from 'vuex';
import { getWebsocketUrl } from '@/websocket/util/config';
import ConnectWebSocket from '@/websocket';
import { $Clipboard } from "@smallwei/avue";
import { mapGetters } from 'vuex'
import { getWebsocketUrl } from '@/websocket/util/config'
import ConnectWebSocket from '@/websocket'
import { $Clipboard } from "@smallwei/avue"
export default {
    components: {
    },
    data() {
    data () {
        return {
            // 用于记录已经创建的websocket,防止重复创建
            webSocketIdSet: new Set(),
@@ -65,8 +65,7 @@
                menuWidth: 300,
                lazy: true,
                tree: true,
                // height: '460',
                // calcHeight: 32,
                // dialogWidth: 1050,
                tip: false,
                searchShow: true,
@@ -79,6 +78,9 @@
                addBtn: true,
                dialogClickModal: false,
                grid: false,
                height: 'auto',
                calcHeight: 20,
                column: [
                    {
                        label: '工作空间名称',
@@ -197,39 +199,39 @@
            },
            data: [],
        };
        }
    },
    computed: {
        ...mapGetters(['userInfo', 'permission']),
        permissionList() {
        permissionList () {
            return {
                addBtn: this.validData(this.permission.airport_add, false),
                viewBtn: this.validData(this.permission.airport_view, true),
                delBtn: this.validData(this.permission.airport_delete, false),
                editBtn: this.validData(this.permission.airport_edit, false),
            };
            }
        },
        ids() {
            let ids = [];
        ids () {
            let ids = []
            this.selectionList.forEach(ele => {
                ids.push(ele.id);
            });
            return ids.join(',');
                ids.push(ele.id)
            })
            return ids.join(',')
        },
    },
    destroyed() {
    destroyed () {
        this.websocketMap.forEach((k, v) => {
            if (null != v) {
                v.close()
                this.websocketMap.delete(k)
                this.webSocketIdSet.delete(k)
            }
        });
        })
    },
    methods: {
        copyInfo(row, index) {
            if(import.meta.env.VITE_APP_ENV==='development'){
        copyInfo (row, index) {
            if (import.meta.env.VITE_APP_ENV === 'development') {
                // alert('地址'+import.meta.env.VITE_APP_ENV);
                let params = `
            host: tcp://139.196.74.78:1883
@@ -238,111 +240,112 @@
            组织id:${row.bing_id}  
            设备码:${row.bind_code}  
            机场名称:${row.workspace_name}   
            aircrattName:${row.workspace_desc}`;
            $Clipboard({
                text: params,
            }).then(() => {
                this.$message.success("复制成功");
            }).catch(() => {
                this.$message.error("复制失败");
            });
        }else{
            // alert('地址'+import.meta.env.VITE_APP_ENV);
            let params = `
            aircrattName:${row.workspace_desc}`
                $Clipboard({
                    text: params,
                }).then(() => {
                    this.$message.success("复制成功")
                }).catch(() => {
                    this.$message.error("复制失败")
                })
            } else {
                // alert('地址'+import.meta.env.VITE_APP_ENV);
                let params = `
            host: tcp://101.132.85.201:1883 
            账号:drone  
            密码:drone@123
            组织id:${row.bing_id}  
            设备码:${row.bind_code}  
            机场名称:${row.workspace_name}   
            aircrattName:${row.workspace_desc}`;
            $Clipboard({
                text: params,
            }).then(() => {
                this.$message.success("复制成功");
            }).catch(() => {
                this.$message.error("复制失败");
            });}
            aircrattName:${row.workspace_desc}`
                $Clipboard({
                    text: params,
                }).then(() => {
                    this.$message.success("复制成功")
                }).catch(() => {
                    this.$message.error("复制失败")
                })
            }
        },
        init() {
            this.onLoad(this.page);
        init () {
            this.onLoad(this.page)
        },
        handleOpenRemoteDebugging(row) {
            this.curDeviceInfo = row;
            this.remoteDebuggingShow = true;
        handleOpenRemoteDebugging (row) {
            this.curDeviceInfo = row
            this.remoteDebuggingShow = true
        },
        rowSave(row, done, loading) {
        rowSave (row, done, loading) {
            add(row).then(
                () => {
                    this.onLoad(this.page);
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    });
                    done();
                    })
                    done()
                },
                error => {
                    window.console.log(error);
                    loading();
                    window.console.log(error)
                    loading()
                }
            );
            )
        },
        rowUpdate(row, index, done, loading) {
        rowUpdate (row, index, done, loading) {
            update(row).then(
                () => {
                    this.onLoad(this.page);
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    });
                    done();
                    })
                    done()
                },
                error => {
                    window.console.log(error);
                    loading();
                    window.console.log(error)
                    loading()
                }
            );
            )
        },
        rowDel(row) {
        rowDel (row) {
            this.$confirm('确定将选择数据删除?', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            })
                .then(() => {
                    return remove(row.id);
                    return remove(row.id)
                })
                .then(() => {
                    this.onLoad(this.page);
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    });
                });
                    })
                })
        },
        searchReset() {
            this.query = {};
            this.onLoad(this.page);
        searchReset () {
            this.query = {}
            this.onLoad(this.page)
        },
        searchChange(params, done) {
            this.query = params;
            this.page.currentPage = 1;
            this.onLoad(this.page, params);
            done();
        searchChange (params, done) {
            this.query = params
            this.page.currentPage = 1
            this.onLoad(this.page, params)
            done()
        },
        selectionChange(list) {
            this.selectionList = list;
        selectionChange (list) {
            this.selectionList = list
        },
        selectionClear() {
            this.selectionList = [];
            this.$refs.crud.toggleSelection();
        selectionClear () {
            this.selectionList = []
            this.$refs.crud.toggleSelection()
        },
        handleDelete() {
        handleDelete () {
            if (this.selectionList.length === 0) {
                this.$message.warning('请选择至少一条数据');
                return;
                this.$message.warning('请选择至少一条数据')
                return
            }
            this.$confirm('确定将选择数据删除?', {
                confirmButtonText: '确定',
@@ -350,86 +353,78 @@
                type: 'warning',
            })
                .then(() => {
                    return remove(this.ids);
                    return remove(this.ids)
                })
                .then(() => {
                    this.onLoad(this.page);
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    });
                    this.$refs.crud.toggleSelection();
                });
                    })
                    this.$refs.crud.toggleSelection()
                })
        },
        beforeOpen(done, type) {
        beforeOpen (done, type) {
            if (['edit', 'view'].includes(type)) {
                getDetail(this.form.id).then(res => {
                    this.form = res.data.data;
                });
                    this.form = res.data.data
                })
            }
            done();
            done()
        },
        currentChange(currentPage) {
            this.page.currentPage = currentPage;
        currentChange (currentPage) {
            this.page.currentPage = currentPage
        },
        sizeChange(pageSize) {
            this.page.pageSize = pageSize;
        sizeChange (pageSize) {
            this.page.pageSize = pageSize
        },
        refreshChange() {
            this.onLoad(this.page, this.query);
        refreshChange () {
            this.onLoad(this.page, this.query)
        },
        onLoad(page, params = {}) {
            const { releaseTimeRange } = this.query;
        onLoad (page, params = {}) {
            const { releaseTimeRange } = this.query
            let values = {
                ...params,
                ...this.query,
            };
            }
            if (releaseTimeRange) {
                values = {
                    ...values,
                    releaseTime_datege: releaseTimeRange[0],
                    releaseTime_datelt: releaseTimeRange[1],
                };
                values.releaseTimeRange = null;
                }
                values.releaseTimeRange = null
            }
            values['type'] = 1;
            this.loading = true;
            values['type'] = 1
            this.loading = true
            getList(page.currentPage, page.pageSize, values).then(res => {
                const data = res.data.data;
                this.page.total = data.total;
                const data = res.data.data
                this.page.total = data.total
                // data.records.forEach(e => {
                //     e['hasChildren'] = e.has_children;
                //     if (e.firmware_status == 4) {
                //         this.connectWebSocket(e);
                //     }
                // });
                this.data = data.records;
                this.loading = false;
                this.selectionClear();
            });
                this.data = data.records
                this.loading = false
                this.selectionClear()
            })
        },
        treeLoad(tree, treeNode, resolve) {
        treeLoad (tree, treeNode, resolve) {
            var params = {
                childSn: tree.child_sn,
            };
            }
            getList(1, 10, params).then(res => {
                const data = res.data.data.records;
                resolve(data);
            });
                const data = res.data.data.records
                resolve(data)
            })
        },
    },
};
}
</script>
<style lang="scss" scoped>
 :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
.firmware_status {
    background-color: aqua;
    border-radius: 4px;
@@ -448,5 +443,4 @@
        border-color: #409eff;
    }
}
</style>
</style>
src/views/device/airport.vue
@@ -1,5 +1,5 @@
<template>
  <basic-container>
  <basic-main-content>
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel"
      v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen"
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
@@ -123,7 +123,7 @@
      <DockControlPanel v-if="remoteDebuggingShow" :sn="curDeviceInfo.device_sn" :deviceInfo="curDeviceInfo">
      </DockControlPanel>
    </el-drawer>
  </basic-container>
  </basic-main-content>
</template>
<script>
@@ -191,8 +191,7 @@
      option: {
        lazy: true,
        tree: true,
        // height: '400',
        calcHeight: 32,
        dialogWidth: 1050,
        tip: false,
        searchShow: true,
@@ -205,6 +204,10 @@
        addBtn: false,
        dialogClickModal: false,
        grid: false,
        height: 'auto',
        calcHeight: 180,
        column: [
          {
            label: '设备型号',
src/views/device/components/firmwareManage.vue
@@ -113,6 +113,9 @@
        excelBtn: false,
        dialogClickModal: false,
        grid: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '设备名称',
@@ -429,13 +432,4 @@
  /* color: aqua; */
}
</style>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/device/devicePerMenu.vue
@@ -1,35 +1,19 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      ref="crud"
      @row-del="rowDel"
      v-model="form"
      :permission="permissionList"
      @row-save="rowSave"
      @row-update="rowUpdate"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel"
      v-model="form" :permission="permissionList" @row-save="rowSave" @row-update="rowUpdate" :before-open="beforeOpen"
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
    </avue-crud>
  </basic-container>
</template>
<script>
import { getList, remove, add, update } from '@/api/device/devicePerMenu';
import { mapGetters } from 'vuex';
import { getList, remove, add, update } from '@/api/device/devicePerMenu'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -53,6 +37,10 @@
        excelBtn: false,
        dialogClickModal: false,
        grid: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '名称',
@@ -165,98 +153,98 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.device_per_menu_add, true),
        viewBtn: this.validData(this.permission.device_per_menu_view, true),
        delBtn: this.validData(this.permission.device_per_menu_delete, true),
        editBtn: this.validData(this.permission.device_per_menu_edit, true),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.initFlag = false;
          this.onLoad(this.page);
          this.initFlag = false
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -264,41 +252,41 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
      done();
    beforeOpen (done, type) {
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getList(page.currentPage, page.pageSize, params).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style>
@@ -306,19 +294,10 @@
  background-color: aqua;
  border-radius: 4px;
}
.firmware_status:hover {
  cursor: pointer;
  /* color: aqua; */
}
</style>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/device/fly.vue
@@ -1,34 +1,13 @@
<template>
  <basic-container>
    <avue-crud
      id="device"
      :option="option"
      :table-loading="loading"
      :data="data"
      :page="page"
      @update:page="handlePageChange"
      ref="crud"
      @row-del="rowDel"
      v-model="form"
      :permission="permissionList"
      @row-update="rowUpdate"
      @row-save="rowSave"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
    >
  <basic-main-content>
    <avue-crud id="device" :option="option" :table-loading="loading" :data="data" :page="page"
      @update:page="handlePageChange" ref="crud" @row-del="rowDel" v-model="form" :permission="permissionList"
      @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen" @search-change="searchChange"
      @search-reset="searchReset" @selection-change="selectionChange" @current-change="currentChange"
      @size-change="sizeChange" @refresh-change="refreshChange">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.workspace_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.workspace_delete" @click="handleDelete">删
          除
        </el-button>
      </template>
      <template #dept_name="{ row }">
@@ -37,7 +16,7 @@
      <template #status="{ row }">
        <div v-if="row.status == true" class="onlineStatus">
          <div class="onlineStatus-dot"></div>
          <span class="online" >在线</span>
          <span class="online">在线</span>
        </div>
        <div v-if="row.status == false" class="onlineStatus">
          <div class="onlineStatus-dot1"></div>
@@ -45,13 +24,8 @@
        </div>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-setting"
          v-if="permission.operate_password_set"
          @click.stop="handleOperatePassword(scope.row, scope.index)"
          >操控密码设置
        <el-button type="primary" text icon="el-icon-setting" v-if="permission.operate_password_set"
          @click.stop="handleOperatePassword(scope.row, scope.index)">操控密码设置
        </el-button>
        <!-- <el-button
          type="primary"
@@ -64,28 +38,12 @@
      </template>
    </avue-crud>
    <el-dialog title="操控密码设置" append-to-body v-model="operatePasswordSetBox" width="455px">
      <el-form
        :model="passwordForm"
        ref="passwordForm"
        label-width="80px"
        :rules="rules"
        v-loading="loadingForm"
      >
      <el-form :model="passwordForm" ref="passwordForm" label-width="80px" :rules="rules" v-loading="loadingForm">
        <el-form-item label="密码" prop="password">
          <el-input
            type="password"
            v-model="passwordForm.password"
            placeholder="请输入密码"
            show-password
          ></el-input>
          <el-input type="password" v-model="passwordForm.password" placeholder="请输入密码" show-password></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="password2">
          <el-input
            type="password"
            v-model="passwordForm.password2"
            placeholder="请输入确认密码"
            show-password
          ></el-input>
          <el-input type="password" v-model="passwordForm.password2" placeholder="请输入确认密码" show-password></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
@@ -98,7 +56,7 @@
    <el-dialog title="操控密码查看" append-to-body v-model="operatePasswordViewBox" width="455px">
      <el-input v-model="operate_password" disabled></el-input>
    </el-dialog>
  </basic-container>
  </basic-main-content>
</template>
<script>
@@ -110,27 +68,27 @@
  getDetail,
  getOperatePassword,
  operatePasswordUpdate,
} from '@/api/device/device';
import { mapGetters } from 'vuex';
} from '@/api/device/device'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    const validatePass = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请输入密码'));
        callback(new Error('请输入密码'))
      } else {
        callback();
        callback()
      }
    };
    }
    const validatePass2 = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请再次输入密码'));
        callback(new Error('请再次输入密码'))
      } else if (value !== this.passwordForm.password) {
        callback(new Error('两次输入密码不一致!'));
        callback(new Error('两次输入密码不一致!'))
      } else {
        callback();
        callback()
      }
    };
    }
    return {
      loadingForm: false,
      rules: {
@@ -155,8 +113,6 @@
      },
      selectionList: [],
      option: {
        // height: '460',
        // calcHeight: 32,
        // dialogWidth: 950,
        tip: false,
        searchShow: true,
@@ -169,6 +125,10 @@
        excelBtn: false,
        dialogClickModal: false,
        grid: false,
        height: 'auto',
        calcHeight: 180,
        column: [
          {
            label: '设备型号',
@@ -331,144 +291,144 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.fly_add, true),
        viewBtn: this.validData(this.permission.fly_view, true),
        delBtn: this.validData(this.permission.fly_delete, true),
        editBtn: this.validData(this.permission.fly_edit, true),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    // 设置无人机操作密码
    setOperatePasswordConfirm() {
      var that = this;
    setOperatePasswordConfirm () {
      var that = this
      this.$refs.passwordForm.validate(valid => {
        if (valid) {
          this.loadingForm = true;
          this.loadingForm = true
          const data = {
            id: this.passwordForm.id,
            operate_password: this.passwordForm.password,
          };
          }
          // 提交
          operatePasswordUpdate(data).then(res => {
            this.loadingForm = false;
            this.operatePasswordSetBox = false;
            that.passwordForm = {};
            this.onLoad(this.page);
            this.loadingForm = false
            this.operatePasswordSetBox = false
            that.passwordForm = {}
            this.onLoad(this.page)
            this.$message({
              type: 'success',
              message: '操作成功!',
            });
          });
            })
          })
        } else {
          console.log('error submit!!');
          return false;
          console.log('error submit!!')
          return false
        }
      });
      })
    },
    // 操作密码设置
    handleOperatePassword(row) {
      this.operatePasswordSetBox = true;
      this.passwordForm.id = row.id;
    handleOperatePassword (row) {
      this.operatePasswordSetBox = true
      this.passwordForm.id = row.id
    },
    viewOperatePassword(row) {
      this.operatePasswordViewBox = true;
    viewOperatePassword (row) {
      this.operatePasswordViewBox = true
      // 查询密码
      getOperatePassword(row.id).then(res => {
        this.operate_password = res.data.data;
      });
        this.operate_password = res.data.data
      })
    },
    init() {
    init () {
      (this.page = {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      }),
        this.onLoad(this.page);
        this.onLoad(this.page)
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -476,67 +436,67 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    handlePageChange(val) {
      this.page = val;
      this.onLoad(this.page);
    handlePageChange (val) {
      this.page = val
      this.onLoad(this.page)
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
      this.onLoad(this.page);
    currentChange (currentPage) {
      this.page.currentPage = currentPage
      this.onLoad(this.page)
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
      this.page.currentPage = 1;
      this.onLoad(this.page);
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
      this.page.currentPage = 1
      this.onLoad(this.page)
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      const { releaseTimeRange } = this.query;
    onLoad (page, params = {}) {
      const { releaseTimeRange } = this.query
      let values = {
        ...params,
        ...this.query,
      };
      }
      if (releaseTimeRange) {
        values = {
          ...values,
          releaseTime_datege: releaseTimeRange[0],
          releaseTime_datelt: releaseTimeRange[1],
        };
        values.releaseTimeRange = null;
        }
        values.releaseTimeRange = null
      }
      values['domain'] = 0;
      this.loading = true;
      values['domain'] = 0
      this.loading = true
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style lang="scss" scoped>
@@ -544,25 +504,30 @@
  height: 655px !important;
  overflow: auto;
}
.online {
  margin-left: 5px;
}
.onlineStatus1 {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.onlineStatus {
  display: flex;
  flex-direction: row;
  align-items: center;
}
.onlineStatus-dot {
  width: 15px;
  height: 15px;
  background-color: #40cb8b;
  border-radius: 50%;
}
.onlineStatus-dot1 {
  width: 15px;
  height: 15px;
src/views/device/index.vue
@@ -1,12 +1,12 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2024-09-09 11:40:40
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-09-09 12:02:50
 * @FilePath: \drone-web-manage\src\views\device\index.vue
 * @Description:
 *
 * Copyright (c) 2024 by shuishen, All Rights Reserved.
 * @Author       : yuan
 * @Date         : 2025-04-27 21:01:20
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-25 16:23:45
 * @FilePath     : \src\views\device\index.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-04-27 21:01:20
-->
<template>
  <basic-container>
@@ -22,47 +22,75 @@
</template>
<script>
import { mapGetters } from 'vuex';
import airport from './airport.vue';
import fly from './fly.vue';
import { mapGetters } from 'vuex'
import airport from './airport.vue'
import fly from './fly.vue'
export default {
  components: { airport, fly },
  data() {
  data () {
    return {
      activeName: '1',
      form: {},
      query: {},
      loading: true,
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
      return {};
    permissionList () {
      return {}
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  created(){
  created () {
    // 默认打开机场页面
  },
  methods: {
    handleClick(tab, event) {
    handleClick (tab, event) {
      if (tab.index == 1) {
        this.$refs.fly.init();
        this.$refs.fly.init()
      }
      if (tab.index == 0) {
        this.$refs.airport.init();
        this.$refs.airport.init()
      }
    },
  },
};
}
</script>
<style></style>
<style lang="scss" scoped>
::v-deep(.el-tabs) {
  height: 100%;
  display: flex;
  flex-direction: column;
  .el-tabs__header {
    order: 1;
  }
  .el-tabs__content {
    order: 2;
  }
  .el-tabs__content {
    height: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    .el-tab-pane {
      height: 0;
      flex: 1;
      display: flex;
      flex-direction: column;
    }
  }
}
</style>
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -6,7 +6,8 @@
            <el-table border :data="jobListData" class="custom-header">
                <el-table-column label="序号" type="index" width="60">
                    <template #default="{ $index }">
                        {{ ($index + 1 + (jobListParams.current - 1) * jobListParams.size).toString().padStart(2, '0') }}
                        {{ ($index + 1 + (jobListParams.current - 1) * jobListParams.size).toString().padStart(2,
                            '0') }}
                    </template>
                </el-table-column>
                <el-table-column prop="job_info_num" label="任务编号" show-overflow-tooltip align="center" />
@@ -16,22 +17,20 @@
                <el-table-column prop="ai_type_str" label="关联算法" show-overflow-tooltip align="center" />
                <el-table-column label="任务状态" align="center">
                    <template #default="scope">
                        <span
                            :style="{
                                color:
                                    scope.row.status === 1
                                        ? '#00CDC2'
                                        : scope.row.status === 2
                        <span :style="{
                            color:
                                scope.row.status === 1
                                    ? '#00CDC2'
                                    : scope.row.status === 2
                                        ? '#FF720F'
                                        : scope.row.status === 3
                                        ? '#00AA2D'
                                        : scope.row.status === 5
                                        ? '#FF4848'
                                        : scope.row.status === 7
                                        ? '#A6A6A6'
                                        : '',
                            }"
                        >
                                            ? '#00AA2D'
                                            : scope.row.status === 5
                                                ? '#FF4848'
                                                : scope.row.status === 7
                                                    ? '#A6A6A6'
                                                    : '',
                        }">
                            {{ scope.row.status ? getStatusText(scope.row.status) : '' }}
                        </span>
                    </template>
@@ -51,22 +50,12 @@
                <el-table-column prop="creator_name" label="创建人" align="center" show-overflow-tooltip />
                <el-table-column label="操作" width="200" align="center">
                    <template #default="scope">
                        <div
                            v-if="scope.row.status === 2"
                            class="btnItem turnBack"
                            link
                            type="primary"
                            @click="turnBack(scope.row)"
                        >
                        <div v-if="scope.row.status === 2" class="btnItem turnBack" link type="primary"
                            @click="turnBack(scope.row)">
                            返航
                        </div>
                        <div
                            v-if="scope.row.status === 1"
                            class="btnItem cancelTask"
                            link
                            type="primary"
                            @click="cancelTask(scope.row)"
                        >
                        <div v-if="scope.row.status === 1" class="btnItem cancelTask" link type="primary"
                            @click="cancelTask(scope.row)">
                            取消任务
                        </div>
                        <div class="btnItem" link type="primary" @click="handleDetail(scope.row)">查看</div>
@@ -75,17 +64,10 @@
            </el-table>
        </div>
        <div class="pagination">
            <el-pagination
                class="ztzf-pagination"
                popper-class="custom-pagination-dropdown"
                v-model:current-page="jobListParams.current"
                v-model:page-size="jobListParams.size"
                :page-sizes="[10, 20, 30, 40]"
                layout="prev, pager, next, sizes, jumper"
                :total="total"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
            />
            <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown"
                v-model:current-page="jobListParams.current" v-model:page-size="jobListParams.size"
                :page-sizes="[10, 20, 30, 40]" layout="prev, pager, next, sizes, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
        </div>
    </div>
    <!-- 添加任务 -->
@@ -97,18 +79,10 @@
        :wayLineJodInfoId="rowData.id"
    /> -->
    <!-- 历史任务详情 -->
    <DeviceJobDetails
        v-if="isShowDeviceJobDetails"
        v-model:show="isShowDeviceJobDetails"
        :wayLineJodInfoId="rowData.id"
        :batchNo="rowData.batch_no"
    />
    <CancelTaskDialog
        ref="cancelTaskDialogRef"
        v-model:isShowCancelTask="isShowCancelTask"
        :row-data="rowData"
        @refresh="getJobList"
    />
    <DeviceJobDetails v-if="isShowDeviceJobDetails" v-model:show="isShowDeviceJobDetails" :wayLineJodInfoId="rowData.id"
        :batchNo="rowData.batch_no" />
    <CancelTaskDialog ref="cancelTaskDialogRef" v-model:isShowCancelTask="isShowCancelTask" :row-data="rowData"
        @refresh="getJobList" />
</template>
<script setup>
@@ -236,15 +210,23 @@
<style lang="scss" scoped>
.task-intermediate-content {
    margin: 0 18px 16px 10px;
    height: 0;
    flex: 1;
    margin: 0 10px 10px 10px;
    background-color: #ffffff;
    padding: 14px 18px;
    padding: 20px;
    border-radius: 5px;
    display: flex;
    flex-direction: column;
    // 表格
    .task-table {
        height: 580px;
        height: 0;
        flex: 1;
        margin-top: 18px;
        overflow: auto;
    }
    .btnItem {
        height: 27px;
        line-height: 27px;
@@ -263,15 +245,17 @@
            border: 1px solid #ffa500;
            color: #ffa500;
        }
        &.cancelTask {
            border: 1px solid #3efe96;
            color: #3efe96;
        }
    }
    // :deep(.el-table) {
    //         --el-table-tr-bg-color: #ffffff;
    //         --el-table-striped-bg-color: #EFEFEF;
    //         .el-table__body tr.el-table__row--striped td {
    //             background-color: var(--el-table-striped-bg-color);
    //         }
@@ -280,13 +264,16 @@
    :deep(.custom-header th.el-table__cell) {
        color: rgba(0, 0, 0, 0.85);
    }
    :deep(.el-table td.el-table__cell) {
        color: #606266;
    }
    :deep(.el-pagination) {
        display: flex;
        justify-content: flex-end;
    }
    :deep(.el-pagination button) {
        background: center center no-repeat none !important;
        color: #8eb8ea !important;
src/views/job/components/TaskTop/TaskEvent.vue
@@ -132,11 +132,13 @@
<style lang="scss" scoped>
.task-event {
width: 529px;
    width: 0;
    flex: 1;
    height: 100%;
    margin-left: 9px;
    margin-left: 10px;
    background: #FFFFFF;
    border-radius: 8px 8px 8px 8px;
    .title {
        padding: 10px 0 10px 16px;
        width: 144px;
@@ -147,11 +149,12 @@
        color: #363636;
        line-height: 18px;
    }
    .chart {
        padding: 6px 0px;
        width: 100%;
        height: 166px;
    }
}
</style>
src/views/job/components/TaskTop/TaskIndustry.vue
@@ -42,7 +42,7 @@
            radius: ['40%', '70%'],
            // center: ['50%', '45%'],
            // radius: '70%', // 设置饼图的半径
            center: ['50%', '50%'], // 调整饼图位置
            center: ['50%', '50%'], // 调整饼图位置
            data: [],
            label: {
                show: true,
@@ -96,11 +96,12 @@
<style lang="scss" scoped>
.task-industry {
width: 276px;
    width: 276px;
    height: 100%;
    background: #FFFFFF;
    border-radius: 8px 8px 8px 8px;
    margin-left: 9px;
    margin-left: 10px;
    .title {
        font-family: Segoe UI, Segoe UI;
        padding: 10px 0 10px 16px;
@@ -111,6 +112,7 @@
        color: #363636;
        line-height: 18px;
    }
    .chart {
        width: 100%;
        height: 180px;
src/views/job/components/TaskTop/TaskTime.vue
@@ -142,9 +142,10 @@
.task-time {
    width: 529px;
    height: 100%;
    margin-left: 9px;
    margin-left: 10px;
    background: #FFFFFF;
    border-radius: 8px 8px 8px 8px;
    .title {
        padding: 10px 0 10px 16px;
        width: 96px;
@@ -155,6 +156,7 @@
        color: #363636;
        line-height: 18px;
    }
    .chart {
        padding: 6px 0px;
        width: 100%;
src/views/job/components/TaskTop/TaskTop.vue
@@ -1,8 +1,16 @@
<!--
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-26 11:46:38
 * @FilePath     : \src\views\job\components\TaskTop\TaskTop.vue
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-06-14 15:19:16
-->
<template>
    <div class="task-top">
        <!--时间 天气-->
        <common-weather />
        <UserOperate />
        <div class="statistical-chart">
            <TaskTotal />
            <TaskIndustry />
@@ -14,8 +22,6 @@
<script setup>
import { pxToRem } from '@/utils/rem'
// import CommonWeather from '@/components/CommonWeather.vue'
// import UserOperate from '@/components/UserOperate.vue'
import TaskTotal from './TaskTotal.vue'
import TaskIndustry from './TaskIndustry.vue'
import TaskTime from './TaskTime.vue'
@@ -25,12 +31,14 @@
<style lang="scss" scoped>
.task-top {
    position: relative;
    margin-bottom: 6px;
    display: flex;
    .statistical-chart {
        margin-left: 10px;
        margin-right: 40px;
        margin-right: 10px;
        display: flex;
        width:100%;
        width: 0;
        flex: 1;
        height: 216px;
        box-sizing: border-box;
        margin-bottom: 10px;
src/views/job/jobinfo.vue
@@ -1,68 +1,37 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      v-model:search="search"
      v-model:page="page"
      v-model="form"
      :table-loading="loading"
      :data="data"
      :permission="permissionList"
      :before-open="beforeOpen"
      ref="crud"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @row-del="rowDel"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" v-model:search="search" v-model:page="page" v-model="form" :table-loading="loading"
      :data="data" :permission="permissionList" :before-open="beforeOpen" ref="crud" @row-update="rowUpdate"
      @row-save="rowSave" @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.jobinfo_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.jobinfo_delete" @click="handleDelete">删 除
        </el-button>
        <el-button type="info" plain icon="el-icon-sort" @click="handleSync"
          >数 据 同 步
        <el-button type="info" plain icon="el-icon-sort" @click="handleSync">数 据 同 步
        </el-button>
      </template>
      <template #menu="scope">
        <el-button type="primary" text icon="el-icon-video-play" @click="handleRun(scope.row)"
          >运 行
        <el-button type="primary" text icon="el-icon-video-play" @click="handleRun(scope.row)">运 行
        </el-button>
      </template>
      <template #enable="{ row }">
        <el-switch
          v-model="row.enable"
          inline-prompt
          @change="slotChange(row)"
          active-text="启用"
          inactive-text="暂停"
          :active-value="1"
          :inactive-value="0"
        />
        <el-switch v-model="row.enable" inline-prompt @change="slotChange(row)" active-text="启用" inactive-text="暂停"
          :active-value="1" :inactive-value="0" />
      </template>
    </avue-crud>
  </basic-container>
</template>
<script>
import { getList, getDetail, add, update, remove, change, run, sync } from '@/api/job/jobinfo';
import option from '@/option/job/jobinfo';
import { mapGetters } from 'vuex';
import 'nprogress/nprogress.css';
import func from '@/utils/func';
import { getList, getDetail, add, update, remove, change, run, sync } from '@/api/job/jobinfo'
import option from '@/option/job/jobinfo'
import { mapGetters } from 'vuex'
import 'nprogress/nprogress.css'
import func from '@/utils/func'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -76,98 +45,98 @@
      selectionList: [],
      option: option,
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.jobinfo_add, false),
        viewBtn: this.validData(this.permission.jobinfo_view, false),
        delBtn: this.validData(this.permission.jobinfo_delete, false),
        editBtn: this.validData(this.permission.jobinfo_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      if (func.isArrayAndNotEmpty(row.lifecycle)) {
        const lifecycleStart = row.lifecycle[0];
        const lifecycleEnd = row.lifecycle[1];
        const lifecycleStart = row.lifecycle[0]
        const lifecycleEnd = row.lifecycle[1]
        if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
          row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
          row.lifecycle = lifecycleStart + ',' + lifecycleEnd
        }
      } else {
        row.lifecycle = '';
        row.lifecycle = ''
      }
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          loading();
          window.console.log(error);
          loading()
          window.console.log(error)
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      if (func.isArrayAndNotEmpty(row.lifecycle)) {
        const lifecycleStart = row.lifecycle[0];
        const lifecycleEnd = row.lifecycle[1];
        const lifecycleStart = row.lifecycle[0]
        const lifecycleEnd = row.lifecycle[1]
        if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
          row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
          row.lifecycle = lifecycleStart + ',' + lifecycleEnd
        }
      }else{
      } else {
        row.lifecycle = ""
      }
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          loading();
          console.log(error);
          loading()
          console.log(error)
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -175,130 +144,121 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleSync() {
    handleSync () {
      this.$confirm('确定进行数据双向同步?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return sync();
          return sync()
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleRun(row) {
    handleRun (row) {
      this.$confirm('运行后将创建一个实例执行,是否继续?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return run(row);
          return run(row)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '运行成功!',
          });
        });
          })
        })
    },
    slotChange(row) {
    slotChange (row) {
      if (!row.id) {
        return;
        return
      }
      change(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          })
        },
        error => {
          window.console.log(error);
          window.console.log(error)
        }
      );
      )
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page) {
      this.loading = true;
    onLoad (page) {
      this.loading = true
      const { jobServerId, jobName } = this.query;
      const { jobServerId, jobName } = this.query
      let values = {
        jobServerId_like: jobServerId,
        jobName_like: jobName,
      };
      }
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/job/jobserver.vue
@@ -1,42 +1,19 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      v-model:search="search"
      v-model:page="page"
      v-model="form"
      :table-loading="loading"
      :data="data"
      :permission="permissionList"
      :before-open="beforeOpen"
      ref="crud"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @row-del="rowDel"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" v-model:search="search" v-model:page="page" v-model="form" :table-loading="loading"
      :data="data" :permission="permissionList" :before-open="beforeOpen" ref="crud" @row-update="rowUpdate"
      @row-save="rowSave" @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.jobserver_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.jobserver_delete" @click="handleDelete">删
          除
        </el-button>
        <el-button type="info" plain icon="el-icon-sort" @click="handleSync"
          >数 据 同 步
        <el-button type="info" plain icon="el-icon-sort" @click="handleSync">数 据 同 步
        </el-button>
      </template>
      <template #menu="scope">
        <el-button type="primary" text icon="el-icon-video-play" @click="handleLink(scope.row)"
          >服务跳转
        <el-button type="primary" text icon="el-icon-video-play" @click="handleLink(scope.row)">服务跳转
        </el-button>
      </template>
    </avue-crud>
@@ -44,14 +21,14 @@
</template>
<script>
import { getList, getDetail, add, update, remove, sync } from '@/api/job/jobserver';
import option from '@/option/job/jobserver';
import { mapGetters } from 'vuex';
import 'nprogress/nprogress.css';
import func from '@/utils/func';
import { getList, getDetail, add, update, remove, sync } from '@/api/job/jobserver'
import option from '@/option/job/jobserver'
import { mapGetters } from 'vuex'
import 'nprogress/nprogress.css'
import func from '@/utils/func'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -65,80 +42,80 @@
      selectionList: [],
      option: option,
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.jobserver_add, false),
        viewBtn: this.validData(this.permission.jobserver_view, false),
        delBtn: this.validData(this.permission.jobserver_delete, false),
        editBtn: this.validData(this.permission.jobserver_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          loading();
          window.console.log(error);
          loading()
          window.console.log(error)
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          loading();
          console.log(error);
          loading()
          console.log(error)
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -146,100 +123,91 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleLink(row) {
      window.open(func.formatUrl(row.jobServerUrl));
    handleLink (row) {
      window.open(func.formatUrl(row.jobServerUrl))
    },
    handleSync() {
    handleSync () {
      this.$confirm('确定进行数据双向同步?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return sync();
          return sync()
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page) {
      this.loading = true;
    onLoad (page) {
      this.loading = true
      const { jobServerName, jobAppName } = this.query;
      const { jobServerName, jobAppName } = this.query
      let values = {
        jobServerName_like: jobServerName,
        jobAppName_like: jobAppName,
      };
      }
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/job/jobstatistics.vue
@@ -1,14 +1,11 @@
<script setup>
import TaskTop from "@/views/job/components/TaskTop/TaskTop.vue";
import TaskIntermediateContent from "@/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue";
import TaskTop from "@/views/job/components/TaskTop/TaskTop.vue"
import TaskIntermediateContent from "@/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue"
</script>
<template>
  <TaskTop/>
  <TaskTop />
  <TaskIntermediateContent />
</template>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>
src/views/license/license.vue
@@ -1,32 +1,15 @@
<template>
    <basic-container>
        <avue-crud
            :option="option"
            :table-loading="loading"
            :data="data"
            v-model:page="page"
            ref="crud"
            @row-del="rowDel"
            v-model="form"
            :permission="permissionList"
            @row-update="rowUpdate"
            @row-save="rowSave"
            :before-open="beforeOpen"
            @search-change="searchChange"
            @search-reset="searchReset"
            @selection-change="selectionChange"
            @current-change="currentChange"
            @size-change="sizeChange"
            @refresh-change="refreshChange"
            @on-load="onLoad">
        <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud"
            @row-del="rowDel" v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave"
            :before-open="beforeOpen" @search-change="searchChange" @search-reset="searchReset"
            @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
            @refresh-change="refreshChange" @on-load="onLoad">
            <!-- 自定义证书地址列 -->
            <template #url="scope">
                <el-link
                    :href="scope.row.url"
                    target="_blank"
                    type="primary">
                <el-link :href="scope.row.url" target="_blank" type="primary">
                    下载密钥
                </el-link>
            </template>
@@ -34,7 +17,7 @@
            <!-- 自定义过期时间列 -->
            <template #expireDay="scope">
                <span :style="{ color: isExpired(scope.row.expireDay) ? 'red' : 'inherit' }">
                    {{ formatExpireDay(scope.row.expireDay,scope.row.licenseType) }}
                    {{ formatExpireDay(scope.row.expireDay, scope.row.licenseType) }}
                </span>
            </template>
@@ -52,21 +35,16 @@
                </span>
            </template>
            <template #menu-left>
            <el-button type="primary" icon="el-icon-plus"
              @click="createLicenseShow()">新 增
            </el-button>
                <el-button type="primary" icon="el-icon-plus" @click="createLicenseShow()">新 增
                </el-button>
            </template>
        </avue-crud>
        <!-- 新增弹窗 -->
        <el-dialog title="新增 License"
      v-model="addDialogVisible"
      width="70%"
      :close-on-click-modal="false"
      @close="resetForm"
    >
        <el-dialog title="新增 License" v-model="addDialogVisible" width="70%" :close-on-click-modal="false"
            @close="resetForm">
            <el-form :model="newLicense" ref="licenseForm" label-width="120px">
                <el-form-item label="机构ID" prop="licenseId" >
                <el-form-item label="机构ID" prop="licenseId">
                    <el-input v-model="newLicense.licenseId" />
                </el-form-item>
                <el-form-item label="机构名称" prop="licenseName" required>
@@ -78,7 +56,7 @@
                        <el-option label="验证过期时间" :value="2" />
                    </el-select>
                </el-form-item>
                <el-form-item v-if="newLicense.licenseType==2" label="过期时间" prop="expireDay">
                <el-form-item v-if="newLicense.licenseType == 2" label="过期时间" prop="expireDay">
                    <el-date-picker v-model="newLicense.expireDay" type="date" placeholder="选择日期" />
                </el-form-item>
                <el-form-item label="设备数量" prop="printClientCount">
@@ -86,13 +64,8 @@
                        <el-radio :label="-1">不限制</el-radio>
                        <el-radio :label="1">限制</el-radio>
                    </el-radio-group>
                    <el-input-number
                        v-if="deviceLimit === 1"
                        v-model="newLicense.printClientCount"
                        :min="0"
                        placeholder="请输入数量"
                        style="margin-top: 10px;"
                    />
                    <el-input-number v-if="deviceLimit === 1" v-model="newLicense.printClientCount" :min="0"
                        placeholder="请输入数量" style="margin-top: 10px;" />
                </el-form-item>
                <el-form-item label="是否校验机器码" prop="checkMachineCode">
                    <el-switch v-model="newLicense.checkMachineCode" />
@@ -106,21 +79,21 @@
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                <el-button @click="addDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="createLicense">保存</el-button>
                    <el-button @click="addDialogVisible = false">取消</el-button>
                    <el-button type="primary" @click="createLicense">保存</el-button>
                </div>
            </template>
        </el-dialog>
    </basic-container>
</template>
<script>
import { getList, add } from '@/api/license/license';
import { mapGetters } from 'vuex';
import { getList, add } from '@/api/license/license'
import { mapGetters } from 'vuex'
export default {
    data() {
    data () {
        return {
            loading: true,
            page: {
@@ -153,10 +126,14 @@
                viewBtn: true,
                selection: false,
                excelBtn: false,
              addBtnText: '新建license',
                 addBtn: true,
                addBtnText: '新建license',
                addBtn: true,
                dialogClickModal: false,
                grid: false,
                height: 'auto',
                calcHeight: 20,
                column: [
                    {
                        label: '机构ID',
@@ -289,89 +266,88 @@
                ],
            },
            data: [],
        };
        }
    },
    computed: {
        ...mapGetters(['userInfo', 'permission']),
        permissionList() {
        permissionList () {
            return {
                addBtn: this.validData(this.permission.license_add, false),
                viewBtn: this.validData(this.permission.license_view, true),
                delBtn: this.validData(this.permission.license_delete, false),
                editBtn: this.validData(this.permission.license_edit, false),
            };
            }
        },
    },
    methods: {
        isExpired(expireDay) {
            const now = new Date();
            return new Date(expireDay) < now;
        isExpired (expireDay) {
            const now = new Date()
            return new Date(expireDay) < now
        },
        formatExpireDay(expireDay,licenseType) {
            if (licenseType==2&& this.isExpired(expireDay)) {
                return `${expireDay} (已过期)`;
        formatExpireDay (expireDay, licenseType) {
            if (licenseType == 2 && this.isExpired(expireDay)) {
                return `${expireDay} (已过期)`
            }
            if(licenseType==1){
                return `永久`;
            if (licenseType == 1) {
                return `永久`
            }
            return expireDay;
            return expireDay
        },
        rowSave(row, done, loading) {
        rowSave (row, done, loading) {
            add(row).then(
                () => {
                    this.onLoad(this.page);
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    });
                    done();
                    })
                    done()
                },
                error => {
                    console.error(error);
                    loading();
                    console.error(error)
                    loading()
                }
            );
            )
        },
        createLicenseShow(){
                    this.addDialogVisible = true;
        createLicenseShow () {
            this.addDialogVisible = true
        },
        createLicense() {
        createLicense () {
            // 格式化过期时间
            if (this.newLicense.expireDay) {
                this.newLicense.expireDay = this.formatDate(this.newLicense.expireDay);
                console.log(this.newLicense.expireDay);
          }
                this.newLicense.expireDay = this.formatDate(this.newLicense.expireDay)
                console.log(this.newLicense.expireDay)
            }
            // 保存数据
            add(this.newLicense).then(() => {
                this.$message.success('新增成功');
                this.addDialogVisible = false;
                this.onLoad(this.page);
                this.$message.success('新增成功')
                this.addDialogVisible = false
                this.onLoad(this.page)
            }).catch(error => {
                console.error(error);
                this.$message.error('新增失败');
            });
                console.error(error)
                this.$message.error('新增失败')
            })
        },
        formatDate(date) {
            const d = new Date(date);
            const year = d.getFullYear();
            const month = String(d.getMonth() + 1).padStart(2, '0');
            const day = String(d.getDate()).padStart(2, '0');
            return `${year}-${month}-${day}`;
        formatDate (date) {
            const d = new Date(date)
            const year = d.getFullYear()
            const month = String(d.getMonth() + 1).padStart(2, '0')
            const day = String(d.getDate()).padStart(2, '0')
            return `${year}-${month}-${day}`
        },
        onLoad(page, params = {}) {
            this.loading = true;
        onLoad (page, params = {}) {
            this.loading = true
            getList(page.currentPage, page.pageSize, params).then(res => {
                const data = res.data.data;
                this.page.total = data.total;
                this.data = data.records;
                this.loading = false;
            });
                const data = res.data.data
                this.page.total = data.total
                this.data = data.records
                this.loading = false
            })
        },
    },
};
}
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>
src/views/monitor/log/api.vue
@@ -1,31 +1,19 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      v-model:page="page"
      @search-change="searchChange"
      @search-reset="searchReset"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" v-model:page="page" @search-change="searchChange"
      @search-reset="searchReset" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
    </avue-crud>
  </basic-container>
</template>
<script>
import { getApiList, getApiLogs } from '@/api/logs';
import { mapGetters } from 'vuex';
import { getApiList, getApiLogs } from '@/api/logs'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      selectionList: [],
@@ -50,6 +38,10 @@
        delBtn: false,
        menuWidth: 120,
        dialogType: 'drawer',
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '操作名称',
@@ -120,14 +112,14 @@
            label: '操作时间',
            prop: 'daterange',
            type: 'daterange',
            search:true,
            search: true,
            searchRange: true,
            searchSpan:6,
            searchSpan: 6,
            format: 'YYYY-MM-DD',
            valueFormat: 'YYYY-MM-DD',
            startPlaceholder: '开始时间',
            endPlaceholder: '结束时间',
            viewDisplay:false,
            viewDisplay: false,
            hide: true,
            rules: [
              {
@@ -145,78 +137,69 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        viewBtn: this.validData(this.permission.log_api_view, false),
      };
      }
    },
  },
  methods: {
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getApiLogs(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      const { daterange } = this.query;
    onLoad (page, params = {}) {
      const { daterange } = this.query
      let values = {
        ...params,
        ...this.query,
      };
      }
      if (daterange) {
        values = {
          ...values,
          startTime: daterange[0],
          endTime: daterange[1],
        };
        values.daterange = null;
        }
        values.daterange = null
      }
      this.loading = true;
      this.loading = true
      getApiList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/monitor/log/error.vue
@@ -1,31 +1,19 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      :before-open="beforeOpen"
      v-model="form"
      :permission="permissionList"
      v-model:page="page"
      @search-change="searchChange"
      @search-reset="searchReset"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" :before-open="beforeOpen"
      v-model="form" :permission="permissionList" v-model:page="page" @search-change="searchChange"
      @search-reset="searchReset" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
    </avue-crud>
  </basic-container>
</template>
<script>
import { getErrorList, getErrorLogs } from '@/api/logs';
import { mapGetters } from 'vuex';
import { getErrorList, getErrorLogs } from '@/api/logs'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      selectionList: [],
@@ -37,8 +25,6 @@
        total: 0,
      },
      option: {
        // height: 'auto',
        // calcHeight: 32,
        tip: false,
        searchShow: true,
        searchMenuSpan: 6,
@@ -50,12 +36,16 @@
        delBtn: false,
        menuWidth: 120,
        dialogType: 'drawer',
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '服务名称',
            prop: 'serviceId',
            search: true,
            searchSpan:4,
            searchSpan: 4,
            width: '120',
          },
@@ -82,13 +72,13 @@
            label: '请求接口',
            prop: 'requestUri',
            search: true,
            searchSpan:6,
            searchSpan: 6,
          },
          {
            label: '操作人',
            prop: 'createBy',
            search: true,
            searchSpan:4,
            searchSpan: 4,
            width: '120',
          },
          {
@@ -121,68 +111,59 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        viewBtn: this.validData(this.permission.log_error_view, false),
      };
      }
    },
  },
  methods: {
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getErrorLogs(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
    onLoad (page, params = {}) {
      let values = {
        ...params,
        ...this.query,
      };
      this.loading = true;
      }
      this.loading = true
      getErrorList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/odm/task.vue
@@ -1,25 +1,9 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      ref="crud"
      @row-del="rowDel"
      v-model="form"
      :permission="permissionList"
      @row-update="rowUpdate"
      @row-save="rowSave"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel"
      v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen"
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <!-- <el-button
          type="danger"
@@ -37,21 +21,13 @@
        {{ formatSeconds(row.processingTime) }}
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-guide"
        <el-button type="primary" text icon="el-icon-guide"
          v-if="permission.odm_task_handle && scope.row.taskId == null"
          @click.stop="atHandle(scope.row, scope.index)"
          >立即启动
          @click.stop="atHandle(scope.row, scope.index)">立即启动
        </el-button>
        <el-button
          type="primary"
          text
          icon="el-icon-refresh-right"
        <el-button type="primary" text icon="el-icon-refresh-right"
          v-if="(permission.odm_task_handle && scope.row.status == 30) || scope.row.status == 50"
          @click.stop="restartHandle(scope.row, scope.index)"
          >重新启动
          @click.stop="restartHandle(scope.row, scope.index)">重新启动
        </el-button>
      </template>
    </avue-crud>
@@ -59,12 +35,12 @@
</template>
<script>
import { getList, remove, update, add, createOdmTask, restartOdmTask } from '@/api/odm/task';
import { tourStrategies } from 'element-plus';
import { mapGetters } from 'vuex';
import { getList, remove, update, add, createOdmTask, restartOdmTask } from '@/api/odm/task'
import { tourStrategies } from 'element-plus'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -90,14 +66,18 @@
        dialogClickModal: false,
        grid: false,
        menuWidth: 260,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '任务名称',
            prop: 'taskName',
            search: true,
            searchSpan:4,
            searchSpan: 4,
            labelWidth: 130,
            searchPlaceholder:"任务名称",
            searchPlaceholder: "任务名称",
            rules: [
              {
                required: true,
@@ -137,7 +117,7 @@
            prop: 'waylineJobId',
            addDisplay: false,
            editDisplay: false,
            hide:true,
            hide: true,
            labelWidth: 130,
            width: 160,
          },
@@ -149,7 +129,7 @@
            searchSpan: 3,
            width: 100,
            labelWidth: 130,
            searchPlaceholder:"任务类型",
            searchPlaceholder: "任务类型",
            dicData: [
              {
                label: '航测',
@@ -193,7 +173,7 @@
            search: true,
            searchSpan: 3,
            labelWidth: 130,
            searchPlaceholder:"任务状态",
            searchPlaceholder: "任务状态",
            width: 90,
            dicData: [
              {
@@ -237,178 +217,178 @@
          {
            label: '飞行任务完成时间',
            prop: 'completedTime',
            type:"date",
            type: "date",
            addDisplay: false,
            editDisplay: false,
            searchPlaceholder:"任务完成时间",
            searchPlaceholder: "任务完成时间",
            labelWidth: 130,
            search:true,
            searchSpan:4,
            searchLabelWidth:130,
            search: true,
            searchSpan: 4,
            searchLabelWidth: 130,
            valueFormat: 'YYYY-MM-DD',
            width: 160,
          },
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        viewBtn: this.validData(this.permission.odm_task_view, true),
        delBtn: this.validData(this.permission.odm_task_delete, true),
        // editBtn: this.validData(this.permission.workspace_edit, true),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    // 立即处理
    atHandle(row) {
      createOdmTask(row.waylineJobId,row.gsd).then(
    atHandle (row) {
      createOdmTask(row.waylineJobId, row.gsd).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    // 重启启动
    restartHandle(row) {
    restartHandle (row) {
      if (!row.taskName) {
        this.$message({
          type: 'error',
          message: '该航线任务已删除,无法进行重启启动!',
        });
        return;
        })
        return
      }
      restartOdmTask(row.taskId).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    // 时间格式化
    formatSeconds(value) {
    formatSeconds (value) {
      // 如果value不是数字或者小于0,则直接返回'00:00:00'
      if (isNaN(value) || value < 0) {
        return '00:00:00';
        return '00:00:00'
      }
      // 计算小时、分钟和秒
      let seconds = Math.floor(value / 1000);
      let minutes = Math.floor(seconds / 60);
      let hours = Math.floor(minutes / 60);
      let seconds = Math.floor(value / 1000)
      let minutes = Math.floor(seconds / 60)
      let hours = Math.floor(minutes / 60)
      // 格式化为两位数字的字符串
      seconds %= 60;
      minutes %= 60;
      seconds %= 60
      minutes %= 60
      const formattedTime = `${this.padZero(hours)}:${this.padZero(minutes)}:${this.padZero(
        seconds
      )}`;
      )}`
      // 返回格式化后的时间字符串
      return formattedTime;
      return formattedTime
    },
    // 函数用于在数字不足两位数时,在前面补零
    padZero(value) {
      return String(value).padStart(2, '0');
    padZero (value) {
      return String(value).padStart(2, '0')
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -416,68 +396,59 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      // if (['edit', 'view'].includes(type)) {
      //   getDetail(this.form.id).then(res => {
      //     this.form = res.data.data;
      //   });
      // }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      const { releaseTimeRange } = this.query;
    onLoad (page, params = {}) {
      const { releaseTimeRange } = this.query
      let values = {
        ...params,
        ...this.query,
      };
      }
      if (releaseTimeRange) {
        values = {
          ...values,
          releaseTime_datege: releaseTimeRange[0],
          releaseTime_datelt: releaseTimeRange[1],
        };
        values.releaseTimeRange = null;
        }
        values.releaseTimeRange = null
      }
      this.loading = true;
      this.loading = true
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/resource/attach.vue
@@ -1,49 +1,18 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      :permission="permissionList"
      :before-open="beforeOpen"
      v-model="form"
      ref="crud"
      @row-del="rowDel"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" :permission="permissionList"
      :before-open="beforeOpen" v-model="form" ref="crud" @row-del="rowDel" @search-change="searchChange"
      @search-reset="searchReset" @selection-change="selectionChange" @current-change="currentChange"
      @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="primary"
          plain
          v-if="permission.attach_upload"
          icon="el-icon-upload"
          @click="handleUpload"
          >上 传
        <el-button type="primary" plain v-if="permission.attach_upload" icon="el-icon-upload" @click="handleUpload">上 传
        </el-button>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.attach_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.attach_delete" @click="handleDelete">删 除
        </el-button>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-download"
          v-if="permission.attach_download"
          @click="handleDownload(scope.row)"
          >下载
        <el-button type="primary" text icon="el-icon-download" v-if="permission.attach_download"
          @click="handleDownload(scope.row)">下载
        </el-button>
      </template>
      <template #attachSize="{ row }">
@@ -58,12 +27,12 @@
</template>
<script>
import { getList, getDetail, removeAttachAndData } from '@/api/resource/attach';
import { mapGetters } from 'vuex';
import func from '@/utils/func';
import { getList, getDetail, removeAttachAndData } from '@/api/resource/attach'
import { mapGetters } from 'vuex'
import func from '@/utils/func'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -85,6 +54,9 @@
        // grid: true,
        selection: true,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '附件地址',
@@ -112,7 +84,7 @@
            label: '附件名称',
            prop: 'name',
            search: true,
            searchSpan:4,
            searchSpan: 4,
            rules: [
              {
                required: true,
@@ -125,7 +97,7 @@
            label: '附件原名',
            prop: 'originalName',
            search: true,
            searchSpan:4,
            searchSpan: 4,
            rules: [
              {
                required: true,
@@ -138,8 +110,8 @@
            label: '附件拓展名',
            prop: 'extension',
            search: true,
            searchSpan:4,
            searchLabelWidth:95,
            searchSpan: 4,
            searchLabelWidth: 95,
            rules: [
              {
                required: true,
@@ -168,14 +140,14 @@
            label: '创建日期',
            prop: 'daterange',
            type: 'daterange',
            search:true,
            search: true,
            searchRange: true,
            searchSpan:6,
            searchSpan: 6,
            format: 'YYYY-MM-DD',
            valueFormat: 'YYYY-MM-DD',
            startPlaceholder: '开始时间',
            endPlaceholder: '结束时间',
            viewDisplay:false,
            viewDisplay: false,
            hide: true,
            rules: [
              {
@@ -207,63 +179,63 @@
          },
        ],
      },
    };
    }
  },
  computed: {
    func() {
      return func;
    func () {
      return func
    },
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: false,
        editBtn: false,
        viewBtn: false,
        delBtn: this.validData(this.permission.attach_delete, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    handleUpload() {
      this.attachBox = true;
    handleUpload () {
      this.attachBox = true
    },
    uploadAfter(res, done, loading, column) {
      window.console.log(column);
      this.attachBox = false;
      this.refreshChange();
      done();
    uploadAfter (res, done, loading, column) {
      window.console.log(column)
      this.attachBox = false
      this.refreshChange()
      done()
    },
    handleDownload(row) {
      window.open(`${row.link}`);
    handleDownload (row) {
      window.open(`${row.link}`)
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据及对应的文件删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return removeAttachAndData(row.id);
          return removeAttachAndData(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据及对应的文件删除?', {
        confirmButtonText: '确定',
@@ -271,85 +243,76 @@
        type: 'warning',
      })
        .then(() => {
          return removeAttachAndData(this.ids);
          return removeAttachAndData(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      const { daterange } = this.query;
    onLoad (page, params = {}) {
      const { daterange } = this.query
      let values = {
        ...params,
        ...this.query,
      };
      }
      if (daterange) {
        values = {
          ...values,
          startTime: daterange[0],
          endTime: daterange[1],
        };
        values.daterange = null;
        }
        values.daterange = null
      }
      this.loading = true;
      this.loading = true
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/resource/media.vue
@@ -28,19 +28,19 @@
</template>
<script>
import JSZip from 'jszip';
import { getMedia, deleteAllFile } from '@/api/resource/media';
import { getAirportList } from '@/api/device/device';
import { mapGetters } from 'vuex';
import func from '@/utils/func';
import JSZip from 'jszip'
import { getMedia, deleteAllFile } from '@/api/resource/media'
import { getAirportList } from '@/api/device/device'
import { mapGetters } from 'vuex'
import func from '@/utils/func'
export default {
  data() {
    const endDate = new Date();
    const startDate = new Date();
    startDate.setMonth(startDate.getMonth() - 1);
    const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`;
    const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`;
  data () {
    const endDate = new Date()
    const startDate = new Date()
    startDate.setMonth(startDate.getMonth() - 1)
    const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
    const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
    return {
      form: {},
@@ -66,6 +66,9 @@
        selection: true,
        dialogClickModal: false,
        delBtn: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          { label: '机场选择', prop: 'airportNickname', type: 'select', search: true, searchSpan: 5, dicData: [], props: { label: 'nickname', value: 'nickname' }, hide: true },
          { label: '创建日期', prop: 'daterange', type: 'daterange', search: true, searchRange: true, searchSpan: 6, format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD', startPlaceholder: '开始时间', endPlaceholder: '结束时间', viewDisplay: false, hide: true, searchValue: [defaultStartDate, defaultEndDate] },
@@ -87,152 +90,152 @@
      },
      domainUrl: '',
      isDownloading: false, // 控制按钮加载状态
    };
    }
  },
  computed: {
    func() { return func; },
    func () { return func },
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: false,
        editBtn: false,
        viewBtn: this.validData(this.permission.attach_view, false),
        delBtn: this.validData(this.permission.attach_delete, false),
      };
      }
    },
    ids() { return this.selectionList.map(ele => ele.id).join(','); },
    ids () { return this.selectionList.map(ele => ele.id).join(',') },
  },
  mounted() {
    this.loadAirportList();
    this.$refs.crud.setSearch({ daterange: this.query.daterange });
    this.onLoad(this.page, this.query);
  mounted () {
    this.loadAirportList()
    this.$refs.crud.setSearch({ daterange: this.query.daterange })
    this.onLoad(this.page, this.query)
  },
  methods: {
    loadAirportList() {
    loadAirportList () {
      getAirportList().then(res => {
        const airportData = res.data.data || [];
        const airportColumn = this.option.column.find(col => col.prop === 'airportNickname');
        const airportData = res.data.data || []
        const airportColumn = this.option.column.find(col => col.prop === 'airportNickname')
        if (airportColumn) {
          airportColumn.dicData = airportData.map(item => ({
            nickname: item.nickname,
            workspace_id: item.workspace_id,
          }));
          }))
        }
      }).catch(error => {
        console.error('加载机场列表时出错:', error);
      });
        console.error('加载机场列表时出错:', error)
      })
    },
    handleUpload() {
      this.taskBox = true;
    handleUpload () {
      this.taskBox = true
    },
    uploadAfter(res, done) {
      this.taskBox = false;
      this.refreshChange();
      done();
    uploadAfter (res, done) {
      this.taskBox = false
      this.refreshChange()
      done()
    },
    async handleRowDownload(row) {
      const mediaUrl = this.domainUrl + row.object_key;
    async handleRowDownload (row) {
      const mediaUrl = this.domainUrl + row.object_key
      try {
        const response = await fetch(mediaUrl);
        if (!response.ok) throw new Error('文件下载失败');
        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = row.file_name || 'download';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
        this.$message({ type: 'success', message: '文件下载成功: ' + row.file_name });
        const response = await fetch(mediaUrl)
        if (!response.ok) throw new Error('文件下载失败')
        const blob = await response.blob()
        const url = window.URL.createObjectURL(blob)
        const link = document.createElement('a')
        link.href = url
        link.download = row.file_name || 'download'
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(url)
        this.$message({ type: 'success', message: '文件下载成功: ' + row.file_name })
      } catch (error) {
        console.error('下载文件时出错:', error);
        this.$message({ type: 'error', message: '下载失败: ' + error.message });
        console.error('下载文件时出错:', error)
        this.$message({ type: 'error', message: '下载失败: ' + error.message })
      }
    },
    handleRowDelete(row) {
    handleRowDelete (row) {
      this.$confirm('确定删除此文件?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
        .then(() => deleteAllFile(row.id))
        .then(() => {
          this.onLoad(this.page);
          this.$message({ type: 'success', message: '删除成功!' });
          this.onLoad(this.page)
          this.$message({ type: 'success', message: '删除成功!' })
        })
        .catch(error => {
          console.error('删除文件时出错:', error);
          this.$message({ type: 'error', message: '删除失败,请稍后重试!' });
        });
          console.error('删除文件时出错:', error)
          this.$message({ type: 'error', message: '删除失败,请稍后重试!' })
        })
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据及对应的文件删除?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
        .then(() => deleteAllFile(row.id))
        .then(() => {
          this.onLoad(this.page);
          this.$message({ type: 'success', message: '操作成功!' });
        });
          this.onLoad(this.page)
          this.$message({ type: 'success', message: '操作成功!' })
        })
    },
    searchReset() {
      const endDate = new Date();
      const startDate = new Date();
      startDate.setMonth(startDate.getMonth() - 1);
      const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`;
      const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`;
      this.query = { daterange: [defaultStartDate, defaultEndDate] };
      this.selectedWorkspaceId = null;
      this.$refs.crud.setSearch({ daterange: this.query.daterange });
      this.onLoad(this.page, this.query);
    searchReset () {
      const endDate = new Date()
      const startDate = new Date()
      startDate.setMonth(startDate.getMonth() - 1)
      const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
      const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
      this.query = { daterange: [defaultStartDate, defaultEndDate] }
      this.selectedWorkspaceId = null
      this.$refs.crud.setSearch({ daterange: this.query.daterange })
      this.onLoad(this.page, this.query)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      const airportColumn = this.option.column.find(col => col.prop === 'airportNickname');
      const selectedAirport = airportColumn.dicData.find(item => item.nickname === params.airportNickname);
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      const airportColumn = this.option.column.find(col => col.prop === 'airportNickname')
      const selectedAirport = airportColumn.dicData.find(item => item.nickname === params.airportNickname)
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
      this.onLoad(this.page, this.query);
    currentChange (currentPage) {
      this.page.currentPage = currentPage
      this.onLoad(this.page, this.query)
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
      this.onLoad(this.page, this.query);
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
      this.onLoad(this.page, this.query)
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
      const { daterange } = params;
      let create_time = null;
      let end_time = null;
    onLoad (page, params = {}) {
      this.loading = true
      const { daterange } = params
      let create_time = null
      let end_time = null
      if (daterange && Array.isArray(daterange) && daterange.length === 2) {
        const startStr = daterange[0];
        const endStr = daterange[1];
        const startDate = new Date(startStr + ' 00:00:00').getTime();
        const endDate = new Date(endStr + ' 23:59:59').getTime();
        const startStr = daterange[0]
        const endStr = daterange[1]
        const startDate = new Date(startStr + ' 00:00:00').getTime()
        const endDate = new Date(endStr + ' 23:59:59').getTime()
        if (!isNaN(startDate) && !isNaN(endDate)) {
          create_time = startDate;
          end_time = endDate;
          create_time = startDate
          end_time = endDate
        }
      }
      let values = { ...params, ...this.query };
      if (daterange) values.daterange = null;
      let values = { ...params, ...this.query }
      if (daterange) values.daterange = null
      getMedia(page.currentPage, page.pageSize, this.selectedWorkspaceId || null, create_time, end_time)
        .then(res => {
          const data = res.data;
          const data = res.data
          if (data && data.data && data.data.list) {
            this.page.total = data.data.pagination.total;
            this.page.total = data.data.pagination.total
            this.data = data.data.list.map(item => ({
              job_name: item.job_name,
              file_name: item.file_name,
@@ -241,118 +244,109 @@
              payload: item.payload,
              airportNickname: item.airportNickname,
              id: item.id,
            }));
            this.domainUrl = data.data.list.length > 0 ? data.data.list[0].domain_url : 'http://localhost:2888/manage/resource';
            }))
            this.domainUrl = data.data.list.length > 0 ? data.data.list[0].domain_url : 'http://localhost:2888/manage/resource'
          } else {
            this.data = [];
            this.data = []
          }
          this.loading = false;
          this.selectionClear();
          this.$refs.crud.refreshTable();
          this.loading = false
          this.selectionClear()
          this.$refs.crud.refreshTable()
        })
        .catch(error => {
          this.loading = false;
          console.error('加载媒体数据时出错:', error);
        });
          this.loading = false
          console.error('加载媒体数据时出错:', error)
        })
    },
    formatCreateTime(createTime) {
      if (typeof createTime !== 'number' || isNaN(createTime)) return '';
      const timestamp = createTime < 10000000000 ? createTime * 1000 : createTime;
      const date = new Date(timestamp);
      if (isNaN(date.getTime())) return '';
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0');
      const day = String(date.getDate()).padStart(2, '0');
      const hours = String(date.getHours()).padStart(2, '0');
      const minutes = String(date.getMinutes()).padStart(2, '0');
      const seconds = String(date.getSeconds()).padStart(2, '0');
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    formatCreateTime (createTime) {
      if (typeof createTime !== 'number' || isNaN(createTime)) return ''
      const timestamp = createTime < 10000000000 ? createTime * 1000 : createTime
      const date = new Date(timestamp)
      if (isNaN(date.getTime())) return ''
      const year = date.getFullYear()
      const month = String(date.getMonth() + 1).padStart(2, '0')
      const day = String(date.getDate()).padStart(2, '0')
      const hours = String(date.getHours()).padStart(2, '0')
      const minutes = String(date.getMinutes()).padStart(2, '0')
      const seconds = String(date.getSeconds()).padStart(2, '0')
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
    },
    handleRowView(row) {
      const mediaUrl = this.domainUrl + row.object_key;
      const mediaType = this.getMediaType(row.object_key);
    handleRowView (row) {
      const mediaUrl = this.domainUrl + row.object_key
      const mediaType = this.getMediaType(row.object_key)
      if (mediaType === 'image' || mediaType === 'video') {
        window.open(mediaUrl, '_blank');
        window.open(mediaUrl, '_blank')
      }
    },
    getMediaType(objectKey) {
      const extension = objectKey.split('.').pop().toLowerCase();
      if (['jpg', 'jpeg', 'png', 'gif'].includes(extension)) return 'image';
      if (['mp4', 'avi', 'mov'].includes(extension)) return 'video';
      return 'unknown';
    getMediaType (objectKey) {
      const extension = objectKey.split('.').pop().toLowerCase()
      if (['jpg', 'jpeg', 'png', 'gif'].includes(extension)) return 'image'
      if (['mp4', 'avi', 'mov'].includes(extension)) return 'video'
      return 'unknown'
    },
    showVideo(url) {
      this.taskBox = true;
      this.taskForm = { videoUrl: url };
    showVideo (url) {
      this.taskBox = true
      this.taskForm = { videoUrl: url }
      this.taskOption = {
        submitBtn: false,
        emptyBtn: false,
        column: [
          { label: '视频播放', prop: 'videoUrl', type: 'video', span: 24, props: { controls: true, autoplay: false } },
        ],
      };
      }
    },
    validData(val, def) {
      return typeof val !== 'undefined' ? val : def;
    validData (val, def) {
      return typeof val !== 'undefined' ? val : def
    },
    async handleZipDownload() {
    async handleZipDownload () {
      if (this.selectionList.length === 0) {
        this.$message({ type: 'warning', message: '请先选择需要下载的文件' });
        return;
        this.$message({ type: 'warning', message: '请先选择需要下载的文件' })
        return
      }
      this.isDownloading = true;
      this.isDownloading = true
      try {
        const zip = new JSZip();
        const zip = new JSZip()
        const files = this.selectionList.map(item => ({
          url: this.domainUrl + item.object_key,
          name: item.file_name
        }));
        }))
        const fetchPromises = files.map(async file => {
          const response = await fetch(file.url, { cache: 'force-cache' });
          if (!response.ok) throw new Error(`下载文件 ${file.name} 失败`);
          const blob = await response.blob();
          zip.file(file.name, blob);
        });
          const response = await fetch(file.url, { cache: 'force-cache' })
          if (!response.ok) throw new Error(`下载文件 ${file.name} 失败`)
          const blob = await response.blob()
          zip.file(file.name, blob)
        })
        await Promise.all(fetchPromises);
        await Promise.all(fetchPromises)
        const content = await zip.generateAsync({
          type: 'blob',
          compression: 'DEFLATE',
          compressionOptions: { level: 1 }
        });
        })
        const url = window.URL.createObjectURL(content);
        const link = document.createElement('a');
        link.href = url;
        link.download = `media_files_${new Date().toISOString().slice(0, 10)}_${new Date().getTime()}.zip`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
        const url = window.URL.createObjectURL(content)
        const link = document.createElement('a')
        link.href = url
        link.download = `media_files_${new Date().toISOString().slice(0, 10)}_${new Date().getTime()}.zip`
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(url)
        this.$message({ type: 'success', message: `成功下载 ${this.selectionList.length} 个文件` });
        this.$message({ type: 'success', message: `成功下载 ${this.selectionList.length} 个文件` })
      } catch (error) {
        console.error('压缩下载失败:', error);
        this.$message({ type: 'error', message: '压缩下载失败: ' + error.message });
        console.error('压缩下载失败:', error)
        this.$message({ type: 'error', message: '压缩下载失败: ' + error.message })
      } finally {
        this.isDownloading = false;
        this.isDownloading = false
      }
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/resource/oss.vue
@@ -1,59 +1,22 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      :permission="permissionList"
      v-model="form"
      ref="crud"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @row-del="rowDel"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" :permission="permissionList"
      v-model="form" ref="crud" @row-update="rowUpdate" @row-save="rowSave" @row-del="rowDel" :before-open="beforeOpen"
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.oss_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.oss_delete" @click="handleDelete">删 除
        </el-button>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-video-play"
          v-if="userInfo.role_name.includes('admin')"
          @click="handleDebug(scope.row)"
          >调试
        <el-button type="primary" text icon="el-icon-video-play" v-if="userInfo.role_name.includes('admin')"
          @click="handleDebug(scope.row)">调试
        </el-button>
        <el-button
          type="primary"
          text
          icon="el-icon-circle-check"
          v-if="scope.row.status==1 && permission.oss_enable"
          @click.stop="handleEnable(scope.row)"
          >启用
        <el-button type="primary" text icon="el-icon-circle-check" v-if="scope.row.status == 1 && permission.oss_enable"
          @click.stop="handleEnable(scope.row)">启用
        </el-button>
        <el-button
          type="primary"
          text
          icon="VideoPause"
          v-if="scope.row.status==2 && permission.oss_enable"
          @click.stop="handleDisable(scope.row)"
          >禁用
        <el-button type="primary" text icon="VideoPause" v-if="scope.row.status == 2 && permission.oss_enable"
          @click.stop="handleDisable(scope.row)">禁用
        </el-button>
      </template>
      <template #status="{ row }">
@@ -70,12 +33,12 @@
</template>
<script>
import { getListPage, getDetail, add, update, remove, enable, disable } from '@/api/resource/oss';
import { mapGetters } from 'vuex';
import func from '@/utils/func';
import { getListPage, getDetail, add, update, remove, enable, disable } from '@/api/resource/oss'
import { mapGetters } from 'vuex'
import func from '@/utils/func'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -100,6 +63,9 @@
        labelWidth: 100,
        dialogWidth: 880,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '分类',
@@ -108,7 +74,7 @@
            span: 24,
            width: 120,
            searchLabelWidth: 50,
            searchSpan:3,
            searchSpan: 3,
            row: true,
            dicUrl: '/blade-system/dict/dictionary?code=oss',
            props: {
@@ -132,7 +98,7 @@
            prop: 'name',
            span: 24,
            search: true,
            searchSpan:5,
            searchSpan: 5,
            rules: [
              {
                required: true,
@@ -148,7 +114,7 @@
            width: 120,
            searchLabelWidth: 100,
            search: true,
            searchSpan:4,
            searchSpan: 4,
            rules: [
              {
                required: true,
@@ -194,7 +160,7 @@
            prop: 'accessKey',
            span: 24,
            search: true,
            searchSpan:5,
            searchSpan: 5,
            searchLabelWidth: 100,
            overHidden: true,
            rules: [
@@ -228,7 +194,7 @@
          {
            label: '过期时间',
            prop: 'expire',
            type:"number",
            type: "number",
            span: 24,
            hide: true,
            rules: [
@@ -299,162 +265,162 @@
          },
        ],
      },
    };
    }
  },
  watch: {
    'form.category'() {
      const category = func.toInt(this.form.category);
    'form.category' () {
      const category = func.toInt(this.form.category)
      this.$refs.crud.option.column.filter(item => {
        if (item.prop === 'appId') {
          item.display = category === 4;
          item.display = category === 4
        }
        // if (item.prop === 'region') {
        //   item.display = category === 4 || category === 5;
        // }
      });
      })
    },
    'debugForm.code'() {
      const column = this.findObject(this.debugOption.column, 'backgroundUrl');
      column.action = `/blade-resource/oss/endpoint/put-file?code=${this.debugForm.code}`;
    'debugForm.code' () {
      const column = this.findObject(this.debugOption.column, 'backgroundUrl')
      column.action = `/blade-resource/oss/endpoint/put-file?code=${this.debugForm.code}`
    },
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.oss_add),
        viewBtn: this.validData(this.permission.oss_view),
        delBtn: this.validData(this.permission.oss_delete),
        editBtn: this.validData(this.permission.oss_edit),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleEnable(row) {
    handleEnable (row) {
      this.$confirm('是否确定启用这条配置?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return enable(row.id);
          return enable(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleDisable(row){
    handleDisable (row) {
      this.$confirm('是否确定禁用用这条配置?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return disable(row.id);
          return disable(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleDebug(row) {
      this.box = true;
      this.debugForm.code = row.ossCode;
      this.debugForm.backgroundUrl = '';
    handleDebug (row) {
      this.box = true
      this.debugForm.code = row.ossCode
      this.debugForm.backgroundUrl = ''
    },
    handleSubmit(form, done) {
    handleSubmit (form, done) {
      this.$message({
        type: 'success',
        message: `获取到图片地址:[${form.backgroundUrl}]`,
      });
      done();
      })
      done()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -462,54 +428,45 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getListPage(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/resource/wayline.vue
@@ -1,40 +1,29 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      :permission="permissionList"
      v-model="form"
      ref="crud"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad">
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" :permission="permissionList"
      v-model="form" ref="crud" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
    </avue-crud>
  </basic-container>
</template>
<script>
import { getJobsByUser } from '@/api/resource/wayline';
import { getAirportList } from '@/api/device/device';
import { mapGetters } from 'vuex';
import func from '@/utils/func';
import { getJobsByUser } from '@/api/resource/wayline'
import { getAirportList } from '@/api/device/device'
import { mapGetters } from 'vuex'
import func from '@/utils/func'
export default {
  data() {
  data () {
    // 计算默认的开始和结束日期
    const endDate = new Date(); // 当前日期
    const startDate = new Date(); // 一个月前的日期
    startDate.setMonth(startDate.getMonth() - 1);
    const endDate = new Date() // 当前日期
    const startDate = new Date() // 一个月前的日期
    startDate.setMonth(startDate.getMonth() - 1)
    // 格式化为 YYYY-MM-DD 字符串
    const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`;
    const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`;
    const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
    const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
    return {
      form: {},
@@ -59,6 +48,9 @@
        selection: true,
        dialogClickModal: false,
        menu: false, // 禁用默认操作列
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '机场选择',
@@ -115,131 +107,131 @@
            label: '错误原因',
            prop: 'reason',
            formatter: (row) => {
              return [4, 5].includes(row.status) ? row.reason || '未知' : '';
              return [4, 5].includes(row.status) ? row.reason || '未知' : ''
            },
          },
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: false,
        editBtn: false,
        viewBtn: this.validData(this.permission.attach_view, false),
        delBtn: this.validData(this.permission.attach_delete, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.job_id);
      });
      return ids.join(',');
        ids.push(ele.job_id)
      })
      return ids.join(',')
    },
  },
  mounted() {
    this.loadAirportList();
  mounted () {
    this.loadAirportList()
    // 初始化时手动触发搜索,以确保默认值生效
    this.$refs.crud.searchChange(this.query);
    this.onLoad(this.page, this.query);
    this.$refs.crud.searchChange(this.query)
    this.onLoad(this.page, this.query)
  },
  methods: {
    loadAirportList() {
    loadAirportList () {
      getAirportList().then(res => {
        const airportData = res.data.data || [];
        const airportColumn = this.option.column.find(col => col.prop === 'dock_name');
        const airportData = res.data.data || []
        const airportColumn = this.option.column.find(col => col.prop === 'dock_name')
        if (airportColumn) {
          airportColumn.dicData = airportData.map(item => ({
            nickname: item.nickname,
            workspace_id: item.workspace_id,
          }));
          }))
        }
      }).catch(error => {
        // 处理错误
      });
      })
    },
    searchReset() {
    searchReset () {
      // 重置时恢复默认时间范围
      const endDate = new Date();
      const startDate = new Date();
      startDate.setMonth(startDate.getMonth() - 1);
      const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`;
      const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`;
      const endDate = new Date()
      const startDate = new Date()
      startDate.setMonth(startDate.getMonth() - 1)
      const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
      const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
      this.query = {
        daterange: [defaultStartDate, defaultEndDate]
      };
      this.selectedWorkspaceId = null;
      }
      this.selectedWorkspaceId = null
      // 更新 searchValue 以确保界面重置后显示默认值
      const daterangeColumn = this.option.column.find(col => col.prop === 'daterange');
      daterangeColumn.searchValue = [defaultStartDate, defaultEndDate];
      this.onLoad(this.page, this.query);
      const daterangeColumn = this.option.column.find(col => col.prop === 'daterange')
      daterangeColumn.searchValue = [defaultStartDate, defaultEndDate]
      this.onLoad(this.page, this.query)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      const airportColumn = this.option.column.find(col => col.prop === 'dock_name');
      const selectedAirport = airportColumn.dicData.find(item => item.nickname === params.dock_name);
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null;
      const airportColumn = this.option.column.find(col => col.prop === 'dock_name')
      const selectedAirport = airportColumn.dicData.find(item => item.nickname === params.dock_name)
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null
      const { daterange } = params;
      let startTime = null;
      let endTime = null;
      const { daterange } = params
      let startTime = null
      let endTime = null
      if (daterange && Array.isArray(daterange) && daterange.length === 2) {
        startTime = this.formatTime(daterange[0]);
        endTime = this.formatTime(daterange[1]);
        startTime = this.formatTime(daterange[0])
        endTime = this.formatTime(daterange[1])
      }
      if (startTime && isNaN(new Date(startTime).getTime())) {
        startTime = null;
        startTime = null
      }
      if (endTime && isNaN(new Date(endTime).getTime())) {
        endTime = null;
        endTime = null
      }
      this.onLoad(this.page, params);
      if (done) done();
      this.onLoad(this.page, params)
      if (done) done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
      this.onLoad(this.page, this.query);
    currentChange (currentPage) {
      this.page.currentPage = currentPage
      this.onLoad(this.page, this.query)
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
      this.onLoad(this.page, this.query);
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
      this.onLoad(this.page, this.query)
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      const workspaceId = this.selectedWorkspaceId || null;
      const { daterange } = params;
      let startTime = null;
      let endTime = null;
      const workspaceId = this.selectedWorkspaceId || null
      const { daterange } = params
      let startTime = null
      let endTime = null
      if (daterange && Array.isArray(daterange) && daterange.length === 2) {
        startTime = this.formatTime(daterange[0]);
        endTime = this.formatTime(daterange[1]);
        startTime = this.formatTime(daterange[0])
        endTime = this.formatTime(daterange[1])
      }
      if (startTime && isNaN(new Date(startTime).getTime())) {
        startTime = null;
        startTime = null
      }
      if (endTime && isNaN(new Date(endTime).getTime())) {
        endTime = null;
        endTime = null
      }
      getJobsByUser(
@@ -251,10 +243,10 @@
        endTime,
        params.job_name || null
      ).then(res => {
        const responseData = res.data;
        const responseData = res.data
        if (responseData && responseData.data && Array.isArray(responseData.data.list)) {
          this.page.total = responseData.data.pagination.total || responseData.data.list.length;
          this.page.total = responseData.data.pagination.total || responseData.data.list.length
          this.data = responseData.data.list.map(item => ({
            job_id: item.job_id,
            job_name: item.job_name,
@@ -265,53 +257,44 @@
            reason: item.reason,
            begin_time: item.begin_time,
            completed_time: item.completed_time,
          }));
          }))
        } else {
          this.data = [];
          this.page.total = 0;
          this.data = []
          this.page.total = 0
        }
        this.loading = false;
        this.selectionClear();
        this.loading = false
        this.selectionClear()
      }).catch(error => {
        this.loading = false;
        this.data = [];
        this.page.total = 0;
      });
        this.loading = false
        this.data = []
        this.page.total = 0
      })
    },
    formatTime(time) {
      if (!time) return '';
      let date;
    formatTime (time) {
      if (!time) return ''
      let date
      if (typeof time === 'number') {
        date = new Date(time < 10000000000 ? time * 1000 : time);
        date = new Date(time < 10000000000 ? time * 1000 : time)
      } else if (typeof time === 'string') {
        date = new Date(time);
        date = new Date(time)
      }
      if (isNaN(date.getTime())) {
        return null;
        return null
      }
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0');
      const day = String(date.getDate()).padStart(2, '0');
      const hours = String(date.getHours()).padStart(2, '0');
      const minutes = String(date.getMinutes()).padStart(2, '0');
      const seconds = String(date.getSeconds()).padStart(2, '0');
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
      const year = date.getFullYear()
      const month = String(date.getMonth() + 1).padStart(2, '0')
      const day = String(date.getDate()).padStart(2, '0')
      const hours = String(date.getHours()).padStart(2, '0')
      const minutes = String(date.getMinutes()).padStart(2, '0')
      const seconds = String(date.getSeconds()).padStart(2, '0')
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
    },
    validData(val, def) {
      return typeof val !== 'undefined' ? val : def;
    validData (val, def) {
      return typeof val !== 'undefined' ? val : def
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/resource/waylineFile.vue
@@ -1,30 +1,20 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      :page.sync="page"
      v-model="form"
      ref="crud"
      @search-change="searchChange"
      @search-reset="searchReset"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" :page.sync="page" v-model="form" ref="crud"
      @search-change="searchChange" @search-reset="searchReset" @current-change="currentChange"
      @size-change="sizeChange" @refresh-change="refreshChange">
    </avue-crud>
  </basic-container>
</template>
<script>
import { getWaylineFileByUser } from '@/api/resource/waylineFile';
import { getAirportList } from '@/api/device/device';
import { getWaylineFileByUser } from '@/api/resource/waylineFile'
import { getAirportList } from '@/api/device/device'
export default {
  name: 'WaylineFile',
  data() {
    const { defaultStartDate, defaultEndDate } = this.getDefaultDates();
  data () {
    const { defaultStartDate, defaultEndDate } = this.getDefaultDates()
    return {
      form: {},
      query: { name: '', dock_name: '', daterange: [defaultStartDate, defaultEndDate] },
@@ -40,6 +30,9 @@
        index: true,
        menu: false,
        page: true,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '机场选择',
@@ -86,53 +79,53 @@
          },
        ],
      },
    };
    }
  },
  mounted() {
    this.fetchAirports();
    this.fetchData(this.page, this.query); // 初次加载包含默认时间范围
  mounted () {
    this.fetchAirports()
    this.fetchData(this.page, this.query) // 初次加载包含默认时间范围
  },
  methods: {
    getDefaultDates() {
      const endDate = new Date();
      const startDate = new Date();
      startDate.setMonth(startDate.getMonth() - 1);
    getDefaultDates () {
      const endDate = new Date()
      const startDate = new Date()
      startDate.setMonth(startDate.getMonth() - 1)
      // 结束时间设定为当天 23:59:59
      endDate.setHours(23, 59, 59, 999);
      endDate.setHours(23, 59, 59, 999)
      return {
        defaultStartDate: `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`,
        defaultEndDate: `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`,
      };
      }
    },
    async fetchAirports() {
    async fetchAirports () {
      try {
        const res = await getAirportList();
        const airportData = res.data.data || [];
        const airportColumn = this.option.column.find((col) => col.prop === 'dock_name');
        const res = await getAirportList()
        const airportData = res.data.data || []
        const airportColumn = this.option.column.find((col) => col.prop === 'dock_name')
        if (airportColumn) {
          airportColumn.dicData = airportData.map((item) => ({
            nickname: item.nickname,
            workspace_id: item.workspace_id,
          }));
          }))
        }
      } catch (error) {
        console.error('加载机场列表失败:', error);
        console.error('加载机场列表失败:', error)
      }
    },
    async fetchData(page, params = {}) {
      this.loading = true;
      const { name, dock_name, daterange } = params;
      const workspaceId = this.selectedWorkspaceId || null;
    async fetchData (page, params = {}) {
      this.loading = true
      const { name, dock_name, daterange } = params
      const workspaceId = this.selectedWorkspaceId || null
      // 时间范围处理
      const startDate = new Date(daterange[0]);
      let endDate = new Date(daterange[1]);
      const startDate = new Date(daterange[0])
      let endDate = new Date(daterange[1])
      // 结束时间始终设定为当天 23:59:59
      endDate.setHours(23, 59, 59, 999);
      endDate.setHours(23, 59, 59, 999)
      const startTime = isNaN(startDate.getTime()) ? null : startDate.getTime();
      const endTime = isNaN(endDate.getTime()) ? null : endDate.getTime();
      const startTime = isNaN(startDate.getTime()) ? null : startDate.getTime()
      const endTime = isNaN(endDate.getTime()) ? null : endDate.getTime()
      const requestParams = {
        workspaceId,
@@ -141,9 +134,9 @@
        endTime,
        page: page.currentPage,
        pageSize: page.pageSize,
      };
      }
      console.log('请求参数:', requestParams);
      console.log('请求参数:', requestParams)
      try {
        const res = await getWaylineFileByUser(
@@ -153,8 +146,8 @@
          requestParams.endTime,
          requestParams.page,
          requestParams.pageSize
        );
        const responseData = res.data;
        )
        const responseData = res.data
        if (responseData?.code === 0 && Array.isArray(responseData.data?.list)) {
          this.data = responseData.data.list.map((item) => ({
@@ -163,80 +156,72 @@
            wayline_type: item.wayline_type,
            user_name: item.user_name,
            update_time: item.update_time,
          }));
          this.page.total = responseData.data.pagination?.total || responseData.data.list.length;
          this.page.currentPage = page.currentPage;
          console.log('返回数据条数:', this.data.length);
          console.log('当前页码:', this.page.currentPage);
          console.log('总条数:', this.page.total);
          }))
          this.page.total = responseData.data.pagination?.total || responseData.data.list.length
          this.page.currentPage = page.currentPage
          console.log('返回数据条数:', this.data.length)
          console.log('当前页码:', this.page.currentPage)
          console.log('总条数:', this.page.total)
        } else {
          this.data = [];
          this.page.total = 0;
          this.data = []
          this.page.total = 0
        }
      } catch (error) {
        console.error('加载数据失败:', error);
        this.data = [];
        this.page.total = 0;
        console.error('加载数据失败:', error)
        this.data = []
        this.page.total = 0
      } finally {
        this.loading = false;
        this.loading = false
      }
    },
    searchChange(params, done) {
      this.query = { ...params };
      const airportColumn = this.option.column.find((col) => col.prop === 'dock_name');
      const selectedAirport = airportColumn.dicData.find((item) => item.nickname === params.dock_name);
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null;
      this.page.currentPage = 1;
      this.fetchData(this.page, this.query);
      done();
    searchChange (params, done) {
      this.query = { ...params }
      const airportColumn = this.option.column.find((col) => col.prop === 'dock_name')
      const selectedAirport = airportColumn.dicData.find((item) => item.nickname === params.dock_name)
      this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null
      this.page.currentPage = 1
      this.fetchData(this.page, this.query)
      done()
    },
    searchReset() {
      const { defaultStartDate, defaultEndDate } = this.getDefaultDates();
      this.query = { name: '', dock_name: '', daterange: [defaultStartDate, defaultEndDate] };
      this.selectedWorkspaceId = null;
      const daterangeColumn = this.option.column.find((col) => col.prop === 'daterange');
      daterangeColumn.searchValue = [defaultStartDate, defaultEndDate];
      this.page.currentPage = 1;
      this.fetchData(this.page, this.query);
    searchReset () {
      const { defaultStartDate, defaultEndDate } = this.getDefaultDates()
      this.query = { name: '', dock_name: '', daterange: [defaultStartDate, defaultEndDate] }
      this.selectedWorkspaceId = null
      const daterangeColumn = this.option.column.find((col) => col.prop === 'daterange')
      daterangeColumn.searchValue = [defaultStartDate, defaultEndDate]
      this.page.currentPage = 1
      this.fetchData(this.page, this.query)
    },
    currentChange(currentPage) {
      console.log('切换页码至:', currentPage);
      this.page.currentPage = currentPage;
      this.fetchData(this.page, this.query);
    currentChange (currentPage) {
      console.log('切换页码至:', currentPage)
      this.page.currentPage = currentPage
      this.fetchData(this.page, this.query)
    },
    sizeChange(pageSize) {
      console.log('每页条数改为:', pageSize);
      this.page.pageSize = pageSize;
      this.page.currentPage = 1;
      this.fetchData(this.page, this.query);
    sizeChange (pageSize) {
      console.log('每页条数改为:', pageSize)
      this.page.pageSize = pageSize
      this.page.currentPage = 1
      this.fetchData(this.page, this.query)
    },
    refreshChange() {
      this.fetchData(this.page, this.query);
    refreshChange () {
      this.fetchData(this.page, this.query)
    },
    formatTime(time) {
      if (!time) return '';
      const date = new Date(time < 10000000000 ? time * 1000 : time);
      if (isNaN(date.getTime())) return '无效时间';
      return date.toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-');
    formatTime (time) {
      if (!time) return ''
      const date = new Date(time < 10000000000 ? time * 1000 : time)
      if (isNaN(date.getTime())) return '无效时间'
      return date.toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-')
    },
    formatWaylineType(type) {
      const typeMap = { '0': '普通航线', '1': '图斑举证航线', '2': '航测航线', '3': '航点航线', '4': '正射举证航线' };
      return typeMap[type] || '未知';
    formatWaylineType (type) {
      const typeMap = { '0': '普通航线', '1': '图斑举证航线', '2': '航测航线', '3': '航点航线', '4': '正射举证航线' }
      return typeMap[type] || '未知'
    },
  },
};
}
</script>
<style scoped lang="scss">
.avue-crud .el-table {
  max-height: none !important;
}
 :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
src/views/system/client.vue
@@ -1,33 +1,12 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      @row-del="rowDel"
      v-model="form"
      ref="crud"
      :permission="permissionList"
      @row-update="rowUpdate"
      @row-save="rowSave"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" @row-del="rowDel"
      v-model="form" ref="crud" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave"
      :before-open="beforeOpen" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.client_delete"
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" plain v-if="permission.client_delete" @click="handleDelete">删 除
        </el-button>
      </template>
    </avue-crud>
@@ -35,11 +14,11 @@
</template>
<script>
import { getList, getDetail, add, update, remove } from '@/api/system/client';
import { mapGetters } from 'vuex';
import { getList, getDetail, add, update, remove } from '@/api/system/client'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -60,6 +39,10 @@
        grid: true,
        selection: true,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '应用id',
@@ -240,97 +223,97 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.client_add),
        viewBtn: this.validData(this.permission.client_view),
        delBtn: this.validData(this.permission.client_delete),
        editBtn: this.validData(this.permission.client_edit),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -338,55 +321,46 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/system/dept.vue
@@ -1,45 +1,18 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      :before-close="beforeClose"
      @row-del="rowDel"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
      @tree-load="treeLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" :before-close="beforeClose" @row-del="rowDel"
      @row-update="rowUpdate" @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad" @tree-load="treeLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          v-if="permission.dept_delete"
          plain
          @click="handleDelete"
        >
        <el-button type="danger" icon="el-icon-delete" v-if="permission.dept_delete" plain @click="handleDelete">
          删除
        </el-button>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-plus"
          @click.stop="handleAdd(scope.row, scope.index)"
          v-if="userInfo.role_name.includes('admin')"
        >
        <el-button type="primary" text icon="el-icon-plus" @click.stop="handleAdd(scope.row, scope.index)"
          v-if="userInfo.role_name.includes('admin')">
          新增子项
        </el-button>
      </template>
@@ -63,13 +36,13 @@
  add,
  getDept,
  getDeptTree,
} from '@/api/system/dept';
import { getLazyTree } from '@/api/base/region';
import { mapGetters } from 'vuex';
import website from '@/config/website';
} from '@/api/system/dept'
import { getLazyTree } from '@/api/base/region'
import { mapGetters } from 'vuex'
import website from '@/config/website'
export default {
  data() {
  data () {
    return {
      form: {},
      selectionList: [],
@@ -94,6 +67,10 @@
        viewBtn: true,
        menuWidth: 320,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '机构名称',
@@ -239,23 +216,23 @@
            ],
            change: ({ value }) => {
              if (!value) {
                this.form.areaCode = null; // 未选择时置为空
                this.form.areaCode = null // 未选择时置为空
              } else {
                // 如果 value 是逗号分隔的字符串,拆分并取最后一个值
                if (typeof value === 'string' && value.includes(',')) {
                  const codes = value.split(',');
                  this.form.areaCode = codes[codes.length - 1];
                  const codes = value.split(',')
                  this.form.areaCode = codes[codes.length - 1]
                } else {
                  this.form.areaCode = value; // 直接使用单一值
                  this.form.areaCode = value // 直接使用单一值
                }
              }
              console.log('cascader change value:', value);
              console.log('处理后的区县编码:', this.form.areaCode);
              console.log('cascader change value:', value)
              console.log('处理后的区县编码:', this.form.areaCode)
            },
            lazy: true,
            lazyLoad(node, resolve) {
              let level = node.level;
              let list = [];
            lazyLoad (node, resolve) {
              let level = node.level
              let list = []
              let callback = () => {
                resolve(
                  (list || []).map(ele => ({
@@ -263,26 +240,26 @@
                    value: ele.value, // 保持原始值类型(字符串)
                    leaf: level >= 2, // 县级为最后一级
                  }))
                );
              };
                )
              }
              if (level === 0) {
                getLazyTree('000000000000').then(res => {
                  list = res.data.data;
                  callback();
                });
                  list = res.data.data
                  callback()
                })
              } else if (level === 1) {
                getLazyTree(node.value).then(res => {
                  list = res.data.data;
                  callback();
                });
                  list = res.data.data
                  callback()
                })
              } else if (level === 2) {
                getLazyTree(node.value).then(res => {
                  list = res.data.data;
                  callback();
                });
                  list = res.data.data
                  callback()
                })
              } else {
                callback();
                callback()
              }
            },
          },
@@ -302,148 +279,148 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.dept_add, false),
        viewBtn: this.validData(this.permission.dept_view, false),
        delBtn: this.validData(this.permission.dept_delete, false),
        editBtn: this.validData(this.permission.dept_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    initData() {
    initData () {
      getDeptTree().then(res => {
        const column = this.findObject(this.option.column, 'parentId');
        column.dicData = res.data.data;
      });
        const column = this.findObject(this.option.column, 'parentId')
        column.dicData = res.data.data
      })
    },
    handleAdd(row) {
      this.parentId = row.id;
      const column = this.findObject(this.option.column, 'parentId');
      column.value = row.id;
      column.addDisabled = true;
      this.form.areaCode = null; // 初始化为空
      this.$refs.crud.rowAdd();
    handleAdd (row) {
      this.parentId = row.id
      const column = this.findObject(this.option.column, 'parentId')
      column.value = row.id
      column.addDisabled = true
      this.form.areaCode = null // 初始化为空
      this.$refs.crud.rowAdd()
    },
    validateSubmitData(row) {
    validateSubmitData (row) {
      if (!row.areaCode) {
        this.$message.warning('请选择行政区划');
        return false;
        this.$message.warning('请选择行政区划')
        return false
      }
      return true;
      return true
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      if (!this.validateSubmitData(row)) {
        loading();
        return;
        loading()
        return
      }
      console.log('--- Start rowSave ---');
      console.log('Form data before save:', this.form);
      console.log('--- Start rowSave ---')
      console.log('Form data before save:', this.form)
      // 处理 areaCode,确保只取最后一个值
      let areaCode = this.form.areaCode;
      let areaCode = this.form.areaCode
      if (Array.isArray(areaCode)) {
        areaCode = areaCode[areaCode.length - 1]; // 数组取最后一个
        areaCode = areaCode[areaCode.length - 1] // 数组取最后一个
      } else if (typeof areaCode === 'string' && areaCode.includes(',')) {
        const codes = areaCode.split(',');
        areaCode = codes[codes.length - 1]; // 字符串拆分取最后一个
        const codes = areaCode.split(',')
        areaCode = codes[codes.length - 1] // 字符串拆分取最后一个
      }
      row.areaCode = areaCode || null; // 确保非空
      row.areaCode = areaCode || null // 确保非空
      console.log('Row data before submit:', row);
      console.log('Row data before submit:', row)
      add(row).then(
        res => {
          console.log('Save response:', res.data);
          const data = res.data.data;
          row.id = data.id;
          row.deptCategoryName = data.deptCategoryName;
          row.tenantId = data.tenantId;
          console.log('Save response:', res.data)
          const data = res.data.data
          row.id = data.id
          row.deptCategoryName = data.deptCategoryName
          row.tenantId = data.tenantId
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.onLoad(this.page);
          done(row);
          })
          this.onLoad(this.page)
          done(row)
        },
        error => {
          console.log('Save error:', error);
          this.loading = false;
          loading();
          console.log('Save error:', error)
          this.loading = false
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      if (!this.validateSubmitData(row)) {
        loading();
        return;
        loading()
        return
      }
      console.log('--- Start rowUpdate ---');
      console.log('--- Start rowUpdate ---')
      // 提交时只使用县级代码
      const submitData = {
        ...row,
        areaCode: row.areaCode.split(',').pop(), // 取最后一个代码(县级)
      };
      }
      console.log('Row data before submit:', row);
      console.log('Row data before submit:', row)
      update(submitData)
        .then(
          res => {
            console.log('Update response:', res.data);
            console.log('Update response:', res.data)
            this.$message({
              type: 'success',
              message: '操作成功!',
            });
            this.onLoad(this.page);
            done(row);
            })
            this.onLoad(this.page)
            done(row)
          },
          error => {
            console.log('Update error:', error);
            this.loading = false;
            loading();
            console.log('Update error:', error)
            this.loading = false
            loading()
          }
        )
        .catch(error => {
          console.log('Update promise rejected:', error);
          this.loading = false;
        });
          console.log('Update promise rejected:', error)
          this.loading = false
        })
    },
    rowDel(row, index, done) {
    rowDel (row, index, done) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.onLoad(this.page);
          done(row);
        });
          })
          this.onLoad(this.page)
          done(row)
        })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -451,153 +428,153 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.data = [];
          this.parentId = 0;
          this.$refs.crud.refreshTable();
          this.$refs.crud.toggleSelection();
          this.onLoad(this.page);
          this.data = []
          this.parentId = 0
          this.$refs.crud.refreshTable()
          this.$refs.crud.toggleSelection()
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.parentId = 0;
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.parentId = 0
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.parentId = '';
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.parentId = ''
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    // 修改 beforeOpen 方法
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['add', 'edit'].includes(type)) {
        this.initData();
        this.initData()
      }
      if (['edit', 'view'].includes(type)) {
        getDept(this.form.id).then(res => {
          const data = res.data.data;
          const areaCode = data.areaCode;
          const areaName = data.areaName;
          const data = res.data.data
          const areaCode = data.areaCode
          const areaName = data.areaName
          // 生成完整的行政区划代码(考虑不同级别)
          const fullAreaCode = this.getFullAreaCode(areaCode);
          const fullAreaCode = this.getFullAreaCode(areaCode)
          this.form = Object.assign({}, data, {
            hasChildren: this.form.hasChildren,
            areaCode: fullAreaCode,
            areaName: areaName || '',
          });
          })
          // 调试输出
          console.log('回显数据:', {
            originalAreaCode: areaCode,
            fullAreaCode: fullAreaCode,
            areaName: areaName,
          });
        });
          })
        })
      } else if (type === 'add') {
        this.form.areaCode = '';
        this.form.areaName = '';
        this.form.areaCode = ''
        this.form.areaName = ''
      }
      done();
      done()
    },
    beforeClose(done) {
      this.parentId = '';
      const column = this.findObject(this.option.column, 'parentId');
      column.value = '';
      column.addDisabled = false;
      this.form.areaCode = null;
      done();
    beforeClose (done) {
      this.parentId = ''
      const column = this.findObject(this.option.column, 'parentId')
      column.value = ''
      column.addDisabled = false
      this.form.areaCode = null
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
      this.onLoad(this.page);
    currentChange (currentPage) {
      this.page.currentPage = currentPage
      this.onLoad(this.page)
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
      this.onLoad(this.page);
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
      this.onLoad(this.page)
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
      console.log('onLoad triggered with page:', page, 'params:', params);
      console.log('Current parentId:', this.parentId, 'query:', this.query);
    onLoad (page, params = {}) {
      this.loading = true
      console.log('onLoad triggered with page:', page, 'params:', params)
      console.log('Current parentId:', this.parentId, 'query:', this.query)
      getLazyList(this.parentId, Object.assign(params, this.query))
        .then(res => {
          console.log('onLoad response:', res.data);
          console.log('onLoad response:', res.data)
          // 处理 areaCode,确保是单一字符串
          const data = (res.data.data || []).map(item => {
            const fullAreaCode = this.getFullAreaCode(item.areaCode);
            const fullAreaCode = this.getFullAreaCode(item.areaCode)
            return {
              ...item,
              // 保存完整的行政区划代码
              areaCode: fullAreaCode,
              // 使用原始的县级代码作为实际值
              originalAreaCode: item.areaCode,
            };
          });
          this.data = data;
          this.loading = false;
          this.selectionClear();
          console.log('Table data after onLoad:', this.data);
            }
          })
          this.data = data
          this.loading = false
          this.selectionClear()
          console.log('Table data after onLoad:', this.data)
        })
        .catch(error => {
          console.log('onLoad error:', error);
          this.loading = false;
          this.data = this.data; // 保留当前数据
          console.log('Table data after onLoad error:', this.data);
        });
          console.log('onLoad error:', error)
          this.loading = false
          this.data = this.data // 保留当前数据
          console.log('Table data after onLoad error:', this.data)
        })
    },
    treeLoad(tree, treeNode, resolve) {
    treeLoad (tree, treeNode, resolve) {
      getChildLazyTree({ parentId: tree.id }).then(res => {
        resolve(res.data.data);
      });
        resolve(res.data.data)
      })
    },
    // 修改 getFullAreaCode 方法
    getFullAreaCode(areaCode) {
      if (!areaCode) return '';
    getFullAreaCode (areaCode) {
      if (!areaCode) return ''
      // 根据编码长度判断层级
      const code = areaCode.toString();
      const code = areaCode.toString()
      // 如果已经是完整的逗号分隔格式,直接返回
      if (code.includes(',')) return code;
      if (code.includes(',')) return code
      // 处理不同级别的编码
      if (code.endsWith('0000000000')) {
        // 省级编码
        return code;
        return code
      } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) {
        // 市级编码
        const provinceCode = code.substring(0, 2) + '0000000000';
        return `${provinceCode},${code}`;
        const provinceCode = code.substring(0, 2) + '0000000000'
        return `${provinceCode},${code}`
      } else {
        // 县级编码
        const provinceCode = code.substring(0, 2) + '0000000000';
        const cityCode = code.substring(0, 4) + '00000000';
        return `${provinceCode},${cityCode},${code}`;
        const provinceCode = code.substring(0, 2) + '0000000000'
        const cityCode = code.substring(0, 4) + '00000000'
        return `${provinceCode},${cityCode},${code}`
      }
    },
  },
};
}
</script>
<style></style>
src/views/system/dict.vue
@@ -449,12 +449,4 @@
};
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
src/views/system/menu.vue
@@ -1,48 +1,21 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      ref="crud"
      v-model="form"
      :permission="permissionList"
      :before-open="beforeOpen"
      :before-close="beforeClose"
      @row-del="rowDel"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
      @tree-load="treeLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" :before-close="beforeClose" @row-del="rowDel"
      @row-update="rowUpdate" @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset"
      @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
      @refresh-change="refreshChange" @on-load="onLoad" @tree-load="treeLoad">
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          v-if="permission.menu_delete"
          plain
          @click="handleDelete"
          >删 除
        <el-button type="danger" icon="el-icon-delete" v-if="permission.menu_delete" plain @click="handleDelete">删 除
        </el-button>
      </template>
      <template #menu="scope">
        <el-button
          type="primary"
          text
          icon="el-icon-plus"
          @click.stop="handleAdd(scope.row, scope.index)"
          v-if="userInfo.role_name.includes('admin') && scope.row.category === 1"
          >新增子项
        <el-button type="primary" text icon="el-icon-plus" @click.stop="handleAdd(scope.row, scope.index)"
          v-if="userInfo.role_name.includes('admin') && scope.row.category === 1">新增子项
        </el-button>
      </template>
      <template #name="{ row }">
        <i class="namei" :class="row.source"  />
        <i class="namei" :class="row.source" />
        <span>{{ row.name }}</span>
      </template>
      <template #source="{ row }">
@@ -55,14 +28,14 @@
</template>
<script>
import { getLazyList, remove, update, add, getMenu } from '@/api/system/menu';
import { mapGetters } from 'vuex';
import iconList from '@/config/iconList';
import func from '@/utils/func';
import { getMenuTree } from '@/api/system/menu';
import { getLazyList, remove, update, add, getMenu } from '@/api/system/menu'
import { mapGetters } from 'vuex'
import iconList from '@/config/iconList'
import func from '@/utils/func'
import { getMenuTree } from '@/api/system/menu'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -88,6 +61,10 @@
        viewBtn: true,
        menuWidth: 320,
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '菜单名称',
@@ -287,110 +264,110 @@
        ],
      },
      data: [],
    };
    }
  },
  watch: {
    'form.category'() {
      const category = func.toInt(this.form.category);
    'form.category' () {
      const category = func.toInt(this.form.category)
      this.$refs.crud.option.column.filter(item => {
        if (item.prop === 'path') {
          item.rules[0].required = category === 1;
          item.rules[0].required = category === 1
        }
        if (item.prop === 'isOpen') {
          item.disabled = category === 2;
          item.disabled = category === 2
        }
      });
      })
    },
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.menu_add, false),
        viewBtn: this.validData(this.permission.menu_view, false),
        delBtn: this.validData(this.permission.menu_delete, false),
        editBtn: this.validData(this.permission.menu_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    initData() {
    initData () {
      getMenuTree().then(res => {
        const column = this.findObject(this.option.column, 'parentId');
        column.dicData = res.data.data;
      });
        const column = this.findObject(this.option.column, 'parentId')
        column.dicData = res.data.data
      })
    },
    handleAdd(row) {
      this.parentId = row.id;
      const column = this.findObject(this.option.column, 'parentId');
      column.value = row.id;
      column.addDisabled = true;
      this.$refs.crud.rowAdd();
    handleAdd (row) {
      this.parentId = row.id
      const column = this.findObject(this.option.column, 'parentId')
      column.value = row.id
      column.addDisabled = true
      this.$refs.crud.rowAdd()
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        res => {
          // 获取新增数据的相关字段
          const data = res.data.data;
          row.id = data.id;
          const data = res.data.data
          row.id = data.id
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          })
          // 数据回调进行刷新
          done(row);
          done(row)
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          })
          // 数据回调进行刷新
          done(row);
          done(row)
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row, index, done) {
    rowDel (row, index, done) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          })
          // 数据回调进行刷新
          done(row);
        });
          done(row)
        })
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -398,86 +375,86 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          // 刷新表格数据并重载
          this.data = [];
          this.parentId = 0;
          this.$refs.crud.refreshTable();
          this.$refs.crud.toggleSelection();
          this.data = []
          this.parentId = 0
          this.$refs.crud.refreshTable()
          this.$refs.crud.toggleSelection()
          // 表格数据重载
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.parentId = 0;
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.parentId = 0
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.parentId = '';
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.parentId = ''
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['add', 'edit'].includes(type)) {
        this.initData();
        this.initData()
      }
      if (['edit', 'view'].includes(type)) {
        getMenu(this.form.id).then(res => {
          this.form = Object.assign(res.data.data, {
            hasChildren: this.form.hasChildren,
          });
        });
          })
        })
      }
      done();
      done()
    },
    beforeClose(done) {
      this.parentId = '';
      const column = this.findObject(this.option.column, 'parentId');
      column.value = '';
      column.addDisabled = false;
      done();
    beforeClose (done) {
      this.parentId = ''
      const column = this.findObject(this.option.column, 'parentId')
      column.value = ''
      column.addDisabled = false
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getLazyList(this.parentId, Object.assign(params, this.query)).then(res => {
        this.data = res.data.data;
        this.loading = false;
        this.selectionClear();
      });
        this.data = res.data.data
        this.loading = false
        this.selectionClear()
      })
    },
    treeLoad(tree, treeNode, resolve) {
      const parentId = tree.id;
    treeLoad (tree, treeNode, resolve) {
      const parentId = tree.id
      getLazyList(parentId).then(res => {
        resolve(res.data.data);
      });
        resolve(res.data.data)
      })
    },
  },
};
}
</script>
@@ -485,12 +462,4 @@
.namei {
  margin-right: 5px;
}
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
src/views/system/sysConfig.vue
@@ -1,64 +1,31 @@
<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      ref="crud"
      @row-del="rowDel"
      v-model="form"
      :permission="permissionList"
      @row-update="rowUpdate"
      @row-save="rowSave"
      :before-open="beforeOpen"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
    <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel"
      v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen"
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button
          type="primary"
          icon="el-icon-plus"
          v-if="permissionList.addBtn"
          @click="$refs.crud.rowAdd()"
        >新增</el-button>
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          v-if="permissionList.delBtn"
          @click="handleDelete"
        >删除</el-button>
        <el-button type="primary" icon="el-icon-plus" v-if="permissionList.addBtn"
          @click="$refs.crud.rowAdd()">新增</el-button>
        <el-button type="danger" plain icon="el-icon-delete" v-if="permissionList.delBtn"
          @click="handleDelete">删除</el-button>
      </template>
      <template #menu="{ row, index }">
        <el-button
          type="text"
          icon="el-icon-view"
          v-if="permissionList.viewBtn"
          @click.stop="$refs.crud.rowView(row, index)"
        >查看</el-button>
        <el-button
          type="text"
          icon="el-icon-edit"
          v-if="permissionList.editBtn"
          @click.stop="$refs.crud.rowEdit(row, index)"
        >编辑</el-button>
        <el-button type="text" icon="el-icon-view" v-if="permissionList.viewBtn"
          @click.stop="$refs.crud.rowView(row, index)">查看</el-button>
        <el-button type="text" icon="el-icon-edit" v-if="permissionList.editBtn"
          @click.stop="$refs.crud.rowEdit(row, index)">编辑</el-button>
      </template>
    </avue-crud>
  </basic-container>
</template>
<script>
import { getList, remove, update, add, getDetail } from '@/api/system/sysConfig';
import { mapGetters } from 'vuex';
import { getList, remove, update, add, getDetail } from '@/api/system/sysConfig'
import { mapGetters } from 'vuex'
export default {
  data() {
  data () {
    return {
      form: {},
      query: {},
@@ -85,6 +52,10 @@
        excelBtn: true,
        dialogClickModal: false,
        grid: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '系统名称',
@@ -149,97 +120,97 @@
        ],
      },
      data: [],
    };
    }
  },
  computed: {
    ...mapGetters(['permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.permission.sysConfig_add,
        viewBtn: this.permission.sysConfig_view,
        delBtn: this.permission.sysConfig_delete,
        editBtn: this.permission.sysConfig_edit,
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  methods: {
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -247,68 +218,59 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
          this.form = res.data.data
        })
      }
      done();
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      const { releaseTimeRange } = this.query;
    onLoad (page, params = {}) {
      const { releaseTimeRange } = this.query
      let values = {
        ...params,
        ...this.query,
      };
      }
      if (releaseTimeRange) {
        values = {
          ...values,
          releaseTime_datege: releaseTimeRange[0],
          releaseTime_datelt: releaseTimeRange[1],
        };
        values.releaseTimeRange = null;
        }
        values.releaseTimeRange = null
      }
      this.loading = true;
      this.loading = true
      getList(page.currentPage, page.pageSize, values).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
  },
};
}
</script>
<style scoped lang="scss">
  :deep(){
    .avue-crud__body{
      .el-form{
        height: 745px;
        overflow: auto;
      }
    }
  }
</style>
<style scoped lang="scss"></style>
src/views/system/user.vue
@@ -1,16 +1,19 @@
<template>
  <el-row>
    <el-col :span="5">
      <div class="box">
  <div class="manage-p-all-10 manage-p-t-0 manage-h-0 manage-flex-1 manage-flex">
    <div
      class="manage-p-all-20 manage-flex-2 manage-h-100 manage-b-r-5 manage-box-s-b-b manage-overflow-h manage-b-c-w">
      <div class="box ">
        <el-scrollbar>
          <basic-container>
          <div class="manage-w-100 manage-h-100">
            <avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick" />
          </basic-container>
          </div>
        </el-scrollbar>
      </div>
    </el-col>
    <el-col :span="19">
      <basic-container>
    </div>
    <div
      class="manage-p-all-10 manage-p-t-0 manage-p-b-0 manage-flex-9 manage-h-100 manage-flex manage-box-s-b-b manage-f-d-c">
      <div class="manage-p-all-20 manage-h-100 manage-box-s-b-b manage-b-c-w  manage-b-r-5">
        <avue-crud :option="option" v-model:search="search" :table-loading="loading" :data="data" ref="crud"
          v-model="form" :permission="permissionList" @row-del="rowDel" @row-update="rowUpdate" @row-save="rowSave"
          :before-open="beforeOpen" v-model:page="page" @search-change="searchChange" @search-reset="searchReset"
@@ -26,8 +29,8 @@
            <el-button type="primary" plain icon="el-icon-operation"
              v-if="userInfo.role_name.includes('admin') && !auditMode" @click="handleAudit">审 核
            </el-button>
            <el-button type="success" plain icon="el-icon-check" v-if="userInfo.role_name.includes('admin') && auditMode"
              @click="handleAuditPass">通 过
            <el-button type="success" plain icon="el-icon-check"
              v-if="userInfo.role_name.includes('admin') && auditMode" @click="handleAuditPass">通 过
            </el-button>
            <el-button type="danger" plain icon="el-icon-close" v-if="userInfo.role_name.includes('admin') && auditMode"
              @click="handleAuditRefuse">拒 绝
@@ -41,8 +44,8 @@
            <el-button type="info" plain v-if="permission.user_reset && !auditMode" icon="el-icon-refresh"
              @click="handleReset">密码重置
            </el-button>
            <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode" icon="el-icon-setting"
              @click="handlePlatform">平台配置
            <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode"
              icon="el-icon-setting" @click="handlePlatform">平台配置
            </el-button>
            <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode"
              icon="el-icon-coordinate" @click="handleLock">账号解封
@@ -66,7 +69,7 @@
          <template #userTypeName="{ row }">
            <el-tag>{{ row.userTypeName }}</el-tag>
          </template>
        </avue-crud>
        <el-dialog title="用户角色配置" append-to-body v-model="roleBox" width="345px">
          <el-tree :data="roleGrantList" show-checkbox check-strictly default-expand-all node-key="id" ref="treeRole"
@@ -103,9 +106,9 @@
            </template>
          </avue-crud>
        </el-dialog>
      </basic-container>
    </el-col>
  </el-row>
      </div>
    </div>
  </div>
</template>
<script>
@@ -122,37 +125,37 @@
  unlock,
  auditPass,
  auditRefuse,
} from '@/api/system/user';
import { exportBlob } from '@/api/common';
import { getDeptTree, getDeptLazyTree } from '@/api/system/dept';
import { getRoleTree } from '@/api/system/role';
import { getPostList } from '@/api/system/post';
import { mapGetters } from 'vuex';
import website from '@/config/website';
import { getToken } from '@/utils/auth';
import { downloadXls } from '@/utils/util';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import func from '@/utils/func';
} from '@/api/system/user'
import { exportBlob } from '@/api/common'
import { getDeptTree, getDeptLazyTree } from '@/api/system/dept'
import { getRoleTree } from '@/api/system/role'
import { getPostList } from '@/api/system/post'
import { mapGetters } from 'vuex'
import website from '@/config/website'
import { getToken } from '@/utils/auth'
import { downloadXls } from '@/utils/util'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import func from '@/utils/func'
export default {
  data() {
  data () {
    const validatePass = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请输入密码'));
        callback(new Error('请输入密码'))
      } else {
        callback();
        callback()
      }
    };
    }
    const validatePass2 = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请再次输入密码'));
        callback(new Error('请再次输入密码'))
      } else if (value !== this.form.password) {
        callback(new Error('两次输入密码不一致!'));
        callback(new Error('两次输入密码不一致!'))
      } else {
        callback();
        callback()
      }
    };
    }
    return {
      form: {
        flightStartTime: null, // 飞行事件开始时间
@@ -194,11 +197,11 @@
        nodeKey: 'id',
        lazy: true,
        treeLoad: function (node, resolve) {
          const parentId = node.level === 0 ? 0 : node.data.id;
          console.log(node,666)
          const parentId = node.level === 0 ? 0 : node.data.id
          console.log(node, 666)
          const param = {
            parentId:parentId,
            level:node.level
            parentId: parentId,
            level: node.level
          }
          getDeptLazyTree(param).then(res => {
            resolve(
@@ -206,10 +209,10 @@
                return {
                  ...item,
                  leaf: !item.hasChildren,
                };
                }
              })
            );
          });
            )
          })
        },
        addBtn: false,
        menu: false,
@@ -222,8 +225,6 @@
        },
      },
      option: {
        // height: 'auto',
        // calcHeight: 140,
        tip: false,
        searchShow: true,
        searchMenuSpan: 6,
@@ -234,6 +235,9 @@
        viewBtn: true,
        dialogType: 'drawer',
        dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
        column: [
          {
            label: '登录账号',
@@ -367,7 +371,7 @@
                viewDisplay: false,
                rules: [{ required: true, validator: validatePass2, trigger: 'blur' }],
              },
            ],
          },
          {
@@ -501,32 +505,32 @@
                  },
                ],
              },
            ],
          },
           {
          {
            label: '登录限制',
            prop: '',
            icon: 'el-icon-s-custom',
            column: [
               {
              {
                label: '到期时间',
                prop: 'expireTime',
                type: 'datetime',
                format: 'YYYY-MM-DD HH:mm:ss',
                valueFormat: 'YYYY-MM-DD HH:mm:ss',
              },
                {
              {
                label: '次数限制',
                prop: 'maxLoginNum',
                type: 'input',
              },
               {
              {
                label: '飞行开始时间',
                prop: 'flightStartTime',
               type: 'time',
                 format: 'HH:mm',
                type: 'time',
                format: 'HH:mm',
                valueFormat: 'HH:mm',
                rules: [
                  {
@@ -550,9 +554,9 @@
                  },
                ],
              },
            ],
          },
        ],
@@ -683,206 +687,206 @@
          },
        ],
      },
    };
    }
  },
  watch: {
    'form.tenantId'() {
    'form.tenantId' () {
      if (this.form.tenantId !== '' && this.initFlag) {
        this.initData(this.form.tenantId);
        this.initData(this.form.tenantId)
      }
    },
    'excelForm.isCovered'() {
    'excelForm.isCovered' () {
      if (this.excelForm.isCovered !== '') {
        const column = this.findObject(this.excelOption.column, 'excelFile');
        column.action = `/blade-system/user/import-user?isCovered=${this.excelForm.isCovered}`;
        const column = this.findObject(this.excelOption.column, 'excelFile')
        column.action = `/blade-system/user/import-user?isCovered=${this.excelForm.isCovered}`
      }
    },
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList() {
    permissionList () {
      return {
        addBtn: this.validData(this.permission.user_add, false),
        viewBtn: this.validData(this.permission.user_view, false),
        delBtn: this.validData(this.permission.user_delete, false),
        editBtn: this.validData(this.permission.user_edit, false),
      };
      }
    },
    platformPermissionList() {
    platformPermissionList () {
      return {
        addBtn: false,
        viewBtn: false,
        delBtn: false,
        editBtn: this.validData(this.permission.user_edit, false),
      };
      }
    },
    ids() {
      let ids = [];
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
        ids.push(ele.id)
      })
      return ids.join(',')
    },
  },
  mounted() {
  mounted () {
    // 非租户模式默认加载管理组数据
    if (!website.tenantMode) {
      this.initData(website.tenantId);
      this.initData(website.tenantId)
    }
    this.initData(this.form.tenantId);
    this.initData(this.form.tenantId)
  },
  methods: {
    nodeClick(data) {
      this.treeDeptId = data.id;
      this.page.currentPage = 1;
      this.onLoad(this.page);
    nodeClick (data) {
      this.treeDeptId = data.id
      this.page.currentPage = 1
      this.onLoad(this.page)
    },
    initData(tenantId) {
      tenantId = '000000';
    initData (tenantId) {
      tenantId = '000000'
      getRoleTree(tenantId).then(res => {
        const column = this.findObject(this.option.group, 'roleId');
        column.dicData = res.data.data;
      });
        const column = this.findObject(this.option.group, 'roleId')
        column.dicData = res.data.data
      })
      getDeptTree(tenantId).then(res => {
        const column = this.findObject(this.option.group, 'deptId');
        column.dicData = res.data.data;
      });
        const column = this.findObject(this.option.group, 'deptId')
        column.dicData = res.data.data
      })
      // getPostList(tenantId).then(res => {
      //   const column = this.findObject(this.option.group, 'postId');
      //   column.dicData = res.data.data;
      // });
    },
    submitRole() {
      const roleList = this.$refs.treeRole.getCheckedKeys().join(',');
    submitRole () {
      const roleList = this.$refs.treeRole.getCheckedKeys().join(',')
      grant(this.ids, roleList).then(() => {
        this.roleBox = false;
        this.roleBox = false
        this.$message({
          type: 'success',
          message: '操作成功!',
        });
        this.onLoad(this.page);
      });
        })
        this.onLoad(this.page)
      })
    },
    rowSave(row, done, loading) {
    rowSave (row, done, loading) {
      // 格式化时间数据
       if (row.flightStartTime && row.flightEndTime) {
    // 验证 flightEndTime 是否小于 flightStartTime
   const [startHours, startMinutes] = row.flightStartTime.split(':').map(Number);
    const [endHours, endMinutes] = row.flightEndTime.split(':').map(Number);
    const startTotalMinutes = startHours * 60 + startMinutes;
    const endTotalMinutes = endHours * 60 + endMinutes;
      if (row.flightStartTime && row.flightEndTime) {
        // 验证 flightEndTime 是否小于 flightStartTime
        const [startHours, startMinutes] = row.flightStartTime.split(':').map(Number)
        const [endHours, endMinutes] = row.flightEndTime.split(':').map(Number)
    if (endTotalMinutes < startTotalMinutes) {
        this.$message.error('飞行结束时间不能小于飞行开始时间');
            loading();
        return;
    }
       }
      row.deptId = func.join(row.deptId);
      row.roleId = func.join(row.roleId);
      row.postId = func.join(row.postId);
        const startTotalMinutes = startHours * 60 + startMinutes
        const endTotalMinutes = endHours * 60 + endMinutes
        if (endTotalMinutes < startTotalMinutes) {
          this.$message.error('飞行结束时间不能小于飞行开始时间')
          loading()
          return
        }
      }
      row.deptId = func.join(row.deptId)
      row.roleId = func.join(row.roleId)
      row.postId = func.join(row.postId)
      row.name = row.realName
      add(row).then(
        () => {
          this.initFlag = false;
          this.onLoad(this.page);
          this.initFlag = false
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowUpdate(row, index, done, loading) {
         // 格式化时间数据
    rowUpdate (row, index, done, loading) {
      // 格式化时间数据
      // if (row.flightStartTime) {
      //   row.flightStartTime = this.formatTime(row.flightStartTime);
      // }
      // if (row.flightEndTime) {
      //   row.flightEndTime = this.formatTime(row.flightEndTime);
      // }
       if (row.flightStartTime && row.flightEndTime) {
    // 验证 flightEndTime 是否小于 flightStartTime
   const [startHours, startMinutes] = row.flightStartTime.split(':').map(Number);
    const [endHours, endMinutes] = row.flightEndTime.split(':').map(Number);
      if (row.flightStartTime && row.flightEndTime) {
        // 验证 flightEndTime 是否小于 flightStartTime
        const [startHours, startMinutes] = row.flightStartTime.split(':').map(Number)
        const [endHours, endMinutes] = row.flightEndTime.split(':').map(Number)
    const startTotalMinutes = startHours * 60 + startMinutes;
    const endTotalMinutes = endHours * 60 + endMinutes;
        const startTotalMinutes = startHours * 60 + startMinutes
        const endTotalMinutes = endHours * 60 + endMinutes
    if (endTotalMinutes < startTotalMinutes) {
        this.$message.error('飞行结束时间不能小于飞行开始时间');
           loading();
        return ;
        if (endTotalMinutes < startTotalMinutes) {
          this.$message.error('飞行结束时间不能小于飞行开始时间')
          loading()
          return
        }
      }
       }
      row.deptId = func.join(row.deptId);
      row.roleId = func.join(row.roleId);
      row.postId = func.join(row.postId);
      row.deptId = func.join(row.deptId)
      row.roleId = func.join(row.roleId)
      row.postId = func.join(row.postId)
      row.name = row.realName
      update(row).then(
        () => {
          this.initFlag = false;
          this.onLoad(this.page);
          this.initFlag = false
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    rowDel(row) {
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    searchReset() {
      this.query = {};
      this.treeDeptId = '';
      this.onLoad(this.page);
    searchReset () {
      this.query = {}
      this.treeDeptId = ''
      this.onLoad(this.page)
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange(list) {
      this.selectionList = list;
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    handleDelete() {
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
@@ -890,25 +894,25 @@
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleAudit() {
      this.auditMode = true;
      this.onLoad(this.page, this.query);
    handleAudit () {
      this.auditMode = true
      this.onLoad(this.page, this.query)
    },
    handleAuditPass() {
    handleAuditPass () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据通过审核?', {
        confirmButtonText: '确定',
@@ -916,21 +920,21 @@
        type: 'warning',
      })
        .then(() => {
          return auditPass(this.ids);
          return auditPass(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleAuditRefuse() {
    handleAuditRefuse () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据拒绝审核?', {
        confirmButtonText: '确定',
@@ -938,25 +942,25 @@
        type: 'warning',
      })
        .then(() => {
          return auditRefuse(this.ids);
          return auditRefuse(this.ids)
        })
        .then(() => {
          this.onLoad(this.page);
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleAuditBack() {
      this.auditMode = false;
      this.onLoad(this.page, this.query);
    handleAuditBack () {
      this.auditMode = false
      this.onLoad(this.page, this.query)
    },
    handleReset() {
    handleReset () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择账号密码重置为123456?', {
        confirmButtonText: '确定',
@@ -964,37 +968,37 @@
        type: 'warning',
      })
        .then(() => {
          return resetPassword(this.ids);
          return resetPassword(this.ids)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
          })
          this.$refs.crud.toggleSelection()
        })
    },
    handleGrant() {
    handleGrant () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.roleTreeObj = [];
      this.roleTreeObj = []
      if (this.selectionList.length === 1) {
        this.roleTreeObj = this.selectionList[0].roleId.split(',');
        this.roleTreeObj = this.selectionList[0].roleId.split(',')
      }
      getRoleTree().then(res => {
        this.roleGrantList = res.data.data;
        this.roleBox = true;
      });
        this.roleGrantList = res.data.data
        this.roleBox = true
      })
    },
    handlePlatform() {
      this.platformBox = true;
    handlePlatform () {
      this.platformBox = true
    },
    handleLock() {
    handleLock () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择账号解封?', {
        confirmButtonText: '确定',
@@ -1002,180 +1006,178 @@
        type: 'warning',
      })
        .then(() => {
          return unlock(this.ids);
          return unlock(this.ids)
        })
        .then(() => {
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
          })
        })
    },
    handleImport() {
      this.excelBox = true;
    handleImport () {
      this.excelBox = true
    },
    uploadAfter(res, done, loading, column) {
      window.console.log(column);
      this.excelBox = false;
      this.refreshChange();
      done();
    uploadAfter (res, done, loading, column) {
      window.console.log(column)
      this.excelBox = false
      this.refreshChange()
      done()
    },
    handleExport() {
      const account = func.toStr(this.search.account);
      const realName = func.toStr(this.search.realName);
    handleExport () {
      const account = func.toStr(this.search.account)
      const realName = func.toStr(this.search.realName)
      this.$confirm('是否导出用户数据?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        NProgress.start();
        NProgress.start()
        exportBlob(
          `/blade-system/user/export-user?${this.website.tokenHeader
          }=${getToken()}&account=${account}&realName=${realName}`
        ).then(res => {
          downloadXls(res.data, `用户数据表${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);
          NProgress.done();
        });
      });
          downloadXls(res.data, `用户数据表${this.$dayjs().format('YYYY-MM-DD')}.xlsx`)
          NProgress.done()
        })
      })
    },
    handleTemplate() {
    handleTemplate () {
      exportBlob(
        `/blade-system/user/export-template?${this.website.tokenHeader}=${getToken()}`
      ).then(res => {
        downloadXls(res.data, '用户数据模板.xlsx');
      });
        downloadXls(res.data, '用户数据模板.xlsx')
      })
    },
    beforeOpen(done, type) {
    beforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getUser(this.form.id).then(res => {
          this.form = res.data.data;
          this.form = res.data.data
          if (this.form.hasOwnProperty('deptId')) {
            this.form.deptId = func.split(this.form.deptId);
            this.form.deptId = func.split(this.form.deptId)
          }
          if (this.form.hasOwnProperty('roleId')) {
            this.form.roleId = func.split(this.form.roleId);
            this.form.roleId = func.split(this.form.roleId)
          }
          if (this.form.hasOwnProperty('postId')) {
            this.form.postId = func.split(this.form.postId);
            this.form.postId = func.split(this.form.postId)
          }
        });
        })
      }
      this.initFlag = true;
      done();
      this.initFlag = true
      done()
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad(page, params = {}) {
      this.loading = true;
    onLoad (page, params = {}) {
      this.loading = true
      getList(
        page.currentPage,
        page.pageSize,
        Object.assign(params, this.query, { status: this.auditMode ? 0 : 1 }),
        this.treeDeptId
      ).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.page.total = data.total
        this.data = data.records
        this.loading = false
        this.selectionClear()
      })
    },
    platformRowUpdate(row, index, done, loading) {
    platformRowUpdate (row, index, done, loading) {
      updatePlatform(row.id, row.userType, row.userExt).then(
        () => {
          this.platformOnLoad(this.platformPage);
          this.platformOnLoad(this.platformPage)
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
          })
          done()
        },
        error => {
          window.console.log(error);
          loading();
          window.console.log(error)
          loading()
        }
      );
      )
    },
    platformBeforeOpen(done, type) {
    platformBeforeOpen (done, type) {
      if (['edit', 'view'].includes(type)) {
        getUserPlatform(this.platformForm.id).then(res => {
          this.platformForm = res.data.data;
        });
          this.platformForm = res.data.data
        })
      }
      done();
      done()
    },
    platformSearchReset() {
      this.platformQuery = {};
      this.platformOnLoad(this.platformPage);
    platformSearchReset () {
      this.platformQuery = {}
      this.platformOnLoad(this.platformPage)
    },
    platformSearchChange(params, done) {
      this.platformQuery = params;
      this.platformPage.currentPage = 1;
      this.platformOnLoad(this.platformPage, params);
      done();
    platformSearchChange (params, done) {
      this.platformQuery = params
      this.platformPage.currentPage = 1
      this.platformOnLoad(this.platformPage, params)
      done()
    },
    platformSelectionChange(list) {
      this.platformSelectionList = list;
    platformSelectionChange (list) {
      this.platformSelectionList = list
    },
    platformSelectionClear() {
      this.platformSelectionList = [];
      this.$refs.platformCrud.toggleSelection();
    platformSelectionClear () {
      this.platformSelectionList = []
      this.$refs.platformCrud.toggleSelection()
    },
    platformCurrentChange(currentPage) {
      this.platformPage.currentPage = currentPage;
    platformCurrentChange (currentPage) {
      this.platformPage.currentPage = currentPage
    },
    platformSizeChange(pageSize) {
      this.platformPage.pageSize = pageSize;
    platformSizeChange (pageSize) {
      this.platformPage.pageSize = pageSize
    },
    platformRefreshChange() {
      this.platformOnLoad(this.platformPage, this.platformQuery);
    platformRefreshChange () {
      this.platformOnLoad(this.platformPage, this.platformQuery)
    },
    platformOnLoad(page, params = {}) {
      this.platformLoading = true;
    platformOnLoad (page, params = {}) {
      this.platformLoading = true
      getList(
        page.currentPage,
        page.pageSize,
        Object.assign(params, this.query),
        this.treeDeptId
      ).then(res => {
        const data = res.data.data;
        this.platformPage.total = data.total;
        this.platformData = data.records;
        this.platformLoading = false;
        this.selectionClear();
      });
        const data = res.data.data
        this.platformPage.total = data.total
        this.platformData = data.records
        this.platformLoading = false
        this.selectionClear()
      })
    },
    formatTime(time) {
    formatTime (time) {
      // 格式化时间为 HH:mm
      const date = new Date(time);
      const hours = String(date.getHours()).padStart(2, '0');
      const minutes = String(date.getMinutes()).padStart(2, '0');
      return `${hours}:${minutes}`;
      const date = new Date(time)
      const hours = String(date.getHours()).padStart(2, '0')
      const minutes = String(date.getMinutes()).padStart(2, '0')
      return `${hours}:${minutes}`
    },
  },
};
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.box {
  height: 800px;
}
.el-scrollbar {
  height: 100%;
}
.box .el-scrollbar__wrap {
  overflow: scroll;
::v-deep(.el-scrollbar) {
  height: 100%;
  .el-scrollbar__wrap {
    overflow: auto;
  }
}
</style>
src/views/tickets/orderLog.vue
@@ -12,7 +12,7 @@
  <basic-container>
    <el-tabs v-model="activeTab" @tab-click="handleTabChange">
      <el-tab-pane v-for="tab in filteredTabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name">
        <div class="tab-content">
        <basic-main-content>
          <!-- 查询条件筛选栏 -->
          <div class="filter-bar">
            <div class="search-bar-box">
@@ -92,7 +92,7 @@
          <!-- 表格部分 -->
          <avue-crud :data="tableData" :option="option" v-model:page="page" ref="crud" :table-loading="loading"
            @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad"
            @search-change="searchChange" @size-change="sizeChange">
            @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name">
            <template #menu-left>
              <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus"
                @click="handleAdd">新建工单</el-button>
@@ -111,8 +111,9 @@
                  " -->
                <template v-if="row.status == 1">
                  <!--待审核状态-->
                  <el-button type="text" icon="el-icon-warning" v-if="hasRecallPaddingBtnPermission()" @click="orderLogRecall(row.id)">撤回</el-button>
                    <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button>
                  <el-button type="text" icon="el-icon-warning" v-if="hasRecallPaddingBtnPermission()"
                    @click="orderLogRecall(row.id)">撤回</el-button>
                  <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button>
                </template>
                <!--已驳回-->
                <template v-if="row.status == 2">
@@ -140,7 +141,7 @@
              </el-tooltip>
            </template>
          </avue-crud>
        </div>
        </basic-main-content>
      </el-tab-pane>
    </el-tabs>
@@ -567,6 +568,10 @@
        menu: true,
        menuWidth: 210,
        menuClassName: 'cur-menu',
        height: 'auto',
        calcHeight: 196,
        column: [
          { label: '工单编号', prop: 'job_info_num', width: 100, ellipsis: true, overHidden: true },
          { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true },
@@ -1144,42 +1149,32 @@
</script>
<style lang="scss" scoped>
:deep() {
  .avue-crud__body {
    .el-form {
      height: 625px;
      overflow: auto;
::v-deep(.el-tabs) {
  height: 100%;
  display: flex;
  flex-direction: column;
  .el-tabs__header {
    order: 1;
  }
  .el-tabs__content {
    order: 2;
  }
  .el-tabs__content {
    height: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    .el-tab-pane {
      height: 0;
      flex: 1;
      display: flex;
      flex-direction: column;
    }
  }
}
: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;
}
:deep(.el-input__inner),
:deep(.el-select),
:deep(.el-select .el-input) {
  width: 100%; // 确保所有输入框和选择框宽度一致
}
.tab-content {
  padding: 10px;
}
.filter-bar {
@@ -1212,6 +1207,31 @@
  }
}
: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;
}
:deep(.el-input__inner),
:deep(.el-select),
:deep(.el-select .el-input) {
  width: 100%; // 确保所有输入框和选择框宽度一致
}
.action-bar {
  margin-bottom: 16px;
}
@@ -1219,25 +1239,6 @@
.el-tabs {
  :deep(.el-tabs__content) {
    overflow: visible;
  }
}
.tab-content {
  min-height: 200px;
}
.detail-form {
  :deep(.el-form-item) {
    margin-bottom: 10px;
  }
  :deep(.el-form-item__label) {
    color: #606266;
    font-weight: normal;
  }
  :deep(.el-form-item__content) {
    color: #303133;
  }
}
src/views/tickets/ticket.vue
@@ -2,7 +2,7 @@
  <basic-container>
    <el-tabs v-model="activeTab" @tab-click="handleTabChange">
      <el-tab-pane v-for="tab in filteredTabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name">
        <div class="tab-content">
        <basic-main-content>
          <!-- 查询条件筛选栏 -->
          <div class="filter-bar">
            <el-input v-model="filters.keyword" placeholder="请输入关键字" class="filter-item" clearable
@@ -33,7 +33,7 @@
          <!-- 表格部分 -->
          <avue-crud ref="avueCrud" v-model="tableData" :option="option" :data="tableData" v-model:page="page"
            @size-change="sizeChange" @current-change="handleCurrentChange" :table-loading="loading"
            @selection-change="handleSelectionChange" :permission="permissionList">
            @selection-change="handleSelectionChange" :permission="permissionList" v-if="activeTab === tab.name">
            <template #menu-left>
              <el-button v-if="(activeTab === 'all' || activeTab === 'myTickets') && permissionList.addBtn"
                type="primary" icon="el-icon-plus" @click="handleAdd">新建工单</el-button>
@@ -69,12 +69,13 @@
              <span>{{ showIsReviewText(row) }}</span>
            </template>
          </avue-crud>
        </div>
        </basic-main-content>
      </el-tab-pane>
    </el-tabs>
    <!-- 新建工单对话框 -->
    <el-dialog v-model="dialogVisible" v-if="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm">
    <el-dialog v-model="dialogVisible" v-if="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false"
      @close="resetForm">
      <el-form :model="form" :rules="rules" ref="form" label-width="90px" class="create-ticket-form">
        <div class="form-section">
          <el-row :gutter="16">
@@ -585,6 +586,10 @@
        addBtn: false,
        menu: true,
        page: true,
        height: 'auto',
        calcHeight: 196,
        column: [
          // { label: "序号", prop: "id", width: 70 },
          { label: '工单编号', prop: 'orderNumber', width: 120, overHidden: true, tooltip: true },
@@ -2449,12 +2454,51 @@
</script>
<style lang="scss" scoped>
:deep() {
  .avue-crud__body {
    .el-form {
      height: 655px;
      overflow: auto;
::v-deep(.el-tabs) {
  height: 100%;
  display: flex;
  flex-direction: column;
  .el-tabs__header {
    order: 1;
  }
  .el-tabs__content {
    order: 2;
  }
  .el-tabs__content {
    height: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    .el-tab-pane {
      height: 0;
      flex: 1;
      display: flex;
      flex-direction: column;
    }
  }
}
.filter-bar {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
  flex-wrap: wrap;
  gap: 8px; // 使用 gap 统一设置间距
  .filter-item {
    width: 160px; // 减小宽度
  }
  .date-picker {
    width: 240px; // 日期选择器宽度适当调整
  }
  .el-button {
    margin-left: 0; // 覆盖 element-ui 默认的按钮间距
  }
}
@@ -2485,57 +2529,8 @@
  margin-bottom: 12px
}
.tab-content {
  padding: 10px;
}
.filter-bar {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
  flex-wrap: wrap;
  gap: 8px; // 使用 gap 统一设置间距
  .filter-item {
    width: 160px; // 减小宽度
  }
  .date-picker {
    width: 240px; // 日期选择器宽度适当调整
  }
  .el-button {
    margin-left: 0; // 覆盖 element-ui 默认的按钮间距
  }
}
.action-bar {
  margin-bottom: 16px;
}
.el-tabs {
  :deep(.el-tabs__content) {
    overflow: visible;
  }
}
.tab-content {
  min-height: 200px;
}
.detail-form {
  :deep(.el-form-item) {
    margin-bottom: 10px;
  }
  :deep(.el-form-item__label) {
    color: #606266;
    font-weight: normal;
  }
  :deep(.el-form-item__content) {
    color: #303133;
  }
}
.el-dialog {
src/views/wel/components/backlog.vue
@@ -4,33 +4,21 @@
      <div class="titleBox">
        <div class="title">待办事项</div>
        <div class="rightTab">
          <div
            class="titleItem"
            :class="item.name === checked ? 'activeItem' : ''"
            v-for="(item, index) in titleList"
            :key="index"
            @click="titleClick(item, index)"
          >
          <div class="titleItem" :class="item.name === checked ? 'activeItem' : ''" v-for="(item, index) in titleList"
            :key="index" @click="titleClick(item, index)">
            <div>
              {{ item.name }}
              <div class="dots" >{{ item.number >= 100 ? '99+' : item.number }}</div>
                <!-- <div class="dots">99+</div> -->
              <div class="dots">{{ item.number >= 100 ? '99+' : item.number }}</div>
              <!-- <div class="dots">99+</div> -->
            </div>
          </div>
        </div>
      </div>
      <div class="todo-items" v-if="todos.length > 0" v-loading="loading">
        <div
          v-for="(item, index) in todos"
          :key="index"
          class="todo-item"
          :style="getStatusStyle(item.status)"
          @click="jumporder(item)"
        >
          <div
            class="status-indicator"
          ><img :src="getStatus(item.status)" alt=""></div>
        <div v-for="(item, index) in todos" :key="index" class="todo-item" :style="getStatusStyle(item.status)"
          @click="jumporder(item)">
          <div class="status-indicator"><img :src="getStatus(item.status)" alt=""></div>
          <div class="content-wrapper">
            <div class="main-content">
@@ -60,22 +48,22 @@
</template>
<script setup>
import st7 from '@/assets/images/workbench/st7.svg';
import db1 from '@/assets/images/workbench/db1.svg';
import db2 from '@/assets/images/workbench/db2.svg';
import db3 from '@/assets/images/workbench/db3.svg';
import db4 from '@/assets/images/workbench/db4.svg';
import db5 from '@/assets/images/workbench/db5.svg';
import { getOrderCountApi, addOrderRecordApi, getOrderOrEventApi } from '@/api/home/index';
import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
const zfTotal = ref(0);
const sjTotal = ref(0);
const router = useRouter();
const store = useStore();
const permission = computed(() => store.getters.permission);
const checked = ref('事件工单');
import st7 from '@/assets/images/workbench/st7.svg'
import db1 from '@/assets/images/workbench/db1.svg'
import db2 from '@/assets/images/workbench/db2.svg'
import db3 from '@/assets/images/workbench/db3.svg'
import db4 from '@/assets/images/workbench/db4.svg'
import db5 from '@/assets/images/workbench/db5.svg'
import { getOrderCountApi, addOrderRecordApi, getOrderOrEventApi } from '@/api/home/index'
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
const zfTotal = ref(0)
const sjTotal = ref(0)
const router = useRouter()
const store = useStore()
const permission = computed(() => store.getters.permission)
const checked = ref('事件工单')
const titleList = ref([
  {
    label: 'event_order_count',
@@ -87,37 +75,37 @@
    name: '智飞工单',
    number: 0,
  },
]);
])
const titleClick = (item, index) => {
  checked.value = item.name;
  getListMatter();
};
  checked.value = item.name
  getListMatter()
}
const userInfo = computed(() => store.getters.userInfo);
const todos = ref([]);
const loading = ref(false);
const userInfo = computed(() => store.getters.userInfo)
const todos = ref([])
const loading = ref(false)
// * 事件工单中2=待审核,0=待处理,3=处理中
// * 智飞工单中1: 待审核,2=已驳回,3=已通过
const statusMap = {
  0: '待处理',
  2: '待审核',
  3: '处理中',
};
}
const statusMapColor = {
  0: { color: '#FF7411', backgroundImage: db3, borderLeftColor: '#FF7411' },
  2: { color: '#FF472F', backgroundImage: db1, borderLeftColor: '#FF472F' },
  3: { color: '#FFC300', backgroundImage: db4, borderLeftColor: '#FFC300' },
};
}
const zfstatusMap = {
  1: '待审核',
  2: '已驳回',
  3: '已通过',
};
}
const zfstatusMapColor = {
  1: { color: '#FF472F', backgroundImage: db2, borderLeftColor: '#FF472F' },
  2: { color: '#4200DD', backgroundImage: db5, borderLeftColor: '#4200DD' },
  3: { color: '#FF472F', backgroundImage: db2, borderLeftColor: '#FF472F' },
};
}
// 根据状态获取图标
// const getStatusBackground = statusIndex => {
//   if (checked.value === '智飞工单') {
@@ -130,134 +118,145 @@
// };
const getStatus = statusIndex => {
  if (checked.value === '智飞工单') {
    const style = zfstatusMapColor[statusIndex];
    return style ? style.backgroundImage : '';
    const style = zfstatusMapColor[statusIndex]
    return style ? style.backgroundImage : ''
  } else {
    const style = statusMapColor[statusIndex];
    return style ? style.backgroundImage : '';
    const style = statusMapColor[statusIndex]
    return style ? style.backgroundImage : ''
  }
};
}
const getStatusStyle = statusIndex => {
  if (checked.value === '智飞工单') {
    const style = zfstatusMapColor[statusIndex] || {
      color: '#999',
      borderLeftColor: '#999',
    };
    }
    return {
      color: style.color,
      borderLeft: `3px solid ${style.borderLeftColor}`,
    };
    }
  } else {
    const style = statusMapColor[statusIndex] || {
      color: '#999',
      borderLeftColor: '#999',
    };
    }
    return {
      color: style.color,
      borderLeft: `3px solid ${style.borderLeftColor}`,
    };
    }
  }
};
}
// 获取状态对应的文字颜色
const getStatusColor = statusIndex => {
  if (checked.value === '智飞工单') {
    return zfstatusMapColor[statusIndex]?.color || '#999';
    return zfstatusMapColor[statusIndex]?.color || '#999'
  } else {
    return statusMapColor[statusIndex]?.color || '#999';
    return statusMapColor[statusIndex]?.color || '#999'
  }
};
}
// 跳转到对应详情页
const jumporder = val => {
  if (checked.value === '智飞工单') {
    const id = val.id;
    getaddOrderRecord(id);
    const id = val.id
    getaddOrderRecord(id)
    router.push({
      path: `/tickets/orderLog`,
      query: {
        id,
      },
    });
    })
  } else {
    getaddOrderRecord(val.id);
    const orderNumber = val.event_num;
    getaddOrderRecord(val.id)
    const orderNumber = val.event_num
    router.push({
      path: `/tickets/ticket`,
      query: {
        orderNumber,
      },
    });
    })
  }
};
}
// 标记事件工单或智飞工单已读
const getaddOrderRecord = async id => {
  const ticketId = id;
  const ticketId = id
  if (checked.value === '事件工单') {
    loading.value = true;
    const type = 1;
    const res = await addOrderRecordApi(type, ticketId);
    loading.value = true
    const type = 1
    const res = await addOrderRecordApi(type, ticketId)
  } else if (checked.value === '智飞工单') {
    loading.value = true;
    const type = 2;
    const res = await addOrderRecordApi(type, ticketId);
    loading.value = true
    const type = 2
    const res = await addOrderRecordApi(type, ticketId)
  }
};
}
// 获取事件工单和智飞工单的数量
const getOrderCount = async () => {
  const res = await getOrderCountApi(userInfo.value.detail.areaCode);
  const data = res.data.data;
  const res = await getOrderCountApi(userInfo.value.detail.areaCode)
  const data = res.data.data
  titleList.value.forEach(item => {
    if (data[item.label] !== undefined) {
      item.number = data[item.label];
      item.number = data[item.label]
    }
  });
};
  })
}
// 获取事件工单或者智飞工单的信息
const getListMatter = async () => {
  if (checked.value === '事件工单') {
    loading.value = true;
    const type = 1;
    const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode);
    todos.value = res.data.data || [];
    loading.value = false;
    loading.value = true
    const type = 1
    const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode)
    todos.value = res.data.data || []
    loading.value = false
  } else if (checked.value === '智飞工单') {
    loading.value = true;
    const type = 2;
    const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode);
    todos.value = res.data.data || [];
    loading.value = false;
    loading.value = true
    const type = 2
    const res = await getOrderOrEventApi(type, userInfo.value.detail.areaCode)
    todos.value = res.data.data || []
    loading.value = false
  }
};
}
onMounted(() => {
  getListMatter();
  getOrderCount();
});
  getListMatter()
  getOrderCount()
})
</script>
<style>
.el-empty__image {
  width: 100px !important;
}
</style>
<style lang="scss" scoped>
.bocklogBox {
  width: 100%;
  height: 306px;
  // height: 306px;
  height: pxToVh(326);
  background: rgba(255, 255, 255, 0.41);
  box-shadow: 0px 3px 4px -1px rgba(125, 125, 125, 0.25);
  border-radius: 8px 8px 8px 8px;
  border: 2px solid #ffffff;
  box-sizing: border-box;
  .block {
    margin: 11px 21px 13px 22px;
    .titleBox {
      display: flex;
      justify-content: space-between;
    }
    .title {
      font-family: 'Source Han Sans CN';
      font-weight: bold;
      font-size: 16px;
      color: #363636;
    }
    .rightTab {
      display: flex;
      text-align: center;
      padding-right: 10px;
      .titleItem {
        position: relative;
        font-weight: 400;
@@ -266,6 +265,7 @@
        margin-left: 18px;
        cursor: pointer;
      }
      .activeItem {
        border-radius: 0px 0px 0px 0px;
        font-family: Source Han Sans CN, Source Han Sans CN;
@@ -274,11 +274,13 @@
        color: #1c5cff;
        border-bottom: 2px solid #1c5cff;
      }
      .dots {
        width: 17px;
        height: 17px;
        // height: 17px;
        height: pxToVh(17);
        border-radius: 50%;
         background: #366fff;
        background: #366fff;
        color: #fff;
        font-size: 9px;
        position: absolute;
@@ -287,9 +289,11 @@
        padding: 1px;
        line-height: 17px;
      }
           .dots1 {
      .dots1 {
        width: 17px;
        height: 17px;
        // height: 17px;
        height: pxToVh(17);
        border-radius: 50%;
        background: none !important;
        color: #366fff !important;
@@ -301,9 +305,11 @@
        line-height: 17px;
      }
    }
    .todo-items {
      margin-top: 10px;
      height: 250px;
      // height: 250px;
      height: pxToVh(250);
      overflow-y: scroll;
    }
@@ -317,7 +323,9 @@
      box-shadow: 0px 2px 4px 0px rgba(173, 173, 173, 0.18);
      transition: all 0.2s ease;
      border-left: 4px solid transparent;
      height: 42px;
      // height: 42px;
      height: pxToVh(42);
      &:hover {
        transform: translateY(-2px);
        box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
@@ -327,9 +335,10 @@
        margin-left: 3px;
        width: 1.6rem;
        height: 1.6rem;
        img{
        width: 100%;
        height: 100%;
        img {
          width: 100%;
          height: 100%;
        }
      }
@@ -359,6 +368,7 @@
          font-size: 14px;
          color: #343434;
        }
        .unread {
          width: 5px;
          height: 5px;
@@ -366,6 +376,7 @@
          border-radius: 50%;
          margin: 0 4px;
        }
        .reads {
          width: 5px;
          height: 5px;
@@ -379,10 +390,12 @@
        display: flex;
        align-items: center;
        margin-right: 42px;
        img {
          width: 15px;
          height: 15px;
        }
        .todo-date {
          font-weight: 400;
          font-size: 14px;
@@ -392,6 +405,7 @@
      }
    }
  }
  .custom-text {
    font-size: 14px;
    color: #7c8091;
src/views/wel/components/calendarBox.vue
@@ -5,13 +5,8 @@
        <div :class="data.isSelected ? 'is-selected' : ''">
          <div class="date-number">{{ data.day.slice(8, 10) }}</div>
          <div class="events">
            <div
              v-for="(event, index) in getEvents(data.day)"
              :key="index"
              class="event-item"
              :class="event.type"
              @click="jumpcalendar(event, data.day)"
            >
            <div v-for="(event, index) in getEvents(data.day)" :key="index" class="event-item" :class="event.type"
              @click="jumpcalendar(event, data.day)">
              <img :src="getEventIcon(event.type)" alt="" />
              {{ event.name }}<span>{{ event.value }}</span>
            </div>
@@ -23,65 +18,65 @@
</template>
<script setup>
import dayjs from 'dayjs';
import { jobEventBar, getCalen } from '@/api/home/index';
import { useRouter } from 'vue-router';
import ev1 from '@/assets/images/workbench/ev1.svg';
import ev2 from '@/assets/images/workbench/ev2.svg';
import { ElMessage } from 'element-plus';
const router = useRouter();
const events = ref({});
import dayjs from 'dayjs'
import { jobEventBar, getCalen } from '@/api/home/index'
import { useRouter } from 'vue-router'
import ev1 from '@/assets/images/workbench/ev1.svg'
import ev2 from '@/assets/images/workbench/ev2.svg'
import { ElMessage } from 'element-plus'
const router = useRouter()
const events = ref({})
const params = ref({
  end_date: undefined,
  start_date: undefined,
});
})
const eventIcons = ref({
  'work-order': ev1,
  task: ev2,
});
})
const getEventIcon = type => {
  return eventIcons.value[type] || eventIcons.value.default;
};
function getCurrentMonthRange() {
  return eventIcons.value[type] || eventIcons.value.default
}
function getCurrentMonthRange () {
  return {
    start_date: dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'),
    end_date: dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss'),
  };
  }
}
const leftValue = ref(new Date());
const leftValue = ref(new Date())
watch(
  () => leftValue.value,
  (newV, oldV) => {
    if (newV && oldV) {
      const newDate = dayjs(newV);
      const newDate = dayjs(newV)
      params.value = {
        start_date: newDate.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
        end_date: newDate.endOf('month').format('YYYY-MM-DD HH:mm:ss'),
      };
      getJobEventBar();
      }
      getJobEventBar()
    }
  },
  { deep: true, immediate: true }
);
)
// 获取日期数字
const getDate = date => {
  return date.getDate();
};
  return date.getDate()
}
// 获取对应日期的事件
const getEvents = dateString => {
  return events.value[dateString] || [];
};
const monthRange = getCurrentMonthRange();
params.value = monthRange;
  return events.value[dateString] || []
}
const monthRange = getCurrentMonthRange()
params.value = monthRange
const getJobEventBar = () => {
  getCalen(params.value).then(res => {
    if (res.data.code !== 0) return;
    events.value = res.data.data;
  });
};
    if (res.data.code !== 0) return
    events.value = res.data.data
  })
}
const jumpcalendar = (event, day) => {
  if (event.name === '工单') {
    router.push({
@@ -89,39 +84,64 @@
      query: {
        day: day,
      },
    });
    })
  } else {
    router.push({
      path: '/job/jobstatistics',
      query: {
        day: day,
      },
    });
    })
  }
};
}
onMounted(() => {
  getJobEventBar();
});
  getJobEventBar()
})
</script>
<style lang="scss">
.calenBox {
 height: 630px;
  .el-calendar {
    height: 100%; // 日历填充容器
  //  height: 630px;
  height: pxToVh(660);
  // 隐藏按钮组中间按钮
  .el-button-group>.el-button:not(:first-child):not(:last-child) {
    display: none;
  }
}
</style>
<style lang="scss" scoped>
.calenBox {
  margin-top: 10px;
  // height: 630px;
  height: pxToVh(622);
  border-radius: 10px;
  overflow: hidden;
  background-color: #fff;
  ::v-deep(.el-calendar) {
    // height: 80%; // 日历填充容器
    // 标题样式
    &__title {
    .el-calendar__title {
      font-weight: bold;
      font-size: 14px;
      color: #363636;
    }
    // 日历主体
    &__body {
      height: 98%; // 关键:继承父高度
      .el-calendar-table {
        height: 90% !important; // 百分比生效
      }
    // &__body {
    //   height: 98%; // 关键:继承父高度
    //   .el-calendar-table {
    //     height: 90% !important; // 百分比生效
    //   }
    // }
    .el-calendar-table .el-calendar-day {
      height: 78px !important;
    }
    // 选中日期样式
@@ -129,7 +149,7 @@
      background-color: #f0f7ff;
      border: 2px solid #409eff;
      border-radius: 4px;
      .date-number {
        font-weight: bold;
        color: #409eff;
@@ -137,30 +157,18 @@
    }
  }
  // 隐藏按钮组中间按钮
  .el-button-group > .el-button:not(:first-child):not(:last-child) {
    display: none;
  }
}
</style>
<style lang="scss" scoped>
.calenBox {
  margin-top: 17px;
  height: 630px;
  overflow: hidden;
  .event-item {
    font-size: 12px;
    // padding: 2px;
    border-radius: 3px;
    white-space: nowrap;
    // overflow: hidden;
    // text-overflow: ellipsis;
    &.work-order {
      font-weight: 400;
      font-size: 12px;
      color: #7b7b7b;
      span {
        font-weight: 600;
        font-size: 14px;
@@ -173,6 +181,7 @@
      font-weight: 400;
      font-size: 12px;
      color: #7b7b7b;
      span {
        font-weight: 600;
        font-size: 14px;
src/views/wel/components/flyratio.vue
@@ -230,7 +230,8 @@
  }
  .nestCenter {
    width: 100%;
    height: 600px;
    // height: 600px;
    height: pxToVh(600);
    .chart {
      width: 100%;
      height: 100%;
src/views/wel/components/statistics.vue
@@ -295,7 +295,8 @@
<style scoped lang="scss">
.statistics {
  min-height: 174px;
  // height: 174px;
   height: pxToVh(174);
  background: #ffffff;
  border-radius: 8px 8px 8px 8px;
  margin-bottom: 10px;
@@ -332,15 +333,15 @@
    padding: 0 14px 0 21px;
    // display: grid;
    // grid-template-columns: repeat(4, 1fr);
    height: 80px;
   height: pxToVh(80);
    display: flex;
    justify-content: space-around;
    :deep(.el-empty ){
      width: 60px;
      height: 60px;
      height: pxToVh(60);
      .el-empty__image{
      width: 60px !important;
      height: 60px !important;}
       height: pxToVh(60)!important;}
      .custom-text {
        font-size: 12px;
        color: #7c8091;
src/views/wel/index.vue
@@ -1,214 +1,208 @@
<template>
  <div class="workbench">
    <div class="workleft">
      <!-- 设备统计  -->
      <statistics></statistics>
      <!-- 综合统计分析 -->
      <div class="comprehensiveCon">
        <div class="comprehensive">
          <div class="title">
            <div class="name">
              <span> 综合统计分析</span>
              <img @click="refresh" src="/src/assets/images/workbench/st1.png" alt="" />
              <div class="time-card">
                <div
                  class="card-item"
                  :class="item === checked ? 'active' : ''"
                  v-for="(item, index) in timeListEnum"
                  :key="index"
                  @click="timeClick(item, index)"
                >
                  {{ timeListStr[index] }}
  <wel-container>
    <div class="workbench">
      <div class="workleft">
        <!-- 设备统计  -->
        <statistics></statistics>
        <!-- 综合统计分析 -->
        <div class="comprehensiveCon">
          <div class="comprehensive">
            <div class="title">
              <div class="name">
                <span> 综合统计分析</span>
                <img @click="refresh" src="/src/assets/images/workbench/st1.png" alt="" />
                <div class="time-card">
                  <div class="card-item" :class="item === checked ? 'active' : ''" v-for="(item, index) in timeListEnum"
                    :key="index" @click="timeClick(item, index)">
                    {{ timeListStr[index] }}
                  </div>
                </div>
              </div>
              <div class="arrow" @click="jumpshebei">
                <img src="/src/assets/images/workbench/st2.png" alt="" />
              </div>
            </div>
            <div class="arrow" @click="jumpshebei">
              <img src="/src/assets/images/workbench/st2.png" alt="" />
            </div>
          </div>
          <div class="center">
            <div class="centerLeft">
              <!-- 工单统计 -->
              <div class="workOrder">
                <div class="card-group">
                  <div class="main-card">
                    <div class="card-title">
                      <img :src="overviewImg1" alt="" />
                      <div class="cardtotal">
                        <p>工单统计占比</p>
                      </div>
                    </div>
                    <div class="status-grid">
                      <div class="status-item" v-for="(item, index) in eventTypeList" :key="index">
                        <div class="statusCon">
                          <div class="status-label">{{ item.name }}</div>
                          <div class="status-value">{{ item.value }}<span>个</span></div>
                          <div class="ratio">
                            占比
                            <span :style="{ color: item.color }"
                              >{{ ((item.rate * 100) / 100).toFixed(2) }}%</span
                            >
                          </div>
            <div class="center">
              <div class="centerLeft">
                <!-- 工单统计 -->
                <div class="workOrder">
                  <div class="card-group">
                    <div class="main-card">
                      <div class="card-title">
                        <img :src="overviewImg1" alt="" />
                        <div class="cardtotal">
                          <p>工单统计占比</p>
                        </div>
                        <img :src="item.img" alt="" />
                      </div>
                      <div class="status-grid">
                        <div class="status-item" v-for="(item, index) in eventTypeList" :key="index">
                          <div class="statusCon">
                            <div class="status-label">{{ item.name }}</div>
                            <div class="status-value">{{ item.value }}<span>个</span></div>
                            <div class="ratio">
                              占比
                              <span :style="{ color: item.color }">{{ ((item.rate * 100) / 100).toFixed(2) }}%</span>
                            </div>
                          </div>
                          <img :src="item.img" alt="" />
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="charts">
                    <div class="chart" ref="echartsRef"></div>
                  </div>
                </div>
                <div class="charts">
                  <div class="chart" ref="echartsRef"></div>
                </div>
              </div>
              <!-- 飞行统计 -->
              <div class="flyOrder">
                <div class="fytitle">
                   <img :src="fy1" alt="" />
                  <span>飞行统计</span>
                </div>
                <div class="flycenter">
                  <div class="centerBox">
                    <div class="centerItem" v-for="(itemfly, index) in flyTypeList" :key="index">
                      <div class="flyimg"><img :src="itemfly.img" alt="" />{{ itemfly.name }}</div>
                      <div class="flydata">
                        <span>{{ itemfly.value }}</span
                        >{{ unitMap[itemfly.name] }}
                <!-- 飞行统计 -->
                <div class="flyOrder">
                  <div class="fytitle">
                    <img :src="fy1" alt="" />
                    <span>飞行统计</span>
                  </div>
                  <div class="flycenter">
                    <div class="centerBox">
                      <div class="centerItem" v-for="(itemfly, index) in flyTypeList" :key="index">
                        <div class="flyimg"><img :src="itemfly.img" alt="" />{{ itemfly.name }}</div>
                        <div class="flydata">
                          <span>{{ itemfly.value }}</span>{{ unitMap[itemfly.name] }}
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="lineChart">
                    <div class="lineBox" ref="chartRef"></div>
                    <div class="lineChart">
                      <div class="lineBox" ref="chartRef"></div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div class="centerRight">
              <flyratio :dateSelect="dateSelect"></flyratio>
              <div class="centerRight">
                <flyratio :dateSelect="dateSelect"></flyratio>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="workright">
      <Bocklog></Bocklog>
      <CalenBox></CalenBox>
      <div class="workright">
        <Bocklog></Bocklog>
        <CalenBox></CalenBox>
      </div>
    </div>
  </div>
  </wel-container>
</template>
<script setup>
import flyratio from './components/flyratio.vue';
import Bocklog from './components/backlog.vue';
import CalenBox from './components/calendarBox.vue';
import * as echarts from 'echarts';
import useEchartsResize from '@/hooks/useEchartsResize';
import { mapGetters } from 'vuex';
import { getJobEventByStatus, getJobEventTotal, getFly, getFlyTime } from '@/api/home/index';
import overviewImg1 from '@/assets/images/workbench/tc1.png';
import overviewImg2 from '@/assets/images/workbench/tc2.svg';
import overviewImg3 from '@/assets/images/workbench/tc3.svg';
import overviewImg4 from '@/assets/images/workbench/tc4.svg';
import overviewImg5 from '@/assets/images/workbench/tc5.svg';
import fy1 from '@/assets/images/workbench/fy1.png';
import flyImg1 from '@/assets/images/workbench/fy2.svg';
import flyImg2 from '@/assets/images/workbench/fy3.svg';
import flyImg3 from '@/assets/images/workbench/fy4.svg';
import statistics from './components/statistics.vue';
import { ElMessage } from 'element-plus';
let checked = ref('CURRENT_YEAR');
let timeListStr = ['本周', '本月', '本年'];
let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR'];
import flyratio from './components/flyratio.vue'
import Bocklog from './components/backlog.vue'
import CalenBox from './components/calendarBox.vue'
import * as echarts from 'echarts'
import useEchartsResize from '@/hooks/useEchartsResize'
import { mapGetters } from 'vuex'
import { getJobEventByStatus, getJobEventTotal, getFly, getFlyTime } from '@/api/home/index'
import overviewImg1 from '@/assets/images/workbench/tc1.png'
import overviewImg2 from '@/assets/images/workbench/tc2.svg'
import overviewImg3 from '@/assets/images/workbench/tc3.svg'
import overviewImg4 from '@/assets/images/workbench/tc4.svg'
import overviewImg5 from '@/assets/images/workbench/tc5.svg'
import fy1 from '@/assets/images/workbench/fy1.png'
import flyImg1 from '@/assets/images/workbench/fy2.svg'
import flyImg2 from '@/assets/images/workbench/fy3.svg'
import flyImg3 from '@/assets/images/workbench/fy4.svg'
import statistics from './components/statistics.vue'
import { ElMessage } from 'element-plus'
let checked = ref('CURRENT_YEAR')
let timeListStr = ['本周', '本月', '本年']
let timeListEnum = ['CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR']
const params = ref({
  date_enum: 'CURRENT_YEAR',
  device_sn: '',
  end_date: undefined,
  start_date: undefined,
});
const dateSelect = ref('CURRENT_YEAR');
})
const dateSelect = ref('CURRENT_YEAR')
let timeClick = (item, index) => {
  checked.value = item;
  params.value.date_enum = item;
  dateSelect.value = item;
  getTypeData();
  getFlyList();
  getFlyTimeList();
};
  checked.value = item
  params.value.date_enum = item
  dateSelect.value = item
  getTypeData()
  getFlyList()
  getFlyTimeList()
}
const refresh = () => {
  params.value.date_enum = 'CURRENT_YEAR';
  checked.value = 'CURRENT_YEAR';
  dateSelect.value = 'CURRENT_YEAR';
  getTypeData();
  getFlyList();
  getFlyTimeList();
};
  params.value.date_enum = 'CURRENT_YEAR'
  checked.value = 'CURRENT_YEAR'
  dateSelect.value = 'CURRENT_YEAR'
  getTypeData()
  getFlyList()
  getFlyTimeList()
}
// 跳转
const jumpshebei = () => {
  ElMessage.warning('加急开发中...');
};
  ElMessage.warning('加急开发中...')
}
const eventTypeList = ref([
  { name: '待审核', value: 0, img: overviewImg2, color: '#FF472F', status: '2', rate: 0 },
  { name: '待处理', value: 0, img: overviewImg3, color: '#FF7411', status: '0', rate: 0 },
  { name: '处理中', value: 0, img: overviewImg4, color: '#FFC300', status: '3', rate: 0 },
  { name: '已完成', value: 0, img: overviewImg5, color: '#0291A1', status: '4', rate: 0 },
]);
])
const keyMapping = {
  飞行时长: 'total_flight_time',
  飞行里程: 'total_flight_distance',
  任务成果: 'event_num',
};
}
const unitMap = {
  飞行时长: '时',
  飞行里程: '千米',
  任务成果: '个',
};
}
const flyTypeList = ref([
  { name: '飞行时长', value: 0, img: flyImg1 },
  { name: '飞行里程', value: 0, img: flyImg2 },
  { name: '任务成果', value: 0, img: flyImg3 },
]);
])
const eventTotal = ref(0);
const data = ref([]);
const eventTotal = ref(0)
const data = ref([])
//  工单统计
const getTypeData = () => {
  getJobEventByStatus(params.value).then(res => {
    const resList = res?.data?.data || [];
    const resList = res?.data?.data || []
    resList.forEach(item => {
      eventTypeList.value.forEach(item1 => {
        if (item1.name === item.name) {
          item1.value = item.num;
          item1.rate = item.rate;
          item1.value = item.num
          item1.rate = item.rate
        }
      });
    });
    initChart(resList);
  });
};
      })
    })
    initChart(resList)
  })
}
// 飞行统计
const getFlyList = () => {
  getFly(params.value).then(res => {
    flyTypeList.value = flyTypeList.value.map(item => ({
      ...item,
      value: res.data.data[keyMapping[item.name]] || 0,
    }));
  });
};
    }))
  })
}
const getFlyTimeList = () => {
  getFlyTime(params.value).then(res => {
    const resList = res?.data?.data || [];
    lineCharts(resList);
  });
};
    const resList = res?.data?.data || []
    lineCharts(resList)
  })
}
// 图表
const echartsRef = ref(null);
let { chart } = useEchartsResize(echartsRef);
const chartRef = ref(null);
let { chart: lineChart } = useEchartsResize(chartRef);
const echartsRef = ref(null)
let { chart } = useEchartsResize(echartsRef)
const chartRef = ref(null)
let { chart: lineChart } = useEchartsResize(chartRef)
const initChart = val => {
  const totalNum = val.reduce((sum, item) => sum + item.num, 0);
  const totalNum = val.reduce((sum, item) => sum + item.num, 0)
  const data = {
    total: {
      title: '总计',
@@ -219,7 +213,7 @@
      name: item.name,
      rate: item.rate,
    })),
  };
  }
  const containerWidth = chart.value.clientWidth
  const isSmallScreen = containerWidth < 768 // 移动端判断
  const echartsOption = {
@@ -238,7 +232,7 @@
          font-size: 1.2rem;" class="tooltip-area">
          <p>${params.marker}${params.name}</p>
          <h4 style="margin-left: 1rem;">${params.data.rate || params.percent}%</h4>
        </div>`;
        </div>`
      },
    },
    legend: {
@@ -271,22 +265,22 @@
        label: {
          formatter: params => {
            // 使用 b 样式标记包裹百分比数值
            return `{a|${params.name}}: {b|${params.data.rate}%}`;
            return `{a|${params.name}}: {b|${params.data.rate}%}`
          },
        fontSize: isSmallScreen ? '0.8rem' : '1.2rem',
         position: isSmallScreen ? 'outer' : 'outer',
          fontSize: isSmallScreen ? '0.8rem' : '1.2rem',
          position: isSmallScreen ? 'outer' : 'outer',
          alignTo: 'labelLine',
          overflow: 'truncate',
           overflow: 'none', // 禁用省略号
          overflow: 'none', // 禁用省略号
          bleedMargin: 30, // 防止标签被截断
          rich: {
            a: {
              color: 'rgba(28, 31, 35, 0.80)',
              fontSize: isSmallScreen ? '1rem' : '1.2rem',
              fontSize: isSmallScreen ? '1rem' : '1.2rem',
            },
            b: {
              color: '#1C1F23',
               fontSize: isSmallScreen ? '1rem' : '1.3rem',
              color: '#1C1F23',
              fontSize: isSmallScreen ? '1rem' : '1.3rem',
              fontWeight: 'bold',
            },
          },
@@ -303,47 +297,47 @@
        data: data.data,
      },
    ],
  };
  }
  chart.value.setOption(echartsOption);
};
  chart.value.setOption(echartsOption)
}
// 折线/柱状图
const lineCharts = bardata => {
  const categories = bardata?.map(item => item.name); // x轴类别
  const flight_distance = [];
  const flight_time = [];
  const event_num = [];
  const categories = bardata?.map(item => item.name) // x轴类别
  const flight_distance = []
  const flight_time = []
  const event_num = []
  // /遍历数据填充各系列
  bardata?.forEach(period => {
    let hasDuration = false,
      hasDistance = false,
      hasResult = false;
      hasResult = false
    period.data?.forEach(item => {
      switch (item.name) {
        case '飞行时长':
          flight_time.push(parseFloat(item.value) || 0);
          hasDuration = true;
          break;
          flight_time.push(parseFloat(item.value) || 0)
          hasDuration = true
          break
        case '飞行里程':
          // 转换为万公 (假设原始单位是米)
          // flight_distance.push((parseFloat(item.value) / 10 || 0));
          flight_distance.push(parseFloat(item.value) || 0);
          hasDistance = true;
          break;
          flight_distance.push(parseFloat(item.value) || 0)
          hasDistance = true
          break
        case '任务成果':
          event_num.push(Number(item.value) || 0);
          hasResult = true;
          break;
          event_num.push(Number(item.value) || 0)
          hasResult = true
          break
      }
    });
    })
    // 处理可能缺失的数据项
    if (!hasDuration) flight_time.push(0);
    if (!hasDistance) flight_distance.push(0);
    if (!hasResult) event_num.push(0);
  });
    if (!hasDuration) flight_time.push(0)
    if (!hasDistance) flight_distance.push(0)
    if (!hasResult) event_num.push(0)
  })
  var option = {
    tooltip: {
      trigger: 'item',
@@ -366,7 +360,7 @@
      itemWidth: 15,
      itemHeight: 10,
      itemGap: 25,
       fontSize:'1.2rem'
      fontSize: '1.2rem'
    },
    xAxis: {
      type: 'category',
@@ -378,7 +372,7 @@
      },
      axisLabel: {
        interval: 0,
        color: '#383838',
        color: '#383838',
      },
    },
    yAxis: [
@@ -387,7 +381,7 @@
        name: '单位:万套',
        nameTextStyle: {
          color: '#383838',
          fontSize:'1.2rem'
          fontSize: '1.2rem'
        },
        min: 1,
        logBase: 3,
@@ -489,19 +483,19 @@
        data: event_num,
      },
    ],
  };
  lineChart.value.setOption(option);
};
  }
  lineChart.value.setOption(option)
}
onMounted(() => {
  getJobEventTotal().then(res => {
    eventTotal.value = res?.data?.data || 0;
  });
    eventTotal.value = res?.data?.data || 0
  })
  getTypeData();
  lineCharts();
  getFlyList();
  getFlyTimeList();
});
  getTypeData()
  lineCharts()
  getFlyList()
  getFlyTimeList()
})
</script>
<style>
@@ -511,24 +505,32 @@
</style>
<style scoped lang="scss">
.workbench {
  padding: 0px 20px 0 10px;
  height: 100%;
  flex: 1;
  // padding: 0px 20px 0 10px;
  display: flex;
  justify-content: space-between;
height: calc(100% - 5rem);
}
.workleft {
  width: 68%;
  margin-right: 10px;
  height: 100%;
  .comprehensiveCon {
    // height: 736px;
    background: #ffffff;
    background: #ffffff !important;
    height: pxToVh(776);
    border-radius: 8px 8px 8px 8px;
    .comprehensive {
      padding: 17px 14px 0 21px;
      padding: 14px 14px 0 21px;
      .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        .name {
          display: flex;
          align-items: center;
@@ -536,19 +538,24 @@
          font-size: 16px;
          color: #363636;
          font-family: 'Source Han Sans CN';
          span {
            margin-right: 4px;
          }
          img {
            cursor: pointer;
          }
        }
        .arrow {
          cursor: pointer;
        }
        .time-card {
          text-align: center;
          height: 30px;
          // height: 30px;
          height: pxToVh(30);
          background: #ffffff;
          border-radius: 4px 0px 0px 4px;
          border: 1px solid #e5e5e5;
@@ -558,64 +565,78 @@
          display: flex;
          width: 282px;
          margin-left: 15px;
          .card-item {
            width: 94px;
            height: 100%;
            line-height: 32px;
            line-height: 28px;
            cursor: pointer;
            font-family: 'Source Han Sans CN';
            font-weight: 400;
            font-size: 14px;
            color: #7c8091;
          }
          .card-item:first-child {
            border-right: 1px solid #e5e5e5;
          }
          .card-item:nth-child(2) {
            border-right: 1px solid #e5e5e5;
          }
          .card-item.active {
            color: #1441ff;
            border: 1px solid #1c5cff;
          }
        }
      }
      // 工、单
      .center {
        display: flex;
        .centerLeft {
          width: 60%;
          border-right: 1px solid #dfdfdf;
          .workOrder {
            margin-top: 31px;
            margin-top: 11px;
            display: flex;
            justify-content: space-between;
            border-bottom: 1px solid #dfdfdf;
            .card-group {
              width: 40%;
              .card-title {
                display: flex;
                margin-bottom: 17px;
                margin-bottom: 10px;
                align-items: center;
                img {
                  width: 36px;
                  height: 40px;
                }
              }
              .cardtotal {
                display: flex;
                align-items: center;
                margin-left: 9px;
                p {
                  font-weight: bold;
                  font-size: 14px;
                  color: #363636;
                }
                span {
                  font-weight: 400;
                  font-size: 14px;
                  color: #7c8091;
                }
                .total-number {
                  font-family: 'Source Han Sans CN';
@@ -624,38 +645,44 @@
                  color: #2a54ff;
                }
              }
              .status-grid {
                display: grid;
                grid-template-columns: repeat(2, 1fr);
                row-gap: 14px;
                gap: 10px;
                margin-bottom: 27px;
                padding-bottom: 5px;
                .status-item {
                  display: flex;
                  text-align: center;
                  justify-content: space-between;
                  height: 97px;
                  // height: pxToVh(107);
                  max-width: 158px;
                  background: #f6f8fe;
                  border-radius: 8px 8px 8px 8px;
                  margin-bottom: 9px;
                  img {
                    width: 26px;
                    height: 26px;
                    padding: 9px 10px 9px 2px;
                  }
                  .statusCon {
                    display: flex;
                    flex-direction: column;
                    // align-items: center;
                    padding: 5px 4px 9px 10px;
                    text-align: left;
                    .status-label {
                      font-weight: 400;
                      font-size: 14px;
                      text-align: left;
                      color: #383838;
                    }
                    .ratio {
                      font-weight: 400;
                      font-size: 12px;
@@ -663,6 +690,7 @@
                      white-space: nowrap;
                      text-align: left;
                    }
                    .status-value {
                      font-family: 'Source Han Sans CN';
                      font-weight: bold;
@@ -683,10 +711,12 @@
                }
              }
            }
            .charts {
              margin-top: 30px;
              width: 348px;
              height: 242px;
              width: 100%;
              height: auto;
              .chart {
                width: 100%;
                height: 100%;
@@ -696,41 +726,50 @@
          // 飞行统计
          .flyOrder {
            margin-top: 10px;
            margin-top: 5px;
            .fytitle {
              display: flex;
              align-items: center;
              img {
                width: 39px;
                height: 38px;
              }
              span {
                font-weight: bold;
                font-size: 14px;
                color: #363636;
              }
            }
            .flycenter {
              margin-top: 22px;
              margin-top: 13px;
              .centerBox {
                display: flex;
                justify-content: space-between;
                margin-bottom: 10px;
                .centerItem {
                  padding: 7px 7px 0 15px;
                  // width: 196px;
                  flex: 1;
                  height: 80px;
                  // height: 80px;
                  height: pxToVh(80);
                  background: #f6f8fe;
                  box-shadow: 0px 5px 4px 0px #ebf1ff;
                  border-radius: 8px 8px 8px 8px;
                  border: 1px solid #ffffff;
                  margin-right: 16px;
                  img {
                    width: 15px;
                    height: 15px;
                    margin-right: 5px;
                  }
                  .flyimg {
                    display: flex;
                    align-items: center;
@@ -738,11 +777,13 @@
                    font-size: 14px;
                    color: #7c8091;
                  }
                  .flydata {
                    margin-top: 15px;
                    margin-top: 10px;
                    text-align: right;
                    font-size: 14px;
                    color: #7c8091;
                    span {
                      font-weight: bold;
                      font-size: 24px;
@@ -756,6 +797,7 @@
              .lineChart {
                height: 209px;
                width: 100%;
                .lineBox {
                  width: 100%;
                  height: 100%;
@@ -764,6 +806,7 @@
            }
          }
        }
        .centerRight {
          width: 40%;
        }
@@ -773,6 +816,10 @@
}
.workright {
  width: 32%;
  width: 0;
  flex: 1;
  height: 100%;
  // display: flex;
  // flex-direction: column;
}
</style>
vite.config.mjs
@@ -56,6 +56,12 @@
      },
    },
    css: {
      preprocessorOptions: {
        scss: {
          api: 'modern-compiler',
          additionalData: `@use "@/styles/variables.scss" as *;`,
        },
      },
      postcss: {
        plugins: [
          postCssPxToRem({