Ver Fonte

实现数据库表数据读取

liujiejie há 7 meses atrás
pai
commit
a24cda9c78

+ 2 - 2
package-lock.json

@@ -5922,7 +5922,7 @@
     },
     "node_modules/body-parser": {
       "version": "1.20.3",
-      "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/body-parser/-/body-parser-1.20.3.tgz",
       "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
       "dev": true,
       "license": "MIT",
@@ -26181,7 +26181,7 @@
     },
     "body-parser": {
       "version": "1.20.3",
-      "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/body-parser/-/body-parser-1.20.3.tgz",
       "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
       "dev": true,
       "requires": {

+ 142 - 96
src/views/performance/components/custonAsCom/DatabaseTable.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-10-28 17:43:21
- * @LastEditTime: 2024-11-06 14:05:23
+ * @LastEditTime: 2024-12-05 14:33:36
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/custonAsCom/DatabaseTable.vue
@@ -57,24 +57,30 @@
         width="550px"
         :before-close="handleClose"
       >
-        <el-input placeholder="输入关键字进行过滤" v-model="filterText">
-        </el-input>
+        <el-input
+          placeholder="输入关键字进行过滤"
+          v-model="filterText"
+        ></el-input>
         <el-tree
           class="filter-tree"
-          :data="data"
+          :data="treeData"
+          show-checkbox
           :props="defaultProps"
-          default-expand-all
+          :check-strictly="false"
           :filter-node-method="filterNode"
           ref="tree"
-          :render-content="renderContent"
         >
+          <!-- :check-strictly="false"
+          :render-content="renderContent" -->
         </el-tree>
         <el-row type="flex" class="row-bg" justify="space-between">
           <el-col :span="4">
-            <el-button size="small" @click="">选择相关表</el-button>
+            <el-button size="small" @click="handleClose">取消</el-button>
           </el-col>
           <el-col :span="4">
-            <el-button size="small" type="primary" @click="">加载</el-button>
+            <el-button size="small" type="primary" @click="loadData"
+              >加载</el-button
+            >
           </el-col>
         </el-row>
       </el-dialog>
@@ -82,6 +88,9 @@
   </div>
 </template>
 <script>
+import axios from "axios";
+import { storeSetData, initDatabase } from "@/utils/indexedDb";
+import { mapMutations, mapState } from "vuex";
 export default {
   props: {
     dialogVisible: {
@@ -118,65 +127,34 @@ export default {
         children: "children",
         label: "label",
       },
+      // 转换后的树数据
+      treeData: [],
       filterText: "", // 初始化 filterText
-      data: [
-        {
-          id: 1,
-          label: "一级 1",
-          children: [
-            {
-              id: 4,
-              label: "二级 1-1",
-              children: [
-                {
-                  id: 9,
-                  label: "三级 1-1-1",
-                },
-                {
-                  id: 10,
-                  label: "三级 1-1-2",
-                },
-              ],
-            },
-          ],
-        },
-        {
-          id: 2,
-          label: "一级 2",
-          children: [
-            {
-              id: 5,
-              label: "二级 2-1",
-            },
-            {
-              id: 6,
-              label: "二级 2-2",
-            },
-          ],
-        },
-        {
-          id: 3,
-          label: "一级 3",
-          children: [
-            {
-              id: 7,
-              label: "二级 3-1",
-            },
-            {
-              id: 8,
-              label: "二级 3-2",
-            },
-          ],
-        },
-      ],
+      data: [],
     };
   },
+  computed: {
+    ...mapState("dragChart", {
+      updateTriggerGetData: "updateTriggerGetData",
+    }),
+  },
   watch: {
     filterText(val) {
       this.$refs.tree.filter(val);
     },
+    updateTriggerGetData: function (newVal) {
+      if (newVal) {
+        console.log(newVal, "newVal dataTable");
+        this.setUpdateTriggerGetData(false);
+        // 重置 triggerGetData 状态为 false
+      }
+    },
   },
   methods: {
+    ...mapMutations("dragChart", [
+      "setTriggerGetData",
+      "setUpdateTriggerGetData",
+    ]),
     handleClose(done) {
       this.$confirm("确认关闭?")
         .then((_) => {
@@ -186,53 +164,117 @@ export default {
         })
         .catch((_) => {});
     },
+    // 将原始数据转化为树形数据
+    initializeTreeData(data) {
+      // 构建树结构
+      this.treeData = Object.keys(data).map((tableName) => ({
+        label: tableName,
+        children: data[tableName].map((field) => ({
+          label: field,
+          field: field,
+          parentNode: tableName,
+        })),
+      }));
+    },
+    // 节点过滤方法
     filterNode(value, data) {
-      if (!value) return true;
-      return data.label.indexOf(value) !== -1;
+      if (!value) return true; // 如果没有输入过滤文本,则显示所有节点
+      return data.label.indexOf(value) !== -1; // 如果节点的label包含过滤文本,则返回true
     },
-    renderContent(h, { node, data }) {
-      return h("span", [
-        // 检查是否为子节点,如果是,则显示 checkbox
-        node.level > 1
-          ? h("el-checkbox", {
-              props: {
-                value: node.checked,
-                // disabled: node.disabled,
-              },
-              on: {
-                change: (val) => {
-                  this.$refs.tree.setChecked(node, val);
-                },
-              },
-            })
-          : null, // 父节点不显示 checkbox
-
-        node.level > 1
-          ? h("svg-icon", {
-              props: {
-                "icon-class": "database1", // 设置自定义组件的属性
-              },
-              style: { margin: "0 10px" }, // 设置边距
-            })
-          : h("svg-icon", {
-              props: {
-                "icon-class": "dicfiles", // 设置自定义组件的属性
-              },
-              style: { margin: "0 10px" }, // 设置边距
-            }),
-        h("span", data.label),
-      ]);
+    // 加载按钮的点击事件
+    async loadData() {
+      const selectedNodes = this.$refs.tree.getCheckedNodes();
+      if (selectedNodes.length <= 0) {
+        this.$message.warning("请最少选择1表数据");
+        return;
+      }
+      const output = [];
+      // 使用 Map 分组数据
+      const grouped = new Map();
+      selectedNodes.forEach((item) => {
+        const { parentNode, label } = item;
+        if (!grouped.has(parentNode)) {
+          grouped.set(parentNode, []);
+        }
+        grouped.get(parentNode).push(label);
+      });
+      // 转换为目标结构
+      grouped.forEach((checkChildNode, parentNode) => {
+        output.push({ parentNode, checkChildNode });
+      });
+      const response = await axios.post("/databaseApi/filterTablesData", {
+        database: { ...this.formLabelAlign },
+        filterData: output,
+      });
+      this.conversionData(response.data);
     },
     submitForm(formName) {
-      this.$refs[formName].validate((valid) => {
+      this.$refs[formName].validate(async (valid) => {
         if (valid) {
-          this.$emit("closeDialog");
-          this.showDialog = true;
+          try {
+            const response = await axios.get("/databaseApi/tables", {
+              params: { ...this.formLabelAlign },
+            });
+            // this.tableFieldMap = response.data;
+            this.initializeTreeData(response.data);
+            this.$emit("closeDialog");
+            this.showDialog = true;
+          } catch (error) {
+            this.$message.error(
+              "数据库连接失败,请查看填写的数据库数据是否有误" + error
+            );
+            console.error("Failed to fetch database structure:", error);
+          }
         } else {
           return false;
         }
       });
     },
+    async conversionData(data) {
+      try {
+        // 定义最终结果数组
+        const result = Object.keys(data)
+          .filter((key) => data[key].length > 0) // 过滤掉空数组的字段
+          .map((key) => ({
+            fileData: data[key],
+            fileId: `${new Date().getTime()}_${key}`, // 添加唯一时间戳和字段标识
+            fileOldName: `${key}.xlsx`, // 文件原始名称
+            filename: `${new Date().getTime()}_${key}.xlsx`, // 包含时间戳的文件名
+          }));
+
+        // 初始化数据库
+        const database = await initDatabase();
+
+        // 遍历结果并存储数据
+        for (const item of result) {
+          await storeSetData(database, "files", "fileDataArray", item, () => {
+            this.$message({
+              type: "success",
+              message: `关联完成,关联表可在数据表格中查看。`,
+            });
+          });
+        }
+
+        // 更新状态和关闭对话框
+        this.setTriggerGetData(true);
+        this.showDialog = false;
+
+        // 重置表单
+        this.formLabelAlign = {
+          IP: "",
+          host: "",
+          baseName: "",
+          username: "",
+          password: "",
+        };
+      } catch (error) {
+        console.error("操作失败:", error);
+        this.$message({
+          type: "error",
+          message: "操作失败,详情请查看控制台。",
+        });
+      }
+    },
   },
 };
 </script>
@@ -241,6 +283,10 @@ export default {
   margin-bottom: 18px;
 }
 .row-bg {
-  margin-top: 50px;
+  margin-top: 30px;
+}
+.el-tree {
+  height: 350px;
+  overflow: scroll;
 }
 </style>

+ 194 - 47
src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/chartLogic/modules/scatter.js

@@ -1,15 +1,124 @@
 //散点图+散点折线图
 import { filterData } from "../dargChartFIlter";
+// export function handleScatterChartLogic(
+//   item, //curEdit编辑项配置
+//   formLabelAlign, //每一项label显示
+//   formFilterAlign, //过滤数据 数据筛选from
+//   isFilter, //是否是过滤
+//   type //图表类型
+// ) {
+//   // 封装柱状图的具体逻辑
+//   if (isFilter === "filter") {
+//     // 数据筛选逻辑
+//     const filterResult = formLabelAlign.Ydata.map((yItem, index) => ({
+//       label: yItem.label,
+//       id: yItem.id,
+//       data: yItem.data.map((val) => {
+//         const filters = formFilterAlign[index]?.filters || [];
+//         return val === null ||
+//           (filters.length > 0 && !filters.includes(val[yItem.label]))
+//           ? null
+//           : val;
+//       }),
+//     }));
+//     // 条件筛选逻辑
+//     const filterList = filterResult.map((filteredItem, index) => {
+//       const filter = formFilterAlign[index];
+//       const { filterType1, filterType2, number1, number2 } = filter;
+//       if (
+//         (number1 === null || number1 === "") &&
+//         (number2 === null || number2 === "")
+//       ) {
+//         return {
+//           label: filteredItem.label,
+//           id: filteredItem.id,
+//           data: filteredItem.data,
+//         };
+//       } else {
+//         const filterDatas = filterData(
+//           filter,
+//           filteredItem.data,
+//           filteredItem.label
+//         );
+//         return {
+//           label: filteredItem.label,
+//           id: filteredItem.id,
+//           data: [...filterDatas],
+//         };
+//       }
+//     });
+//     item.Xdata = formLabelAlign.Xdata;
+//     item.Ydata = filterList;
+//   } else {
+//     item.Xdata = formLabelAlign.Xdata;
+//     item.Ydata = formLabelAlign.Ydata;
+//   }
+//   item.option.xAxis = {
+//     ...item.option.xAxis,
+//     type: "value",
+//     name: formLabelAlign.Xlable,
+//   };
+//   item.option.yAxis = {
+//     ...item.option.yAxis,
+
+//     name: formLabelAlign.Ylable,
+//   };
+
+//   if (item.Ydata[0]?.data?.length > 0 || item.Xdata[0]?.data?.length > 0) {
+//     // console.log(item.Xdata, "item.Xdata", item.Ydata);
+//     item.option.series = (
+//       item.Xdata.length > item.Ydata.length ? item.Xdata : item.Ydata
+//     ).map((val, ind) => {
+//       const xData = item.Xdata[ind]?.data || [];
+//       const yData = item.Ydata[ind]?.data || [];
+
+//       const scatterData = xData
+//         .map((xItem, idx) => {
+//           const yItem = yData[idx];
+//           if (!xItem || !yItem) return null; // 如果对应项不存在,则跳过
+//           return [
+//             Number(
+//               xItem[item.Xdata[ind].label] === undefined
+//                 ? 0
+//                 : xItem[item.Xdata[ind].label]
+//             ) || 0, // 取 X 值
+//             Number(
+//               yItem[item.Ydata[ind].label] === undefined
+//                 ? 0
+//                 : yItem[item.Ydata[ind].label]
+//             ) || 0, // 取 Y 值
+//           ];
+//         })
+//         .filter((point) => point !== null); // 过滤掉无效的点
+
+//       return {
+//         name: `${item.Xdata[ind]?.label || "X"} - ${
+//           item.Ydata[ind]?.label || "Y"
+//         }`,
+//         type: type,
+//         renderMode: "webgl", // 启用 WebGL 渲染
+//         data: scatterData, // 生成的散点图数据
+//         tooltip: {
+//           trigger: "item", // 鼠标悬停触发
+//         },
+//         itemStyle: {
+//           shadowBlur: 3,
+//           shadowColor: "rgba(25, 100, 150, 0.3)",
+//           shadowOffsetY: 1.5,
+//         },
+//       };
+//     });
+//   }
+// }
 export function handleScatterChartLogic(
-  item, //curEdit编辑项配置
-  formLabelAlign, //每一项label显示
-  formFilterAlign, //过滤数据 数据筛选from
-  isFilter, //是否是过滤
-  type //图表类型
+  item, // curEdit编辑项配置
+  formLabelAlign, // 每一项label显示
+  formFilterAlign, // 过滤数据 数据筛选from
+  isFilter, // 是否是过滤
+  type // 图表类型
 ) {
-  // 封装柱状图的具体逻辑
+  // 处理过滤数据的逻辑
   if (isFilter === "filter") {
-    // 数据筛选逻辑
     const filterResult = formLabelAlign.Ydata.map((yItem, index) => ({
       label: yItem.label,
       id: yItem.id,
@@ -21,7 +130,7 @@ export function handleScatterChartLogic(
           : val;
       }),
     }));
-    // 条件筛选逻辑
+
     const filterList = filterResult.map((filteredItem, index) => {
       const filter = formFilterAlign[index];
       const { filterType1, filterType2, number1, number2 } = filter;
@@ -53,6 +162,7 @@ export function handleScatterChartLogic(
     item.Xdata = formLabelAlign.Xdata;
     item.Ydata = formLabelAlign.Ydata;
   }
+
   item.option.xAxis = {
     ...item.option.xAxis,
     type: "value",
@@ -60,12 +170,11 @@ export function handleScatterChartLogic(
   };
   item.option.yAxis = {
     ...item.option.yAxis,
-
     name: formLabelAlign.Ylable,
   };
 
   if (item.Ydata[0]?.data?.length > 0 || item.Xdata[0]?.data?.length > 0) {
-    // console.log(item.Xdata, "item.Xdata", item.Ydata);
+    // 设置series配置
     item.option.series = (
       item.Xdata.length > item.Ydata.length ? item.Xdata : item.Ydata
     ).map((val, ind) => {
@@ -100,50 +209,88 @@ export function handleScatterChartLogic(
         data: scatterData, // 生成的散点图数据
         tooltip: {
           trigger: "item", // 鼠标悬停触发
-          // formatter: (params) => {
-          //   // params.dataIndex: 当前点的索引
-          //   const idx = params.dataIndex;
-          //   const xTooltipData = item.Xdata.map(
-          //     (xItem) =>
-          //       `${xItem.label}: ${
-          //         xItem.data[idx]
-          //           ? Number(xItem.data[idx][xItem.label] || 0)
-          //           : "N/A"
-          //       }`
-          //   ).join("<br>");
-          //   const yTooltipData = item.Ydata.map(
-          //     (yItem) =>
-          //       `${yItem.label}: ${
-          //         yItem.data[idx]
-          //           ? Number(yItem.data[idx][yItem.label]) || 0
-          //           : "N/A"
-          //       }`
-          //   ).join("<br>");
-          //   return `X轴数据:<br>${xTooltipData}<br>Y轴数据:<br>${yTooltipData}`;
-          // },
         },
         itemStyle: {
           shadowBlur: 3,
           shadowColor: "rgba(25, 100, 150, 0.3)",
           shadowOffsetY: 1.5,
-          // color: {
-          //   type: "radial",
-          //   x: 0.4,
-          //   y: 0.3,
-          //   r: 1,
-          //   colorStops: [
-          //     {
-          //       offset: 0,
-          //       color: "#f90",
-          //     },
-          //     {
-          //       offset: 1,
-          //       color: "#F00",
-          //     },
-          //   ],
-          // },
         },
       };
     });
+
+    // 记录选中的散点
+    const selectedPoints = [];
+
+    // 点击事件的处理函数
+    function handleClick(event) {
+      const selectedIndex = event.dataIndex;
+      const scatterData = item.option.series[0].data;
+
+      // 获取点击点的坐标
+      const xClick = event.data[0];
+      const yClick = event.data[1];
+
+      // 判断该点是否已经被选中
+      const existingIndex = selectedPoints.findIndex(
+        (point) => point.index === selectedIndex
+      );
+
+      if (existingIndex === -1) {
+        // 如果没有选中,则加入
+        selectedPoints.push({
+          index: selectedIndex,
+          x: xClick,
+          y: yClick,
+        });
+      } else {
+        // 如果已经选中,则取消选中
+        selectedPoints.splice(existingIndex, 1);
+      }
+
+      // 更新选中点的样式
+      const newColors = scatterData.map((point, index) => {
+        // 如果该点在 selectedPoints 中,则改变其颜色
+        if (
+          selectedPoints.some((selectedPoint) => selectedPoint.index === index)
+        ) {
+          return "red"; // 选中点为红色
+        }
+        return point[0] && point[1] ? "#000" : "transparent"; // 默认颜色,未选中点为黑色
+      });
+
+      const newSizes = scatterData.map((point, index) => {
+        // 如果该点在 selectedPoints 中,则增大其大小
+        if (
+          selectedPoints.some((selectedPoint) => selectedPoint.index === index)
+        ) {
+          return 10; // 选中点大小为 10
+        }
+        return 5; // 默认大小
+      });
+
+      // 更新图表的颜色和大小
+      item.option.series[0].itemStyle = {
+        color: newColors,
+        size: newSizes,
+      };
+
+      // 通过重新设置 `option` 来更新图表
+      if (item.chartInstance) {
+        item.chartInstance.setOption(item.option);
+      }
+    }
+
+    // 绑定点击事件
+    item.option.series[0].emphasis = {
+      itemStyle: {
+        color: "red",
+      },
+    };
+
+    // 添加点击事件
+    if (item.chartInstance) {
+      item.chartInstance.off("click"); // 先移除之前的事件
+      item.chartInstance.on("click", handleClick); // 然后绑定新的点击事件
+    }
   }
 }

+ 7 - 0
vue.config.js

@@ -93,6 +93,13 @@ module.exports = {
           console.log("Proxying request to:", proxyReq.path); // 打印代理请求路径
         },
       },
+      "/databaseApi": {
+        target: "http://192.168.5.18:3000",
+        changeOrigin: true,
+        pathRewrite: {
+          "^/databaseApi": "", // 如果后端需要 `/api` 前缀
+        },
+      },
     },
   },