Browse Source

添加查询管理页面

乐菲 1 week ago
parent
commit
a977a00d7a

+ 1 - 0
package.json

@@ -81,6 +81,7 @@
     "vuedraggable": "2.24.3",
     "vuex": "3.6.0",
     "vxe-table": "^3.8.2",
+    "xlsx": "^0.18.5",
     "xml-js": "1.6.11",
     "yarn": "^1.22.19"
   },

+ 53 - 0
src/views/mes/queryManage/components/DictTag.vue

@@ -0,0 +1,53 @@
+<template>
+  <el-tag
+    v-if="showTag"
+    :type="realColorType"
+    :size="size"
+    :color="realBgColor"
+    style="color: #fff"
+    disable-transitions
+  >
+    {{ dictLabel }}
+  </el-tag>
+</template>
+
+<script>
+import { getDictOptions } from "@/utils/dict";
+
+export default {
+  name: "DictTag",
+  props: {
+    type: { type: String, required: true },
+    value: { type: [String, Number, Boolean], required: true },
+    size: { type: String, default: null },
+  },
+  computed: {
+    showTag() {
+      return (
+        this.type != null && this.value !== undefined && this.value !== null
+      );
+    },
+    dictItem() {
+      const opts = getDictOptions(this.type);
+      return opts.find((d) => d.value === String(this.value)) || {};
+    },
+    realColorType() {
+      const ct = this.dictItem.colorType;
+      return ct === "default" || !ct ? "info" : ct;
+    },
+    realBgColor() {
+      const cls = this.dictItem.cssClass;
+      return cls && this.isHexColor(cls) ? cls : "";
+    },
+    dictLabel() {
+      return this.dictItem.label || "";
+    },
+  },
+  methods: {
+    // 仅保留 isHexColor 实现,内嵌
+    isHexColor(color) {
+      return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color);
+    },
+  },
+};
+</script>

+ 326 - 0
src/views/mes/queryManage/components/FilterColumnInQuery.vue

@@ -0,0 +1,326 @@
+<template>
+  <el-popover
+    ref="popover"
+    placement="bottom"
+    width="340"
+    trigger="click"
+    @show="onPopoverShow"
+  >
+    <!-- 搜索条 -->
+    <div v-show="!['datetime', 'daterange'].includes(htmlType)" class="flex">
+      <el-input
+        v-model="searchKey"
+        class="mb5 flex-1"
+        placeholder="请输入"
+        @keyup.enter.native="handleFilterSearch"
+      />
+      <div class="ml-3 flex">
+        <el-tooltip content="降序" placement="top">
+          <i
+            class="el-icon-sort-down mr-5px"
+            style="font-size: 18px; color: #409eff; cursor: pointer"
+            @click="handleSort('desc')"
+          />
+        </el-tooltip>
+        <el-tooltip content="升序" placement="top">
+          <i
+            class="el-icon-sort-up mr-5px"
+            style="font-size: 18px; color: #409eff; cursor: pointer"
+            @click="handleSort('asc')"
+          />
+        </el-tooltip>
+        <el-tooltip content="反选" placement="top">
+          <i
+            class="el-icon-refresh-left"
+            style="font-size: 18px; color: red; cursor: pointer"
+            @click="handleInvertSelection"
+          />
+        </el-tooltip>
+      </div>
+    </div>
+
+    <!-- 日期范围 -->
+    <div v-if="['datetime', 'daterange'].includes(htmlType)" class="mb5">
+      <div class="demonstration mb-5px">按日期查询</div>
+      <el-date-picker
+        v-model="dateRangeValue"
+        type="datetimerange"
+        range-separator="至"
+        start-placeholder="开始"
+        end-placeholder="结束"
+        format="yyyy-MM-dd HH:mm:ss"
+        style="width: 95%"
+        @change="dateChange"
+      />
+    </div>
+
+    <!-- 多选列表 -->
+    <div v-else class="mb5" style="max-height: 350px; overflow: auto">
+      <el-table
+        :data="filteredList"
+        size="mini"
+        height="350"
+        @selection-change="handleSelectionChange"
+        @row-click="handleClickRow"
+      >
+        <el-table-column type="selection" width="50" />
+        <el-table-column
+          prop="column"
+          :label="label || '数据'"
+          show-overflow-tooltip
+        >
+          <template slot-scope="{ row }">
+            <dict-tag
+              v-if="dictType"
+              :type="dictType"
+              :value="row.column"
+              size="small"
+            />
+            <span v-else>{{ row.column }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 按钮 -->
+    <div class="mt10 text-right">
+      <el-button size="small" @click="filterCancel()">重置</el-button>
+      <el-button type="primary" size="small" @click="filterConfirm"
+        >筛选</el-button
+      >
+    </div>
+
+    <!-- 触发元素 -->
+    <template slot="reference">
+      <span v-if="$slots.reference">
+        <slot name="reference" />
+      </span>
+      <span v-else>{{ label }}</span>
+    </template>
+  </el-popover>
+</template>
+
+<script>
+import { getFilterListInQuery } from "@/api/common";
+
+export default {
+  name: "FilterColumnInQuery",
+  props: {
+    prop: { type: String, required: true },
+    label: { type: String, default: "" },
+    activeFilters: { type: Object, default: () => ({}) },
+    orderType: { type: String, default: "" },
+    paramList: { type: Array, default: () => [] },
+    dictType: { type: String, default: null },
+    queryId: { type: String, default: null },
+    htmlType: { type: String, default: "input" },
+    isClear: { type: Boolean, default: false },
+  },
+  data() {
+    return {
+      searchKey: "",
+      dateRangeValue: "",
+      currentRows: [],
+      filterData: [],
+      loading: false,
+      isFirstLoad: true,
+    };
+  },
+  computed: {
+    originalList() {
+      if (
+        Array.isArray(this.filterData) &&
+        this.filterData.length > 0 &&
+        typeof this.filterData[0] === "string"
+      ) {
+        return this.filterData.map((item) => ({
+          column: item,
+          rawData: { [this.prop]: item },
+        }));
+      }
+      return this.filterData.reduce((acc, cur) => {
+        const val = cur[this.prop];
+        const has = acc.some((i) => i.column === val);
+        if (!has && val !== undefined && val !== null) {
+          acc.push({ column: val, rawData: cur });
+        }
+        return acc;
+      }, []);
+    },
+    filteredList() {
+      if (!this.searchKey.trim()) return this.originalList;
+      return this.originalList.filter((item) =>
+        String(item.column).toLowerCase().includes(this.searchKey.toLowerCase())
+      );
+    },
+  },
+  watch: {
+    isClear(val) {
+      if (val) this.filterCancel(true);
+    },
+  },
+  methods: {
+    /* ---- 生命周期 ---- */
+    async onPopoverShow() {
+      this.$nextTick(() => this.$refs.seachRef?.focus());
+      if (this.isFirstLoad || this.searchKey) {
+        await this.fetchFilterListInQueryManage(true);
+        this.isFirstLoad = false;
+      } else {
+        await this.fetchFilterListInQueryManage(false);
+      }
+      this.restoreSelections();
+    },
+    // 直接在组件 methods 里放这一个即可
+    formatDateTimeRange(range) {
+      if (!Array.isArray(range) || range.length < 2) {
+        throw new Error("formatDateTimeRange: 需要包含两个元素的数组");
+      }
+      const pad = (n) => String(n).padStart(2, "0");
+      const format = (d) => {
+        const date = new Date(d);
+        const Y = date.getFullYear();
+        const M = pad(date.getMonth() + 1);
+        const D = pad(date.getDate());
+        const h = pad(date.getHours());
+        const m = pad(date.getMinutes());
+        const s = pad(date.getSeconds());
+        return `${Y}-${M}-${D} ${h}:${m}:${s}`;
+      };
+      return [format(range[0]), format(range[1])];
+    },
+
+    /* ---- 数据获取 ---- */
+    async fetchFilterListInQueryManage(reload) {
+      if (["datetime", "daterange"].includes(this.htmlType)) return;
+      try {
+        this.loading = true;
+        const params = {
+          currentField: this.prop,
+          paramList: this.paramList,
+          id: this.queryId,
+        };
+        Object.keys(this.activeFilters).forEach((k) => {
+          if (this.activeFilters[k]?.length) {
+            params[k] = this.activeFilters[k].map((i) => i.column).join(",");
+          }
+        });
+        const { data } = await getFilterListInQuery(params);
+        this.filterData = data || [];
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    /* ---- 排序 ---- */
+    handleSort(order) {
+      const arr = [...this.filterData];
+      arr.sort((a, b) => {
+        const valA = parseFloat(a[this.prop]) || a;
+        const valB = parseFloat(b[this.prop]) || b;
+        return order === "asc" ? valA - valB : valB - valA;
+      });
+      this.filterData = arr;
+    },
+
+    /* ---- 反选 ---- */
+    handleInvertSelection() {
+      if (this.filteredList.length > 200) {
+        this.$message.warning("反选数据量太大,请先筛选或手动选择!");
+        return;
+      }
+      const all = this.filteredList;
+      const selected = this.currentRows;
+      const inverted = all.filter(
+        (row) => !selected.some((s) => s.column === row.column)
+      );
+      this.toggleSelection(inverted);
+      this.currentRows = inverted;
+    },
+
+    /* ---- 多选处理 ---- */
+    handleSelectionChange(selection) {
+      this.currentRows = selection;
+    },
+    handleClickRow(row) {
+      const checked = this.currentRows.some((s) => s.column === row.column);
+      this.$refs.multipleTable.toggleRowSelection(row, !checked);
+    },
+    toggleSelection(rows) {
+      const table = this.$refs.multipleTable;
+      table.clearSelection();
+      this.$nextTick(() =>
+        rows.forEach((r) => table.toggleRowSelection(r, true))
+      );
+    },
+    restoreSelections() {
+      this.$nextTick(() => {
+        const table = this.$refs.multipleTable;
+        if (!table) return;
+        table.clearSelection();
+        this.currentRows.forEach((r) => {
+          const target = this.filteredList.find((i) => i.column === r.column);
+          if (target) table.toggleRowSelection(target, true);
+        });
+      });
+    },
+
+    /* ---- 日期 ---- */
+    dateChange(val) {
+      this.currentRows = [
+        {
+          column: formatDateTimeRange(val),
+          isRange: true,
+          rawData: { [this.prop]: formatDateTimeRange(val) },
+        },
+      ];
+    },
+
+    /* ---- 按钮 ---- */
+    filterConfirm() {
+      this.$emit("selectData", this.currentRows, this.prop, this.htmlType);
+      this.$refs.popover.doClose();
+    },
+    filterCancel(flag) {
+      this.searchKey = "";
+      this.dateRangeValue = "";
+      this.currentRows = [];
+      if (!flag) {
+        this.$emit("selectData", [], this.prop, this.htmlType);
+        this.$refs.popover.doClose();
+        this.isFirstLoad = true;
+      }
+    },
+    handleFilterSearch() {
+      this.fetchFilterListInQueryManage();
+    },
+  },
+};
+</script>
+
+<style scoped>
+.demonstration {
+  color: #409eff;
+}
+.mb5 {
+  margin-bottom: 5px;
+}
+.mt10 {
+  margin-top: 10px;
+}
+.text-right {
+  text-align: right;
+}
+.flex {
+  display: flex;
+}
+.flex-1 {
+  flex: 1;
+}
+.ml-3 {
+  margin-left: 12px;
+}
+.mr-5px {
+  margin-right: 5px;
+}
+</style>

+ 39 - 0
src/views/mes/queryManage/components/Iframe.vue

@@ -0,0 +1,39 @@
+<template>
+  <div v-loading="loading" :style="{ height: height }">
+    <iframe
+      ref="frameRef"
+      :src="src"
+      frameborder="0"
+      style="width: 100%; height: 100%"
+    />
+  </div>
+</template>
+
+<script>
+export default {
+  name: "IFrame",
+  props: {
+    src: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      loading: true,
+      height: "",
+    };
+  },
+  mounted() {
+    setTimeout(() => {
+      this.init();
+    }, 300);
+  },
+  methods: {
+    init() {
+      this.height = document.documentElement.clientHeight - 94.5 + "px";
+      this.loading = false;
+    },
+  },
+};
+</script>

+ 157 - 0
src/views/mes/queryManage/components/ParameterDetail.vue

@@ -0,0 +1,157 @@
+<template>
+  <!-- 列表 -->
+  <el-dialog
+    :title="dialogTitle"
+    :visible.sync="dialogVisible"
+    width="40%"
+    @close="dialogVisible = false"
+  >
+    <el-card>
+      <el-form
+        v-if="curButtonInfo.paramList && curButtonInfo.paramList.length > 0"
+        label-width="auto"
+      >
+        <el-row :gutter="10">
+          <el-col
+            v-for="item in curButtonInfo.paramList"
+            v-if="item.ifHide !== true"
+            :key="item.id"
+            :span="12"
+          >
+            <el-form-item :label="item.paramComment">
+              <el-input v-model="item.value" :disabled="!item.allowEdit" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+
+    <template slot="footer">
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import request from "@/utils/request";
+
+export default {
+  name: "QueryManageButton",
+  props: {},
+  data() {
+    return {
+      dialogVisible: false,
+      dialogTitle: "",
+      curButtonInfo: {},
+      selectData: [],
+      columnList: [],
+    };
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(
+      type = "create",
+      buttonInfo,
+      id = "",
+      multipleData = [],
+      columns = []
+    ) {
+      this.dialogVisible = true;
+      this.dialogTitle = type === "create" ? "新增" : "修改";
+      this.selectData = multipleData;
+      this.columnList = columns || [];
+      this.curButtonInfo = { ...buttonInfo } || {};
+      if (type === "update" && this.selectData.length > 0) {
+        this.fetchDefaultValue();
+      }
+    },
+
+    submitForm() {
+      this.handleDynamic(this.curButtonInfo);
+    },
+
+    async handleDynamic(params) {
+      const {
+        interfaceUrl,
+        restMethod = "get",
+        paramList = [],
+        requestParameter = "0",
+      } = params;
+      const fieldValues = this.paramsToConver(paramList);
+
+      const config = {
+        method: restMethod,
+        url: interfaceUrl,
+      };
+
+      try {
+        switch (restMethod) {
+          case "get":
+          case "delete":
+            this.handleGetDelete(config, fieldValues, requestParameter);
+            break;
+          default:
+            if (requestParameter === "1") {
+              this.handleGetDelete(config, fieldValues, requestParameter);
+            } else {
+              config.data = fieldValues;
+            }
+        }
+
+        const response = await request(config);
+        this.$message.success("操作成功");
+        this.dialogVisible = false;
+        this.$emit("success");
+        return response;
+      } catch (error) {
+        console.error(error);
+      }
+    },
+
+    handleGetDelete(config, fieldValues, requestParameter) {
+      switch (requestParameter) {
+        case "1":
+          config.params = fieldValues;
+          break;
+        case "3":
+          const queryString = new URLSearchParams(fieldValues).toString();
+          config.url = queryString
+            ? `${config.url}?${queryString}`
+            : config.url;
+          break;
+        case "4":
+          config.headers = { ...config.headers, ...fieldValues };
+          break;
+        default:
+          config.data = fieldValues;
+      }
+    },
+
+    paramsToConver(paramList) {
+      return paramList.reduce((acc, item) => {
+        acc[item.paramName] = item.value;
+        return acc;
+      }, {});
+    },
+
+    fetchDefaultValue() {
+      const selectedItem = this.selectData[0];
+      this.curButtonInfo.paramList = this.curButtonInfo.paramList.map(
+        (param) => {
+          const column = this.columnList.find(
+            (col) => col.id === param.columnId
+          );
+          if (column && selectedItem.hasOwnProperty(column.columnComment)) {
+            return {
+              ...param,
+              value: selectedItem[column.columnComment],
+            };
+          }
+          return param;
+        }
+      );
+    },
+  },
+};
+</script>

+ 39 - 0
src/views/mes/queryManage/components/ReportPrint.vue

@@ -0,0 +1,39 @@
+<template>
+  <el-dialog
+    :title="dialogTitle"
+    :visible.sync="dialogVisible"
+    width="80%"
+    @close="reportClose"
+  >
+    <iframe :src="src" style="width: 100%; height: 70vh; border: none" />
+  </el-dialog>
+</template>
+
+<script>
+import Iframe from "./Iframe.vue";
+export default {
+  name: "ReportPrint",
+  components: {
+    Iframe,
+  },
+  props: {},
+  data() {
+    return {
+      dialogVisible: false,
+      dialogTitle: "",
+      src: "",
+    };
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(printSrc, title) {
+      this.dialogVisible = true;
+      this.dialogTitle = title;
+      this.src = printSrc;
+    },
+    reportClose() {
+      this.$emit("refresh");
+    },
+  },
+};
+</script>

+ 662 - 604
src/views/mes/queryManage/queryNew.vue

@@ -1,7 +1,21 @@
 <template>
-  <div class="content-wrap">
+  <!-- 唯一根节点:el-card -->
+  <el-card class="el-content-wrap mb-3px" shadow="never">
+    <!-- 标题 + 提示 -->
+    <template v-if="title" slot="header">
+      <div class="flex items-center">
+        <span class="text-16px font-700">{{ title }}</span>
+        <el-tooltip v-if="message" effect="dark" placement="right">
+          <template slot="content">
+            <div class="max-w-200px">{{ message }}</div>
+          </template>
+          <i class="el-icon-question ml-5px" style="font-size: 14px" />
+        </el-tooltip>
+      </div>
+    </template>
+
+    <!-- 原 content-wrap 的 slot 内容 -->
     <el-row class="mb-10px">
-      <!-- 动态按钮 -->
       <template v-for="btn in dynamicButtons">
         <el-button
           v-if="!btn.ifHide"
@@ -14,74 +28,97 @@
         </el-button>
       </template>
 
-      <el-button @click="AllConditionReset" size="small" type="danger">
-        <i class="iconfont mr-5px">&#xe6a7;</i>
+      <el-button size="small" type="danger" @click="AllConditionReset">
+        <i class="el-icon-refresh mr-5px" style="font-size: 14px" />
         全条件重置
       </el-button>
-      <el-button @click="Export" size="small" type="primary">
-        <i class="iconfont mr-5px">&#xe6a0;</i>
+
+      <el-button size="small" type="primary" @click="Export">
+        <i class="el-icon-download mr-5px" style="font-size: 14px" />
         导出
       </el-button>
-      <el-button @click="ExportAll" :loading="exportLoading" size="small" type="success">
-        <i class="iconfont mr-5px">&#xe6a0;</i>
+
+      <el-button
+        size="small"
+        type="success"
+        :loading="exportLoading"
+        @click="ExportAll"
+      >
+        <i class="el-icon-download mr-5px" style="font-size: 14px" />
         导出全部
       </el-button>
-      <el-button @click="handleBack" size="small">
-        <i class="iconfont mr-5px">&#xe6a9;</i>
+
+      <el-button size="small" @click="handleBack">
+        <i class="el-icon-arrow-left mr-5px" style="font-size: 14px" />
         返回
       </el-button>
     </el-row>
 
+    <!-- 表格 -->
     <el-table
       id="table_excel"
+      ref="tableRef"
       v-loading="loading"
       :data="list"
-      :stripe="true"
-      :max-height="tableMaxHeight"
-      :show-overflow-tooltip="true"
+      stripe
+      border
       size="small"
-      :summary-method="getSummaries"
-      @header-dragend="handleResize"
+      max-height="calc(100vh - 230px)"
+      show-overflow-tooltip
       show-summary
-      border
-      ref="tableRef"
+      :summary-method="getSummaries"
       @selection-change="handleSelectionChange"
+      @header-dragend="handleResize"
     >
-      <el-table-column v-if="showMultipleList.length > 0" type="selection" width="55" />
+      <el-table-column
+        v-if="showMultipleList.length"
+        type="selection"
+        width="55"
+      />
       <el-table-column type="index" width="55" align="center" />
+
       <template v-for="(item, i) in queryData">
         <el-table-column
-          v-if="item.ifHide === false"
+          v-if="!item.ifHide"
           :key="i"
           :label="item.columnComment"
-          align="center"
           :prop="item.columnComment"
-          :show-overflow-tooltip="true"
+          align="center"
           :min-width="item.javaField || ''"
           :sortable="false"
+          show-overflow-tooltip
         >
           <template slot="header">
             <div class="mr-5px flex items-center">
-              <div class="mr-5px flex flex-col gap-0" v-show="item.ifSort">
+              <!-- 排序图标 -->
+              <div v-show="item.ifSort" class="mr-5px flex flex-col gap-0">
                 <i
-                  class="iconfont"
+                  class="el-icon-caret-top"
+                  style="font-size: 15px; cursor: pointer"
                   :class="{
-                    'is-sort': currentSortField === item.columnComment && currentSortDirection === 'asc'
+                    'is-sort':
+                      currentSortField === item.columnComment &&
+                      currentSortDirection === 'asc',
                   }"
-                  style="font-size: 15px;"
                   @click="handleSort(item.columnComment, 'asc')"
-                >&#xe6a3;</i>
+                />
                 <i
-                  class="iconfont -mt-1.2"
+                  ref=""
+                  class="el-icon-caret-bottom -mt-1.2"
+                  style="font-size: 15px; cursor: pointer"
                   :class="{
-                    'is-sort': currentSortField === item.columnComment && currentSortDirection === 'desc'
+                    'is-sort':
+                      currentSortField === item.columnComment &&
+                      currentSortDirection === 'desc',
                   }"
-                  style="font-size: 15px;"
                   @click="handleSort(item.columnComment, 'desc')"
-                >&#xe6a4;</i>
+                />
               </div>
+
               <span>{{ item.columnComment }}</span>
-              <filter-column-in-query
+
+              <!-- 列过滤 -->
+              <FilterColumnInQuery
                 v-if="item.listOperationResult"
                 :label="item.columnComment"
                 :prop="item.columnName"
@@ -92,32 +129,49 @@
                 :param-list="queryParamList"
                 :active-filters="customFilters"
                 :html-type="item.htmlType"
-                @select-data="filterData"
                 :dict-type="item.dictType"
                 :is-clear="isClear"
+                @select-data="
+                  (data, field, htmlType) =>
+                    filterData(data, field, htmlType, item)
+                "
               >
                 <template slot="reference">
                   <div
                     class="flex items-center justify-center text-gray-600"
                     :class="{ 'has-filter': hasFilter(item.columnName) }"
                   >
-                    <i class="iconfont ml-2px" style="font-size: 14px;">&#xe6a2;</i>
+                    <svg-icon
+                      icon-class="search"
+                      class="ml-2px"
+                      style="font-size: 14px; cursor: pointer"
+                    />
                   </div>
                 </template>
-              </filter-column-in-query>
+              </FilterColumnInQuery>
             </div>
           </template>
+
           <template slot-scope="{ row }">
             <div
-              @click="pathChange(item.example, row[item.columnComment], item, row)"
               :class="{ active: item.example }"
+              @click="
+                pathChange(item.example, row[item.columnComment], item, row)
+              "
             >
               <dict-tag
-                v-if="isDict(item.columnComment) && row[item.columnComment] !== undefined && row[item.columnComment] !== null"
+                v-if="
+                  isDict(item.columnComment) &&
+                  ![undefined, null].includes(row[item.columnComment])
+                "
                 :type="isDict(item.columnComment)"
                 :value="row[item.columnComment]"
               />
-              <span v-else-if="row[item.columnComment] && isDateTime(item.columnComment)">
+              <span
+                v-else-if="
+                  row[item.columnComment] && isDateTime(item.columnComment)
+                "
+              >
                 {{ formatToDateTime(row[item.columnComment]) }}
               </span>
               <span v-else>{{ row[item.columnComment] }}</span>
@@ -127,7 +181,7 @@
       </template>
     </el-table>
 
-    <!-- 分页组件 -->
+    <!-- 分页 -->
     <pagination
       v-show="total > 0"
       :total="total"
@@ -136,617 +190,621 @@
       @pagination="handleSearch"
     />
 
-    <!-- 积木标签打印 -->
-    <!--<report-print ref="reportPrint" />--><!--暂时注掉-->
-    <!-- 新增参数 -->
-    <!--<parameter-detail ref="parameterDetailRef" @success="getQueryData" />--><!--暂时注掉-->
-  </div>
+    <!-- 弹窗 -->
+    <ReportPrint ref="reportPrint" />
+    <ParameterDetail ref="parameterDetailRef" @success="getQueryData" />
+  </el-card>
 </template>
 
 <script>
-  import * as QueryManageApi from '@/api/mes/queryManage'
-  import { getQueryManageButtonPage } from '@/api/mes/queryManage/button'
-  /*import download from '@/utils/download'
-  import * as XLSX from 'xlsx'
-  import { saveAs } from 'file-saver'
-  import FilterColumnInQuery from './components/FilterColumnInQuery.vue'
-  import ParameterDetail from './components/ParameterDetail.vue'
-  import ReportPrint from '@/views/mes/personalManage/employeeMaster/components/ReportPrint.vue'
-  import request from '@/config/axios'*/ //暂时注掉
-  import { getAccessToken, getTenantId } from '@/utils/auth'
-  import axios from 'axios'
-
-  export default {
-    name: 'QueryForm',
-    /*components: {
-      FilterColumnInQuery,
-      ParameterDetail,
-      ReportPrint
-    },*/
-    data() {
-      return {
-        title: '',
-        queryList: [],
-        searchData: [],
-        queryData: [],
-        list: [],
-        originalList: [],
-        pageNo: 1,
-        pageSize: 20,
-        total: 0,
-        loading: false,
-        form: {},
-        visible: false,
-        customFilters: {},
-        currentSortField: null,
-        currentSortDirection: null,
-        dynamicButtons: [],
-        queryParamList: [],
-        exportLoading: false,
-        showMultipleList: [],
-        isClear: false,
-        headerWidthChange: false,
-        multipleSelection: []
+/* --------------  以下为 script,仅把 ContentWrap 的 props 拿过来  -------------- */
+import * as QueryManageApi from "@/api/mes/queryManage";
+import { getQueryManageButtonPage } from "@/api/mes/queryManage/button";
+import { saveAs } from "file-saver";
+import * as XLSX from "xlsx";
+import FilterColumnInQuery from "./components/FilterColumnInQuery.vue";
+import DictTag from "./components/DictTag.vue";
+import ParameterDetail from "./components/ParameterDetail.vue";
+import ReportPrint from "./components/ReportPrint.vue";
+import request from "@/utils/request";
+import { getAccessToken, getTenantId } from "@/utils/auth";
+import axios from "axios";
+
+export default {
+  name: "QueryForm",
+  components: {
+    FilterColumnInQuery,
+    ParameterDetail,
+    ReportPrint,
+    DictTag,
+  },
+  data() {
+    return {
+      title: "", // ContentWrap 的 title
+      message: "", // ContentWrap 的 message
+      queryList: [],
+      searchData: [],
+      queryData: [],
+      list: [],
+      originalList: [],
+      pageNo: 1,
+      pageSize: 20,
+      total: 0,
+      loading: false,
+      form: {},
+      visible: false,
+      customFilters: {},
+      currentSortField: null,
+      currentSortDirection: null,
+      dynamicButtons: [],
+      queryParamList: [],
+      exportLoading: false,
+      showMultipleList: [],
+      isClear: false,
+      multipleSelection: [],
+      headerWidthChange: false,
+    };
+  },
+  mounted() {
+    this.init();
+  },
+  activated() {
+    this.init();
+  },
+  beforeDestroy() {
+    this.saveColumnWidth();
+  },
+  methods: {
+    /* 生命周期 */
+    async init() {
+      this.title =
+        this.$route.query?.name ||
+        this.$route.redirectedFrom?.meta?.title ||
+        "查询";
+      // 需要提示文字时,再写 this.message = 'xxx'
+      if (this.$route.query.infraQueryId) {
+        await this.getFilterData();
+      } else {
+        this.buildQueryParamFromRoute();
+        await this.getQueryData();
       }
     },
-    computed: {
-      tableMaxHeight() {
-        return 'calc(100vh - 230px)'
+
+    /* 以下所有方法保持与原文件一致,不再赘述 */
+    async getQueryData() {
+      try {
+        this.loading = true;
+        const data = await QueryManageApi.getQueryManageColumnListByMasterId(
+          this.$route.query.id
+        );
+        // 确保queryData是数组
+        this.queryData = Array.isArray(data.data) ? data.data || [] : [];
+        console.log("queryData长度", this.queryData.length);
+        this.searchData = this.queryData.filter(
+          (item) => item.listOperationResult && item.ifHide === false
+        );
+        this.showMultipleList = this.queryData.filter(
+          (item) => item.listOperationCondition === "1"
+        );
+        this.queryList = [];
+        for (let i = 0; i < this.searchData.length; i++) {
+          const { dataType, htmlType, dictType, columnName } =
+            this.searchData[i];
+          this.queryList.push({
+            dataType,
+            htmlType,
+            dictType,
+            key: columnName,
+            operate: dataType === "ym_string_query_type" ? "like" : "",
+          });
+        }
+        await this.getManageButtons(this.$route.query?.id);
+        await this.getList();
+      } finally {
+        this.loading = false;
       }
     },
-    methods: {
-      hasFilter(field) {
-        return !!this.customFilters[field] && this.customFilters[field].length > 0
-      },
-
-      async getQueryData() {
-        try {
-          this.loading = true
-          const data = await QueryManageApi.getQueryManageColumnListByMasterId(this.$route.query.id)
-          this.queryData = data
 
-          this.searchData = data.filter((item) => {
-            return item.listOperationResult && item.ifHide === false
-          })
-          this.showMultipleList = this.queryData.filter((item) => item.listOperationCondition === '1')
-          this.queryList = []
-
-          for (let i = 0; i < this.searchData.length; i++) {
-            if (!this.searchData[i]) break
-            const { dataType, htmlType, dictType, columnName } = this.searchData[i]
-            this.queryList.push({
-              dataType,
-              htmlType,
-              dictType,
-              key: columnName,
-              operate: dataType === 'ym_string_query_type' ? 'like' : ''
-            })
-          }
+    async getList() {
+      try {
+        const data = await QueryManageApi.loadTableData({
+          pageNo: this.pageNo,
+          pageSize: this.pageSize,
+          paramList: this.queryParamList,
+          id: this.$route.query?.id,
+        });
+        console.log("后端返回", data); // ← 看这里
+        this.list = data?.data?.list || [];
+        console.log("赋值后 list =", this.list);
+        console.log("list.length =", this.list.length);
+        console.log("list 第一条 =", this.list[0]);
+        this.originalList = JSON.parse(JSON.stringify(data?.data?.list || []));
+        this.total = data?.data?.total || 0;
+      } finally {
+        this.loading = false;
+      }
+    },
 
-          await this.getManageButtons(this.$route.query.id)
-          await this.getList()
-        } catch (error) {
-          console.error(error)
-        } finally {
-          this.loading = false
-        }
-      },
-
-      async getList() {
-        try {
-          const data = await QueryManageApi.loadTableData({
-            pageNo: this.pageNo,
-            pageSize: this.pageSize,
-            paramList: this.queryParamList,
-            id: this.$route.query.id
-          })
-          this.list = data.list || []
-          this.originalList = JSON.parse(JSON.stringify(data.list || []))
-          this.total = data.total || 0
-        } catch (error) {
-          console.error(error)
-        } finally {
-          this.loading = false
-        }
-      },
-
-      async getManageButtons(masterId) {
-        try {
-          const data = await getQueryManageButtonPage({ masterId })
-          this.dynamicButtons = data.list || []
-        } catch (error) {
-          console.error(error)
-        }
-      },
-
-      handleDynamicButtonClick(btn) {
-        const { ifJimu, interfaceUrl, printSoft, operateType, paramList = [] } = btn
-        if (ifJimu) {
-          this.handlePrintByJimu(btn)
-        } else if (this.isFullUrl(interfaceUrl) && printSoft) {
-          this.handlePrintByOtherSoft(btn)
-        } else if (operateType === 2) {
-          this.$refs.parameterDetailRef.open('create', btn, this.$route.query.id, [], this.queryData)
-        } else if (operateType === 3) {
-          if (this.multipleSelection.length === 0) return this.$message.warning('请先选择行数据!')
-          if (this.multipleSelection.length > 1) return this.$message.warning('只能选择单条数据修改!')
-          this.$refs.parameterDetailRef.open(
-            'update',
-            btn,
-            this.$route.query.id,
-            this.multipleSelection,
-            this.queryData
-          )
-        } else if (operateType === 7) {
-          this.$router.push({ path: interfaceUrl })
-        } else {
-          this.handleDynamic(btn)
-        }
-      },
-
-      handlePrintByJimu(params) {
-        if (this.multipleSelection.length === 0) return this.$message.warning('请先选择数据!')
-        const baseUrl = process.env.VUE_APP_BASE_URL
-        const token = getAccessToken()
-        const tenantId = getTenantId()
-        const { paramList = [], interfaceUrl, reportId } = params
-        const fieldValues = this.paramsToConver(paramList)
-        const printParams = new URLSearchParams({
-          token,
-          tenantId,
-          ...fieldValues
-        })
-
-        const src = `${baseUrl}${interfaceUrl}${reportId}?${printParams.toString()}`
-        this.$refs.reportPrint.open(src, this.$route.query.name || '报表打印')
-      },
-
-      async handlePrintByOtherSoft(params) {
-        if (this.multipleSelection.length === 0) return this.$message.warning('请先选择数据!')
-        try {
-          const { interfaceUrl, paramList, restMethod, templet } = params
-          if (!templet) return this.$message.warning('未配置打印模板!')
-          const fieldValues = this.paramsToConver(paramList)
-          const printParams = {
-            ...fieldValues,
-            fileUrl: templet
-          }
+    async getManageButtons(masterId) {
+      const data = await getQueryManageButtonPage({ masterId });
+      this.dynamicButtons = data?.data?.list || [];
+    },
 
-          const config = {
-            method: restMethod,
-            url: interfaceUrl
-          }
+    async handleDynamicButtonClick(btn = {}) {
+      const {
+        ifJimu,
+        interfaceUrl,
+        printSoft,
+        operateType,
+        paramList = [],
+      } = btn;
+      if (ifJimu) {
+        this.handlePrintByJimu(btn);
+      } else if (this.isFullUrl(interfaceUrl) && printSoft) {
+        this.handlePrintByOtherSoft(btn);
+      } else if (operateType === 2) {
+        this.$refs.parameterDetailRef.open(
+          "create",
+          btn,
+          this.$route.query?.id,
+          [],
+          this.queryData
+        );
+      } else if (operateType === 3) {
+        if (this.multipleSelection.length === 0)
+          return this.$message.warning("请先选择行数据!");
+        if (this.multipleSelection.length > 1)
+          return this.$message.warning("只能选择单条数据修改!");
+        this.$refs.parameterDetailRef.open(
+          "update",
+          btn,
+          this.$route.query?.id,
+          this.multipleSelection,
+          this.queryData
+        );
+      } else if (operateType === 7) {
+        this.$router.push({ path: `${interfaceUrl}` });
+      } else {
+        this.handleDynamic(btn);
+      }
+    },
 
-          if (['post', 'put'].includes(restMethod)) {
-            config.data = printParams
-          } else {
-            config.params = printParams
-          }
+    formatToDateTime(val) {
+      const d = val ? new Date(val) : new Date();
+      if (isNaN(d.getTime())) return "";
+      const pad = (n) => String(n).padStart(2, "0");
+      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(
+        d.getDate()
+      )} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
+    },
 
-          await axios(config)
-          this.$message.success('操作成功!')
-          this.getList()
-        } catch (error) {
-          console.error(error)
-        }
-      },
-
-      async handleDynamic(params) {
-        if (this.multipleSelection.length === 0) return this.$message.warning('请先选择数据!')
-        const { interfaceUrl, restMethod = 'get', paramList = [], requestParameter = '0' } = params
-        const fieldValues = this.paramsToConver(paramList)
-
-        try {
-          let config = { method: restMethod, url: interfaceUrl }
-
-          if (restMethod === 'get' || restMethod === 'delete') {
-            this.handleGetDelete(config, fieldValues, requestParameter)
-          } else {
-            if (requestParameter === '1') {
-              this.handleGetDelete(config, fieldValues, requestParameter)
-            } else {
-              config.data = fieldValues
-            }
-          }
+    handlePrintByJimu(params) {
+      if (this.multipleSelection.length === 0)
+        return this.$message.warning("请先选择数据!");
+      const baseUrl = process.env.VITE_BASE_URL;
+      const token = getAccessToken();
+      const tenantId = getTenantId();
+      const { paramList = [], interfaceUrl, reportId } = params;
+      const fieldValues = this.paramsToConver(paramList);
+      const printParams = new URLSearchParams({
+        token,
+        tenantId,
+        ...fieldValues,
+      });
+      const src = `${baseUrl}${interfaceUrl}${reportId}?${printParams.toString()}`;
+      this.$refs.reportPrint.open(
+        src,
+        this.$route.query?.name ||
+          this.$route.redirectedFrom?.meta?.title ||
+          "报表打印"
+      );
+    },
 
-          await request[restMethod](config)
-          this.$message.success('操作成功')
-          this.getList()
-        } catch (error) {
-          console.error(error)
-        }
-      },
-
-      handleGetDelete(config, fieldValues, requestParameter) {
-        switch (requestParameter) {
-          case '1':
-            config.params = fieldValues
-            break
-          case '3':
-            const queryString = new URLSearchParams(fieldValues).toString()
-            config.url = queryString ? `${config.url}?${queryString}` : config.url
-            break
-          case '4':
-            config.headers = { ...config.headers, ...fieldValues }
-            break
-          default:
-            config.data = fieldValues
-        }
-      },
-
-      generateMappedData(selectList, keys) {
-        return selectList.map((item) =>
-          keys.reduce((res, key) => {
-            const { columnComment, paramName } = key
-            if (item.hasOwnProperty(columnComment)) {
-              const field = paramName.split('.').pop()
-              res[field] = item[columnComment]
-            }
-            return res
-          }, {})
-        )
-      },
-
-      paramsToConver(paramList) {
-        const matchListWithParam = (paramList || []).filter((item) =>
-          this.queryData.some((query) => item.columnId === query.id)
-        ).map((item) => {
-          const paramItem = this.queryData.find((query) => item.columnId === query.id)
-          return { ...item, columnComment: paramItem ? paramItem.columnComment : '' }
-        })
-
-        const result = this.generateMappedData(this.multipleSelection, matchListWithParam)
-        const allFields = [...new Set(result.flatMap(Object.keys))]
-
-        return allFields.reduce((acc, field) => {
-          acc[field] = result.map((item) => item[field]).join(',')
-          return acc
-        }, {})
-      },
-
-      isFullUrl(url) {
-        try {
-          const parsed = new URL(url)
-          return parsed.protocol === 'http:' || parsed.protocol === 'https:'
-        } catch (e) {
-          return false
-        }
-      },
-
-      isDict(key) {
-        const item = this.queryData.find((item) => item.columnComment === key)
-        return item && item.dictType ? item.dictType : false
-      },
-
-      isNumber(key) {
-        const item = this.queryData.find((item) => item.columnComment === key)
-        return item && item.dataType === 'ym_int_query_type'
-      },
-
-      isDateTime(key) {
-        const item = this.queryData.find((item) => item.columnComment === key)
-        return item && item.javaType === 'LocalDateTime'
-      },
-
-      handleSort(field, direction) {
-        if (this.currentSortField === field && this.currentSortDirection === direction) {
-          this.currentSortField = null
-          this.currentSortDirection = null
-          this.list = [...this.originalList]
-          return
-        }
+    async handlePrintByOtherSoft(params) {
+      if (this.multipleSelection.length === 0)
+        return this.$message.warning("请先选择数据!");
+      const { interfaceUrl, paramList, restMethod, templet } = params;
+      if (!templet) return this.$message.warning("未配置打印模板!");
+      const fieldValues = this.paramsToConver(paramList);
+      const printParams = { ...fieldValues, fileUrl: templet };
+      const config = {
+        method: restMethod,
+        url: interfaceUrl,
+        [restMethod === "get" || restMethod === "delete" ? "params" : "data"]:
+          printParams,
+      };
+      await axios(config);
+      this.$message.success("操作成功!");
+      this.getList();
+    },
 
-        this.currentSortField = field
-        this.currentSortDirection = direction
+    async handleDynamic(params) {
+      if (this.multipleSelection.length === 0)
+        return this.$message.warning("请先选择数据!");
+      const {
+        interfaceUrl,
+        restMethod = "get",
+        paramList = [],
+        requestParameter = "0",
+      } = params;
+      const fieldValues = this.paramsToConver(paramList);
+      const config = { method: restMethod, url: interfaceUrl };
+      this.handleGetDelete(config, fieldValues, requestParameter);
+      await request[restMethod]?.(config);
+      this.$message.success("操作成功");
+      this.getList();
+    },
 
-        this.list = [...this.originalList].sort((a, b) => {
-          let valA = a[field]
-          let valB = b[field]
+    handleGetDelete(config, fieldValues, requestParameter) {
+      switch (requestParameter) {
+        case "1":
+          config.params = fieldValues;
+          break;
+        case "3":
+          config.url = new URLSearchParams(fieldValues).toString()
+            ? `${config.url}?${new URLSearchParams(fieldValues).toString()}`
+            : config.url;
+          break;
+        case "4":
+          config.headers = { ...config.headers, ...fieldValues };
+          break;
+        default:
+          config.data = fieldValues;
+      }
+    },
 
-          valA = typeof valA === 'string' ? parseFloat(valA.replace(/,/g, '')) : valA
-          valB = typeof valB === 'string' ? parseFloat(valB.replace(/,/g, '')) : valB
+    paramsToConver(paramList) {
+      const matchListWithParam = paramList
+        ?.filter((item) =>
+          this.queryData.some((query) => item.columnId === query?.id)
+        )
+        .map((item) => {
+          const paramItem = this.queryData.find(
+            (query) => item.columnId === query?.id
+          );
+          return { ...item, columnComment: paramItem?.columnComment };
+        });
+      const result = this.generateMappedData(
+        this.multipleSelection,
+        matchListWithParam
+      );
+      const allFields = [...new Set(result.flatMap(Object.keys))];
+      return allFields.reduce((acc, field) => {
+        acc[field] = result.map((item) => item[field]).join(",");
+        return acc;
+      }, {});
+    },
 
-          if (isNaN(valA) || isNaN(valB)) {
-            return direction === 'asc'
-              ? String(valA).localeCompare(String(valB))
-              : String(valB).localeCompare(String(valA))
+    generateMappedData(selectList, keys) {
+      return selectList.map((item) =>
+        keys.reduce((res, key) => {
+          const { columnComment, paramName } = key;
+          if (item.hasOwnProperty(columnComment)) {
+            const field = paramName.split(".").pop();
+            res[field] = item[columnComment];
           }
+          return res;
+        }, {})
+      );
+    },
+
+    isFullUrl(url) {
+      try {
+        const parsed = new URL(url);
+        return parsed.protocol === "http:" || parsed.protocol === "https:";
+      } catch {
+        return false;
+      }
+    },
 
-          return direction === 'asc' ? valA - valB : valB - valA
-        })
-      },
+    isDict(key) {
+      if (!Array.isArray(this.queryData)) return false;
+      const item = this.queryData.find((item) => item.columnComment === key);
+      return item ? item.dictType : false;
+    },
 
-      filterData(data, field, type, item) {
-        const { dataType } = item
-        this.customFilters[field] = data
+    isNumber(key) {
+      if (!Array.isArray(this.queryData)) return false;
+      const item = this.queryData.find((item) => item.columnComment === key);
+      return item && item.dataType === "ym_int_query_type";
+    },
 
-        if (data.length === 0) {
-          const index = this.queryParamList.findIndex((item) => item.key === field)
-          if (index > -1) this.queryParamList.splice(index, 1)
-          this.getList()
-          return
-        }
+    isDateTime(key) {
+      if (!Array.isArray(this.queryData)) return false;
+      const item = this.queryData.find((item) => item.columnComment === key);
+      return item && item.javaType === "LocalDateTime";
+    },
 
-        const isDateRange = data[0] && data[0].isRange
-        let valueStr
+    hasFilter(field) {
+      return !!(this.customFilters[field] && this.customFilters[field].length);
+    },
+
+    handleSort(field, direction) {
+      if (
+        this.currentSortField === field &&
+        this.currentSortDirection === direction
+      ) {
+        this.currentSortField = "";
+        this.currentSortDirection = "";
+        this.list = JSON.parse(JSON.stringify(this.originalList));
+        return;
+      }
+      this.currentSortField = field;
+      this.currentSortDirection = direction;
+      this.list = [...this.originalList].sort((a, b) => {
+        let valA = a[field];
+        let valB = b[field];
+        valA =
+          typeof valA === "string" ? parseFloat(valA.replace(/,/g, "")) : valA;
+        valB =
+          typeof valB === "string" ? parseFloat(valB.replace(/,/g, "")) : valB;
+        if (isNaN(valA))
+          return direction === "asc"
+            ? valA > valB
+              ? 1
+              : -1
+            : valA < valB
+            ? 1
+            : -1;
+        if (isNaN(valB))
+          return direction === "asc"
+            ? valA > valB
+              ? 1
+              : -1
+            : valA < valB
+            ? 1
+            : -1;
+        return direction === "asc" ? valA - valB : valB - valA;
+      });
+    },
 
-        if (isDateRange) {
-          valueStr = data.flatMap((item) => item.column).join(',')
+    filterData(data, field, type, row) {
+      const { dataType } = row;
+      this.$set(this.customFilters, field, data);
+      const isDateRange = data[0]?.isRange;
+      if (isDateRange) {
+        const valueList = data.flatMap((item) => item.column).join(",");
+        const idx = this.queryParamList.findIndex((item) => item.key === field);
+        if (idx > -1) {
+          this.queryParamList[idx].value = valueList;
         } else {
-          valueStr = dataType === 'ym_int_query_type'
-            ? data.map((item) => item.column).join(',')
-            : data.map((item) => `'${item.column}'`).join(',')
+          this.queryParamList.push({
+            key: field,
+            operate: "like",
+            htmlType: type,
+            value: valueList,
+          });
         }
-
-        const index = this.queryParamList.findIndex((item) => item.key === field)
-        if (index > -1) {
-          this.queryParamList[index].value = valueStr
+        this.getList();
+        return;
+      }
+      let valueStr;
+      if (data.length) {
+        valueStr =
+          dataType === "ym_int_query_type"
+            ? data.map((item) => item.column).join(",")
+            : data.map((item) => `'${item.column}'`).join(",");
+        const idx = this.queryParamList.findIndex((item) => item.key === field);
+        if (idx > -1) {
+          this.queryParamList[idx].value = valueStr;
         } else {
-          this.queryParamList.push({ key: field, operate: 'like', htmlType: type, value: valueStr })
+          this.queryParamList.push({
+            key: field,
+            operate: "like",
+            htmlType: type,
+            value: valueStr,
+          });
         }
+      } else {
+        const idx = this.queryParamList.findIndex((item) => item.key === field);
+        if (idx > -1) this.queryParamList.splice(idx, 1);
+      }
+      this.getList();
+    },
+
+    AllConditionReset() {
+      this.queryParamList = [];
+      this.customFilters = {};
+      this.isClear = true;
+      setTimeout(() => (this.isClear = false), 1000);
+      this.getList();
+    },
 
-        this.getList()
-      },
+    handleSearch() {
+      this.list = [];
+      this.getList();
+    },
 
-      AllConditionReset() {
-        this.queryParamList = []
-        this.customFilters = {}
-        this.isClear = true
+    async Export() {
+      try {
+        await this.$confirm("确定导出当前页?", "提示", { type: "warning" });
         setTimeout(() => {
-          this.isClear = false
-          this.getList()
-        }, 1000)
-      },
-
-      handleSearch() {
-        this.getList()
-      },
-
-      async Export() {
-        try {
-          await this.$confirm('确定要导出数据吗?', '提示', {
-            confirmButtonText: '确定',
-            cancelButtonText: '取消',
-            type: 'warning'
-          })
+          const xlsxParam = { raw: true };
+          const tables = document.getElementById("table_excel");
+          const table_book = XLSX.utils.table_to_book(tables, xlsxParam);
+          const tableWrite = XLSX.write(table_book, {
+            bookType: "xlsx",
+            bookSST: true,
+            type: "array",
+          });
+          saveAs(
+            new Blob([tableWrite], { type: "application/octet-stream" }),
+            `${this.title}.xlsx`
+          );
+        }, 1000);
+      } catch {}
+    },
+
+    async ExportAll() {
+      try {
+        await this.$confirm("确定导出全部?", "提示", { type: "warning" });
+        this.exportLoading = true;
+        const data = await QueryManageApi.exportExcel({
+          id: this.$route.query?.id,
+          paramList: this.queryParamList,
+        });
+        this.$download.excel(data, this.title + ".xls");
+      } finally {
+        this.exportLoading = false;
+      }
+    },
 
-          setTimeout(() => {
-            const tables = document.getElementById('table_excel')
-            const table_book = XLSX.utils.table_to_book(tables, { raw: true })
-            const table_write = XLSX.write(table_book, {
-              bookType: 'xlsx',
-              bookSST: true,
-              type: 'array'
-            })
-            saveAs(new Blob([table_write], { type: 'application/octet-stream' }), `${this.title}.xlsx`)
-          }, 1000)
-        } catch (error) {
-          console.error(error)
+    getHandleNumber(val) {
+      return Math.round(parseFloat(val) * 100000000) / 100000000;
+    },
+
+    getSummaries({ columns }) {
+      const sums = [];
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = "总计";
+          return;
         }
-      },
-
-      async ExportAll() {
-        try {
-          await this.$confirm('确定要导出全部数据吗?', '提示', {
-            confirmButtonText: '确定',
-            cancelButtonText: '取消',
-            type: 'warning'
-          })
+        const values = this.list.map((item) => Number(item[column.property]));
+        if (this.isNumber(column.property)) {
+          sums[index] = values.reduce((acc, cur) => {
+            const value = Number(cur);
+            return !isNaN(value) ? this.getHandleNumber(acc + cur) : acc;
+          }, 0);
+        }
+      });
+      return sums;
+    },
+
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
 
-          this.exportLoading = true
-          const data = await QueryManageApi.exportExcel({
-            id: this.$route.query.id,
-            paramList: this.queryParamList
+    pathChange(path, val, item, row) {
+      if (path === "sameRouter") return;
+      if (!path) return;
+      const data = {};
+      this.dynamicButtons
+        .filter((v) => v.ifHide && v.paramList.length)
+        .forEach((v) =>
+          v.paramList.forEach((val) => {
+            data[val.paramName] = val.value || row[val.paramComment];
           })
-          download.excel(data, this.title + '.xls')
-        } catch (error) {
-          console.error(error)
-        } finally {
-          this.exportLoading = false
-        }
-      },
-
-      getHandleNumber(val) {
-        return Math.round(parseFloat(val) * 100000000) / 100000000
-      },
-
-      getSummaries(param) {
-        const { columns } = param
-        const sums = []
-        columns.forEach((column, index) => {
-          if (index === 0) {
-            sums[index] = '总计'
-            return
-          }
+        );
+      this.$router.push({ path: `${path}`, query: { id: val, ...data } });
+    },
 
-          if (this.isNumber(column.property)) {
-            const values = this.list.map((item) => Number(item[column.property])).filter(val => !isNaN(val))
-            sums[index] = values.reduce((acc, cur) => this.getHandleNumber(acc + cur), 0)
-          } else {
-            sums[index] = ''
-          }
-        })
-        return sums
-      },
-
-      handleSelectionChange(val) {
-        this.multipleSelection = val
-      },
-
-      pathChange(path, val, item, row) {
-        if (!path || path === 'sameRouter') return
-
-        const data = {}
-        this.dynamicButtons.filter((v) => {
-          if (v.ifHide && v.paramList) {
-            v.paramList.forEach((param) => {
-              data[param.paramName] = param.value || row[param.paramComment]
-            })
-          }
-        })
-
-        this.$router.push({ path, query: { id: val, ...data } })
-      },
-
-      handleResize() {
-        this.headerWidthChange = true
-      },
-
-      handleBack() {
-        this.$router.push({ path: '/querymanage/queryManageIndex' })
-      },
-
-      formatToDateTime(date) {
-        if (!date) return ''
-        try {
-          const d = new Date(date)
-          return d.toLocaleString()
-        } catch (e) {
-          return date
-        }
-      },
+    handleBack() {
+      this.$router.push({ path: "/querymanage/queryManageIndex" });
+    },
 
-      async getFilterData() {
-        const data = await getQueryManageButtonPage({ masterId: this.$route.query.infraQueryId })
-        const arr = []
+    handleResize() {
+      this.headerWidthChange = true;
+    },
 
-        data.list.forEach((v) => {
-          if (v.ifHide && v.paramList) {
-            v.paramList.forEach((val) => {
-              if (val.filter) arr.push(val.paramName)
-            })
-          }
-        })
-
-        this.queryParamList = Object.keys(this.$route.query)
-          .filter(key => key !== 'id' && key !== 'name' && arr.includes(key))
-          .map(key => ({
-            key,
-            operate: 'like',
-            htmlType: 'input',
-            value: `'${this.$route.query[key]}'`
-          }))
-
-        await this.getQueryData()
-      }
+    async saveColumnWidth() {
+      if (!this.headerWidthChange) return;
+      this.headerWidthChange = false;
+      const widthList = [];
+      // 确保queryData是数组
+      if (!Array.isArray(this.queryData)) return;
+
+      const headers = this.$refs.tableRef.$el.querySelectorAll(
+        ".el-table__header-wrapper tr th"
+      );
+      const visibleColumns = this.queryData.filter((item) => !item.ifHide);
+      const validHeaders = [...headers].filter(
+        (th) =>
+          !th.classList.contains("el-table-column--selection") &&
+          th.innerText.trim() !== ""
+      );
+      validHeaders.forEach((header, i) => {
+        const correspondingItem = visibleColumns[i];
+        if (correspondingItem) {
+          widthList.push({
+            id: correspondingItem.id,
+            javaField: header.offsetWidth,
+          });
+        }
+      });
+      await QueryManageApi.updateWidth(widthList);
     },
 
-    async mounted() {
-      this.title = this.$route.query.name || ''
-      if (this.$route.query.infraQueryId) {
-        await this.getFilterData()
-      } else {
-        this.queryParamList = Object.keys(this.$route.query)
-          .filter(key => key !== 'id' && key !== 'name')
-          .map(key => ({
-            key,
-            operate: 'like',
-            htmlType: 'input',
-            value: `'${this.$route.query[key]}'`
-          }))
-        await this.getQueryData()
+    buildQueryParamFromRoute() {
+      const queryParam = [];
+      for (const i in this.$route.query) {
+        if (i !== "id" && i !== "name") {
+          queryParam.push({
+            key: i,
+            operate: "like",
+            htmlType: "input",
+            value: "'" + this.$route.query[i] + "'",
+          });
+        }
       }
+      this.queryParamList = queryParam;
     },
 
-    activated() {
-      if (this.$route.query.name) {
-        this.title = this.$route.query.name
-        if (this.$route.query.infraQueryId) {
-          this.getFilterData()
-        } else {
-          this.queryParamList = Object.keys(this.$route.query)
-            .filter(key => key !== 'id' && key !== 'name')
-            .map(key => ({
-              key,
-              operate: 'like',
-              htmlType: 'input',
-              value: `'${this.$route.query[key]}'`
-            }))
-          this.getQueryData()
+    async getFilterData() {
+      const data = await getQueryManageButtonPage({
+        masterId: this.$route.query?.infraQueryId,
+      });
+      const arr = [];
+      data.list
+        .filter((v) => v.ifHide && v.paramList.length)
+        .forEach((v) =>
+          v.paramList.forEach((val) => {
+            if (val.filter) arr.push(val.paramName);
+          })
+        );
+      const queryParam = [];
+      for (const i in this.$route.query) {
+        if (arr.indexOf(i) > -1) {
+          queryParam.push({
+            key: i,
+            operate: "like",
+            htmlType: "input",
+            value: "'" + this.$route.query[i] + "'",
+          });
         }
       }
+      this.queryParamList = queryParam;
+      await this.getQueryData();
     },
-
-    beforeDestroy() {
-      if (!this.headerWidthChange) return
-
-      const headers = this.$refs.tableRef.$el.querySelectorAll('.el-table__header-wrapper tr th')
-      const visibleColumns = this.queryData.filter((item) => !item.ifHide)
-      const widthList = []
-
-      Array.from(headers).forEach((header, i) => {
-        if (i >= visibleColumns.length) return
-        if (header.classList.contains('el-table-column--selection')) return
-        if (header.innerText.trim() === '') return
-
-        widthList.push({
-          id: visibleColumns[i].id,
-          javaField: header.offsetWidth
-        })
-      })
-
-      QueryManageApi.updateWidth(widthList).catch(console.error)
-    }
-  }
+  },
+};
 </script>
 
-<style scoped>
-  .mb-10px {
-    margin-bottom: 10px;
-  }
-
-  .mr-5px {
-    margin-right: 5px;
-  }
-
-  .ml-2px {
-    margin-left: 2px;
-  }
-
-  .mt-1\.2 {
-    margin-top: -1.2em;
-  }
-
-  .flex {
-    display: flex;
-  }
-
-  .items-center {
-    align-items: center;
-  }
-
-  .justify-center {
-    justify-content: center;
-  }
-
-  .flex-col {
-    flex-direction: column;
-  }
-
-  .gap-0 {
-    gap: 0;
-  }
-
-  .text-gray-600 {
-    color: #718096;
-  }
-
-  .active:hover {
-    cursor: pointer;
-    color: #1890ff;
-  }
-
-  .has-filter,
-  .is-sort {
-    color: #1890ff !important;
-  }
-
-  :deep(.el-table__row) {
-    height: 15px !important;
-  }
-
-  :deep(.el-table--medium .el-table__cell),
-  :deep(.el-table--small .el-table__cell) {
-    padding: 1px 0 !important;
-  }
+<style scoped lang="scss">
+/* ---------- ContentWrap 原样式 ---------- */
+.el-content-wrap {
+  /* 自定义 */
+}
+.text-16px {
+  font-size: 16px;
+}
+.font-700 {
+  font-weight: 700;
+}
+.ml-5px {
+  margin-left: 5px;
+}
+.max-w-200px {
+  max-width: 200px;
+}
+.mb-3px {
+  margin-bottom: 3px;
+}
+
+/* ---------- 你原来的表格样式 ---------- */
+:deep(.el-table__row) {
+  height: 15px !important;
+}
+.el-table--medium .el-table__cell,
+.el-table--small .el-table__cell {
+  padding: 1px 0 !important;
+}
+.active:hover {
+  cursor: pointer;
+}
+.has-filter,
+.is-sort,
+.active {
+  color: #1890ff !important;
+}
 </style>

File diff suppressed because it is too large
+ 759 - 891
yarn.lock


Some files were not shown because too many files changed in this diff