Jay
2024-11-22 328968f75a4dd4292ebc71f01d759a824765ac72
Merge remote-tracking branch 'origin/master'

# Conflicts:
# src/views/data/point/DaPointChart.vue
已修改32个文件
已删除5个文件
已重命名2个文件
已添加5个文件
3524 ■■■■■ 文件已修改
.env.dev 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.local 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.test 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/data/plan/item/index.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/data/video/image/index.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/mcs/index.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/dm/index.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/item/index.ts 145 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/predict/index.ts 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/result/index.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/type/index.ts 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/sche/model/index.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/remaining.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dict.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/hostMap.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/is.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Home/Index.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Login/components/LoginForm.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/model/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/simpleWorkflow/index.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/channel/http/api/tag/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/channel/kio/tag/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/channel/modbus/tag/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/channel/opcda/tag/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/channel/opcua/tag/index.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/point/DaPointChart.vue 213 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/video/camera/CameraImage.vue 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/video/camera/index.vue 197 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/video/nvr/NvrCamera.vue 174 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/video/nvr/index.vue 187 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/wiki/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/mpk/file/MpkForm.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/mpk/file/MpkRun.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/mpk/icon/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/analysis/index.vue 1100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/item/MmPredictItemChart.vue 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/item/MmPredictItemForm.vue 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/item/index.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/result/MmResultTableForm.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/result/index.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/type/ItemTypeForm.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/type/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/wiki/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/env.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.dev
@@ -24,13 +24,19 @@
VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
VITE_BASE_PATH=/plat
# 输出路径
VITE_OUT_DIR=dist
# 公共静态文件路径
VITE_STATIC_DIR=/
# 商城H5会员端域名iai
VITE_MALL_H5_DOMAIN='http://'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
VITE_APP_CAPTCHA_ENABLE=false
# MDK模型上传路径
MDK_UPLOAD_URL='http://localhost:48080/admin-api/model//pre/item/upload-model'
.env.local
@@ -24,7 +24,10 @@
VITE_SOURCEMAP=false
# 打包路径
VITE_BASE_PATH=/
VITE_BASE_PATH=/plat
# 公共静态文件路径
VITE_STATIC_DIR=/
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://localhost:3000'
.env.test
@@ -26,9 +26,15 @@
# 打包路径
VITE_BASE_PATH=/plat
# 数据采集服务所在服务器,映射截图图片用
VITE_VIDEO_CAMERA_DOMAIN='172.16.59.105'
# 输出路径
VITE_OUT_DIR=dist
# 公共静态文件路径
VITE_STATIC_DIR=/plat/
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://'
src/api/data/plan/item/index.ts
@@ -50,7 +50,7 @@
//获取下拉集合
export const getItemList = (params: PageParam) => {
  return request.get({ url: '/data/plan-item/getList', params})
  return request.get({ url: '/data/plan-item/list', params})
}
// 查询Plan图表
src/api/data/video/image/index.ts
对比新文件
@@ -0,0 +1,50 @@
import request from '@/config/axios'
export interface ImageVO {
  id: undefined,
  cameraId: string,
  imagePath: string,
  imageUrl: string,
  createDate: undefined,
}
// 查询列表
export const getImagePage = (params: PageParam) => {
  return request.get({url: '/data/video/image/page', params})
}
// 获得
export const getImage = (id: number) => {
  return request.get({url: '/data/video/image/get?id=' + id})
}
// 查询应用列表
export const getImageList = () => {
  return request.get({url: '/data/video/image/list'})
}
// 新增
export const createImage = (data: ImageVO) => {
  return request.post({url: '/data/video/image/create', data})
}
// 修改
export const updateImage = (data: ImageVO) => {
  return request.put({url: '/data/video/image/update', data})
}
// 删除
export const deleteImage = (id: number) => {
  return request.delete({url: '/data/video/image/delete?id=' + id})
}
// 导出
export const exportImage = (params: ImageVO) => {
  return request.download({url: '/data/video/image/export-excel', params})
}
//预览摄像头截图
export const getPreviewUrl = (url: string) => {
  const host = import.meta.env.VITE_VIDEO_CAMERA_DOMAIN ? import.meta.env.VITE_VIDEO_CAMERA_DOMAIN : window.location.host
  return `http://${host}:8899` + url
}
src/api/model/mcs/index.ts
对比新文件
@@ -0,0 +1,30 @@
import request from '@/config/axios'
export interface PreDataBarLineReqVO {
  outIds: string[],
  predictTime: string,
  startTime: string,
  endTime: string
}
export interface PreDataItemChartReqVO {
  itemId: string,
  startTime: string,
  endTime: string
}
export const getPredictItemTree = () => {
  return request.get({ url: '/model/api/mcs/predict-item/tree'})
}
export const getPreDataCharts = (data: PreDataBarLineReqVO) => {
  return request.post({ url: '/model/api/mcs/predict-data/charts', data })
}
export const getPreDataItemChart = (data: PreDataItemChartReqVO) => {
  return request.post({ url: '/model/api/mcs/predict-data/item-chart', data })
}
export const exportPredictValue = (params) => {
  return request.download({ url: '/model/api/mcs/predict-data/exportValue', params })
}
src/api/model/pre/dm/index.ts
@@ -1,7 +1,7 @@
import request from '@/config/axios'
export interface DmModuleVO {
  id: string
  id: string,
  modulename: string,
  moduletype: string,
  cycle: string
src/api/model/pre/item/index.ts
@@ -1,43 +1,140 @@
import request from '@/config/axios'
import {UploadRequestOptions} from "element-plus/es/components/upload/src/upload";
export interface MmItemTypeVO {
  id: string
export interface MmPredictItemVO {
  id: string,
  itemtypename: string,
  itemclasstype: string,
  assemblyname: string
  mmPredictItem: {
    id: string,
    itemno: string,
    itemname: string,
    caltypeid: string,
    itemtypeid: string,
    predictlength: string,
    granularity: number,
    status: string,
    isfuse: number,
    predictphase: string,
    workchecked: number,
    unittransfactor: string,
    saveindex: string
  },
  dmModuleItem: {
    id: string,
    moduleid: string,
    itemid: string,
    itemorder: number,
    status: number,
    categoryid: string
  },
  mmItemOutput: {
    id: string,
    itemid: string,
    pointid: string,
    resulttableid: string,
    tagname: string,
    outputorder: number
  },
  mmPredictModel: {
    id: string,
    modelno: string,
    modelname: string,
    itemid: string,
    arithid: string,
    trainsamplength: number,
    predictsamplength: string,
    isonlinetrain: string,
    modelpath: string,
    isnormal: string,
    normalmax: string,
    normalmin: string,
    status: number,
    classname: string,
    methodname: string,
    modelparamstructure: string,
    resultstrid: string,
    settingmap: string
  },
  mmPredictMergeItem: {
    id: string,
    itemid: string,
    expression: string,
    num: string
  },
  modelparamtypeList: [],
  mmModelArithSettingsList: [],
  mmModelParamList: []
}
export interface MmItemTypePageReqVO extends PageParam {
  itemtypename?: string
export interface MmPredictItemPageReqVO extends PageParam {
  itemno?: string,
  itemname?: string,
}
// 查询MmItemType列表
export const getMmItemTypePage = (params: MmItemTypePageReqVO) => {
  return request.get({ url: '/model/pre/item-type/page', params })
// 查询MmPredictItem列表
export const getMmPredictItemPage = (params: MmPredictItemPageReqVO) => {
  return request.get({ url: '/model/pre/item/page', params })
}
// 查询MmItemType详情
export const getMmItemType = (id: number) => {
  return request.get({ url: `/model/pre/item-type/get/${id}`})
// 查询MmPredictItem详情
export const getMmPredictItem = (id: number) => {
  return request.get({ url: `/model/pre/item/get/${id}`})
}
// 新增MmItemType
export const createMmItemType = (data: MmItemTypeVO) => {
  return request.post({ url: '/model/pre/item-type/create', data })
// 新增MmPredictItem
export const createMmPredictItem = (data: MmPredictItemVO) => {
  return request.post({ url: '/model/pre/item/create', data })
}
// 修改MmItemType
export const updateMmItemType = (data: MmItemTypeVO) => {
  return request.put({ url: '/model/pre/item-type/update', data })
// 修改MmPredictItem
export const updateMmPredictItem = (data: MmPredictItemVO) => {
  return request.put({ url: '/model/pre/item/update', data })
}
// 删除MmItemType
export const deleteMmItemType = (id: number) => {
  return request.delete({ url: '/model/pre/item-type/delete?id=' + id })
// 删除MmPredictItem
export const deleteMmPredictItem = (id: number) => {
  return request.delete({ url: '/model/pre/item/delete?id=' + id })
}
// 查询MmPredictItem列表
export const getMmPredictItemList = (params) => {
  return request.get({ url: `/model/pre/item/list`, params})
}
// 查询getItemTypeList详情
export const getItemTypeList = () => {
  return request.get({ url: `/model/pre/item-type/list`})
export const updateModel = (data: any) => {
  return request.upload({ url: '/model/pre/item/upload-model', data })
}
export const useUpload = () => {
  const uploadUrl = import.meta.env.VITE_BASE_URL + '/admin-api/model/pre/item/upload-model'
  const httpRequest = async (options: UploadRequestOptions) => {
    return new Promise((resolve, reject) => {
      updateModel({ file: options.file })
        .then((res) => {
          if (res.code === 0) {
            resolve(res)
          } else {
            reject(res)
          }
        })
        .catch((res) => {
          reject(res)
        })
    })
  }
  return {
    uploadUrl,
    httpRequest
  }
}
export const getMmPredictItemTree = () => {
  return request.get({ url: `/model/pre/item/tree`})
}
export const getViewCharts = (params) => {
  return request.get({ url: `/model/pre/item/view-charts`,params})
}
src/api/model/pre/predict/index.ts
文件已删除
src/api/model/pre/result/index.ts
文件已删除
src/api/model/pre/type/index.ts
对比新文件
@@ -0,0 +1,43 @@
import request from '@/config/axios'
export interface MmItemTypeVO {
  id: string
  itemtypename: string,
  itemclasstype: string,
  assemblyname: string
}
export interface MmItemTypePageReqVO extends PageParam {
  itemtypename?: string
}
// 查询MmItemType列表
export const getMmItemTypePage = (params: MmItemTypePageReqVO) => {
  return request.get({ url: '/model/pre/item-type/page', params })
}
// 查询MmItemType详情
export const getMmItemType = (id: number) => {
  return request.get({ url: `/model/pre/item-type/get/${id}`})
}
// 新增MmItemType
export const createMmItemType = (data: MmItemTypeVO) => {
  return request.post({ url: '/model/pre/item-type/create', data })
}
// 修改MmItemType
export const updateMmItemType = (data: MmItemTypeVO) => {
  return request.put({ url: '/model/pre/item-type/update', data })
}
// 删除MmItemType
export const deleteMmItemType = (id: number) => {
  return request.delete({ url: '/model/pre/item-type/delete?id=' + id })
}
// 查询getItemTypeList详情
export const getItemTypeList = () => {
  return request.get({ url: `/model/pre/item-type/list`})
}
src/api/model/sche/model/index.ts
@@ -1,7 +1,9 @@
import request from '@/config/axios'
import * as DataPointApi from '@/api/data/da/point'
import * as PredictItemApi from '@/api/model/pre/predict'
import * as PredictItemApi from '@/api/model/pre/item'
import * as PlanItemApi from '@/api/data/plan/item'
import {CommonEnabled} from "@/utils/constants";
import {getItemList, ItemVO} from "@/api/data/plan/item";
export interface ScheduleModelVO {
  id: string
@@ -100,8 +102,25 @@
      )
    })
  }
  const planItemList = ref([] as PlanItemApi.ItemVO)
  planItemList.value = await PlanItemApi.getItemList({
  })
  const planList = []
  if (planItemList.value) {
    planItemList.value.forEach(item => {
      planList.push(
        {
          id: item.id,
          name:  item.itemName
        }
      )
    })
  }
  return {
    'DATAPOINT':pointList,
    'PREDICTITEM': itemList
    'PREDICTITEM': itemList,
    'PLAN': planList,
  }
}
src/router/modules/remaining.ts
@@ -50,6 +50,15 @@
      noTagsView: true
    }
  },
  // {
  //   path: '/shasteel',
  //   component: () => import('@/views/micro/index.vue'),
  //   name: 'shasteel',
  //   meta: {
  //     hidden: true,
  //     noTagsView: true
  //   },
  // },
  {
    path: '/home2',
    component: () => import('@/views/Home/Index2.vue'),
@@ -274,18 +283,6 @@
          hidden: true,
          canTo: true,
          title: '设计流程',
          activeMenu: '/bpm/manager/model'
        }
      },
      {
        path: 'manager/simple/workflow/model/edit',
        component: () => import('@/views/bpm/simpleWorkflow/index.vue'),
        name: 'SimpleWorkflowDesignEditor',
        meta: {
          noCache: true,
          hidden: true,
          canTo: true,
          title: '仿钉钉设计流程',
          activeMenu: '/bpm/manager/model'
        }
      },
src/utils/dict.ts
@@ -162,6 +162,8 @@
  MODEL_TYPE = 'model_type',
  MODEL_METHOD_SETTING_TYPE = 'model_method_setting_type',
  MODEL_METHOD_SETTING_VALUE_TYPE = 'model_method_setting_value_type',
  PRED_GRANULARITY = 'pred_granularity',
  ITEM_RUN_STATUS = 'item_run_status',
  // ========== DATA - 数据平台模块  ==========
  DATA_FIELD_TYPE = 'data_field_type',
@@ -183,4 +185,5 @@
  NVR_ONLINE_STATUS = 'nvr_online_status',
  CAMERA_BRAND = 'camera_brand',
  CAPTURE_TYPE = 'capture_type',
  MODEL_RESULT_TYPE = 'model_result_type',
}
src/utils/hostMap.ts
@@ -1,6 +1,6 @@
const map = {
  "//localhost:7200/": "//wujie-micro.github.io/demo-vue2/",
  "//localhost:90/": "//localhost:90/",
  "//localhost:9000/": "//localhost:9000/",
  "//localhost:8000/": "//wujie-micro.github.io/demo-main-vue/",
};
src/utils/is.ts
@@ -98,8 +98,9 @@
export const isClient = !isServer
export const isUrl = (path: string): boolean => {
  // fix:修复hash路由无法跳转的问题
  const reg =
    /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
    /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
  return reg.test(path)
}
src/views/Home/Index.vue
@@ -5,9 +5,9 @@
  <el-skeleton :loading="loading" animated>
    <div id="app" v-for="(item, index) in appList" :key="`dynamics-${index}`">
      <div class="card" @click="gotoApp(item)">
        <img :src="item.icon" style="width: 100px; height: 100px" />
        <img :src="item.icon" style="width: 100px; height: 100px"/>
        <div>
          {{item.appName}}
          {{ item.appName }}
        </div>
      </div>
    </div>
@@ -19,12 +19,11 @@
import * as AppApi from '@/api/system/app'
import {Apps} from "@/views/Home/types";
import {CACHE_KEY, useCache} from "@/hooks/web/useCache";
import * as authUtil from "@/utils/auth";
defineOptions({ name: 'Home' })
defineOptions({name: 'Home'})
const { wsCache } = useCache()
const {wsCache} = useCache()
const loading = ref(true)
@@ -35,7 +34,7 @@
  appList = Object.assign(appList, data)
}
const getAppMenuList = async (id) => {
const getAppMenuList = async (id, appCode) => {
  const data = await AppApi.getAppMenuList(id)
  let userInfo = wsCache.get(CACHE_KEY.USER)
  userInfo.menus = data
@@ -55,32 +54,38 @@
// 进入应用
const gotoApp = async (item) => {
  let path = window.location.pathname
  let appName = path.split("/")[0]
  console.log(appName)
  let id = item.id
  let type = item.type
  if(type === 0) {
    getAppMenuList(id)
  let appCode = item.appCode
  if (type === 0) {
    await getAppMenuList(id, appCode)
  } else {
    const data = await AppApi.getAppMenuList(id)
    let userInfo = wsCache.get(CACHE_KEY.USER)
    userInfo.menus = data
    wsCache.set(CACHE_KEY.USER, userInfo)
    wsCache.set(CACHE_KEY.ROLE_ROUTERS, data)
    // await OAuth2Login(formData.value)
    // window.open(item.appDomain + '/login?appid=' + item.id + "&username=" + authUtil.getLoginForm().username, '_blank')
    window.open(item.appDomain + '/index', '_blank')
    // window.open('/plat/shasteel', '_blank')
    // window.location.href = '/plat/shasteel'
    // window.location.href = `/plat/shasteel?key=energy&url=http://localhost:9000&energy=/energy/demo`
  }
}
</script>
<style lang="scss" scoped>
#app{
#app {
  width: 300px;
  height: 200px;
  display: inline-block;
  background: transparent;
}
.card{
.card {
  border: thin dashed gainsboro;
  width: 150px;
  height: 120px;
src/views/Login/components/LoginForm.vue
@@ -12,7 +12,7 @@
    <el-row style="margin-right: -10px; margin-left: -10px">
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item>
          <LoginFormTitle style="width: 100%" />
          <LoginFormTitle style="width: 100%"/>
        </el-form-item>
      </el-col>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
@@ -86,27 +86,27 @@
  </el-form>
</template>
<script lang="ts" setup>
import { ElLoading } from 'element-plus'
import {ElLoading} from 'element-plus'
import LoginFormTitle from './LoginFormTitle.vue'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import type {RouteLocationNormalizedLoaded} from 'vue-router'
import { useIcon } from '@/hooks/web/useIcon'
import {useIcon} from '@/hooks/web/useIcon'
import * as authUtil from '@/utils/auth'
import { usePermissionStore } from '@/store/modules/permission'
import {usePermissionStore} from '@/store/modules/permission'
import * as LoginApi from '@/api/login'
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
import {LoginStateEnum, useFormValid, useLoginState} from './useLogin'
defineOptions({ name: 'LoginForm' })
defineOptions({name: 'LoginForm'})
const { t } = useI18n()
const iconHouse = useIcon({ icon: 'ep:house' })
const iconAvatar = useIcon({ icon: 'ep:avatar' })
const iconLock = useIcon({ icon: 'ep:lock' })
const {t} = useI18n()
const iconHouse = useIcon({icon: 'ep:house'})
const iconAvatar = useIcon({icon: 'ep:avatar'})
const iconLock = useIcon({icon: 'ep:lock'})
const formLogin = ref()
const { validForm } = useFormValid(formLogin)
const { getLoginState } = useLoginState()
const { currentRoute, push } = useRouter()
const {validForm} = useFormValid(formLogin)
const {getLoginState} = useLoginState()
const {currentRoute, push} = useRouter()
const permissionStore = usePermissionStore()
const redirect = ref<string>('')
const loginLoading = ref(false)
@@ -199,14 +199,14 @@
      authUtil.removeLoginForm()
    }
    authUtil.setToken(res)
    if (!redirect.value) {
    if (!redirect.value || redirect.value == "/") {
      redirect.value = '/index'
    }
    // 判断是否为SSO登录
    if (redirect.value.indexOf('sso') !== -1) {
      window.location.href = window.location.href.replace('/login?redirect=', '')
    } else {
      push({ path: redirect.value || permissionStore.addRouters[0].path })
      push({path: redirect.value || permissionStore.addRouters[0].path})
    }
  } finally {
    loginLoading.value = false
src/views/bpm/model/index.vue
@@ -163,14 +163,6 @@
          <el-button
            link
            type="primary"
            @click="handleSimpleDesign(scope.row.id)"
            v-hasPermi="['bpm:model:update']"
          >
            仿钉钉设计流程
          </el-button>
          <el-button
            link
            type="primary"
            @click="handleDeploy(scope.row)"
            v-hasPermi="['bpm:model:deploy']"
          >
@@ -328,15 +320,6 @@
const handleDesign = (row) => {
  push({
    name: 'BpmModelEditor',
    query: {
      modelId: row.id
    }
  })
}
const handleSimpleDesign = (row) => {
  push({
    name: 'SimpleWorkflowDesignEditor',
    query: {
      modelId: row.id
    }
src/views/bpm/simpleWorkflow/index.vue
文件已删除
src/views/data/channel/http/api/tag/index.vue
@@ -1,7 +1,7 @@
<template>
  <el-drawer
    v-model="drawer"
    size="50%"
    size="60%"
    title="Http Tag"
    :direction="direction"
    :before-close="handleClose"
@@ -107,20 +107,22 @@
          label="数据值"
          header-align="center"
          align="center"
          min-width="100"
          :formatter="(row) => {if (row.dataValue === -2.0) {return '--';}return row.dataValue;}"
        />
        <el-table-column
          prop="quality"
          prop="dataTime"
          label="数据时间"
          header-align="center"
          align="center"
          min-width="150"
        />
        <el-table-column
          prop="dataQuality"
          label="数据质量"
          header-align="center"
          align="center"
        >
          <template #default="scope">
            <el-tag v-if="scope.row.dataValue === Number(-2.0)" type="danger" size="small">bad
            </el-tag>
            <el-tag v-else size="small">good</el-tag>
          </template>
        </el-table-column>
        />
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
src/views/data/channel/kio/tag/index.vue
@@ -1,7 +1,7 @@
<template>
  <el-drawer
    v-model="drawer"
    size="50%"
    size="60%"
    title="Kio Tag"
    :direction="direction"
    :before-close="handleClose"
@@ -105,19 +105,22 @@
          label="数据值"
          header-align="center"
          align="center"
          min-width="100"
          :formatter="(row) => {if (row.dataValue === -2.0) {return '--';}return row.dataValue;}"
        />
        <el-table-column
          prop="quality"
          prop="dataTime"
          label="数据时间"
          header-align="center"
          align="center"
          min-width="150"
        />
        <el-table-column
          prop="dataQuality"
          label="数据质量"
          header-align="center"
          align="center"
        >
          <template #default="scope">
            <el-tag v-if="scope.row.dataValue === Number(-2.0)" type="danger" size="small">bad</el-tag>
            <el-tag v-else size="small">good</el-tag>
          </template>
        </el-table-column>
        />
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
src/views/data/channel/modbus/tag/index.vue
@@ -1,7 +1,7 @@
<template>
  <el-drawer
    v-model="drawer"
    size="50%"
    size="60%"
    title="ModBus Tag"
    :direction="direction"
    :before-close="handleClose"
@@ -133,19 +133,22 @@
          label="数据值"
          header-align="center"
          align="center"
          min-width="100"
          :formatter="(row) => {if (row.dataValue === -2.0) {return '--';}return row.dataValue;}"
        />
        <el-table-column
          prop="quality"
          prop="dataTime"
          label="数据时间"
          header-align="center"
          align="center"
          min-width="150"
        />
        <el-table-column
          prop="dataQuality"
          label="数据质量"
          header-align="center"
          align="center"
        >
          <template #default="scope">
            <el-tag v-if="scope.row.dataValue === Number(-2.0)" type="danger" size="small">bad</el-tag>
            <el-tag v-else size="small">good</el-tag>
          </template>
        </el-table-column>
        />
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
src/views/data/channel/opcda/tag/index.vue
@@ -1,7 +1,7 @@
<template>
  <el-drawer
    v-model="drawer"
    size="50%"
    size="60%"
    title="OpcDA Tag"
    :direction="direction"
    :before-close="handleClose"
@@ -98,19 +98,22 @@
          label="数据值"
          header-align="center"
          align="center"
          min-width="100"
          :formatter="(row) => {if (row.dataValue === -2.0) {return '--';}return row.dataValue;}"
        />
        <el-table-column
          prop="quality"
          prop="dataTime"
          label="数据时间"
          header-align="center"
          align="center"
          min-width="150"
        />
        <el-table-column
          prop="dataQuality"
          label="数据质量"
          header-align="center"
          align="center"
        >
          <template #default="scope">
            <el-tag v-if="scope.row.dataValue === Number(-2.0)" type="danger" size="small">bad</el-tag>
            <el-tag v-else size="small">good</el-tag>
          </template>
        </el-table-column>
        />
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
src/views/data/channel/opcua/tag/index.vue
@@ -1,7 +1,7 @@
<template>
  <el-drawer
    v-model="drawer"
    size="50%"
    size="60%"
    title="Opcua Tag"
    :direction="direction"
    :before-close="handleClose"
@@ -67,6 +67,12 @@
            <Icon icon="ep:download" />导出
          </el-button>
        </el-form-item>
        <el-form-item label="更新当前值" label-width="100px">
          <el-switch
            v-model="queryParams.currentValue"
            active-color="#13ce66"
            inactive-color="#ff4949"/>
        </el-form-item>
      </el-form>
    </ContentWrap>
    <!-- 列表 -->
@@ -113,19 +119,22 @@
          label="数据值"
          header-align="center"
          align="center"
          min-width="100"
          :formatter="(row) => {if (row.dataValue === -2.0) {return '--';}return row.dataValue;}"
        />
        <el-table-column
          prop="quality"
          prop="dataTime"
          label="数据时间"
          header-align="center"
          align="center"
          min-width="150"
        />
        <el-table-column
          prop="dataQuality"
          label="数据质量"
          header-align="center"
          align="center"
        >
          <template #default="scope">
            <el-tag v-if="scope.row.dataValue === Number(-2.0)" type="danger" size="small">bad</el-tag>
            <el-tag v-else size="small">good</el-tag>
          </template>
        </el-table-column>
        />
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
src/views/data/point/DaPointChart.vue
@@ -41,7 +41,8 @@
          :loading="exportLoading"
          v-hasPermi="['data:point:export']"
        >
          <Icon icon="ep:download" />导出
          <Icon icon="ep:download"/>
          导出
        </el-button>
      </el-form-item>
    </el-form>
@@ -50,39 +51,41 @@
</template>
<script lang="ts" setup>
  import {ref} from 'vue';
  import * as echarts from 'echarts';
  import * as DaPoint from '@/api/data/da/point/daPointChart'
  import {getYMDHM0} from "@/utils/dateUtil"
  import download from "@/utils/download";
  const message = useMessage() // 消息弹窗
  const visible = ref(false);
  const chartDom = ref(null);
  let myChart = null;
  const chartParams = reactive({
    codes:[],
    startDate : undefined,
    endDate: undefined,
  })
  const dataForm = ref({
    id: "",
    pointNo: "",
    pointName: "",
    pointTypeName: "",
    startTime: getYMDHM0(new Date() - 60 * 60 * 1000),
    endTime: "",
  });
  const loading = ref(true) // 列表的加载中
  /** 打开弹窗 */
  const open = async (row: object) => {
    visible.value = true
    dataForm.value.id = row.id;
    dataForm.value.pointNo = row.pointNo;
    dataForm.value.pointName = row.pointName;
    getDataList();
  }
import {ref} from 'vue';
import * as echarts from 'echarts';
import * as DaPoint from '@/api/data/da/point/daPointChart'
import {getYMDHM0} from "@/utils/dateUtil"
import download from "@/utils/download";
  defineExpose({open}) // 提供 open 方法,用于打开弹窗
const message = useMessage() // 消息弹窗
const visible = ref(false);
const chartDom = ref(null);
let myChart = null;
const chartParams = reactive({
  codes: [],
  pointNo: undefined,
  startDate: undefined,
  endDate: undefined,
})
const dataForm = ref({
  id: "",
  pointNo: "",
  pointName: "",
  pointTypeName: "",
  startTime: getYMDHM0(new Date() - 60 * 60 * 1000),
  endTime: "",
});
/** 打开弹窗 */
const open = async (row: object) => {
  visible.value = true
  dataForm.value.id = row.id;
  dataForm.value.pointNo = row.pointNo;
  dataForm.value.pointName = row.pointName;
  getDataList();
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
  async function getDataList() {
    visible.value = true;
@@ -111,84 +114,86 @@
          });
        })
        myChart = echarts.init(chartDom.value);
        const option = {
          title: {
            text: dataForm.value.pointName,
            top: 0,
            left: "1%",
            textStyle: {
              fontSize: 14,
      myChart = echarts.init(chartDom.value);
      const option = {
        title: {
          text: dataForm.value.pointName,
          top: 0,
          left: "1%",
          textStyle: {
            fontSize: 14,
          },
        },
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "line",
            lineStyle: {
              color: "#cccccc",
              width: "1",
              type: "dashed",
            },
          },
          tooltip: {
            trigger: "axis",
            axisPointer: {
              type: "line",
              lineStyle: {
                color: "#cccccc",
                width: "1",
                type: "dashed",
              },
            },
        },
        legend: {
          show: false,
          top: 10,
        },
        grid: {
          top: 30,
          left: "3%",
          right: "5%",
          bottom: 10,
          containLabel: true,
        },
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: data.categories,
        },
        yAxis: {
          type: "value",
        },
        dataZoom: [
          {
            type: "inside",
          },
          legend: {
            show: false,
            top: 10,
          },
          grid: {
            top: 30,
            left: "3%",
            right: "5%",
            bottom: 10,
            containLabel: true,
          },
          xAxis: {
            type: "category",
            boundaryGap: false,
            data: data.categories,
          },
          yAxis: {
            type: "value",
          },
          dataZoom: [
            {
              type: "inside",
            },
          ],
          series: seriesData,
        };
        myChart.setOption(option);
      } catch (error) {
        console.error(error)
      }
        ],
        series: seriesData,
      };
      myChart.setOption(option);
    } catch (error) {
      console.error(error)
    }
  }
  /** 导出按钮操作 */
  const exportLoading = ref(false)
  const handleExport = async () => {
    chartParams.codes=[dataForm.value.pointNo];
    chartParams.startDate = dataForm.value.startTime;
    chartParams.endDate = dataForm.value.endTime;
    try {
      // 导出的二次确认
      await message.exportConfirm()
      // 发起导出
      exportLoading.value = true
      const data = await DaPoint.exportDaPointValue(chartParams)
      download.excel(data, dataForm.value.pointName +'.xls')
    } catch {
    } finally {
      exportLoading.value = false
    }
}
/** 导出按钮操作 */
const exportLoading = ref(false)
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await DaPoint.exportDaPointValue({
      pointNo: dataForm.value.pointNo,
      start: dataForm.value.startTime,
      end: dataForm.value.endTime
    })
    download.excel(data, dataForm.value.pointName + '.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}
</script>
<style>
  .el-select {
    width: 100%;
  }
.el-select {
  width: 100%;
}
  .result-chart {
    height: 500px;
  }
.result-chart {
  height: 500px;
}
</style>
src/views/data/video/camera/CameraImage.vue
对比新文件
@@ -0,0 +1,114 @@
<template>
  <el-dialog v-model="dialogVisible" :title="dialogTitle" :close="handleClose"
             style="width: 50%; margin-top: 20px; overflow: auto; z-index: 1">
    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column
        label="截图时间"
        align="center"
        prop="createDate"
        :formatter="dateFormatter"
        width="200"
      />
      <el-table-column label="图片路径" align="center" prop="imagePath" width="500"
                       :show-overflow-tooltip="true"/>
      <el-table-column label="图片预览" align="center" prop="imageUrl">
        <template #default="scope">
          <el-image style="height: 50px; z-index: 1"
                    :src="getPreviewUrl(scope.row.imageUrl)"
                    :preview-src-list="getPreviewSrcList(scope.row.imageUrl)" fit="cover"
                    preview-teleported/>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" min-width="60" fixed="right">
        <template #default="scope">
          <el-button
            link
            type="danger"
            style="z-index: 1"
            @click="handleDelete(scope.row.id)"
            v-hasPermi="['video:camera:delete']"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="open"
    />
  </el-dialog>
</template>
<script lang="ts" setup>
import * as ImageApi from '@/api/data/video/image'
import {getPreviewUrl} from "@/api/data/video/image";
import {dateFormatter} from "@/utils/formatTime";
defineOptions({name: 'CameraImage'})
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
const dialogTitle = ref('截图列表')
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  id: undefined,
  brand: undefined,
  code: undefined,
  device: undefined,
  cameraId: '',
  imagePath: undefined,
  imageUrl: undefined,
  createDate: undefined,
})
const cameraId = ref('')
const dialogVisible = ref(false)
/** 查询列表 */
const open = async (camera_id: string) => {
  dialogVisible.value = true
  cameraId.value = camera_id
  queryParams.cameraId = camera_id
  loading.value = true
  try {
    const data = await ImageApi.getImagePage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
const handleClose = async () => {
  dialogVisible.value = false
}
const getPreviewSrcList = (imageUrl: string) => {
  let previewSrcList: string[] = []
  previewSrcList.push(getPreviewUrl(imageUrl))
  return previewSrcList
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await ImageApi.deleteImage(id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await open(cameraId.value)
  } catch {
  }
}
</script>
src/views/data/video/camera/index.vue
@@ -61,11 +61,11 @@
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery">
          <Icon icon="ep:search" class="mr-5px" />
          <Icon icon="ep:search" class="mr-5px"/>
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon icon="ep:refresh" class="mr-5px" />
          <Icon icon="ep:refresh" class="mr-5px"/>
          重置
        </el-button>
        <el-button
@@ -74,7 +74,7 @@
          @click="openForm('create')"
          v-hasPermi="['video:camera:save']"
        >
          <Icon icon="ep:plus" class="mr-5px" />
          <Icon icon="ep:plus" class="mr-5px"/>
          新增
        </el-button>
        <el-button
@@ -84,7 +84,7 @@
          :loading="exportLoading"
          v-hasPermi="['video:camera:export']"
        >
          <Icon icon="ep:download" class="mr-5px" />
          <Icon icon="ep:download" class="mr-5px"/>
          导出
        </el-button>
      </el-form-item>
@@ -96,20 +96,21 @@
    <el-table v-loading="loading" :data="list">
      <el-table-column label="品牌" align="center" prop="brand" width="80">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand" />
          <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand"/>
        </template>
      </el-table-column>
      <el-table-column label="设备类型" align="center" prop="device" width="200"/>
      <el-table-column label="编码" align="center" prop="code" width="200"/>
      <el-table-column label="IP" align="center" prop="ip" />
      <el-table-column label="端口" align="center" prop="port" width="100"/>
      <el-table-column label="IP" align="center" prop="ip"/>
      <el-table-column label="端口" align="center" prop="port" width="80"/>
      <el-table-column label="通道" align="center" prop="channel" width="80"/>
      <el-table-column label="用户名" align="center" prop="username" width="100"/>
      <el-table-column label="状态" prop="status" width="80">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status" />
          <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="位置" align="center" prop="location" />
      <el-table-column label="位置" align="center" prop="location"/>
      <el-table-column label="备注" align="center" prop="remark" width="150"/>
      <el-table-column label="操作" align="center" min-width="110" fixed="right">
        <template #default="scope">
@@ -129,6 +130,14 @@
          >
            删除
          </el-button>
          <el-button
            link
            type="success"
            @click="imageHandle(scope.row.id)"
            v-hasPermi="['video:image:query']"
          >
            查看截图
          </el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -142,99 +151,109 @@
  </ContentWrap>
  <!-- 表单弹窗:添加/修改 -->
  <CameraForm ref="formRef" @success="getList" />
  <CameraForm ref="formRef" @success="getList"/>
  <CameraImage ref="imageFormRef"/>
</template>
<script lang="ts" setup>
import {DICT_TYPE, getIntDictOptions} from '@/utils/dict'
  import download from '@/utils/download'
  import * as CameraApi from '@/api/data/video/camera'
  import CameraForm from './CameraForm.vue'
import download from '@/utils/download'
import * as CameraApi from '@/api/data/video/camera'
import CameraForm from './CameraForm.vue'
import CameraImage from './CameraImage.vue'
  defineOptions({name: 'Camera'})
  const message = useMessage() // 消息弹窗
  const {t} = useI18n() // 国际化
defineOptions({name: 'Camera'})
  const loading = ref(true) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 列表的数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    type: 1,
    brand: undefined,
    ip: undefined,
    code: undefined,
    device: undefined,
    location: undefined,
    status: undefined
  })
  const queryFormRef = ref() // 搜索的表单
  const exportLoading = ref(false) // 导出的加载中
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
  /** 查询列表 */
  const getList = async () => {
    loading.value = true
    try {
      const data = await CameraApi.getCameraPage(queryParams)
      list.value = data.list
      total.value = data.total
    } finally {
      loading.value = false
    }
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  type: 1,
  brand: undefined,
  ip: undefined,
  code: undefined,
  device: undefined,
  location: undefined,
  status: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
  loading.value = true
  try {
    const data = await CameraApi.getCameraPage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}
  /** 搜索按钮操作 */
  const handleQuery = () => {
    queryParams.pageNo = 1
    getList()
  }
/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.pageNo = 1
  getList()
}
  /** 重置按钮操作 */
  const resetQuery = () => {
    queryFormRef.value.resetFields()
    handleQuery()
  }
/** 重置按钮操作 */
const resetQuery = () => {
  queryFormRef.value.resetFields()
  queryParams.brand = undefined
  handleQuery()
}
  /** 添加/修改操作 */
  const formRef = ref()
  const openForm = (type: string, id?: number) => {
    formRef.value.open(type, id)
  }
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
  formRef.value.open(type, id)
}
  /** 删除按钮操作 */
  const handleDelete = async (id: number) => {
    try {
      // 删除的二次确认
      await message.delConfirm()
      // 发起删除
      await CameraApi.deleteCamera(id)
      message.success(t('common.delSuccess'))
      // 刷新列表
      await getList()
    } catch {
    }
  }
  /** 导出按钮操作 */
  const handleExport = async () => {
    try {
      // 导出的二次确认
      await message.exportConfirm()
      // 发起导出
      exportLoading.value = true
      const data = await CameraApi.exportCamera(queryParams)
      download.excel(data, '录像机列表.xls')
    } catch {
    } finally {
      exportLoading.value = false
    }
  }
  /** 初始化 **/
  onMounted(async () => {
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await CameraApi.deleteCamera(id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await getList()
  })
  } catch {
  }
}
/** 查看截图 */
const imageFormRef = ref()
const imageHandle = (id: string) => {
  imageFormRef.value.open(id)
}
/** 导出按钮操作 */
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await CameraApi.exportCamera(queryParams)
    download.excel(data, '录像机列表.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}
/** 初始化 **/
onMounted(async () => {
  await getList()
})
</script>
src/views/data/video/nvr/NvrCamera.vue
@@ -6,95 +6,104 @@
    @close="handleClose"
    size="60%">
    <div class="mod-dev__camera" style="padding: 10px;">
    <el-form
      class="-mb-15px"
      :model="queryParams"
      ref="queryFormRef"
      :inline="true"
      label-width="68px"
    >
      <el-form-item label="监控区域" prop="code">
        <el-input
          v-model="queryParams.location"
          placeholder="请输入监控区域"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery">
          <Icon icon="ep:search" class="mr-5px" />
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon icon="ep:refresh" class="mr-5px" />
          重置
        </el-button>
        <el-button
          type="primary"
          plain
          @click="openForm('create')"
        >
          <Icon icon="ep:plus" class="mr-5px" />
          新增
        </el-button>
        <el-button
          type="success"
          plain
          @click="handleExport"
          :loading="exportLoading"
          v-hasPermi="['dev:camera:export']"
        >
          <Icon icon="ep:download" class="mr-5px" />
          导出
        </el-button>
      </el-form-item>
    </el-form>
  <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="编码" align="center" prop="code" width="80" />
      <el-table-column label="抓图方式" align="center" prop="captureType" width="80">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.CAPTURE_TYPE" :value="scope.row.captureType" />
        </template>
      </el-table-column>
      <el-table-column label="通道" align="center" prop="channel" width="80"  />
      <el-table-column label="监控区域" align="center" prop="location" />
      <el-table-column label="备注" align="center" prop="remark" width="200" />
      <el-table-column label="操作" align="center" min-width="110" fixed="right">
        <template #default="scope">
      <el-form
        class="-mb-15px"
        :model="queryParams"
        ref="queryFormRef"
        :inline="true"
        label-width="68px"
      >
        <el-form-item label="监控区域" prop="code">
          <el-input
            v-model="queryParams.location"
            placeholder="请输入监控区域"
            clearable
            @keyup.enter="handleQuery"
            class="!w-240px"
          />
        </el-form-item>
        <el-form-item>
          <el-button @click="handleQuery">
            <Icon icon="ep:search" class="mr-5px"/>
            搜索
          </el-button>
          <el-button @click="resetQuery">
            <Icon icon="ep:refresh" class="mr-5px"/>
            重置
          </el-button>
          <el-button
            link
            type="primary"
            @click="openForm('update', scope.row.id)"
            plain
            @click="openForm('create')"
          >
            编辑
            <Icon icon="ep:plus" class="mr-5px"/>
            新增
          </el-button>
          <el-button
            link
            type="danger"
            @click="handleDelete(scope.row.id)"
            type="success"
            plain
            @click="handleExport"
            :loading="exportLoading"
            v-hasPermi="['dev:camera:export']"
          >
            删除
            <Icon icon="ep:download" class="mr-5px"/>
            导出
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
        </el-form-item>
      </el-form>
      <!-- 列表 -->
      <el-table v-loading="loading" :data="list">
        <el-table-column label="编码" align="center" prop="code" width="80"/>
        <el-table-column label="抓图方式" align="center" prop="captureType" width="80">
          <template #default="scope">
            <dict-tag :type="DICT_TYPE.CAPTURE_TYPE" :value="scope.row.captureType"/>
          </template>
        </el-table-column>
        <el-table-column label="通道" align="center" prop="channel" width="80"/>
        <el-table-column label="监控区域" align="center" prop="location"/>
        <el-table-column label="备注" align="center" prop="remark" width="200"/>
        <el-table-column label="操作" align="center" min-width="110" fixed="right">
          <template #default="scope">
            <el-button
              link
              type="primary"
              @click="openForm('update', scope.row.id)"
            >
              编辑
            </el-button>
            <el-button
              link
              type="danger"
              @click="handleDelete(scope.row.id)"
            >
              删除
            </el-button>
            <el-button
              link
              type="success"
              @click="imageHandle(scope.row.id)"
              v-hasPermi="['video:image:query']"
            >
              查看截图
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <Pagination
        :total="total"
        v-model:page="queryParams.pageNo"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
      />
    </div>
  </el-drawer>
  <!-- 表单弹窗:添加/修改 -->
  <NvrCameraForm ref="formRef" @success="getList" />
  <NvrCameraForm ref="formRef" @success="getList"/>
  <CameraImage ref="imageFormRef"/>
</template>
<script lang="ts" setup>
@@ -102,6 +111,7 @@
import * as CameraApi from '@/api/data/video/camera'
import NvrCameraForm from './NvrCameraForm.vue'
import {DICT_TYPE} from "@/utils/dict";
import CameraImage from "../camera/CameraImage.vue";
defineOptions({name: 'NvrCamera'})
@@ -134,7 +144,7 @@
  await getList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
defineExpose({open}) // 提供 open 方法,用于打开弹窗
/** 查询列表 */
const getList = async () => {
@@ -148,6 +158,12 @@
  }
}
/** 查看截图 */
const imageFormRef = ref()
const imageHandle = (id: string) => {
  imageFormRef.value.open(id)
}
/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.pageNo = 1
@@ -156,7 +172,7 @@
/** 重置按钮操作 */
const resetQuery = () => {
  queryFormRef.value.resetFields()
  queryParams.location = undefined
  handleQuery()
}
src/views/data/video/nvr/index.vue
@@ -52,11 +52,11 @@
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery">
          <Icon icon="ep:search" class="mr-5px" />
          <Icon icon="ep:search" class="mr-5px"/>
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon icon="ep:refresh" class="mr-5px" />
          <Icon icon="ep:refresh" class="mr-5px"/>
          重置
        </el-button>
        <el-button
@@ -65,7 +65,7 @@
          @click="openForm('create')"
          v-hasPermi="['video:nvr:save']"
        >
          <Icon icon="ep:plus" class="mr-5px" />
          <Icon icon="ep:plus" class="mr-5px"/>
          新增
        </el-button>
        <el-button
@@ -75,7 +75,7 @@
          :loading="exportLoading"
          v-hasPermi="['video:nvr:export']"
        >
          <Icon icon="ep:download" class="mr-5px" />
          <Icon icon="ep:download" class="mr-5px"/>
          导出
        </el-button>
      </el-form-item>
@@ -87,20 +87,19 @@
    <el-table v-loading="loading" :data="list">
      <el-table-column label="品牌" align="center" prop="brand" width="80">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand" />
          <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand"/>
        </template>
      </el-table-column>
      <el-table-column label="编码" align="center" prop="code" width="100"/>
      <el-table-column label="名称" align="center" prop="name"/>
      <el-table-column label="IP" align="center" prop="ip" />
      <el-table-column label="IP" align="center" prop="ip"/>
      <el-table-column label="端口" align="center" prop="port" width="100"/>
      <el-table-column label="用户名" align="center" prop="username" width="100"/>
      <el-table-column label="状态" prop="status" width="80">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status" />
          <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="位置" align="center" prop="position" />
      <el-table-column label="备注" align="center" prop="remark" width="150"/>
      <el-table-column label="操作" align="center" min-width="110" fixed="right">
        <template #default="scope">
@@ -120,7 +119,8 @@
          >
            删除
          </el-button>
          <el-button link type="success" size="small" @click="cameraHandle(scope.row.id)">摄像头</el-button>
          <el-button link type="success" size="small" @click="cameraHandle(scope.row.id)">摄像头
          </el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -134,7 +134,7 @@
  </ContentWrap>
  <!-- 表单弹窗:添加/修改 -->
  <NvrForm ref="formRef" @success="getList" />
  <NvrForm ref="formRef" @success="getList"/>
  <!-- 弹窗, 摄像头 -->
  <NvrCamera ref="videoCameraRef"/>
@@ -142,99 +142,100 @@
</template>
<script lang="ts" setup>
import {DICT_TYPE, getIntDictOptions} from '@/utils/dict'
  import download from '@/utils/download'
  import * as NvrApi from '@/api/data/video/nvr'
  import NvrForm from './NvrForm.vue'
  import NvrCamera from './NvrCamera.vue'
import download from '@/utils/download'
import * as NvrApi from '@/api/data/video/nvr'
import NvrForm from './NvrForm.vue'
import NvrCamera from './NvrCamera.vue'
  defineOptions({name: 'Nvr'})
defineOptions({name: 'Nvr'})
  const message = useMessage() // 消息弹窗
  const {t} = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
  const loading = ref(true) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 列表的数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    brand: undefined,
    ip: undefined,
    code: undefined,
    name: undefined,
    status: undefined
  })
  const queryFormRef = ref() // 搜索的表单
  const exportLoading = ref(false) // 导出的加载中
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  brand: undefined,
  ip: undefined,
  code: undefined,
  name: undefined,
  status: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
  const videoCameraRef = ref()
const videoCameraRef = ref()
  /** 查询列表 */
  const getList = async () => {
    loading.value = true
    try {
      const data = await NvrApi.getNvrPage(queryParams)
      list.value = data.list
      total.value = data.total
    } finally {
      loading.value = false
    }
/** 查询列表 */
const getList = async () => {
  loading.value = true
  try {
    const data = await NvrApi.getNvrPage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}
  const cameraHandle = (id: string) => {
    // devCameraVisible.value = true
    videoCameraRef.value.open(id)
  }
const cameraHandle = (id: string) => {
  // devCameraVisible.value = true
  videoCameraRef.value.open(id)
}
  /** 搜索按钮操作 */
  const handleQuery = () => {
    queryParams.pageNo = 1
    getList()
  }
/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.pageNo = 1
  getList()
}
  /** 重置按钮操作 */
  const resetQuery = () => {
    queryFormRef.value.resetFields()
    handleQuery()
  }
/** 重置按钮操作 */
const resetQuery = () => {
  queryFormRef.value.resetFields()
  queryParams.brand = undefined
  handleQuery()
}
  /** 添加/修改操作 */
  const formRef = ref()
  const openForm = (type: string, id?: number) => {
    formRef.value.open(type, id)
  }
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
  formRef.value.open(type, id)
}
  /** 删除按钮操作 */
  const handleDelete = async (id: number) => {
    try {
      // 删除的二次确认
      await message.delConfirm()
      // 发起删除
      await NvrApi.deleteNvr(id)
      message.success(t('common.delSuccess'))
      // 刷新列表
      await getList()
    } catch {
    }
  }
  /** 导出按钮操作 */
  const handleExport = async () => {
    try {
      // 导出的二次确认
      await message.exportConfirm()
      // 发起导出
      exportLoading.value = true
      const data = await NvrApi.exportNvr(queryParams)
      download.excel(data, '录像机列表.xls')
    } catch {
    } finally {
      exportLoading.value = false
    }
  }
  /** 初始化 **/
  onMounted(async () => {
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await NvrApi.deleteNvr(id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await getList()
  })
  } catch {
  }
}
/** 导出按钮操作 */
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await NvrApi.exportNvr(queryParams)
    download.excel(data, '录像机列表.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}
/** 初始化 **/
onMounted(async () => {
  await getList()
})
</script>
src/views/data/wiki/index.vue
文件名从 src/views/data/swagger/index.vue 修改
@@ -6,7 +6,7 @@
<script lang="ts" setup>
import * as ConfigApi from '@/api/infra/config'
defineOptions({ name: 'DataSwagger' })
defineOptions({ name: 'DataWiki' })
const loading = ref(true) // 是否加载中
const src = ref(import.meta.env.VITE_BASE_URL + '/doc.html')
@@ -14,7 +14,7 @@
/** 初始化 */
onMounted(async () => {
  try {
    const data = await ConfigApi.getConfigKey('data.swagger')
    const data = await ConfigApi.getConfigKey('data.wiki')
    if (data && data.length > 0) {
      src.value = data
    }
src/views/model/mpk/file/MpkForm.vue
@@ -116,7 +116,7 @@
                      float: right;
                      color: var(--el-text-color-secondary);
                      font-size: 13px;">
                    <img :src="'/SimtreeUnitImage/' + item.iconName" style="height: 24px;" :alt=" item.iconDesc" />
                    <img :src="staticDir + 'SimtreeUnitImage/' + item.iconName" style="height: 24px;" :alt=" item.iconDesc" />
                  </span>
                </el-option>
              </el-select>
@@ -268,6 +268,8 @@
  const route = useRoute() // 路由
  const router = useRouter();
  const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
  const treeData = ref([])
  const iconList = ref([] as MpkIconApi.MpkIconVO)
  const pkgNameList = ref([] as MpkPackApi.MpkPackVO)
src/views/model/mpk/file/MpkRun.vue
@@ -36,7 +36,7 @@
      <el-divider content-position="left">模型参数信息</el-divider>
      <el-row :gutter="20">
        <el-col :span="2" style="margin-bottom: 10px;margin-left: 20px">
          <el-button tag="a" href="/template/模型参数导入模板.xlsx" download="模型参数导入模板.xlsx" style="text-decoration: none;" type="primary" size="small" link>模板下载</el-button>
          <el-button tag="a" :href="staticDir + '/template/模型参数导入模板.xlsx'" download="模型参数导入模板.xlsx" style="text-decoration: none;" type="primary" size="small" link>模板下载</el-button>
        </el-col>
        <el-col :span="2" style="margin-bottom: 10px;">
          <el-upload
@@ -116,6 +116,7 @@
  import * as MpkApi from '@/api/model/mpk/mpk'
  import {FormRules} from "element-plus";
  import {getAccessToken, getTenantId} from "@/utils/auth";
  const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
  const { t } = useI18n() // 国际化
  const message = useMessage() // 消息弹窗
src/views/model/mpk/icon/index.vue
@@ -50,7 +50,7 @@
      <el-table-column prop="iconDesc" label="描述"/>
      <el-table-column align="center" label="图标" prop="icon" width="100">
        <template #default="scope">
          <img :src="'/SimtreeUnitImage/' + scope.row.iconName" class="mpk-icon-list" :alt=" scope.row.iconDesc" />
          <img :src="staticDir + 'SimtreeUnitImage/' + scope.row.iconName" class="mpk-icon-list" :alt=" scope.row.iconDesc" />
        </template>
      </el-table-column>
      <el-table-column prop="sort" label="排序"/>
@@ -91,6 +91,8 @@
defineOptions({name: 'MpkIcon'})
const staticDir = ref(import.meta.env.VITE_STATIC_DIR)
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
src/views/model/pre/analysis/index.vue
@@ -4,37 +4,42 @@
      <el-form :inline="true" :model="formData" label-width="80px">
        <el-form-item label="开始时间">
          <el-date-picker
              size="mini"
              v-model="formData.startTime"
              type="datetime"
              placeholder="选择日期时间"/>
            v-model="formData.startTime"
            type="datetime"
            format="YYYY-MM-DD HH:mm:00"
            value-format="YYYY-MM-DD HH:mm:00"
            placeholder="选择日期时间"/>
        </el-form-item>
        <el-form-item label="结束时间">
          <el-date-picker
              size="mini"
              v-model="formData.endTime"
              type="datetime"
              placeholder="选择日期时间"/>
            v-model="formData.endTime"
            type="datetime"
            format="YYYY-MM-DD HH:mm:00"
            value-format="YYYY-MM-DD HH:mm:00"
            placeholder="选择日期时间"/>
        </el-form-item>
        <el-form-item label="预测时间">
          <el-date-picker
              size="mini"
              v-model="formData.predictTime"
              type="datetime"
              placeholder="选择日期时间"/>
            v-model="formData.predictTime"
            type="datetime"
            format="YYYY-MM-DD HH:mm:00"
            value-format="YYYY-MM-DD HH:mm:00"
            placeholder="选择日期时间"/>
        </el-form-item>
        <el-form-item label="预测频率">
          <el-input-number size="mini" v-model="formData.predictFreq" controls-position="right" :min="1"
          <el-input-number v-model="formData.predictFreq" controls-position="right"
                           :min="1"
                           :max="10"/>
        </el-form-item>
        <el-form-item>
          <el-button-group>
            <el-button size="mini" type="primary" plain :icon="ArrowLeft"
                       v-loading="loading1" @click="leftSearchDataByRange()"/>
            <el-button size="mini" type="primary" plain :icon="Search"
                       v-loading="loading1" @click="getList()">查询</el-button>
            <el-button size="mini" type="primary" plain :icon="ArrowRight"
                       v-loading="loading1" @click="rightSearchDataByRange()"/>
            <el-button type="primary" plain :icon="ArrowLeft"
                       :loading="loading1" @click="leftSearchDataByRange()"/>
            <el-button type="primary" plain :icon="Search"
                       :loading="loading1" @click="getList()">查询
            </el-button>
            <el-button type="primary" plain :icon="ArrowRight"
                       :loading="loading1" @click="rightSearchDataByRange()"/>
          </el-button-group>
        </el-form-item>
@@ -42,46 +47,52 @@
          <div class="his-body-left">
            <div class="his-body-tree">
              <el-tree
                  :data="treeData"
                  show-checkbox
                  node-key="id"
                  ref="tree"
                  highlight-current
                  :props="defaultProps"
                  @check="onCheckTree"/>
                :data="treeData"
                show-checkbox
                node-key="id"
                ref="tree"
                highlight-current
                :props="defaultProps"
                @check="onCheckTree"/>
            </div>
          </div>
          <div class="his-body-right">
            <div class="his-body-chart">
              <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateForm" label-width="108px">
              <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateFormRef"
                       label-width="108px">
                <el-row>
                  <el-col :span="6" >
                  <el-col :span="6">
                    <el-form-item label="预测项" prop="calItem" style="width: 90%">
                      <el-select v-model="calRateForm.calItem" @change="calItemBaseVale" placeholder="请选择">
                      <el-select size="small" v-model="calRateForm.calItem"
                                 @change="calItemBaseVale"
                                 placeholder="请选择">
                        <el-option
                            v-for="item in formData.checkedItemData"
                            :key="item.id"
                            :label="item.label"
                            :value="item.id"/>
                          v-for="itemOut in formData.checkedItemData"
                          :key="itemOut.id"
                          :label="itemOut.label"
                          :value="itemOut.id"/>
                      </el-select>
                    </el-form-item>
                  </el-col>
                  <el-col :span="6">
                    <el-form-item label="精准度偏差" prop="IN_DEVIATION">
                      <el-input-number size="mini" v-model="calRateForm.IN_DEVIATION" controls-position="right" :min="1"
                      <el-input-number size="small" v-model="calRateForm.IN_DEVIATION"
                                       controls-position="right" :min="1"
                                       :max="10"/>
                    </el-form-item>
                  </el-col>
                  <el-col :span="6">
                    <el-form-item label="不可信率偏差" prop="OUT_DEVIATION">
                      <el-input-number size="mini" v-model="calRateForm.OUT_DEVIATION" controls-position="right"
                      <el-input-number size="small" v-model="calRateForm.OUT_DEVIATION"
                                       controls-position="right"
                                       :min="1"
                                       :max="20"/>
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item>
                      <el-button size="mini" type="primary" plain :loading="loading2" @click="calAccuracyRate">计算精准度
                      <el-button size="small" type="primary" plain :loading="loading2"
                                 @click="calAccuracyRate">计算精准度
                      </el-button>
                    </el-form-item>
                  </el-col>
@@ -89,54 +100,54 @@
                <el-row>
                  <el-col :span="4">
                    <el-form-item label="精准度:">
                      {{calRateForm.IN_ACCURACY_RATE}}%
                      {{ calRateForm.IN_ACCURACY_RATE }}%
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="预测平均值:">
                      {{calRateForm.itemPreAvg}}
                      {{ calRateForm.itemPreAvg }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="预测最大值:">
                      {{calRateForm.itemPreMax}}
                      {{ calRateForm.itemPreMax }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="预测最小值:">
                      {{calRateForm.itemPreMin}}
                      {{ calRateForm.itemPreMin }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="预测累积量:">
                      {{calRateForm.preCumulant}}
                      {{ calRateForm.preCumulant }}
                    </el-form-item>
                  </el-col>
                </el-row>
                <el-row>
                  <el-col :span="4">
                    <el-form-item label="不可信率:">
                      {{calRateForm.OUT_ACCURACY_RATE}}%
                      {{ calRateForm.OUT_ACCURACY_RATE }}%
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="真实平均值:">
                      {{calRateForm.itemAvg}}
                      {{ calRateForm.itemAvg }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="真实最大值:">
                      {{calRateForm.itemMax}}
                      {{ calRateForm.itemMax }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="真实最小值:">
                      {{calRateForm.itemMin}}
                      {{ calRateForm.itemMin }}
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                    <el-form-item label="真实累积量:">
                      {{calRateForm.realCumulant}}
                      {{ calRateForm.realCumulant }}
                    </el-form-item>
                  </el-col>
                </el-row>
@@ -146,54 +157,506 @@
                  <el-col :span="12">
                    <el-form-item label="数据类型">
                      <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck">
                        <el-checkbox v-for="item in formData.chartOptions" :label="item" :key="item">{{item}}
                        <el-checkbox v-for="item in formData.chartOptions" :label="item"
                                     :key="item">{{ item }}
                        </el-checkbox>
                      </el-checkbox-group>
                    </el-form-item>
                  </el-col>
                  <el-col :span="6">
                    <el-form-item>
                      <el-radio v-model="formData.isMultipleY" :label="false" @input="onChangeMultipleY">单坐标轴</el-radio>
                      <el-radio v-model="formData.isMultipleY" :label="true" @input="onChangeMultipleY">多坐标轴</el-radio>
                      <el-radio v-model="formData.isMultipleY" :label="false"
                                @input="onChangeMultipleY">单坐标轴
                      </el-radio>
                      <el-radio v-model="formData.isMultipleY" :label="true"
                                @input="onChangeMultipleY">多坐标轴
                      </el-radio>
                    </el-form-item>
                  </el-col>
                </el-row>
              </el-form>
              <div id="data-analysis" style="height: 500px;"></div>
              <div ref="dataAnalysisChart" style="height: 500px;"></div>
            </div>
          </div>
        </div>
      </el-form>
    </div>
  </el-card>
</template>
<script lang="ts" setup>
  import {getYMDHMS} from "@/utils/dateUtil"
  import * as CategoryApi from "@/api/data/ind/category";
  import * as DmModule from '@/api/model/pre/dm'
  import * as ItemApi from "@/api/data/ind/item/item";
  import * as MmPredictItem from '@/api/model/pre/predict'
  import * as echarts from "echarts";
  import { onMounted, ref } from 'vue';
  import { Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue'
import {getYMDHMS} from "@/utils/dateUtil"
import * as McsApi from '@/api/model/mcs'
import * as echarts from "echarts";
import {Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue'
  defineOptions({name: 'AnalysisformData'})
defineOptions({name: 'AnalysisformData'})
  const message = useMessage() // 消息弹窗
  const { t } = useI18n() // 国际化
  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
  const loading1 = ref(false) // 列表的加载中
  const loading2 = ref(false) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 字典表格数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    itemno: '',
    itemname: '',
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
const dataAnalysisChart = ref(null);
const loading1 = ref(false) // 列表的加载中
const loading2 = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 字典表格数据
let formData = ref({
  rangeDate: '',
  startTime: '',
  endTime: '',
  predictTime: '',
  predictTimeStr: '',
  startTimeStr: '',
  endTimeStr: '',
  predictTimeStamp: 0,
  startTimeStamp: 0,
  endTimeStamp: 0,
  currentStamp: '',
  currentStamp60: '',
  predictStamp: '',
  chartCheck: ['T+L', '真实值'],
  chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'],
  checkedItemData: [],
  backItem: '',
  backValue: 0,
  backCoe: 1,
  preCumulant: 0,
  realCumulant: 0,
  queryStep: 2,
  isMultipleYRadio: '单坐标轴',
  isMultipleY: false,
  predictFreq: 3,
})
const calRateFormRef = ref()
const calRateForm = ref({
  calItem: undefined,
  IN_DEVIATION: 0,
  OUT_DEVIATION: 0,
  IN_ACCURACY_RATE: 0,
  OUT_ACCURACY_RATE: 0,
  itemAvg: 0,
  itemMax: 0,
  itemMin: 0,
  itemPreAvg: 0,
  itemPreMax: 0,
  itemPreMin: 0,
  preCumulant: 0,
  realCumulant: 0
})
let itemData = ref({
  currentTreeList: [],
  chart: {},
  option: {}
})
const treeData = ref([])
const itemDataObject = ref()
const timer = ref()
let myChart = null;
const formRules = reactive({
  calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
  IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}],
  OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}],
})
/** 查询列表 */
const getList = async () => {
  loading1.value = true
  try {
    if (!formData.value.chartCheck) {
      formData.value.chartCheck = ['真实值']
    }
    let chartCheckArray = formData.value.chartCheck;
    if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) {
      itemData.value.option = {};
      return;
    }
    let outIds = formData.value.checkedItemData.map(item => {
      return item.id
    })
    const params = reactive({
      outIds: outIds,
      predictTime: formData.value.predictTime,
      startTime: formData.value.startTime,
      endTime: formData.value.endTime
    })
    const data = await McsApi.getPreDataCharts(params)
    formData.value.predictTime = data.predictTime;
    formData.value.startTime = data.startTime
    formData.value.endTime = data.endTime
    let xAxisData = data.categories;
    let yAxisData = [];
    let offset = 0;
    let yAxisIndex = 0;
    let legendData = [];
    let yMaxArr = [];
    let seriesData = [];
    seriesData.push({
      name: '',
      data: [null],
      type: 'line',
      smooth: true,
      color: 'green',
      markLine: {
        silent: true,
        lineStyle: {
          color: '#32a487',
          width: 2
        },
        data: [{
          xAxis: formData.value.predictTime
        }],
        label: {
          normal: {
            formatter: formData.value.predictTime
          }
        },
        symbol: ['circle', 'none'],
      },
    });
    itemDataObject.value = {}
    for (let i = 0; i < data.dataViewList.length; i++) {
      let dataView = data.dataViewList[i]
      itemDataObject.value[dataView.outId] = dataView;
      let maxValue = dataView.maxValue;
      let minValue = dataView.minValue;
      yAxisIndex = formData.value.isMultipleY ? i : 0;
      let yMax = maxValue;
      if (maxValue < 0) {
        maxValue = 1;
      } else if (maxValue < 10) {
        yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1);
      } else if (maxValue < 100) {
        yMax = (Math.ceil(maxValue * 1.1 / 5) * 5);
      } else {
        yMax = (Math.ceil(maxValue * 1.1 / 10) * 10);
      }
      yMaxArr.push(yMax);
      let yMin = minValue;
      if (minValue >= 0) {
        yMin = 0;
      } else if (minValue > -10) {
        yMin = (Math.floor(minValue * 11) / 10).toFixed(1);
      } else if (minValue > -100) {
        yMin = (Math.floor(minValue * 1.1 / 5) * 5);
      } else {
        yMin = (Math.floor(minValue * 1.1 / 10) * 10);
      }
      yAxisData.push({
        type: 'value',
        name: "",
        min: yMin,
        max: yMax,
        position: 'left',
        offset: offset,
        splitLine: {
          show: false
        },
        axisLine: {
          show: true,
          lineStyle: {}
        },
        axisLabel: {
          formatter: '{value}'
        }
      })
      offset = offset + 40
      if (chartCheckArray.indexOf('真实值') !== -1) {
        let legendName = dataView.resultstr + '(真实)';
        legendData.push(legendName);
        seriesData.push({
          name: legendName,
          data: dataView.realData || [],
          type: 'line',
          yAxisIndex: yAxisIndex,
          showSymbol: false,
          smooth: true,
          lineStyle: {
            width: 2
          }
        });
      }
      if (chartCheckArray.indexOf('T+N') !== -1) {
        let legendName = dataView.resultstr + '(T+N)';
        seriesData.push({
          name: legendName,
          data: dataView.preDataN || [],
          type: 'line',
          yAxisIndex: yAxisIndex,
          showSymbol: false,
          smooth: true,
          lineStyle: {
            width: 2
          }
        });
      }
      if (chartCheckArray.indexOf('T+L') !== -1) {
        let legendName = dataView.resultstr + '(T+L)';
        legendData.push(legendName);
        seriesData.push({
          name: legendName,
          data: dataView.preDataL || [],
          type: 'line',
          showSymbol: false,
          connectNulls: true,
          yAxisIndex: yAxisIndex,
          smooth: true,
          lineStyle: {
            width: 2
          }
        });
      }
      if (chartCheckArray.indexOf('当时') !== -1) {
        let legendName = dataView.resultstr + '(当时)';
        legendData.push(legendName);
        seriesData.push({
          name: legendName,
          data: dataView.curData || [],
          type: 'line',
          yAxisIndex: yAxisIndex,
          showSymbol: false,
          smooth: true,
          lineStyle: {
            width: 2
          }
        });
      }
      if (chartCheckArray.indexOf('调整值') !== -1) {
        let legendName = dataView.resultstr + '(调整值)';
        legendData.push(legendName);
        seriesData.push({
          name: legendName,
          data: dataView.adjData || [],
          type: 'line',
          yAxisIndex: yAxisIndex,
          showSymbol: false,
          connectNulls: true,
          smooth: true,
          lineStyle: {
            width: 2,
            type: 'dashed'
          }
        });
      }
    }
    //如果最大值相差不大,改成一致大小
    if (yMaxArr.length > 1) {
      let max = Math.max.apply(null, yMaxArr);
      let min = Math.min.apply(null, yMaxArr);
      if (Math.abs((max - min) / max) <= 0.2) {
        for (let i = 0; i < yAxisData.length; i++) {
          yAxisData[i].max = max;
        }
      }
    }
    myChart = echarts.init(dataAnalysisChart.value);
    let option = {
      title: {
        text: ''
      },
      tooltip: {
        trigger: 'axis'
      },
      legend: {
        show: true,
        data: legendData,
        top: 10
      },
      grid: {
        top: '20%',
        left: '3%',
        right: '6%',
        bottom: '3%',
        containLabel: true
      },
      xAxis: {
        type: 'category',
        boundaryGap: false,
        data: xAxisData
      },
      yAxis: formData.value.isMultipleY ? yAxisData : {
        type: 'value',
        splitLine: {show: false},
        axisLine: {show: true}
      },
      dataZoom: [
        {
          type: 'inside',
          start: 0,
          end: 100
        },
        {
          start: 0,
          end: 10
        }
      ],
      series: seriesData
    }
    myChart.clear()
    myChart.setOption(option)
  } finally {
    loading1.value = false
  }
}
onMounted(() => {
  resetForm()
  getPreItemTree()
})
async function getPreItemTree() {
  treeData.value = await McsApi.getPredictItemTree()
}
function leftSearchDataByRange() {
  let mins = getRangeMins();
  let startTime = formData.value.startTime;
  let endTime = formData.value.endTime;
  let predictTime = formData.value.predictTime;
  if (predictTime) {
    predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins);
    formData.value.predictTime = predictTime;
  }
  if (startTime) {
    startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins);
    formData.value.startTime = startTime;
  }
  if (endTime) {
    endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins);
    formData.value.endTime = endTime;
  }
  getList();
}
function getRangeMins() {
  let result: string | number = 0;
  if (formData.value.startTime && formData.value.endTime) {
    let startStamp = new Date(formData.value.startTime).getTime();
    let endStamp = new Date(formData.value.endTime).getTime();
    let queryStep = ((endStamp - startStamp) / (1000 * 60)).toFixed(0);
    result = queryStep >= 0 ? queryStep : 0;
  }
  return result;
}
function onCheckTree(data, checked, indeterminate) {
  formData.value.checkedItemData = [];
  if (checked.checkedNodes) {
    formData.value.checkedItemData = [...checked.checkedNodes]
  }
  debounce(getList, 1000);
}
function debounce(func, wait) {
  let args = [];
  if (timer.value) {
    clearTimeout(timer.value);
  }
  timer.value = setTimeout(() => {
    func.apply(this, args);
    timer.value = null;
  }, wait)
}
function calItemBaseVale() {
  if (!calRateForm.value.calItem) {
    calRateForm.value.itemPreMax = 0;
    calRateForm.value.itemPreMin = 0;
    calRateForm.value.itemPreAvg = 0;
    calRateForm.value.preCumulant = 0;
    calRateForm.value.itemMax = 0;
    calRateForm.value.itemMin = 0;
    calRateForm.value.itemAvg = 0;
    calRateForm.value.realCumulant = 0;
  } else {
    let dataView = itemDataObject.value[calRateForm.value.calItem]
    calRateForm.value.itemPreMax = dataView.preMax;
    calRateForm.value.itemPreMin = dataView.preMin;
    calRateForm.value.itemPreAvg = dataView.preAvg;
    calRateForm.value.preCumulant = dataView.preCumulant;
    calRateForm.value.itemMax = dataView.hisMax;
    calRateForm.value.itemMin = dataView.hisMin;
    calRateForm.value.itemAvg = dataView.hisAvg;
    calRateForm.value.realCumulant = dataView.hisCumulant;
  }
}
function calAccuracyRate() {
  this.$refs['calRateForm'].validate((valid) => {
    if (!valid) {
      return false
    }
    let dataView = itemDataObject[calRateForm.value.calItem]
    let seriesReaData = dataView.realData;
    let seriesPreData = dataView.preDataL;
    if (seriesReaData == null || seriesPreData == null ||
      seriesReaData.length === 0 || seriesPreData.length === 0) {
      loading2.value = false;
      return;
    }
    let predictValueMap = {};
    seriesPreData.forEach(function (item) {
      predictValueMap[item[0]] = item[1];
    })
    let pointValueMap = {};
    seriesReaData.forEach(function (item) {
      pointValueMap[item[0]] = item[1];
    })
    let inDeviation = Number(calRateForm.value.IN_DEVIATION);
    let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
    if (inDeviation === 0 && outDeviation === 0) {
      loading2.value = false;
      return;
    }
    let inDeviationCount = 0;
    let outDeviationCount = 0;
    let totalCount = 0;
    for (let key in predictValueMap) {
      let predictValue = predictValueMap[key];
      let pointValue = pointValueMap[key];
      if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
        continue;
      }
      let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
      if (deviationAbs < inDeviation) {
        inDeviationCount = inDeviationCount + 1;
      }
      if (deviationAbs > outDeviation) {
        outDeviationCount = outDeviationCount + 1;
      }
      totalCount = totalCount + 1;
    }
    let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
    let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
    calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
    calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
    loading2.value = false;
  })
  let formData = ref({
}
function rightSearchDataByRange() {
  let mins = getRangeMins();
  let startTime = formData.value.startTime;
  let endTime = formData.value.endTime;
  let predictTime = formData.value.predictTime;
  if (predictTime) {
    predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins);
    formData.value.predictTime = predictTime;
  }
  if (startTime) {
    startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins);
    formData.value.startTime = startTime;
  }
  if (endTime) {
    endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins);
    formData.value.endTime = endTime;
  }
  getList();
}
/** 重置表单 */
const resetForm = () => {
  formData.value = {
    rangeDate: '',
    startTime: '',
    endTime: '',
@@ -219,9 +682,9 @@
    isMultipleYRadio: '单坐标轴',
    isMultipleY: false,
    predictFreq: 3,
  })
  let calRateForm = ref({
    calItem: '',
  }
  calRateForm.value = {
    calItem: undefined,
    IN_DEVIATION: 0,
    OUT_DEVIATION: 0,
    IN_ACCURACY_RATE: 0,
@@ -232,462 +695,47 @@
    itemPreAvg: 0,
    itemPreMax: 0,
    itemPreMin: 0,
    preCumulant:0,
    realCumulant:0
  })
  let itemData = ref({
    currentTreeList: [],
    chart: {},
    option: {}
  })
  const chartContainer = ref(null);
  const treeData = ref([])
  const itemDataObject = ref()
  const timer = ref()
  const formRules = reactive({
    calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
    IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}],
    OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}],
  })
  //const myChart = echarts.init(document.getElementById("data-analysis"));
  /** 查询列表 */
  const getList = async () => {
    loading1.value = true
    try {
      const data = formData.value
      if (!formData.value.chartCheck) {
        formData.value.chartCheck = ['真实值']
      }
      let chartCheckArray = formData.value.chartCheck;
      if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) {
        itemData.value.option = {};
        return;
      }
      let itemIdList = formData.value.checkedItemData.map(item => {
        return item.id
      })
      const params = ref({
        itemIds: itemIdList.join(','),
        predictTime: formData.value.predictTime,
        startTime: formData.value.startTime,
        endTime: formData.value.endTime
      })
      const res = await MmPredictItem.getViewCharts(params)
      if (res.code !== 0) {
        return message.error(res.msg)
      }
      formData.value.predictTime = res.data.predictTime;
      formData.value.startTime = res.data.startTime
      formData.value.endTime = res.data.endTime
      let xAxisData = res.data.categories;
      let yAxisData = [];
      let offset = 0;
      let yAxisIndex = 0;
      let legendData = [];
      let yMaxArr = [];
      let seriesData = [];
      seriesData.push({
        name: '',
        data: [null],
        type: 'line',
        smooth: true,
        color: 'green',
        markLine: {
          silent: true,
          lineStyle: {
            color: '#32a487',
            width: 2
          },
          data: [{
            xAxis: formData.value.predictTime
          }],
          label: {
            normal: {
              formatter: formData.value.predictTime
            }
          },
          symbol: ['circle', 'none'],
        },
      });
      itemDataObject.value = {}
      for (let i = 0; i < res.data.dataViewList.length; i++) {
        let dataView = res.data.dataViewList[i]
        itemDataObject.value.dataView.itemId = dataView;
        let maxValue = dataView.maxValue;
        let minValue = dataView.minValue;
        yAxisIndex = formData.value.isMultipleY ? i : 0;
        let yMax = maxValue;
        if (maxValue < 0) {
          maxValue = 1;
        } else if (maxValue < 10) {
          yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1);
        } else if (maxValue < 100) {
          yMax = (Math.ceil(maxValue * 1.1 / 5) * 5);
        } else {
          yMax = (Math.ceil(maxValue * 1.1 / 10) * 10);
        }
        yMaxArr.push(yMax);
        let yMin = minValue;
        if (minValue >= 0) {
          yMin = 0;
        } else if (minValue > -10) {
          yMin = (Math.floor(minValue * 11) / 10).toFixed(1);
        } else if (minValue > -100) {
          yMin = (Math.floor(minValue * 1.1 / 5) * 5);
        } else {
          yMin = (Math.floor(minValue * 1.1 / 10) * 10);
        }
        yAxisData.push({
          type: 'value',
          name: "",
          min: yMin,
          max: yMax,
          position: 'left',
          offset: offset,
          splitLine: {
            show: false
          },
          axisLine: {
            show: true,
            lineStyle: {}
          },
          axisLabel: {
            formatter: '{value}'
          }
        })
        offset = offset + 40
        if (chartCheckArray.indexOf('真实值') !== -1) {
          let legendName = dataView.itemName + '(真实)';
          legendData.push(legendName);
          seriesData.push({
            name: legendName,
            data: dataView.realData || [],
            type: 'line',
            yAxisIndex: yAxisIndex,
            showSymbol: false,
            smooth: true,
            lineStyle: {
              width: 3
            }
          });
        }
        if (chartCheckArray.indexOf('T+N') !== -1) {
          let legendName = dataView.itemName + '(T+N)';
          seriesData.push({
            name: legendName,
            data: dataView.preDataN || [],
            type: 'line',
            yAxisIndex: yAxisIndex,
            showSymbol: false,
            smooth: true,
            lineStyle: {
              width: 3
            }
          });
        }
        if (chartCheckArray.indexOf('T+L') !== -1) {
          let legendName = dataView.itemName + '(T+L)';
          legendData.push(legendName);
          seriesData.push({
            name: legendName,
            data: dataView.preDataL || [],
            type: 'line',
            showSymbol: false,
            connectNulls: true,
            yAxisIndex: yAxisIndex,
            smooth: true,
            lineStyle: {
              width: 3
            }
          });
        }
        if (chartCheckArray.indexOf('当时') !== -1) {
          let legendName = dataView.itemName + '(当时)';
          legendData.push(legendName);
          seriesData.push({
            name: legendName,
            data: dataView.curData || [],
            type: 'line',
            yAxisIndex: yAxisIndex,
            showSymbol: false,
            smooth: true,
            lineStyle: {
              width: 3
            }
          });
        }
        if (chartCheckArray.indexOf('调整值') !== -1) {
          let legendName = dataView.itemName + '(调整值)';
          legendData.push(legendName);
          seriesData.push({
            name: legendName,
            data: dataView.adjData || [],
            type: 'line',
            yAxisIndex: yAxisIndex,
            showSymbol: false,
            connectNulls: true,
            smooth: true,
            lineStyle: {
              width: 3,
              type: 'dashed'
            }
          });
        }
      }
      //如果最大值相差不大,改成一致大小
      if (yMaxArr.length > 1) {
        let max = Math.max.apply(null, yMaxArr);
        let min = Math.min.apply(null, yMaxArr);
        if (Math.abs((max - min) / max) <= 0.2) {
          for (let i = 0; i < yAxisData.length; i++) {
            yAxisData[i].max = max;
          }
        }
      }
      let option = {
        title: {
          text: ''
        },
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          show: true,
          data: legendData,
          top: 10
        },
        grid: {
          top: 50,
          left: '3%',
          right: '6%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: xAxisData
        },
        yAxis: formData.value.isMultipleY ? yAxisData : {
          type: 'value',
          splitLine: {show: false},
          axisLine: {show: true}
        },
        dataZoom: [
          {
            type: 'inside',
            start: 0,
            end: 100
          },
          {
            start: 0,
            end: 10
          }
        ],
        series: seriesData
      }
      //chart.setOption(option)
    } finally {
      loading1.value = false
    }
    preCumulant: 0,
    realCumulant: 0
  }
  onMounted(() => {
    getPreItemTree()
    getList()
  })
  async function getPreItemTree() {
    treeData.value = await MmPredictItem.getMmPredictItemTree()
  }
  function leftSearchDataByRange() {
    let mins = getRangeMins();
    let startTime = formData.value.startTime;
    let endTime = formData.value.endTime;
    let predictTime = formData.value.predictTime;
    if (predictTime) {
      predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins);
      formData.value.predictTime = predictTime;
    }
    if (startTime) {
      startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins);
      formData.value.startTime = startTime;
    }
    if (endTime) {
      endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins);
      formData.value.endTime = endTime;
    }
    getList();
  }
  function getRangeMins () {
    let result: string | number = 0;
    if(formData.value.startTime && formData.value.endTime) {
      let startStamp = new Date(formData.value.startTime).getTime();
      let endStamp = new Date(formData.value.endTime).getTime();
      let queryStep = ((endStamp - startStamp) / (1000 * 60)).toFixed(0);
      result = queryStep >= 0 ? queryStep : 0;
    }
    return result;
  }
  function onCheckTree(data, checked, indeterminate) {
    formData.value.checkedItemData = [];
    if (checked.checkedNodes) {
      formData.value.checkedItemData = [...checked.checkedNodes]
    }
    //myChart.clear()
    debounce(getList(), 1000);
  }
  function debounce(func, wait) {
    let args = [];
    if (timer.value) {
      clearTimeout(timer.value);
    }
    timer.value = setTimeout(() => {
      func.apply(this, args);
      timer.value = null;
    }, wait)
  }
  function calItemBaseVale() {
    if (!calRateForm.value.calItem) {
      calRateForm.value.itemPreMax = 0;
      calRateForm.value.itemPreMin = 0;
      calRateForm.value.itemPreAvg = 0;
      calRateForm.value.preCumulant = 0;
      calRateForm.value.itemMax = 0;
      calRateForm.value.itemMin = 0;
      calRateForm.value.itemAvg = 0;
      calRateForm.value.realCumulant = 0;
      return
    } else {
      let dataView = itemDataObject[calRateForm.value.calItem]
      calRateForm.value.itemPreMax = dataView.preMax;
      calRateForm.value.itemPreMin = dataView.preMin;
      calRateForm.value.itemPreAvg = dataView.preAvg;
      calRateForm.value.preCumulant = dataView.preCumulant;
      calRateForm.value.itemMax = dataView.hisMax;
      calRateForm.value.itemMin = dataView.hisMin;
      calRateForm.value.itemAvg = dataView.hisAvg;
      calRateForm.value.realCumulant = dataView.hisCumulant;
    }
  }
  function calAccuracyRate() {
    this.$refs['calRateForm'].validate((valid) => {
      if (!valid) {
        return false
      }
      let dataView = itemDataObject[calRateForm.value.calItem]
      let seriesReaData = dataView.realData;
      let seriesPreData = dataView.preDataL;
      if (seriesReaData == null || seriesPreData == null ||
        seriesReaData.length === 0 || seriesPreData.length === 0) {
        loading2.value = false;
        return;
      }
      let predictValueMap = {};
      seriesPreData.forEach(function (item) {
        predictValueMap[item[0]] = item[1];
      })
      let pointValueMap = {};
      seriesReaData.forEach(function (item) {
        pointValueMap[item[0]] = item[1];
      })
      let inDeviation = Number(calRateForm.value.IN_DEVIATION);
      let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
      if (inDeviation === 0 && outDeviation === 0) {
        loading2.value = false;
        return;
      }
      let inDeviationCount = 0;
      let outDeviationCount = 0;
      let totalCount = 0;
      for (let key in predictValueMap) {
        let predictValue = predictValueMap[key];
        let pointValue = pointValueMap[key];
        if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
          continue;
        }
        let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
        if (deviationAbs < inDeviation) {
          inDeviationCount = inDeviationCount + 1;
        }
        if (deviationAbs > outDeviation) {
          outDeviationCount = outDeviationCount + 1;
        }
        totalCount = totalCount + 1;
      }
      let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
      let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
      calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
      calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
      loading2.value = false;
    })
  }
  function rightSearchDataByRange() {
    let mins = getRangeMins();
    let startTime = formData.value.startTime;
    let endTime = formData.value.endTime;
    let predictTime = formData.value.predictTime;
    if (predictTime) {
      predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins);
      formData.value.predictTime = predictTime;
    }
    if (startTime) {
      startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins);
      formData.value.startTime = startTime;
    }
    if (endTime) {
      endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins);
      formData.value.endTime = endTime;
    }
    getList();
  }
  calRateFormRef.value?.resetFields()
}
</script>
<style scoped>
  .el-form-item {
    margin-bottom: 0 !important;
  }
.el-form-item {
  margin-bottom: 0 !important;
}
  .his-body-chart {
    height: 100%;
    border: 1px solid lightgray;
    padding: 10px;
  }
.his-body-chart {
  height: 100%;
  border: 1px solid lightgray;
  padding: 10px;
}
  .his-body-tree {
    height: 100%;
    border: 1px solid lightgray;
    padding: 10px;
  }
.his-body-tree {
  height: 100%;
  border: 1px solid lightgray;
  padding: 10px;
}
  .his-body-right {
    width: 80%;
    height: 100%;
    padding-top: 10px;
  }
.his-body-right {
  width: 80%;
  height: 100%;
  padding-top: 10px;
}
  .his-body-left {
    width: 20%;
    height: 100%;
    padding: 10px 10px 0 0;
  }
.his-body-left {
  width: 20%;
  height: 100%;
  padding: 10px 10px 0 0;
}
  .his-body {
    width: 100%;
    height: calc(calc(100vh - 48px - 38px - 130px));
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
    align-content: flex-start;
  }
.his-body {
  width: 100%;
  height: calc(calc(100vh - 68px - 38px - 160px));
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-content: flex-start;
}
</style>
src/views/model/pre/item/MmPredictItemChart.vue
对比新文件
@@ -0,0 +1,253 @@
<template>
  <el-dialog
    title="预测数据"
    :close-on-click-modal="false"
    width="50%"
    v-model="visible"
  >
    <el-form
      :inline="true"
      :model="dataForm"
      @keydown.enter="getDataList()"
    >
      <el-form-item label="开始时间">
        <el-date-picker
          size="mini"
          v-model="dataForm.startTime"
          format="YYYY-MM-DD HH:mm:00"
          value-format="YYYY-MM-DD HH:mm:00"
          type="datetime"
          :clearable="false"
          placeholder="选择日期时间"/>
      </el-form-item>
      <el-form-item label="结束时间">
        <el-date-picker
          size="mini"
          v-model="dataForm.endTime"
          format="YYYY-MM-DD HH:mm:00"
          value-format="YYYY-MM-DD HH:mm:00"
          type="datetime"
          :clearable="false"
          placeholder="选择日期时间"/>
      </el-form-item>
      <el-form-item>
        <el-button @click="getDataList()">查询</el-button>
      </el-form-item>
      <el-form-item>
        <el-button
          type="success"
          plain
          @click="handleExport"
          :loading="exportLoading"
        >
          <Icon icon="ep:download"/>
          导出
        </el-button>
      </el-form-item>
    </el-form>
    <div ref="chartDomPre" class="result-chart"></div>
  </el-dialog>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import * as echarts from 'echarts';
import * as McsApi from '@/api/model/mcs'
import {getYMDHM0} from "@/utils/dateUtil"
import download from "@/utils/download";
const message = useMessage() // 消息弹窗
const visible = ref(false);
const chartDomPre = ref(null);
let myChart = null;
const chartParams = reactive({
  itemId: undefined,
  startTime: undefined,
  endTime: undefined,
})
const dataForm = ref({
  id: "",
  itemName: "",
  startTime: getYMDHM0(new Date() - 60 * 60 * 1000),
  endTime: getYMDHM0(new Date() + 60 * 60 * 1000),
});
/** 打开弹窗 */
const open = async (row: object) => {
  visible.value = true
  resetForm()
  dataForm.value.id = row.id;
  dataForm.value.itemName = row.itemname;
  if (row.id) {
    getDataList();
  }
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
async function getDataList() {
  visible.value = true;
  if (dataForm.value.id) {
    try {
      chartParams.itemId = dataForm.value.id;
      chartParams.startTime = dataForm.value.startTime;
      chartParams.endTime = dataForm.value.endTime;
      const data = await McsApi.getPreDataItemChart(chartParams)
      let legendData = []
      if (data.legend && data.legend.length > 0) {
        data.legend.forEach(item => {
          legendData.push(item + ":" + '真实值')
          legendData.push(item + ":" + '预测值')
        })
      }
      let seriesData = []
      if (data.predictTime) {
        seriesData.push({
          name: '',
          data: [null],
          type: 'line',
          smooth: true,
          color: 'green',
          markLine: {
            silent: true,
            lineStyle: {
              color: '#32a487',
              width: 2
            },
            data: [{
              xAxis: data.predictTime
            }],
            label: {
              normal: {
                formatter: data.predictTime
              }
            },
            symbol: ['circle', 'none'],
          },
        });
      }
      if (data.viewMap) {
        Object.keys(data.viewMap).forEach(key => {
          let viewData = data.viewMap[key]
          seriesData.push({
            name: key + ":" + '真实值',
            type: "line",
            data: viewData.realData,
            showSymbol: false,
            smooth: false,
            lineStyle: {
              normal: {
                width: 1,
              },
            },
          })
          seriesData.push({
            name: key + ":" + '预测值',
            type: "line",
            data: viewData.preDataN,
            showSymbol: false,
            smooth: false,
            lineStyle: {
              normal: {
                width: 1,
              },
            },
          })
        })
      }
      myChart = echarts.init(chartDomPre.value);
      const option = {
        title: {
          text: dataForm.value.itemName,
          top: 0,
          left: "1%",
          textStyle: {
            fontSize: 14,
          },
        },
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "line",
            lineStyle: {
              color: "#cccccc",
              width: "1",
              type: "dashed",
            },
          },
        },
        legend: {
          show: true,
          top: 20,
          data: legendData
        },
        grid: {
          top: "20%",
          left: "3%",
          right: "5%",
          bottom: 10,
          containLabel: true,
        },
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: data.categories,
        },
        yAxis: {
          type: "value",
        },
        dataZoom: [
          {
            type: "inside",
          },
        ],
        series: seriesData,
      };
      myChart.setOption(option);
    } catch (error) {
      console.error(error)
    }
  }
}
/** 导出按钮操作 */
const exportLoading = ref(false)
const handleExport = async () => {
  chartParams.itemId = dataForm.value.id;
  chartParams.startTime = dataForm.value.startTime?dataForm.value.startTime:"";
  chartParams.endTime = dataForm.value.endTime?dataForm.value.endTime:"";
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await McsApi.exportPredictValue(chartParams)
    download.excel(data, dataForm.value.itemName + '.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}
/** 重置表单 */
const resetForm = () => {
  dataForm.value = {
    id: undefined,
    itemName: undefined,
    startTime: undefined,
    endTime: undefined
  }
}
</script>
<style>
.el-select {
  width: 100%;
}
.result-chart {
  height: 500px;
}
</style>
src/views/model/pre/item/MmPredictItemForm.vue
@@ -17,7 +17,8 @@
        </el-col>
        <el-col :span="12">
          <el-form-item label="编号" prop="mmPredictItem.itemno">
            <el-input v-model="dataForm.mmPredictItem.itemno" placeholder="编号" maxlength="50" readonly/>
            <el-input v-model="dataForm.mmPredictItem.itemno" placeholder="编号" maxlength="50"
                      readonly/>
          </el-form-item>
        </el-col>
      </el-row>
@@ -39,7 +40,7 @@
          <el-form-item label="粒度" prop="mmPredictItem.granularity">
            <el-select v-model="dataForm.mmPredictItem.granularity" placeholder="请选择">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.TIME_GRANULARITY)"
                v-for="dict in getIntDictOptions(DICT_TYPE.PRED_GRANULARITY)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
@@ -99,21 +100,6 @@
                             controls-position="right"/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="数据点" prop="mmItemOutput.pointid">
            <el-select
              v-model="dataForm.mmItemOutput.pointid"
              filterable
              @change="changeOutputPoint"
              placeholder="请选择">
              <el-option
                v-for="item in pointList"
                :key="item.id"
                :label="item.pointName"
                :value="item.id"/>
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'MergeItem'">
        <el-col :span="12">
@@ -143,6 +129,7 @@
            :on-success="uploadModelSuccess"
            :on-error="uploadModelError"
            :action="uploadUrl"
            :show-file-list="false"
            :http-request="httpRequest">
            <el-button type="primary" @click="setReplaceModelOnly(false)">
              <Icon icon="ep:upload"/>
@@ -158,18 +145,7 @@
        </el-col>
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'NormalItem'">
        <el-col :span="8">
          <el-form-item label="结果">
            <el-select v-model="dataForm.mmPredictModel.resultstrid" placeholder="请选择">
              <el-option
                v-for="item in resultstridList"
                :key="item.id"
                :label="item.resultstr"
                :value="item.id"/>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="8">
        <el-col :span="12">
          <el-form-item label="关联项目">
            <el-select v-model="dataForm.mmPredictModel.mpkprojectid" placeholder="请选择">
              <el-option
@@ -180,7 +156,7 @@
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="8">
        <el-col :span="12">
          <el-form-item label="编号">
            <el-input
              v-model="dataForm.mmPredictModel.modelno" placeholder="编号" maxlength="30" readonly
@@ -222,14 +198,83 @@
          </el-form-item>
        </el-col>
      </el-row>
      <el-divider content-position="left" v-if="dataForm.itemtypename === 'NormalItem'">模型输出
      </el-divider>
      <el-button
        @click="addItemOutput(dataForm.mmItemOutputList)"
        type="primary"
        size="small">
        添加
      </el-button>
      <el-table
        v-if="dataForm.itemtypename === 'NormalItem'"
        :data="dataForm.mmItemOutputList"
        border
        style="width: 100%; margin-top: 5px;">
        <el-table-column prop="outputorder" label="排序" align="center" width="80px"/>
        <el-table-column label="结果" align="center" width="150px">
          <template #default="scope">
            <el-input v-model="scope.row.resultstr" placeholder="请输入"/>
          </template>
        </el-table-column>
        <el-table-column label="结果数据类型" align="center" width="150px">
          <template #default="scope">
            <el-select
              v-model="scope.row.resultType"
              @change="(value) => resultTypeChange(value,scope.row)"
              filterable
              placeholder="请选择">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.MODEL_RESULT_TYPE)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"/>
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="索引" align="center" width="120px">
          <template #default="scope">
            <el-input-number style="width:100%;hight:100%" :disabled="scope.row.resultType !== 2"
                             v-model="scope.row.resultIndex" :min="0" step-strictly
                             controls-position="right"/>
          </template>
        </el-table-column>
        <el-table-column label="数据点" align="center">
          <template #default="scope">
            <el-select
              v-model="scope.row.pointid"
              filterable
              @change="(value) => changeOutputPoint(value,scope.row)"
              placeholder="请选择">
              <el-option
                v-for="item in pointList"
                :key="item.id"
                :label="item.pointName"
                :value="item.id"/>
            </el-select>
          </template>
        </el-table-column>
        <el-table-column prop="" label="操作" width="80" align="center">
          <template #default="scope">
            <el-button
              @click="deleteItemOutput(scope.$index, dataForm.mmItemOutputList)"
              type="text"
              size="small">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-divider content-position="left" v-if="dataForm.itemtypename === 'NormalItem'">模型设置参数
      </el-divider>
      <el-table
        v-if="dataForm.itemtypename === 'NormalItem'"
        :data="dataForm.mmModelArithSettingsList"
        border
        style="width: 100%; margin-top: 5px;">
        <el-table-column prop="key" label="键" align="center"/>
        <el-table-column prop="name" label="名称" align="center"/>
        <el-table-column prop="valuetype" label="类型" align="center"/>
        <el-table-column prop="key" label="键" align="center" min-width="150"/>
        <el-table-column prop="name" label="名称" align="center" min-width="150"/>
        <el-table-column prop="valuetype" label="类型" align="center" min-width="150"/>
        <el-table-column prop="" label="值" align="center" min-width="150">
          <template #default="scope">
            <el-input size="mini" v-model="scope.row.value" maxlength="256"
@@ -237,7 +282,7 @@
          </template>
        </el-table-column>
      </el-table>
      <el-divider content-position="left" v-if="dataForm.itemtypename === 'NormalItem'">输入参数
      <el-divider content-position="left" v-if="dataForm.itemtypename === 'NormalItem'">模型输入参数
      </el-divider>
      <el-table
        v-if="dataForm.itemtypename === 'NormalItem'"
@@ -248,7 +293,9 @@
        <el-table-column prop="modelparamorder" label="序号" width="60" align="center"/>
        <el-table-column prop="" label="类型" width="200" align="center">
          <template #default="scope">
            <el-select v-model="scope.row.modelparamtype" placeholder="请选择">
            <el-select v-model="scope.row.modelparamtype"
                       @change="changeModelparamtype(scope.row)"
                       placeholder="请选择">
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.MODEL_PARAM_TYPE)"
                :key="dict.value"
@@ -280,18 +327,18 @@
                             style="width:100%;hight:100%"/>
          </template>
        </el-table-column>
        <el-table-column prop="" label="操作" width="140" align="center">
        <el-table-column prop="" label="操作" width="120" align="center">
          <template #default="scope">
            <el-button
              @click="addRow(scope.$index, dataForm.mmModelParamList)"
              type="text"
              size="small">
              size="mini">
              添加
            </el-button>
            <el-button
              @click="deleteRow(scope.$index, dataForm.mmModelParamList)"
              type="text"
              size="small">
              size="mini">
              删除
            </el-button>
          </template>
@@ -316,8 +363,8 @@
              <el-option
                v-for="(item, index) in predictItemList"
                :key="index"
                :label="item.name"
                :value="item.code"/>
                :label="item.itemname"
                :value="item.itemno"/>
            </el-select>
          </template>
        </el-table-column>
@@ -330,7 +377,7 @@
          label="运算符"
          align="center">
          <template #default="scope">
            <el-select v-model="scope.row.operator" placeholder="请选择">
            <el-select v-model="scope.row.operator" placeholder="请选择" clearable>
              <el-option
                v-for="item in operatorList"
                :key="item"
@@ -369,13 +416,12 @@
</template>
<script lang="ts" setup>
import {DICT_TYPE, getIntDictOptions, getStrDictOptions} from '@/utils/dict'
import * as MmPredictItem from '@/api/model/pre/predict'
import * as MmItemType from '@/api/model/pre/item'
import * as MmPredictItem from '@/api/model/pre/item'
import * as MmItemType from '@/api/model/pre/type'
import * as DmModule from '@/api/model/pre/dm'
import * as MmResultTable from '@/api/model/pre/result'
import * as ProjectApi from '@/api/model/mpk/project'
import * as DaPoint from '@/api/data/da/point'
import {useUpload} from '@/api/model/pre/predict'
import {useUpload} from '@/api/model/pre/item'
import * as ScheduleModelApi from '@/api/model/sche/model'
const {uploadUrl, httpRequest} = useUpload()
@@ -391,7 +437,6 @@
const itemTypeList = ref([])
const itemTypeMap = ref({})
const moduleList = ref([])
const resultstridList = ref([])
const mpkProjectList = ref([])
const pointNoList = ref([])
const pointList = ref([])
@@ -435,14 +480,7 @@
    status: undefined,
    categoryid: undefined
  },
  mmItemOutput: {
    id: undefined,
    itemid: undefined,
    pointid: undefined,
    resulttableid: undefined,
    tagname: undefined,
    outputorder: undefined
  },
  mmItemOutputList: [],
  mmPredictModel: {
    id: undefined,
    modelno: undefined,
@@ -489,7 +527,6 @@
  'mmPredictItem.status': [{required: true, message: '是否启用不能为空', trigger: 'blur'}],
  'dmModuleItem.moduleid': [{required: true, message: '管网不能为空', trigger: 'blur'}],
  'dmModuleItem.itemorder': [{required: true, message: '排序不能为空', trigger: 'blur'}],
  'mmItemOutput.pointid': [{required: true, message: '数据点不能为空', trigger: 'blur'}],
})
const formRef = ref() // 表单 Ref
@@ -503,15 +540,6 @@
  resetForm()
  resetFields(dataForm.value)
  setDefaultFields()
  // 修改时,设置数据
  if (id) {
    formLoading.value = true
    try {
      getInfo(id)
    } finally {
      /*formLoading.value = false*/
    }
  }
  // 加载参数列表
  modelparamListMap.value = await ScheduleModelApi.getModelParamList()
@@ -524,26 +552,37 @@
  if (!dataForm.value.id) {
    dataForm.value.mmPredictItem.itemtypeid = itemTypeList.value[0].id
  }
  dataForm.value.itemtypename = itemTypeMap[dataForm.value.mmPredictItem.itemtypeid]
  // 获取管网列表
  moduleList.value = await DmModule.getModuleList()
  // 获取结果字符串列表
  resultstridList.value = await MmResultTable.getResultstridList()
  // 获取mpk项目列表
  mpkProjectList.value = await ProjectApi.list()
  // 获取normal列表
  predictItemList.value = await MmPredictItem.getMmPredictItemList({
    itemtypename: 'NormalItem'
  })
  // 获取数据点列表
  pointNoList.value = await DaPoint.getPointList(queryParams)
  if (pointNoList.value.length > 0) {
    pointList.value = []
    pointNoList.value.forEach(function (value) {
      pointList.value.push(value)
      pointMap[value.id] = value.pointname
      pointMap[value.id] = value.pointName
    })
  }
  // 修改时,设置数据
  if (id) {
    formLoading.value = true
    try {
      getInfo(id)
    } finally {
      formLoading.value = false
    }
  }
  formLoading.value = false
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
@@ -555,6 +594,22 @@
  if (!formRef) return
  const valid = await formRef.value.validate()
  if (!valid) return
  //校验模型输出
  if (dataForm.value.mmItemOutputList == undefined || dataForm.value.mmItemOutputList.length <= 0) {
    message.error("模型输出不为空")
    return
  }
  let flag = false
  dataForm.value.mmItemOutputList.forEach(e => {
    if (e.resultstr == undefined || e.resultstr === '' || e.resultType == undefined || e.resultType === '' || e.pointid == undefined || e.pointid === '' || (e.resultType === 2 && (e.resultIndex == undefined || e.resultIndex === ''))) {
      message.error("模型输出数据异常")
      flag = true
    }
  })
  if (flag) return
  // 提交请求
  formLoading.value = true
  try {
@@ -608,6 +663,7 @@
const getInfo = async (id) => {
  dataForm.value = await MmPredictItem.getMmPredictItem(id)
  dataForm.value.itemtypename = itemTypeMap[dataForm.value.mmPredictItem.itemtypeid]
  expressionList.value = []
  if (dataForm.value.mmPredictMergeItem && dataForm.value.mmPredictMergeItem.expression) {
    let expression = dataForm.value.mmPredictMergeItem.expression
@@ -710,12 +766,12 @@
  dataForm.value.itemtypename = itemTypeMap[value]
}
function changeModelparamtype(value, row) {
function changeModelparamtype(row) {
  row.modelparamid = ''
}
function changeOutputPoint(value) {
  dataForm.value.mmItemOutput.tagname = pointMap[value]
function changeOutputPoint(value, row) {
  row.tagname = pointMap[value]
}
function deleteExpressionRow(index, rows) {
@@ -742,6 +798,37 @@
  orderRow(rows)
}
function addItemOutput(list) {
  list.push({})
  orderItemOutput(list)
}
function deleteItemOutput(index: string, rows) {
  if (!rows || rows.length === 1) {
    message.error('不能全部删除!')
    return
  }
  rows.splice(index, 1)
  orderItemOutput(rows)
}
function orderItemOutput(list) {
  list.sort((a, b) => a.outputorder - b.outputorder);
  let outputorder = 1
  list.forEach(function (value) {
    value.outputorder = outputorder
    outputorder++
  })
}
function resultTypeChange(value, row) {
  if (value === 1) {
    row.resultIndex = undefined
  } else if (value === 2) {
    row.resultIndex = 0
  }
}
function orderRow(rows) {
  let modelparamorder = 0
  let modelparamportorder = 0
@@ -766,8 +853,7 @@
  dataForm.value.mmPredictModel.trainsamplength = 60
  dataForm.value.mmPredictModel.isonlinetrain = 0
  dataForm.value.mmPredictModel.status = 1
  dataForm.value.mmItemOutput.outputorder = 1
  dataForm.value.mmItemOutput.resulttableid = '3cc2b483-3a01-40f7-a419-0c260210d8eb'
  dataForm.value.mmItemOutputList = []
  expressionList.value = [{
    point: '',
    operator: ''
@@ -805,14 +891,7 @@
      status: 1,
      categoryid: ''
    },
    mmItemOutput: {
      id: '',
      itemid: '',
      pointid: '',
      resulttableid: '3cc2b483-3a01-40f7-a419-0c260210d8eb',
      tagname: '',
      outputorder: 1
    },
    mmItemOutputList: [],
    mmPredictModel: {
      id: '',
      modelno: '',
src/views/model/pre/item/index.vue
@@ -51,18 +51,18 @@
  <!-- 列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list">
      <el-table-column label="编号" align="center" prop="itemno"/>
      <el-table-column label="编号" align="center" min-width="150" prop="itemno"/>
      <el-table-column label="预测项名" header-align="center" align="left" min-width="200" prop="itemname"/>
      <el-table-column label="类型名称" align="center" prop="itemtypename">
      <el-table-column label="类型名称" align="center" min-width="120" prop="itemtypename">
        <template #default="scope">
          <el-tag v-if="scope.row.itemtypename === 'NormalItem'" size="small" type="success">{{scope.row.itemtypename}}</el-tag>
          <el-tag v-else size="small" type="primary">{{scope.row.itemtypename}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="预测长度" align="center" prop="predictlength"/>
      <el-table-column label="预测长度(min)" align="center" prop="predictlength"/>
      <el-table-column label="粒度" align="center" prop="granularity">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.TIME_GRANULARITY" :value="scope.row.granularity" />
          <dict-tag :type="DICT_TYPE.PRED_GRANULARITY" :value="scope.row.granularity" />
        </template>
      </el-table-column>
      <el-table-column label="是否融合" align="center" prop="isfuse">
@@ -80,21 +80,29 @@
          <dict-tag :type="DICT_TYPE.COM_IS_INT" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="数据点名称" align="center" prop="tagname"/>
      <el-table-column label="存放表" align="center" prop="tablename"/>
      <el-table-column label="操作" align="center" min-width="110" fixed="right">
      <el-table-column label="运行时间" min-width="150" align="center" prop="lastTime"/>
      <el-table-column label="运行状态" align="center" prop="runStatus">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.ITEM_RUN_STATUS" :value="scope.row.runStatus" />
        </template>
      </el-table-column>
      <el-table-column label="运行耗时(s)" align="center" prop="duration"/>
      <el-table-column label="操作" align="center" min-width="120" fixed="right">
        <template #default="scope">
          <el-button
            link
            type="primary"
            size="mini"
            @click="openForm('update', scope.row.id, scope.row.itemtypename)"
            v-hasPermi="['model:pre-item:update']"
          >
            编辑
          </el-button>
          <el-button link size="mini" type="primary" @click="chartHandle(scope.row)">数据</el-button>
          <el-button
            link
            type="danger"
            size="mini"
            @click="handleDelete(scope.row.id)"
            v-hasPermi="['model:pre-item:delete']"
          >
@@ -115,10 +123,14 @@
  <!-- 表单弹窗:添加/修改 -->
  <MmPredictItemForm ref="formRef" @success="getList"/>
  <!-- 表单弹窗:数据 -->
  <MmPredictItemChart ref="chartView" @success="getList"/>
</template>
<script lang="ts" setup>
import MmPredictItemForm from './MmPredictItemForm.vue'
import * as MmPredictItem from '@/api/model/pre/predict'
import MmPredictItemChart from './MmPredictItemChart.vue'
import * as MmPredictItem from '@/api/model/pre/item'
import {DICT_TYPE} from "@/utils/dict";
defineOptions({name: 'DataMmPredictItem'})
@@ -171,6 +183,12 @@
  handleQuery()
}
/** 查看数据操作 */
const chartView  = ref()
const chartHandle = (raw: object) => {
  chartView.value.open(raw)
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number, itemtypename?: string) => {
src/views/model/pre/result/MmResultTableForm.vue
文件已删除
src/views/model/pre/result/index.vue
文件已删除
src/views/model/pre/type/ItemTypeForm.vue
@@ -36,7 +36,7 @@
  </Dialog>
</template>
<script lang="ts" setup>
import * as MmItemType from '@/api/model/pre/item'
import * as MmItemType from '@/api/model/pre/type'
defineOptions({name: 'DataMmItemTypeForm'})
src/views/model/pre/type/index.vue
@@ -82,7 +82,7 @@
</template>
<script lang="ts" setup>
import MmItemTypeForm from './ItemTypeForm.vue'
import * as MmItemType from '@/api/model/pre/item'
import * as MmItemType from '@/api/model/pre/type'
defineOptions({name: 'DataMmItemType'})
src/views/model/wiki/index.vue
文件名从 src/views/model/swagger/index.vue 修改
@@ -6,7 +6,7 @@
<script lang="ts" setup>
import * as ConfigApi from '@/api/infra/config'
defineOptions({ name: 'ModelSwagger' })
defineOptions({ name: 'ModelWiki' })
const loading = ref(true) // 是否加载中
const src = ref(import.meta.env.VITE_BASE_URL + '/doc.html')
@@ -15,7 +15,7 @@
/** 初始化 */
onMounted(async () => {
  try {
    const data = await ConfigApi.getConfigKey('model.swagger')
    const data = await ConfigApi.getConfigKey('model.wiki')
    if (data && data.length > 0) {
      src.value = data
    }
types/env.d.ts
@@ -25,10 +25,12 @@
  readonly VITE_UPLOAD_URL: string
  readonly VITE_API_URL: string
  readonly VITE_BASE_PATH: string
  readonly VITE_VIDEO_CAMERA_DOMAIN: string
  readonly VITE_DROP_DEBUGGER: string
  readonly VITE_DROP_CONSOLE: string
  readonly VITE_SOURCEMAP: string
  readonly VITE_OUT_DIR: string
  readonly VITE_STATIC_DIR: string
}
declare global {