vue3 带tabs的后台管理系统,切换tab标签后,共用界面带参数缓存界面状态

如下图,你在【工序过帐】这个界面录入了很多数据后,切换到【字段自定义】界面,再切换回来后,当你发现【工序过帐】界面又回到了解放前,你是不是想骂人

缓存原理,使用keep-alive保存当前界面状态,代码如下

html 复制代码
<template>
  <router-view v-if="!isRefreshing" v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <keep-alive :include="aliveViews">
        <component :is="Component" :key="activeRouteFullPath" />
      </keep-alive>
    </transition>
  </router-view>
  <frame-page />
</template>

ts代码如下,这样可以实现切换tab标签后保存界面状态

TypeScript 复制代码
<script setup lang="ts">
import isBoolean from 'lodash/isBoolean';
import isUndefined from 'lodash/isUndefined';
import type { ComputedRef } from 'vue';
import { computed } from 'vue';

import FramePage from '@/layouts/frame/index.vue';
import { useTabsRouterStore } from '@/store';
import { useRouter,useRoute } from 'vue-router';
const activeRouteFullPath = computed(() => {
  const route = useRoute();
  console.log('route.fullPath', route.fullPath);
  return route.fullPath;
});

const aliveViews = computed(() => {
  const tabsRouterStore = useTabsRouterStore();
  const { tabRouters } = tabsRouterStore;
  return tabRouters
    .filter((route) => {
      const keepAliveConfig = route.meta?.keepAlive;
      const isRouteKeepAlive = isUndefined(keepAliveConfig) || (isBoolean(keepAliveConfig) && keepAliveConfig); // 默认开启keepalive
      return route.isAlive && isRouteKeepAlive;
    })
    .map((route) => route.name);
}) as ComputedRef<string[]>;

const isRefreshing = computed(() => {
  const tabsRouterStore = useTabsRouterStore();
  const { refreshing } = tabsRouterStore;
  return refreshing;
});
</script>

但是你的系统功能很强大,例如ERP或MES等企业管理系统,很多界面功能是共用,例如下面的2个报表,界面相似,但功能不一样

大量图片下载时,会显示下载进度条(下一张图片显示进度)

源码如下:

html 复制代码
<template>
  <div ref="fullscreenElementRef">
    <t-card>
      <t-table size="small" :data="data" :columns="columns" :max-height="tableHeight" :pagination="paginationConfig"
        :hover="hover" :loading="isLoading" bordered resizable @page-change="onPageChange"
        @column-resize-change="handleColumnResize">
        <template #topContent>
          <t-row style="padding-bottom:10px" justify="space-between">
            <t-col flex="auto">
              <advanced-search ref="advancedSearchRef" v-if="filterShow" :filterCode="filterCode" visible-search="true"
                fullscreen="true" Logic="And" @dynamicFilter="dynamicFilter" @exportHandler="exportFile"
                @downloadHandler="downloadFile" @formLoaded="formLoaded" @fullscreenChange="fullscreenChange" />
            </t-col>
          </t-row>
        </template>
        <template #LotNumber="{ row }">
          <t-link theme="primary" @click="queryLotNumberReport(row)">{{ row && row.LotNumber ? row.LotNumber : '' }}
          </t-link>
        </template>
        <template #OriginalLotNumber="{ row }">
          <t-link theme="primary" @click="queryLotNumberReport(row)">{{ row && row.OriginalLotNumber
            ? row.OriginalLotNumber : '' }}
          </t-link>
        </template>
        <template #Image="{ row }">
          <t-link v-if="row && row.Image" theme="primary" @click="viewImage(row)">{{ row && row.ImageText ?
            row.ImageText : t('pages.common.Image') }}
          </t-link>
        </template>
        <template #Image1="{ row }">
          <t-link v-if="row && row.Image1" theme="primary" @click="viewImage1(row)">{{ row && row.Image1Text ?
            row.Image1Text : t('pages.common.Image') }}
          </t-link>
        </template>
        <template #Image2="{ row }">
          <t-link v-if="row && row.Image2" theme="primary" @click="viewImage2(row)">{{ row && row.Image2Text ?
            row.Image2Text : t('pages.common.Image') }}
          </t-link>
        </template>
        <template v-if="summaryShow" #footerSummary>
          <div v-html="summaryData"></div>
        </template>
      </t-table>
      <add-form ref="addFormRef" :form-data="formData" @init="init" />
    </t-card>
  </div>
</template>

ts代码如下:关键代码通过 query.Id区分加载let parma = { Id: query.Id };

TypeScript 复制代码
<script lang="tsx">
export default {
  name: 'CommonReportUI',
};
</script>
<script lang="tsx" setup>
import { computed, ref, watch, onMounted, nextTick } from 'vue';
import { globalPageSize, globalColumnControllerConfig, globalClientHeight, globalForbidStatusOptions, globalStatusTheme, globalPageSizeOptions, globalIconSize } from '@/config/global';
import AdvancedSearch from '@/components/AdvancedSearch/src/AdvancedSearch.vue';
import { CONTRACT_PAYMENT_TYPES, FROM_STATUS, CONTRACT_TYPES } from '@/constants';
import { filterReport, getReports, getReportColumn, getExcel, PostReportData, downLoadCommonExcel, downLoadFile, deleteReport, getSetExcel, getReportCode, getDetail, modifyReport, modifyDetailReportSort, forbidReportBatch, enableReportBatch } from '@/api/report/report';
import { t } from '@/locales';
import { getPermissionStore, useUserStore } from '@/store';
import '@/globals';
import '@boldreports/javascript-reporting-controls/Content/v2.0/tailwind-light/bold.report-viewer.min.css';
import '@boldreports/javascript-reporting-controls/Scripts/v2.0/common/bold.reports.common.min';
import '@boldreports/javascript-reporting-controls/Scripts/v2.0/common/bold.reports.widgets.min';
import '@boldreports/javascript-reporting-controls/Scripts/v2.0/bold.report-viewer.min';
import '@/reportglobal/reportviewer/ej.localetexts.zh-CN.min';
import { useLocale } from '@/locales/useLocale';
import { useRoute } from 'vue-router'
import { getEnumCode } from '@/api/system/enum';
import { filterLot, getLot, createLot, getLotList, exporter, downloadReport } from '@/api/prd/lot';
import addForm from '../lotReport/components/addForm.vue';
import { formatDateFields } from '@/utils/date'
const { locale } = useLocale();
const userStore = useUserStore();
const route = useRoute();
const query = route.query;
const formData = ref({});
const addFormRef = ref();
const advancedSearchRef = ref();
const fullscreenElementRef = ref();
const data = ref([]);
const columns = ref([]);
const TOTAL = ref(0);
const currentPage = ref(1);
const paginationConfig = computed(() => {
  return {
    current: currentPage.value,
    pageSize: userFilter.value?.PageSize || globalPageSize,
    defaultCurrent: 1,
    defaultPageSize: globalPageSize,
    pageSizeOptions: globalPageSizeOptions,
    total: TOTAL.value,
    showJumper: true,
    popupProps: { attach: 'body' }
  };
});
const hover = ref(true);
const init = () => {
  isLoading.value = true;
  let parma = { Id: query.Id };
  getReports(parma).then((res) => {
  if (res.AutoLoad === 'Y') {
    autoLoadQuery.value = true;
  }
    filterCode.value = res.FiltersCode;
    reportCode.value = res.Code;
    //viewerFlag.value = false;
    filterShow.value = true;
  }).then(() => {
    getReportColumn(query.Id).then((resColumns) => {
      console.log('resColumns', resColumns);
      var data = localStorage.getItem(`tableColumnsConfig${reportCode.value}`);
      var localColumnsConfig = null;
      if (data != null) {
        localColumnsConfig = JSON.parse(data);
      }
      columns.value = [{ colKey: 'serial-number', title: t('pages.common.sequence'), width: 65, align: 'center' }];
      if (Array.isArray(resColumns)) {
        resColumns.forEach((item) => {
          if (localColumnsConfig != null && localColumnsConfig.hasOwnProperty(item)) {
            var newcloumns = { colKey: item, title: item, width: localColumnsConfig[item] };
            columns.value.push(newcloumns);
          } else {
            var cloumns = { colKey: item, title: item };
            columns.value.push(cloumns);
          }
        })
      } else {
        for (const key in resColumns) {
          if (resColumns.hasOwnProperty(key)) {
            if (localColumnsConfig != null && localColumnsConfig.hasOwnProperty(resColumns[key])) {
              var newcloumns = { colKey: resColumns[key], title: key, width: localColumnsConfig[resColumns[key]] };
              columns.value.push(newcloumns);
            } else {
              var cloumns = { colKey: resColumns[key], title: key };
              columns.value.push(cloumns);
            }
          }
        }
      }
    })
  }).finally(() => {
    isLoading.value = false;
  });
}
const onPageChange = (pageInfo: any) => {
  userFilter.value.PageSize = pageInfo.pageSize;
  userFilter.value.CurrentPage = pageInfo.current;
  dynamicFilter(userFilter.value);

};
onMounted(() => {
  init();
});
</script>

从上面的代码可以看出报表的搜索条件是通过advanced-search组件配置的

报表配置界面

报表支持类型ECharts图片,Excel图表、RDLC(微软的ReportBuild或PowerBI)等格式

配置带link功能

界面查询条件与数据表格可以通过存储过程自动解析

搜索条件配置界面

界面缓存的关键,是通过组件的name,只要将共用界面的name追加到keep-alive :include="aliveViews"即可,把动态菜单路由改为route.fullPath,不要写错了route与router是有区别的

通过上面操作带参数的共用界面也可以缓存了。

相关推荐
xkxnq17 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A18 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常18 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端
小码哥_常18 小时前
从Groovy到KTS:Android Gradle脚本的华丽转身
前端
灵感__idea19 小时前
Hello 算法:复杂问题的应对策略
前端·javascript·算法
麦麦鸡腿堡19 小时前
JavaWeb_请求参数,设置响应数据,分层解耦
java·开发语言·前端
Dxy123931021620 小时前
CSS常用样式详解:从基础到进阶的全面指南
前端·css
IT_陈寒20 小时前
SpringBoot自动配置揭秘:5个让开发效率翻倍的隐藏技巧
前端·人工智能·后端
Moment20 小时前
前端工程化 + AI 赋能,从需求到运维一条龙怎么搭 ❓❓❓
前端·javascript·面试
Joker Zxc21 小时前
【前端基础(Javascript部分)】6、用JavaScript的递归函数和for循环,计算斐波那契数列的第 n 项值
开发语言·前端·javascript