From e64ac35f0be48b2c4f88861f5da1eae314ac158e Mon Sep 17 00:00:00 2001
From: houzhongjian <houzhongyi@126.com>
Date: 星期四, 31 十月 2024 10:30:52 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 src/views/data/point/index.vue           |   71 ++++++-
 src/api/data/da/point/index.ts           |   10 +
 src/api/data/da/point/daPointChart.ts    |   17 +
 src/utils/dict.ts                        |    2 
 .env                                     |    2 
 src/views/data/point/PointImportForm.vue |  138 +++++++++++++++
 src/views/data/point/DaPointChart.vue    |  223 ++++++++++++++++++++++++
 src/views/data/point/DaPointForm.vue     |   35 ++-
 8 files changed, 473 insertions(+), 25 deletions(-)

diff --git a/.env b/.env
index 1b6f300..3167d36 100644
--- a/.env
+++ b/.env
@@ -2,7 +2,7 @@
 VITE_APP_TITLE=工业互联网平台
 
 # 项目本地运行端口号
-VITE_PORT=80
+VITE_PORT=3000
 
 # open 运行 npm run dev 时自动打开浏览器
 VITE_OPEN=true
diff --git a/src/api/data/da/point/daPointChart.ts b/src/api/data/da/point/daPointChart.ts
new file mode 100644
index 0000000..1090f88
--- /dev/null
+++ b/src/api/data/da/point/daPointChart.ts
@@ -0,0 +1,17 @@
+import request from '@/config/axios'
+
+export interface DaPointChartReqVO {
+  pointNos?: [],
+  start?: Date,
+  end?: Date,
+}
+
+// 查询chart列表
+export const getPointDaChart = (data: DaPointChartReqVO) => {
+  return request.post({ url: '/data/api/query-points/chart', data })
+}
+
+//导出DaPointValue
+export const exportDaPointValue = (params) => {
+  return request.download({ url: '/data/da/point/exportValue', params })
+}
diff --git a/src/api/data/da/point/index.ts b/src/api/data/da/point/index.ts
index 5556ae2..bb0a516 100644
--- a/src/api/data/da/point/index.ts
+++ b/src/api/data/da/point/index.ts
@@ -52,3 +52,13 @@
 export const deleteDaPoint = (id: number) => {
   return request.delete({ url: '/data/da/point/delete?id=' + id })
 }
+
+//导出DaPointList
+export const exportDaPoint = (params) => {
+  return request.download({ url: '/data/da/point/export', params })
+}
+
+// 下载用户导入模板
+export const importPointTemplate = () => {
+  return request.download({ url: '/data/da/point/get-import-template' })
+}
diff --git a/src/utils/dict.ts b/src/utils/dict.ts
index c2403d5..86bd05a 100644
--- a/src/utils/dict.ts
+++ b/src/utils/dict.ts
@@ -179,7 +179,7 @@
   COM_IS_INT = 'com_is_int',
   DATA_POINT_TYPE = 'data_point_type',
   MINFREQID = 'minfreqid',
-  VALUETYPE = 'value_type',
+  MEASURE_VALUE_TYPE = 'measure_value_type',
   NVR_ONLINE_STATUS = 'nvr_online_status',
   CAMERA_BRAND = 'camera_brand',
   CAPTURE_TYPE = 'capture_type',
diff --git a/src/views/data/point/DaPointChart.vue b/src/views/data/point/DaPointChart.vue
new file mode 100644
index 0000000..ddc9a2d
--- /dev/null
+++ b/src/views/data/point/DaPointChart.vue
@@ -0,0 +1,223 @@
+<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>
+        <el-date-picker
+          v-model="dataForm.startTime"
+          type="datetime"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          placeholder="选择日期时间"
+       />
+      </el-form-item>
+      <el-form-item>
+        <el-date-picker
+          v-model="dataForm.endTime"
+          type="datetime"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          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"
+          v-hasPermi="['data:point:export']"
+        >
+          <Icon icon="ep:download" />导出
+        </el-button>
+      </el-form-item>
+    </el-form>
+    <div ref="chartDom" class="result-chart"></div>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+  import {ref} from 'vue';
+  import * as echarts from 'echarts';
+  import * as DaPoint from '@/api/data/da/point/daPointChart'
+  import download from "@/utils/download";
+  const message = useMessage() // 消息弹窗
+  const visible = ref(false);
+  const chartDom = ref(null);
+  let myChart = null;
+  const dataForm = ref({
+    id: "",
+    pointNo: "",
+    pointName: "",
+    pointTypeName: "",
+    startTime: getYMDHMS(),
+    endTime: undefined,
+  });
+  const queryParams = reactive({
+    codes: [],
+    startDate: undefined,
+    endDate: undefined,
+  })
+  function getYMDHMS() {
+    let timestamp = new Date().getTime();
+    let time = new Date(timestamp - 1000 * 60 * 30);
+    let year = time.getFullYear();
+    let month = (time.getMonth() + 1).toString();
+    let date = time.getDate().toString();
+    let hours = time.getHours().toString();
+    let minute = time.getMinutes().toString();
+
+    if (month < 10) {
+      month = "0" + month;
+    }
+    if (date < 10) {
+      date = "0" + date;
+    }
+    if (hours < 10) {
+      hours = "0" + hours;
+    }
+    if (minute < 10) {
+      minute = "0" + minute;
+    }
+    return (
+      year +
+      "-" +
+      month +
+      "-" +
+      date +
+      " " +
+      hours +
+      ":" +
+      minute +
+      ":" +
+      "00"
+    );
+  }
+  /** 打开弹窗 */
+  const open = async (row: object) => {
+    visible.value = true
+    dataForm.value.id = row.id;
+    dataForm.value.pointNo = row.pointNo;
+    dataForm.value.pointName = row.pointName;
+    dataForm.value.startTime = getYMDHMS();
+    dataForm.value.endTime = "";
+    getDataList();
+  }
+
+  defineExpose({open}) // 提供 open 方法,用于打开弹窗
+
+  async function getDataList() {
+    visible.value = true;
+    if (dataForm.value.id) {
+      try {
+        queryParams.codes=[dataForm.value.pointNo];
+        queryParams.startDate = dataForm.value.startTime;
+        queryParams.endDate = dataForm.value.endTime;
+        const data = await DaPoint.getPointDaChart(queryParams)
+        let seriesData = []
+        data.series.forEach(item => {
+          seriesData.push({
+            name: item.name,
+            type: "line",
+            data: item.data,
+            showSymbol: true,
+            smooth: false,
+            lineStyle: {
+              normal: {
+                color: "#5B8FF9",
+                width: 1,
+              },
+            },
+          });
+        })
+
+        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",
+              },
+            },
+          },
+          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)
+      }
+    }
+  }
+  /** 导出按钮操作 */
+  const exportLoading = ref(false)
+  const handleExport = async () => {
+    queryParams.pointNos=[dataForm.value.pointNo];
+    queryParams.start = dataForm.value.startTime;
+    queryParams.end = dataForm.value.endTime;
+    try {
+      // 导出的二次确认
+      await message.exportConfirm()
+      // 发起导出
+      exportLoading.value = true
+      const data = await DaPoint.exportDaPointValue(queryParams)
+      download.excel(data, dataForm.value.pointName +'.xls')
+    } catch {
+    } finally {
+      exportLoading.value = false
+    }
+  }
+</script>
+<style>
+  .el-select {
+    width: 100%;
+  }
+
+  .result-chart {
+    height: 500px;
+  }
+</style>
diff --git a/src/views/data/point/DaPointForm.vue b/src/views/data/point/DaPointForm.vue
index 85758ec..ac36580 100644
--- a/src/views/data/point/DaPointForm.vue
+++ b/src/views/data/point/DaPointForm.vue
@@ -1,5 +1,5 @@
 <template>
-  <Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%">
     <el-form
       ref="formRef"
       v-loading="formLoading"
@@ -137,14 +137,14 @@
       </el-row>
       <el-row v-if="formData.pointType === 'MEASURE'">
         <el-col :span="12">
-          <el-form-item label="值类型" prop="valueType">
+          <el-form-item label="测量值类型" prop="measurePoint.valueType">
             <el-select
               v-model="formData.measurePoint.valueType"
               clearable
               placeholder="请选择值类型"
             >
               <el-option
-                v-for="dict in getDictOptions(DICT_TYPE.VALUETYPE)"
+                v-for="dict in getDictOptions(DICT_TYPE.MEASURE_VALUE_TYPE)"
                 :key="dict.value"
                 :label="dict.label"
                 :value="dict.value"
@@ -153,8 +153,10 @@
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="平滑尺度" prop="dimension">
-            <el-input-number v-model="formData.measurePoint.dimension" style="width: 100%" :controls="false"/>
+          <el-form-item label="平滑尺度(min)" prop="measurePoint.dimension">
+            <el-input-number v-model="formData.measurePoint.dimension" style="width: 100%"
+                             :min="0" :max="100"
+                             :controls="false"/>
           </el-form-item>
         </el-col>
       </el-row>
@@ -166,11 +168,11 @@
               :data="expressionList"
               border
               style="width: 100%">
-<!--              <el-table-column
+              <el-table-column
                 type="index"
                 align="center"
-                width="50"
-                label="序号"/>-->
+                width="60"
+                label="序号"/>
               <el-table-column
                 prop=""
                 label="左括号"
@@ -194,6 +196,7 @@
               <el-table-column
                 prop=""
                 label="测点"
+                min-width="160"
                 align="center">
                 <template #default="scope">
                   <el-select
@@ -211,6 +214,7 @@
               <el-table-column
                 prop=""
                 label="运算值"
+                min-width="120"
                 align="center">
                 <template #default="scope">
                   <el-input
@@ -243,7 +247,7 @@
               <el-table-column
                 prop=""
                 label="运算符"
-                width="120"
+                width="100"
                 align="center">
                 <template #default="scope">
                   <el-select v-model="scope.row.operator" clearable>
@@ -258,19 +262,19 @@
               <el-table-column
                 prop=""
                 label="操作"
-                width="140"
+                width="120"
                 align="center">
                 <template #default="scope">
                   <el-button
                     @click="addExpressionRow(scope.$index, expressionList)"
                     type="text"
-                    size="small">
+                    size="mini">
                     添加
                   </el-button>
                   <el-button
                     @click="deleteExpressionRow(scope.$index, expressionList)"
                     type="text"
-                    size="small">
+                    size="mini">
                     删除
                   </el-button>
                 </template>
@@ -345,6 +349,9 @@
   pointName: [{required: true, message: '测点名称不能为空', trigger: 'blur'}],
   pointType: [{required: true, message: '测点类型不能为空', trigger: 'blur'}],
   dataType: [{required: true, message: '数据类型不能为空', trigger: 'blur'}],
+  minfreqid: [{required: true, message: '采集频率不能为空', trigger: 'blur'}],
+  "measurePoint.valueType": [{required: true, message: '采集频率不能为空', trigger: 'blur'}],
+  "measurePoint.dimension": [{required: true, message: '采集频率不能为空', trigger: 'blur'}],
 })
 const formRef = ref() // 表单 Ref
 
@@ -438,8 +445,8 @@
     storeType: undefined,
     unit: undefined,
     unittransfactor: 1,
-    defaultValue: 10,
-    maxValue: 10000000,
+    defaultValue: 0,
+    maxValue: 100000000,
     minValue: 0,
     minfreqid: undefined,
     remark: undefined,
diff --git a/src/views/data/point/PointImportForm.vue b/src/views/data/point/PointImportForm.vue
new file mode 100644
index 0000000..964e3d6
--- /dev/null
+++ b/src/views/data/point/PointImportForm.vue
@@ -0,0 +1,138 @@
+<template>
+  <Dialog v-model="dialogVisible" title="测点导入" width="400">
+    <el-upload
+      ref="uploadRef"
+      v-model:file-list="fileList"
+      :action="importUrl + '?updateSupport=' + updateSupport"
+      :auto-upload="false"
+      :disabled="formLoading"
+      :headers="uploadHeaders"
+      :limit="1"
+      :on-error="submitFormError"
+      :on-exceed="handleExceed"
+      :on-success="submitFormSuccess"
+      accept=".xlsx, .xls"
+      drag
+    >
+      <Icon icon="ep:upload" />
+      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+      <template #tip>
+        <div class="el-upload__tip text-center">
+          <div class="el-upload__tip">
+            <el-checkbox v-model="updateSupport" />
+            是否更新已经存在的测点数据
+          </div>
+          <span>仅允许导入 xls、xlsx 格式文件。</span>
+          <el-link
+            :underline="false"
+            style="font-size: 12px; vertical-align: baseline"
+            type="primary"
+            @click="importTemplate"
+          >
+            下载模板
+          </el-link>
+        </div>
+      </template>
+    </el-upload>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as DaPointApi from '@/api/data/da/point'
+import { getAccessToken, getTenantId } from '@/utils/auth'
+import download from '@/utils/download'
+
+defineOptions({ name: 'PointImportForm' })
+
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中
+const uploadRef = ref()
+const importUrl =
+  import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/data/da/point/import'
+const uploadHeaders = ref() // 上传 Header 头
+const fileList = ref([]) // 文件列表
+const updateSupport = ref(0) // 是否更新已经存在的测点数据
+
+/** 打开弹窗 */
+const open = () => {
+  dialogVisible.value = true
+  updateSupport.value = 0
+  fileList.value = []
+  resetForm()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const submitForm = async () => {
+  if (fileList.value.length == 0) {
+    message.error('请上传文件')
+    return
+  }
+  // 提交请求
+  uploadHeaders.value = {
+    Authorization: 'Bearer ' + getAccessToken(),
+    'tenant-id': getTenantId()
+  }
+  formLoading.value = true
+  uploadRef.value!.submit()
+}
+
+/** 文件上传成功 */
+const emits = defineEmits(['success'])
+const submitFormSuccess = (response: any) => {
+  if (response.code !== 0) {
+    message.error(response.msg)
+    formLoading.value = false
+    return
+  }
+  // 拼接提示语
+  const data = response.data
+  let text = '上传成功数量:' + data.createPointnames.length + ';'
+  for (let pointname of data.createPointnames) {
+    text += '< ' + pointname + ' >'
+  }
+  text += '更新成功数量:' + data.updatePointnames.length + ';'
+  for (const pointname of data.updatePointnames) {
+    text += '< ' + pointname + ' >'
+  }
+  text += '更新失败数量:' + Object.keys(data.failurePointnames).length + ';'
+  for (const pointname in data.failurePointnames) {
+    text += '< ' + pointname + ': ' + data.failurePointnames[pointname] + ' >'
+  }
+  message.alert(text)
+  formLoading.value = false
+  dialogVisible.value = false
+  // 发送操作成功的事件
+  emits('success')
+}
+
+/** 上传错误提示 */
+const submitFormError = (): void => {
+  message.error('上传失败,请您重新上传!')
+  formLoading.value = false
+}
+
+/** 重置表单 */
+const resetForm = async (): Promise<void> => {
+  // 重置上传状态和文件
+  formLoading.value = false
+  await nextTick()
+  uploadRef.value?.clearFiles()
+}
+
+/** 文件数超出提示 */
+const handleExceed = (): void => {
+  message.error('最多只能上传一个文件!')
+}
+
+/** 下载模板操作 */
+const importTemplate = async () => {
+  const res = await DaPointApi.importPointTemplate()
+  download.excel(res, '测点导入模版.xls')
+}
+</script>
diff --git a/src/views/data/point/index.vue b/src/views/data/point/index.vue
index a7e20cf..af108ba 100644
--- a/src/views/data/point/index.vue
+++ b/src/views/data/point/index.vue
@@ -53,6 +53,23 @@
           <Icon icon="ep:plus" class="mr-5px" />
           新增
         </el-button>
+        <el-button
+          type="warning"
+          plain
+          @click="handleImport"
+          v-hasPermi="['data:point:import']"
+        >
+          <Icon icon="ep:upload" /> 导入
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          @click="handleExport"
+          :loading="exportLoading"
+          v-hasPermi="['data:point:export']"
+        >
+          <Icon icon="ep:download" />导出
+        </el-button>
       </el-form-item>
     </el-form>
   </ContentWrap>
@@ -60,7 +77,7 @@
   <!-- 列表 -->
   <ContentWrap>
     <el-table border stripe v-loading="loading" :data="list">
-      <el-table-column fixed label="测点编码" header-align="center" align="left" min-width="110" prop="pointNo" />
+      <el-table-column fixed label="测点编码" header-align="center" align="left" min-width="120" prop="pointNo" />
       <el-table-column label="测点名称" header-align="center" align="left" min-width="200" prop="pointName" />
       <el-table-column label="测点类型" align="center" prop="pointType" width="100">
         <template #default="scope">
@@ -77,12 +94,12 @@
           <dict-tag :type="DICT_TYPE.VALUETYPE" :value="scope.row.valueType" />
         </template>
       </el-table-column>
-      <el-table-column label="测量单位" align="center" prop="unit" width="100"/>
+      <el-table-column label="测量单位" align="center" prop="unit" width="80"/>
       <el-table-column label="单位转换" align="center" prop="unittransfactor" />
-      <el-table-column label="默认值" align="center" prop="defaultValue" />
+      <el-table-column label="默认值" align="center" prop="defaultValue" width="100"/>
       <el-table-column label="采集频率" align="center" prop="minfreqid" width="100"/>
-      <el-table-column label="数据源类型" align="center" prop="sourceType" width="100"/>
-      <el-table-column label="数据源名称" align="center" prop="sourceName" width="100"/>
+      <el-table-column label="数据源类型" align="center" prop="sourceType" min-width="100"/>
+      <el-table-column label="数据源名称" align="center" prop="sourceName" min-width="100"/>
       <el-table-column label="测点Tag" header-align="center" align="left" prop="tagNo" min-width="150"/>
       <el-table-column label="是否启用" align="center" prop="isEnable" width="100">
         <template #default="scope">
@@ -91,18 +108,21 @@
         </template>
       </el-table-column>
 
-      <el-table-column label="操作" align="center" min-width="110" fixed="right" width="120">
+      <el-table-column label="操作" align="center" min-width="130" fixed="right" width="160">
         <template #default="scope">
           <el-button
             link
+            size="mini"
             type="primary"
             @click="openForm('update', scope.row.id)"
             v-hasPermi="['data:point:update']"
           >
             编辑
           </el-button>
+          <el-button link size="mini" type="primary" @click="chartHandle(scope.row)">数据</el-button>
           <el-button
             link
+            size="mini"
             type="danger"
             @click="handleDelete(scope.row.id)"
             v-hasPermi="['data:point:delete']"
@@ -124,11 +144,19 @@
   <!-- 表单弹窗:添加/修改 -->
   <DaPointForm ref="formRef" @success="getList" />
 
+  <DaPointChart ref="chartView" />
+
+  <!-- 用户导入对话框 -->
+  <PointImportForm ref="importFormRef" @success="getList" />
 </template>
 <script lang="ts" setup>
-import DaPointForm from './DaPointForm.vue'
 import * as DaPoint from '@/api/data/da/point'
-import {DICT_TYPE} from "@/utils/dict";
+import {ref} from "vue";
+import download from "@/utils/download";
+import {DICT_TYPE, getDictOptions} from "@/utils/dict";
+import DaPointForm from './DaPointForm.vue'
+import DaPointChart from './DaPointChart.vue'
+import * as UserApi from "@/api/system/user";
 
 defineOptions({name: 'DataPoint'})
 
@@ -146,7 +174,6 @@
     tagNo: undefined,
   })
   const queryFormRef = ref() // 搜索的表单
-  const exportLoading = ref(false) // 导出的加载中
 
   /** 查询列表 */
   const getList = async () => {
@@ -164,6 +191,12 @@
   const handleQuery = () => {
     queryParams.pageNo = 1
     getList()
+  }
+
+  /** 查看数据操作 */
+  const chartView  = ref()
+  const chartHandle = (raw: object) => {
+    chartView.value.open(raw)
   }
 
   /** 重置按钮操作 */
@@ -191,7 +224,27 @@
     } catch {
     }
   }
+  /** 测点导入 */
+  const importFormRef = ref()
+  const handleImport = () => {
+    importFormRef.value.open()
+  }
 
+  /** 导出按钮操作 */
+  const exportLoading = ref(false)
+  const handleExport = async () => {
+    try {
+      // 导出的二次确认
+      await message.exportConfirm()
+      // 发起导出
+      exportLoading.value = true
+      const data = await DaPoint.exportDaPoint(queryParams)
+      download.excel(data, '测点列表.xlsx')
+    } catch {
+    } finally {
+      exportLoading.value = false
+    }
+  }
   /** 初始化 **/
   onMounted(async () => {
     await getList()

--
Gitblit v1.9.3