Просмотр исходного кода

[Feat 0000] 页面自适应模组重写,去除无效依赖

houzekong 5 месяцев назад
Родитель
Сommit
dbf7e670f5

+ 4 - 27
src/App.vue

@@ -1,5 +1,5 @@
 <template>
-  <AdaptiveContainer :options="{ width: width, height: height }" style="overflow-y: hidden">
+  <AdaptiveContainer :options="adaptiveOptions">
     <ConfigProvider :theme="appTheme" :locale="getAntdLocale">
       <AppProvider>
         <RouterView />
@@ -9,7 +9,7 @@
 </template>
 
 <script lang="ts" setup>
-  import { watch, ref, nextTick, provide, onUnmounted} from 'vue';
+  import { watch, ref } from 'vue';
   import { theme } from 'ant-design-vue';
   import { ConfigProvider } from 'ant-design-vue';
   import { AppProvider } from '/@/components/Application';
@@ -19,38 +19,15 @@
   import { useRootSetting } from '/@/hooks/setting/useRootSetting';
   import { ThemeEnum } from '/@/enums/appEnum';
   import { changeTheme } from '/@/logics/theme/index';
-  import AdaptiveContainer from '/@/components/Container/src/Adaptive.vue';
+  import AdaptiveContainer from '/@/components/Container/src/AdaptiveContainer.vue';
 
   const appStore = useAppStore();
   // 解决日期时间国际化问题
   import 'dayjs/locale/zh-cn';
   // support Multi-language
   const { getAntdLocale } = useLocale();
-  const isReload = ref(false);
-  const width = ref(1920);
-  const height = ref(928);
 
-  const body = document.body.getBoundingClientRect();
-  if (screen.height === body.height && screen.width === body.width) {
-    height.value = 1080;
-  }
-
-  const reloadRouter = () => {
-    isReload.value = true;
-    nextTick(() => {
-      isReload.value = false;
-    });
-  };
-  provide('reloadRouter', reloadRouter);
-  onUnmounted(() => {
-    window['renderer']?.dispose();
-    window['renderer']?.forceContextLoss();
-    if (window['renderer']) {
-      window['renderer'].content = null;
-    }
-    const gl = window['renderer']?.domElement.getContext('webgl');
-    gl && gl.getExtension('WEBGL_lose_context').loseContext();
-  });
+  const adaptiveOptions = ref({});
 
   useTitle();
   /**

+ 0 - 175
src/components/Container/src/Adaptive.vue

@@ -1,175 +0,0 @@
-<template>
-  <div id="adaptive-container" :ref="refName">
-    <template v-if="ready">
-      <slot></slot>
-    </template>
-  </div>
-</template>
-
-<script>
-  import { ref, onMounted, onUnmounted, nextTick, defineComponent } from 'vue';
-  import { debounce, setRem } from '/@/utils/index';
-  import { useAppStore } from '/@/store/modules/app';
-  import { getActions } from '/@/qiankun/state';
-
-  export default defineComponent({
-    name: 'AdaptiveContainer',
-    props: {
-      options: Object,
-    },
-    setup(ctx) {
-      const appStore = useAppStore();
-
-      const refName = 'AdaptiveContainer'; //AdaptiveContainer
-      // 屏幕宽度
-      const width = ref(0);
-      // 屏幕高度
-      const height = ref(0);
-      // 原始屏幕宽度
-      const originalWidth = ref(0);
-      // 原始屏幕高度
-      const originalHeight = ref(0);
-      // 控制显示
-      const ready = ref(false);
-      /*
-       * dom:well-container的dom
-       * observer: window.MutationObserver(Bom实例)监听dom改变
-       */
-      let dom, observer;
-      const actions = getActions();
-
-      //设置初始值
-      const initSize = () => {
-        return new Promise((resolve) => {
-          nextTick(() => {
-            dom = document.getElementById('adaptive-container');
-            // 获取大屏的传入尺寸
-            if (ctx.options && ctx.options.width && ctx.options.height) {
-              //传入宽高
-              width.value = ctx.options.width;
-              height.value = ctx.options.height;
-            } else {
-              //可见宽高
-              width.value = dom.clientWidth;
-              height.value = dom.clientHeight;
-            }
-            // 获取画布尺寸
-            if (!originalWidth.value || !originalHeight.value) {
-              //屏幕分辨率宽高
-              originalWidth.value = window.screen.width;
-              originalHeight.value = window.screen.height;
-            }
-            resolve();
-          });
-        });
-      };
-
-      const updateSize = () => {
-        if (width.value && height.value) {
-          dom.style.width = `${width.value}px`;
-          dom.style.height = `${height.value}px`;
-        } else {
-          dom.style.width = `${originalWidth.value}px`;
-          dom.style.height = `${originalHeight.value}px`;
-        }
-      };
-
-      const updateScale = () => {
-        // debugger
-        // 获取真实的视口尺寸
-        const currentWidth = document.body.clientWidth;
-        const currentHeight = document.body.clientHeight;
-        // 获取大屏最终的宽高
-        const realWidth = width.value || originalWidth.value;
-        const realHeight = height.value || originalHeight.value;
-        // console.log(currentWidth, currentHeight)
-        // 缩放比例  = 分辨率宽高 / 传入宽高(可视宽高)
-        const widthScale = currentWidth / realWidth;
-        const heightScale = currentHeight / realHeight;
-        appStore.setWidthScale(widthScale);
-        appStore.setHeightScale(heightScale);
-        actions.setGlobalState({ widthScale, heightScale });
-
-        //如果dom存在,就按照比例缩放
-        dom && (dom.style.transform = `scale(${widthScale}, ${heightScale})`);
-      };
-      const cssSize = () => {
-        const currentWidth = document.body.clientWidth;
-        const realWidth = width.value || originalWidth.value;
-        const whdef = 100 / currentWidth; // 表示1920的设计图,使用100PX的默认值
-        const wW = currentWidth / realWidth; // 当前窗口的宽度
-        const rem = wW * whdef; // 以默认比例值乘以当前窗口宽度,得到该宽度下的相应FONT-SIZE值
-        // document.documentElement.style.fontSize = rem + 'px';
-        document.documentElement.style.fontSize = rem + 'px';
-        window.addEventListener('resize', function () {
-          const whdef = 100 / currentWidth; // 表示1920的设计图,使用100PX的默认值
-          const wW = window.innerWidth; // 当前窗口的宽度
-          const rem = wW * whdef; // 以默认比例值乘以当前窗口宽度,得到该宽度下的相应FONT-SIZE值
-          document.documentElement.style.fontSize = rem + 'px';
-        });
-      };
-
-      //重置缩放比例
-      const onResize = async () => {
-        // debugger;
-        await initSize();
-        updateScale();
-        setRem();
-        cssSize();
-      };
-
-      const initMutationObserver = () => {
-        //监听元素属性变化
-        const MutationObserver = window.MutationObserver;
-        //如果变化,就用onResize重置屏幕所缩放比例
-        observer = new MutationObserver(onResize);
-        observer.observe(dom, {
-          attributes: true,
-          // attributeFilter: ['style'],
-          attributeOldValue: true,
-        });
-      };
-      //移除监听属性
-      const removeMutationObserver = () => {
-        if (observer) {
-          observer.disconnect();
-          observer.takeRecords();
-          observer = null;
-        }
-      };
-      //
-      onMounted(async () => {
-        ready.value = false;
-        await initSize();
-        updateSize();
-        updateScale();
-        setRem();
-        cssSize();
-        window.addEventListener('resize', debounce(100, onResize));
-        // initMutationObserver();
-        ready.value = true;
-      });
-
-      onUnmounted(() => {
-        window.removeEventListener('resize', onResize);
-        // removeMutationObserver();
-      });
-
-      return {
-        refName,
-        ready,
-      };
-    },
-  });
-</script>
-
-<style lang="less" scoped>
-  #adaptive-container {
-    position: relative;
-    top: 0;
-    left: 0;
-    overflow: hidden;
-    transform-origin: left top;
-    z-index: 0;
-  }
-</style>

+ 197 - 0
src/components/Container/src/AdaptiveContainer.vue

@@ -0,0 +1,197 @@
+<!-- AdaptiveContainer.vue -->
+<template>
+  <div ref="containerRef" class="adaptive-container">
+    <slot />
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent, ref, onMounted, onUnmounted } from 'vue';
+  import { debounce } from 'lodash-es';
+  import { useAppStore } from '/@/store/modules/app';
+  import { computed } from 'vue';
+
+  // 类型定义
+  interface AdaptiveOptions {
+    width?: number;
+    height?: number;
+    baseWidth?: number; // 设计稿基准宽度,用于REM计算
+    minScale?: number; // 最小缩放比例
+    maxScale?: number; // 最大缩放比例
+    debounceTime?: number; // 防抖时间(ms)
+  }
+
+  interface ScaleInfo {
+    widthScale: number;
+    heightScale: number;
+    scale: number;
+  }
+
+  export default defineComponent({
+    name: 'AdaptiveContainer',
+    props: {
+      options: {
+        type: Object as () => AdaptiveOptions,
+        default: () => ({}),
+      },
+    },
+    setup(props) {
+      const appStore = useAppStore();
+      const containerRef = ref<HTMLElement | null>(null);
+      // const isReady = ref(false);
+
+      // 默认配置
+      const defaultOptions = ref<Required<AdaptiveOptions>>({
+        width: 1920,
+        height: 1080,
+        baseWidth: 1920,
+        minScale: 0.5,
+        maxScale: 2,
+        debounceTime: 100,
+      });
+
+      // 合并配置
+      const config = computed(() => {
+        return {
+          ...defaultOptions.value,
+          ...props.options,
+        };
+      });
+
+      // 设计稿尺寸
+      const designSize = computed(() => {
+        return {
+          width: config.value.width,
+          height: config.value.height,
+        };
+      });
+
+      /**
+       * 设置REM基准值
+       * @param baseSize 基准大小,默认16px
+       */
+      const setRem = (baseSize: number = 16): void => {
+        try {
+          const baseVal = baseSize / config.value.baseWidth;
+          const vW = window.innerWidth;
+          const rem = vW * baseVal;
+
+          // 设置全局缩放比例(如果有需要)
+          if (typeof window['$size'] !== 'number') {
+            Object.defineProperty(window, '$size', {
+              value: rem / 100,
+              writable: true,
+            });
+          } else {
+            window['$size'] = rem / 100;
+          }
+
+          // document.documentElement.style.fontSize = `${rem}px`;
+        } catch (error) {
+          console.error('设置REM失败:', error);
+          // 回退方案:使用固定REM
+          document.documentElement.style.fontSize = `${baseSize}px`;
+        }
+      };
+
+      /**
+       * 计算缩放比例
+       */
+      const calculateScale = (): ScaleInfo => {
+        const { width: designWidth, height: designHeight } = designSize.value;
+        const { innerWidth: currentWidth, innerHeight: currentHeight } = window;
+        const { minScale, maxScale } = config.value;
+
+        // 计算原始比例
+        const rawWidthScale = currentWidth / designWidth;
+        const rawHeightScale = currentHeight / designHeight;
+
+        // 应用比例限制
+        const widthScale = Math.max(minScale, Math.min(maxScale, rawWidthScale));
+        const heightScale = Math.max(minScale, Math.min(maxScale, rawHeightScale));
+
+        // 选择最小比例保持内容完整显示
+        const scale = Math.min(widthScale, heightScale);
+
+        return { widthScale, heightScale, scale };
+      };
+
+      /**
+       * 应用缩放变换
+       */
+      const applyScale = (): void => {
+        if (!containerRef.value) return;
+
+        const container = containerRef.value;
+        const { widthScale, heightScale } = calculateScale();
+
+        // 应用缩放
+        container.style.transform = `scale(${widthScale}, ${heightScale})`;
+        container.style.transformOrigin = 'left top';
+
+        // 更新store中的比例信息(如果需要)
+        appStore.setWidthScale(widthScale);
+        appStore.setHeightScale(heightScale);
+      };
+
+      /**
+       * 处理窗口大小变化
+       */
+      const handleResize = debounce((): void => {
+        // 更新缩放和REM
+        applyScale();
+        setRem();
+      }, config.value.debounceTime);
+
+      onMounted(async () => {
+        // 生命周期
+        try {
+          if (!containerRef.value) {
+            // if (!document.getElementById('app')) {
+            throw new Error('容器元素未找到');
+          }
+          const container = containerRef.value;
+          // const container = document.getElementById('app')!;
+          defaultOptions.value.width = container.clientWidth;
+          defaultOptions.value.height = container.clientHeight;
+
+          const { width, height } = designSize.value;
+          container.style.width = `${width}px`;
+          container.style.height = `${height}px`;
+
+          applyScale();
+          setRem();
+        } catch (error) {
+          console.error('初始化自适应容器失败:', error);
+        }
+
+        window.addEventListener('resize', handleResize);
+      });
+
+      onUnmounted(() => {
+        window.removeEventListener('resize', handleResize);
+      });
+
+      return {
+        containerRef,
+        // isReady,
+      };
+    },
+  });
+</script>
+
+<style lang="less" scoped>
+  .adaptive-container {
+    // position: relative;
+    // top: 0;
+    // left: 0;
+    overflow: hidden;
+    height: 100%;
+    width: 100%;
+    // transform-origin: left top;
+    // z-index: 0;
+    // 防止缩放导致的模糊(开启GPU加速)
+    backface-visibility: hidden;
+    // -webkit-font-smoothing: antialiased;
+  }
+</style>

+ 0 - 40
src/qiankun/apps.ts

@@ -1,40 +0,0 @@
-import { prefetchApps, AppMetadata } from 'qiankun';
-/**
- *微应用apps
- * @name: 微应用名称 - 具有唯一性
- * @entry: 微应用入口.必选 - 通过该地址加载微应用,
- * @container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
- * @activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
- */
-//子应用列表
-const _apps: AppMetadata[] = [];
-for (const key in import.meta.env) {
-  if (key === 'VITE_APP_SUB_APP') {
-    const appList = JSON.parse(import.meta.env[key].replace(/'/g, '"'));
-
-    appList.forEach((app) => {
-      let utlStr;
-      if (import.meta.env.PROD) {
-        // 多端口请求
-        if (VUE_APP_URL.baseUrl.split(':').length > 1) {
-          utlStr = VUE_APP_URL.baseUrl.split(':')[1] + app[1];
-        } else {
-          utlStr = app[1];
-        }
-      } else {
-        utlStr = app[1];
-      }
-      const obj = {
-        name: app[0],
-        entry: utlStr,
-        container: `#${app[0]}`,
-        activeRule: app[0],
-      };
-      // debugger;
-
-      _apps.push(obj);
-    });
-  }
-  prefetchApps([..._apps]);
-}
-export const apps = _apps;

+ 0 - 51
src/qiankun/index.ts

@@ -1,51 +0,0 @@
-/**
- * qiankun配置
- */
-import { loadMicroApp, start } from 'qiankun';
-import { apps } from './apps';
-import { getProps } from './state';
-
-const activeApps = {};
-/**
- * 重构apps
- */
-function filterApps() {
-  apps.forEach((item) => {
-    //主应用需要传递给微应用的数据。
-    item['props'] = getProps();
-
-    //微应用触发的路由规则
-    // @ts-ignore
-    item.activeRule = item.activeRule.startsWith('/') ? item.activeRule : `/${item.activeRule}`;
-  });
-  return apps;
-}
-
-const mountMicroApp = (name, isReFresh = false) => {
-  const microApps = filterApps();
-  const app = microApps.find((item) => item.name === name);
-  if (app) {
-    const instance = activeApps[app.name];
-    if (instance && instance.getStatus() === 'NOT_MOUNTED' && !isReFresh) {
-      instance.mount(app);
-    } else {
-      delete activeApps[app.name];
-      activeApps[app.name] = loadMicroApp(app, { autoStart: false, sandbox: { strictStyleIsolation: false } }); // 手动加载子应用'
-    }
-  }
-};
-
-// 卸载app的方法
-const unmountMicroApps = async (multipleApp) => {
-  if (JSON.stringify(activeApps) !== '{}' && multipleApp.some) {
-    for (const key in activeApps) {
-      multipleApp.filter(async (name) => {
-        if (name.includes(key)) {
-          await activeApps[key].unmount();
-        }
-      });
-    }
-  }
-};
-
-export { mountMicroApp, unmountMicroApps, activeApps };

+ 0 - 58
src/qiankun/state.ts

@@ -1,58 +0,0 @@
-/**
- *公共数据
- */
-import { initGlobalState } from 'qiankun';
-import { store } from '/@/store';
-import { router } from '/@/router';
-import { getToken } from '/@/utils/auth';
-
-let actions;
-
-//定义传入子应用的数据
-export function getProps() {
-  return {
-    data: {
-      publicPath: '/',
-      token: getToken(),
-      store: store,
-      router,
-      isMounted: false,
-    },
-    actions: getActions(),
-  };
-}
-
-/**
- * 定义全局状态,并返回通信方法,在主应用使用,微应用通过 props 获取通信方法。
- * @param state 主应用穿的公共数据
- */
-export function initGlState(
-  info: any = { token: '', userInfo: {}, isMounted: false, locationObj: null, locationId: '', pageObj: null, widthScale: 1, heightScale: 1 }
-) {
-  if (actions) return;
-  // 初始化state
-  actions = initGlobalState(info);
-  // 设置新的值
-  actions.setGlobalState({
-    token: getToken(),
-    isMounted: false,
-    pageObj: {},
-    widthScale: 1,
-    heightScale: 1,
-    url: {},
-  });
-  // 注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
-  actions.onGlobalStateChange((newState, prev) => {
-    // state: 变更后的状态; prev 变更前的状态
-    console.info('newState', newState);
-    console.info('prev', prev);
-    for (const key in newState) {
-      console.info('onGlobalStateChange', key);
-    }
-  });
-}
-
-export function getActions() {
-  if (!actions) initGlState();
-  return actions;
-}

+ 16 - 40
src/utils/index.ts

@@ -1,11 +1,11 @@
 import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
 import type { App, Plugin } from 'vue';
-import type { FormSchema, FormActionType } from "@/components/Form";
+import type { FormSchema, FormActionType } from '@/components/Form';
 
 import { unref } from 'vue';
 import { isObject, isFunction, isString } from '/@/utils/is';
 import Big from 'big.js';
-import dayjs from "dayjs";
+import dayjs from 'dayjs';
 // 代码逻辑说明: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
 export const URL_HASH_TAB = `__AGWE4H__HASH__TAG__PWHRG__`;
 
@@ -95,7 +95,7 @@ export function getDynamicProps<T, U>(props: T): Partial<U> {
  * @updateBy:zyf
  */
 export function getValueType(props, field) {
-  const formSchema = unref(unref(props)?.schemas)
+  const formSchema = unref(unref(props)?.schemas);
   let valueType = 'string';
   if (formSchema) {
     const schema = formSchema.filter((item) => item.field === field)[0];
@@ -265,7 +265,7 @@ export function getQueryVariable(url) {
     r,
     i = url.split('?')[1],
     s = {};
-  (t = i.split('&')), (r = null), (n = null);
+  ((t = i.split('&')), (r = null), (n = null));
   for (const o in t) {
     const u = t[o].indexOf('=');
     u !== -1 && ((r = t[o].substr(0, u)), (n = t[o].substr(u + 1)), (s[r] = n));
@@ -376,7 +376,6 @@ export function importViewsFile(path): Promise<any> {
   });
 }
 
-
 /**
  * 跳转至积木报表的 预览页面
  * @param url
@@ -385,22 +384,21 @@ export function importViewsFile(path): Promise<any> {
  */
 export function goJmReportViewPage(url, id, token) {
   // URL支持{{ window.xxx }}占位符变量
-  url = url.replace(/{{([^}]+)?}}/g, (_s1, s2) => _eval(s2))
+  url = url.replace(/{{([^}]+)?}}/g, (_s1, s2) => _eval(s2));
   if (url.includes('?')) {
-    url += '&'
+    url += '&';
   } else {
-    url += '?'
+    url += '?';
   }
-  url += `id=${id}`
-  url += `&token=${token}`
-  window.open(url)
+  url += `id=${id}`;
+  url += `&token=${token}`;
+  window.open(url);
 }
 
 /**
  * 获取随机颜色
  */
 export function getRandomColor(index?) {
-
   const colors = [
     'rgb(100, 181, 246)',
     'rgb(77, 182, 172)',
@@ -423,7 +421,7 @@ export function getRandomColor(index?) {
     'rgb(254, 161, 172)',
     'rgb(194, 163, 205)',
   ];
-  return index && index < 19 ? colors[index] : colors[Math.floor((Math.random()*(colors.length-1)))];
+  return index && index < 19 ? colors[index] : colors[Math.floor(Math.random() * (colors.length - 1))];
 }
 
 export function getRefPromise(componentRef) {
@@ -447,7 +445,7 @@ export function getRefPromise(componentRef) {
  * 用new Function替换eval
  */
 export function _eval(str: string) {
- return new Function(`return ${str}`)();
+  return new Function(`return ${str}`)();
 }
 
 /**
@@ -492,7 +490,7 @@ export const setPopContainer = (node, selector) => {
       const retrospect = (node, elems) => {
         let ele = node.parentNode;
         while (ele) {
-          const findParentNode = elems.find(item => item === ele);
+          const findParentNode = elems.find((item) => item === ele);
           if (findParentNode) {
             ele = null;
             return findParentNode;
@@ -523,12 +521,11 @@ export const setPopContainer = (node, selector) => {
  * label、value通用,title、val给权限管理用的
  */
 export function useConditionFilter() {
-
   // 通用条件
   const commonConditionOptions = [
-    {label: '为空', value: 'empty', val: 'EMPTY'},
-    {label: '不为空', value: 'not_empty', val: 'NOT_EMPTY'},
-  ]
+    { label: '为空', value: 'empty', val: 'EMPTY' },
+    { label: '不为空', value: 'not_empty', val: 'NOT_EMPTY' },
+  ];
 
   // 数值、日期
   const numberConditionOptions = [
@@ -669,24 +666,3 @@ export const split = (str) => {
   }
   return str;
 };
-
-// 防抖截流
-export function debounce(delay, callback) {
-  let task;
-  return function () {
-    clearTimeout(task);
-    task = setTimeout(() => {
-      callback.apply(this, arguments);
-    }, delay);
-  };
-}
-
-export function setRem() {
-  // 默认使用100px作为基准大小
-  const baseSize = 100;
-  const baseVal = baseSize / 1920;
-  const vW = window.innerWidth; // 当前窗口的宽度
-  const rem = vW * baseVal; // 以默认比例值乘以当前窗口宽度,得到该宽度下的相应font-size值
-  window.$size = rem / 100;
-  document.documentElement.style.fontSize = rem + 'px';
-}