소스 검색

feat: 布局本地化

qinlaiqiao 3 년 전
부모
커밋
2ae205c6f6

+ 5 - 2
package.json

@@ -26,7 +26,9 @@
     "lodash": "^4.17.21",
     "mitt": "^2.1.0",
     "vue": "^3.2.16",
-    "vue-router": "^4.0.12"
+    "vue-router": "^4.0.12",
+    "vuex": "^4.0.2",
+    "vuex-persistedstate": "^4.1.0"
   },
   "devDependencies": {
     "@babel/polyfill": "^7.12.1",
@@ -56,6 +58,7 @@
     "tailwindcss": "^2.2.19",
     "typescript": "^4.4.3",
     "vite": "^2.6.4",
-    "vue-tsc": "^0.3.0"
+    "vue-tsc": "^0.3.0",
+    "vuex-module-decorators": "^1.0.1"
   }
 }

+ 0 - 0
src/store/.gitkeep


+ 23 - 0
src/store/.localmodules.ts

@@ -0,0 +1,23 @@
+// import { isShareHref } from '@/utils/share';
+
+interface IConfig {
+  key?: string; // localStorage(sessionStorage) 的键名,默认是 vuex
+  paths: string[]; // 需要本地持久化的 vuex 模块名称
+  storage: Storage; // 使用不同的 storage 对象存储
+}
+
+
+// 获取storage对象
+const getStorage = (): Storage => {
+  // 如果是进入分享链接项目,只需要session持久化,避免与正常项目localStorage冲突
+  // return isShareHref() ? window.sessionStorage : window.localStorage;
+  return window.localStorage;
+};
+
+// 需要本地持久化的 vuex 模块名称
+const config: IConfig = {
+  paths: ['summaryLayout'],
+  storage: getStorage(),
+};
+
+export default config;

+ 35 - 0
src/store/DynamicModule.ts

@@ -0,0 +1,35 @@
+import { Module } from 'vuex-module-decorators';
+import storeInstance from '@/store';
+import { Store } from 'vuex';
+import options from './.localmodules';
+
+const { paths, key = 'vuex', storage } = options;
+
+export interface IOptions {
+  namespaced?: boolean;
+  name: string;
+  dynamic?: true;
+  store?: Store<any>;
+  preserveState?: boolean;
+}
+
+function DynamicModule({ namespaced = true, dynamic = true, name, store = storeInstance, preserveState }: IOptions) {
+  if (preserveState === undefined) {
+    const modulesStr = storage.getItem(key);
+    if (paths.includes(name) && modulesStr != null) {
+      const modules = JSON.parse(modulesStr);
+      preserveState = modules[name];
+    } else {
+      preserveState = false;
+    }
+  }
+  return Module({
+    namespaced,
+    dynamic,
+    name,
+    store,
+    preserveState,
+  });
+}
+
+export default DynamicModule;

+ 14 - 0
src/store/createStorageState.ts

@@ -0,0 +1,14 @@
+import createPersistedState from 'vuex-persistedstate';
+import options from './.localmodules';
+
+const { key = 'vuex', paths, storage } = options;
+
+const createStorageState = () => {
+  return createPersistedState({
+    key,
+    paths,
+    storage,
+  });
+};
+
+export default createStorageState;

+ 8 - 0
src/store/index.ts

@@ -0,0 +1,8 @@
+import { createStore } from 'vuex';
+import createStorageState from './createStorageState';
+
+export default createStore({
+  modules: {},
+  strict: import.meta.env.DEV,
+  plugins: [createStorageState()],
+});

+ 44 - 0
src/store/modules/summaryLayout.ts

@@ -0,0 +1,44 @@
+import { VuexModule, Mutation, getModule } from 'vuex-module-decorators';
+import DynamicModule from '../DynamicModule';
+
+@DynamicModule({ name: 'summaryLayout' })
+export class SummaryLayout extends VuexModule {
+  subjectTreeSize = 60
+  mainContentSize = 300
+  mainLeftSize = 150
+  mainRightSize = 50
+  mainLeftTopSize = 300
+  mainLeftBottomSize = 50
+
+  @Mutation
+  setSubjectTreeSize(size: number) {
+    this.subjectTreeSize = size
+  }
+
+  @Mutation
+  setMainContentSize(size: number) {
+    this.mainContentSize = size
+  }
+
+  @Mutation
+  setMainLeftSize(size: number) {
+    this.mainLeftSize = size
+  }
+
+  @Mutation
+  setMainRightSize(size: number) {
+    this.mainRightSize = size
+  }
+
+  @Mutation
+  setMainLeftTopSize(size: number) {
+    this.mainLeftTopSize = size
+  }
+
+  @Mutation
+  setMainLeftBottomSize(size: number) {
+    this.mainLeftBottomSize = size
+  }
+}
+
+export default getModule<SummaryLayout>(SummaryLayout);

+ 15 - 11
src/views/project/summary/Summary.vue

@@ -11,19 +11,23 @@ import useSummaryLayout from './scripts/useSummaryLayout'
 
 // 布局相关代码
 const {
-  projectExplorerSize,
+  subjectTreeSize,
   mainContentSize,
   mainLeftSize,
   mainRightSize,
   mainLeftTopSize,
   mainLeftBottomSize,
 
-  handleProjectExplorerSize,
+  handleSubjectTreeSize,
   handleMainContentSize,
   handleMainLeftSize,
   handleMainRightSize,
   handleMainLeftTopSize,
   handleMainLeftBottomSize,
+
+  costTableRef,
+  bottomTabsRef,
+  stdBillRef
 } = useSummaryLayout()
 
 const loading = ref(true)
@@ -87,16 +91,16 @@ onMounted(async () => {
   <article class="summary-page" v-loading="loading">
     <resizable-layout>
       <resizable-layout-item
-        :weight="projectExplorerSize"
-        :min-width="200"
-        @resize="handleProjectExplorerSize"
+        :weight="subjectTreeSize"
+        :min-width="180"
+        @resize="handleSubjectTreeSize"
       >
         <!-- 左侧项目结构树 -->
         <subject-tree :tree-data="treeData" />
       </resizable-layout-item>
       <resizable-layout-item
         :weight="mainContentSize"
-        :min-width="800"
+        :min-width="500"
         @resize="handleMainContentSize"
       >
         <!-- 工具栏 -->
@@ -110,19 +114,19 @@ onMounted(async () => {
             <resizable-layout direction="vertical">
               <resizable-layout-item
                 :weight="mainLeftTopSize"
-                :min-width="400"
+                :min-height="100"
                 @resize="handleMainLeftTopSize"
               >
                 <!-- 造价书表格 -->
-                <cost-table />
+                <cost-table ref="costTableRef" />
               </resizable-layout-item>
               <resizable-layout-item
                 :weight="mainLeftBottomSize"
-                :min-width="400"
+                :min-height="100"
                 @resize="handleMainLeftBottomSize"
               >
                 <!-- 底部 tabs -->
-                <bottom-tabs />
+                <bottom-tabs ref="bottomTabsRef" />
               </resizable-layout-item>
             </resizable-layout>
           </resizable-layout-item>
@@ -132,7 +136,7 @@ onMounted(async () => {
             :min-width="150"
             @resize="handleMainRightSize"
           >
-            <std-bill />
+            <std-bill ref="stdBillRef" />
           </resizable-layout-item>
         </resizable-layout>
       </resizable-layout-item>

+ 12 - 4
src/views/project/summary/components/bottom-tabs/BottomTabs.vue

@@ -1,11 +1,19 @@
 <script setup lang="ts">
-import {onMounted, reactive, ref} from "vue";
+import { onMounted, reactive, ref } from "vue";
+
+const Render = () => {
+  // 重新渲染
+  // hotRef.value && hotRef.value.Render();
+  console.log('bottom tabs render')
+}
+
+defineExpose({
+  Render
+})
 </script>
 
 <template>
-  <section class="bottom-tabs">
-    底部 tabs
-  </section>
+  <section class="bottom-tabs">底部 tabs</section>
 </template>
 
 <style lang="scss" src="./style.scss" scoped></style>

+ 9 - 1
src/views/project/summary/components/cost-table/CostTable.vue

@@ -5,11 +5,11 @@ import { expandAllTreeNode } from '@/utils/frontend/tree';
 import useHotRef from '@/composables/useHotRef';
 import rawData from '@/constants/tmp/table-data.json'
 import tblColMeta from '@/constants/tmp/table-columns-meta'
+import { Hot } from "@/types/components";
 
 const { getHotRef, notNull } = useHotRef();
 const hotRef = getHotRef();
 
-///////
 const tree = new Tree([]);
 const tableTreeData: TreeNode[] = tree.data;
 const loading = ref(true);
@@ -56,6 +56,14 @@ const tableSettings = {
   ],
 };
 
+const Render = () => {
+  // 重新渲染
+  hotRef.value && hotRef.value.Render();
+}
+
+defineExpose({
+  Render
+})
 </script>
 
 <template>

+ 12 - 4
src/views/project/summary/components/std-bill/StdBill.vue

@@ -1,11 +1,19 @@
 <script setup lang="ts">
-import {onMounted, reactive, ref} from "vue";
+import { onMounted, reactive, ref } from "vue";
+
+const Render = () => {
+  // 重新渲染
+  // hotRef.value && hotRef.value.Render();
+  console.log('std bill render')
+}
+
+defineExpose({
+  Render
+})
 </script>
 
 <template>
-  <section class="std-bill">
-    标准清单
-  </section>
+  <section class="std-bill">标准清单</section>
 </template>
 
 <style lang="scss" src="./style.scss" scoped></style>

+ 71 - 12
src/views/project/summary/scripts/useSummaryLayout.ts

@@ -1,40 +1,99 @@
-import { ref } from "vue";
+import { ref, computed } from "vue";
+import summaryLayoutStore from '@/store/modules/summaryLayout'
+
+type NeedRenderComponent = Common.IRender;
 
 // 布局相关代码
 export default function useSummaryLayout() {
-    const projectExplorerSize = ref(60)
-    const mainContentSize = ref(300)
-    const mainLeftSize = ref(150)
-    const mainRightSize = ref(50)
-    const mainLeftTopSize = ref(300)
-    const mainLeftBottomSize = ref(150)
-
-    const handleProjectExplorerSize = (size: number) => {
-        projectExplorerSize.value = size
+    const {
+        setSubjectTreeSize,
+        setMainContentSize,
+        setMainLeftSize,
+        setMainRightSize,
+        setMainLeftTopSize,
+        setMainLeftBottomSize,
+    } = summaryLayoutStore
+
+    const subjectTreeSize = computed({
+        get: () => summaryLayoutStore.subjectTreeSize,
+        set: val => setSubjectTreeSize(val),
+    });
+
+    const mainContentSize = computed({
+        get: () => summaryLayoutStore.mainContentSize,
+        set: val => setMainContentSize(val),
+    });
+
+    const mainLeftSize = computed({
+        get: () => summaryLayoutStore.mainLeftSize,
+        set: val => setMainLeftSize(val),
+    });
+
+    const mainRightSize = computed({
+        get: () => summaryLayoutStore.mainRightSize,
+        set: val => setMainRightSize(val),
+    });
+
+    const mainLeftTopSize = computed({
+        get: () => summaryLayoutStore.mainLeftTopSize,
+        set: val => setMainLeftTopSize(val),
+    });
+
+    const mainLeftBottomSize = computed({
+        get: () => summaryLayoutStore.mainLeftBottomSize,
+        set: val => setMainLeftBottomSize(val),
+    });
+
+    const costTableRef = ref<NeedRenderComponent>()
+    const bottomTabsRef = ref<NeedRenderComponent>()
+    const stdBillRef = ref<NeedRenderComponent>()
+
+    const handleSubjectTreeSize = (size: number) => {
+        subjectTreeSize.value = size
     }
     const handleMainContentSize = (size: number) => {
         mainContentSize.value = size
+
+        // 重新 render 各个子组件
+        costTableRef.value && costTableRef.value.Render()
+        bottomTabsRef.value && bottomTabsRef.value.Render()
+        stdBillRef.value && stdBillRef.value.Render()
     }
     const handleMainLeftSize = (size: number) => {
         mainLeftSize.value = size
+
+        // 重新 render 各个子组件
+        costTableRef.value && costTableRef.value.Render()
+        bottomTabsRef.value && bottomTabsRef.value.Render()
     }
     const handleMainRightSize = (size: number) => {
         mainRightSize.value = size
+
+        stdBillRef.value && stdBillRef.value.Render()
     }
     const handleMainLeftTopSize = (size: number) => {
         mainLeftTopSize.value = size
+
+        costTableRef.value && costTableRef.value.Render()
     }
     const handleMainLeftBottomSize = (size: number) => {
         mainLeftBottomSize.value = size
+
+        bottomTabsRef.value && bottomTabsRef.value.Render()
     }
+
     return {
-        projectExplorerSize,
+        subjectTreeSize,
         mainContentSize,
         mainLeftSize,
         mainRightSize,
         mainLeftTopSize,
         mainLeftBottomSize,
-        handleProjectExplorerSize,
+
+        costTableRef,
+        bottomTabsRef,
+        stdBillRef,
+        handleSubjectTreeSize,
         handleMainContentSize,
         handleMainLeftSize,
         handleMainRightSize,