From 6c774ccc1dc28f8bdfe288d565b49d079fdc9c4b Mon Sep 17 00:00:00 2001 From: wangxiang <1827945911@qq.com> Date: Wed, 28 Jun 2023 02:23:15 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=BC=80=E5=90=AF=E5=BE=AE=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 3 + .env.development | 3 + .eslintignore | 1 + cloud/form-design/package.json | 3 +- cloud/form-design/pnpm-lock.yaml | 10 +++ cloud/form-design/src/main.js | 54 ++++++++++++- cloud/form-design/vue.config.js | 37 ++++++++- package.json | 23 +++--- pnpm-lock.yaml | 21 +++++ src/hooks/setting/index.ts | 6 +- src/qiankun/apps.ts | 25 ++++++ src/qiankun/index.ts | 68 ++++++++++++++++ src/qiankun/state.ts | 39 ++++++++++ src/utils/env.ts | 6 +- .../form/helper/WorkflowFormDesign.vue | 78 ++++++++----------- types/config.d.ts | 4 + types/global.d.ts | 4 + 17 files changed, 319 insertions(+), 66 deletions(-) create mode 100644 src/qiankun/apps.ts create mode 100644 src/qiankun/index.ts create mode 100644 src/qiankun/state.ts diff --git a/.env b/.env index 4571253..0c09993 100644 --- a/.env +++ b/.env @@ -15,3 +15,6 @@ VITE_GLOB_CLIENT_SECRET = kicc # 网关ase密码解密密钥,保持跟后端密钥一致,必须要有否则登录会失败的 VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET = changsha-kanglai + +# 开启微前端模式 +VITE_GLOB_APP_OPEN_QIANKUN=true diff --git a/.env.development b/.env.development index 09279ca..f85ae8f 100644 --- a/.env.development +++ b/.env.development @@ -18,3 +18,6 @@ VITE_GLOB_UPLOAD_URL=/upload # 接口地址前缀,有些系统所有接口地址都有前缀,可以在这里统一加,方便切换 VITE_GLOB_API_URL_PREFIX= + +#微前端qiankun应用,命名必须以VITE_APP_SUB_开头,form-design为子应用的项目名称,也是子应用的路由父路径 +VITE_APP_SUB_form-design = '//localhost:7101' diff --git a/.eslintignore b/.eslintignore index 396ac41..de75a4f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,3 +12,4 @@ dist .local /bin Dockerfile +/cloud diff --git a/cloud/form-design/package.json b/cloud/form-design/package.json index 44c2975..7886797 100644 --- a/cloud/form-design/package.json +++ b/cloud/form-design/package.json @@ -9,7 +9,8 @@ }, "dependencies": { "core-js": "^3.8.3", - "vue": "^2.6.14" + "vue": "^2.6.14", + "vue-router": "3.1.3" }, "devDependencies": { "@babel/core": "^7.12.16", diff --git a/cloud/form-design/pnpm-lock.yaml b/cloud/form-design/pnpm-lock.yaml index 655f154..09de0b8 100644 --- a/cloud/form-design/pnpm-lock.yaml +++ b/cloud/form-design/pnpm-lock.yaml @@ -10,11 +10,13 @@ specifiers: eslint: ^7.32.0 eslint-plugin-vue: ^8.0.3 vue: ^2.6.14 + vue-router: 3.1.3 vue-template-compiler: ^2.6.14 dependencies: core-js: 3.31.0 vue: 2.7.14 + vue-router: 3.1.3_vue@2.7.14 devDependencies: '@babel/core': 7.22.5 @@ -6597,6 +6599,14 @@ packages: webpack: 5.88.0 dev: true + /vue-router/3.1.3_vue@2.7.14: + resolution: {integrity: sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ==} + peerDependencies: + vue: ^2 + dependencies: + vue: 2.7.14 + dev: false + /vue-style-loader/4.1.3: resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==} dependencies: diff --git a/cloud/form-design/src/main.js b/cloud/form-design/src/main.js index 63eb05f..adea484 100644 --- a/cloud/form-design/src/main.js +++ b/cloud/form-design/src/main.js @@ -1,8 +1,56 @@ import Vue from 'vue' import App from './App.vue' +import VueRouter from 'vue-router'; Vue.config.productionTip = false +let router = null; +let instance = null; -new Vue({ - render: h => h(App), -}).$mount('#app') +function render(props = {}) { + const { container } = props; + router = new VueRouter({ + base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/', + mode: 'history', + }); + + instance = new Vue({ + router, + render: h => h(App), + }).$mount(container ? container.querySelector('#app') : '#app'); +} + +if (!window.__POWERED_BY_QIANKUN__) { + render(); +} + +function storeTest(props) { + props.onGlobalStateChange && + props.onGlobalStateChange( + (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), + true, + ); + props.setGlobalState && + props.setGlobalState({ + ignore: props.name, + user: { + name: props.name, + }, + }); +} + +export async function bootstrap() { + console.log('[vue] vue app bootstraped'); +} + +export async function mount(props) { + console.log('[vue] props from main framework', props); + storeTest(props); + render(props); +} + +export async function unmount() { + instance.$destroy(); + instance.$el.innerHTML = ''; + instance = null; + router = null; +} diff --git a/cloud/form-design/vue.config.js b/cloud/form-design/vue.config.js index 910e297..a9a077d 100644 --- a/cloud/form-design/vue.config.js +++ b/cloud/form-design/vue.config.js @@ -1,4 +1,39 @@ const { defineConfig } = require('@vue/cli-service') + +const path = require('path'); +const { name } = require('./package'); + +function resolve(dir) { + return path.join(__dirname, dir); +} + +const port = 7101; // dev port + module.exports = defineConfig({ - transpileDependencies: true + outputDir: 'dist', + assetsDir: 'static', + filenameHashing: true, + + transpileDependencies: true, + devServer: { + // host: '0.0.0.0', + hot: true, + port, + headers: { + 'Access-Control-Allow-Origin': '*', + }, + }, + // 自定义webpack配置 + configureWebpack: { + resolve: { + alias: { + '@': resolve('src'), + }, + }, + output: { + // 把子应用打包成 umd 库格式 + library: `${name}-[name]`, + libraryTarget: 'umd' + }, + }, }) diff --git a/package.json b/package.json index 2c54671..412addb 100644 --- a/package.json +++ b/package.json @@ -25,28 +25,30 @@ }, "dependencies": { "@amap/amap-jsapi-loader": "^1.0.1", - "@iconify/iconify": "^2.1.30", - "@vueuse/core": "^9.1.1", - "@vueuse/shared": "^9.1.1", - "@zxcvbn-ts/core": "^2.0.1", "@ant-design/colors": "^6.0.0", "@ant-design/icons-vue": "^6.1.0", + "@iconify/iconify": "^2.1.30", "@vue/runtime-core": "^3.2.33", "@vue/shared": "^3.2.33", + "@vueuse/core": "^9.1.1", + "@vueuse/shared": "^9.1.1", + "@zxcvbn-ts/core": "^2.0.1", "ant-design-vue": "^3.2.12", "axios": "^0.26.1", "cropperjs": "^1.5.12", "crypto-js": "^4.1.1", "dayjs": "^1.11.1", "echarts": "^5.3.2", - "intro.js": "^5.1.0", "event-source-polyfill": "^1.0.31", + "exceljs": "^4.3.0", + "intro.js": "^5.1.0", "js-base64": "^3.6.1", "lodash-es": "^4.17.21", "nprogress": "^0.2.0", "path-to-regexp": "^6.2.0", "pinia": "^2.0.13", "print-js": "^1.6.0", + "qiankun": "^2.10.9", "qrcode": "^1.5.0", "qs": "^6.10.3", "resize-observer-polyfill": "^1.5.1", @@ -57,7 +59,6 @@ "vue-i18n": "^9.1.9", "vue-router": "^4.0.14", "vue-types": "^4.1.1", - "exceljs": "^4.3.0", "vxe-table": "^4.3.9", "vxe-table-plugin-antd": "^3.0.5", "vxe-table-plugin-export-xlsx": "^3.0.4", @@ -70,11 +71,11 @@ "@types/crypto-js": "^4.1.1", "@types/fs-extra": "^9.0.13", "@types/inquirer": "^8.2.1", + "@types/intro.js": "^3.0.2", "@types/lodash-es": "^4.17.6", "@types/nprogress": "^0.2.0", "@types/qrcode": "^1.5.0", "@types/qs": "^6.9.7", - "@types/intro.js": "^3.0.2", "@types/sortablejs": "^1.10.7", "@typescript-eslint/eslint-plugin": "^5.20.0", "@typescript-eslint/parser": "^5.20.0", @@ -82,7 +83,6 @@ "@vitejs/plugin-vue": "^2.3.3", "@vitejs/plugin-vue-jsx": "^1.3.10", "@vue/compiler-sfc": "^3.2.33", - "picocolors": "^1.0.0", "autoprefixer": "^10.4.4", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", @@ -94,6 +94,7 @@ "fs-extra": "^10.1.0", "inquirer": "^8.2.2", "less": "^4.1.2", + "picocolors": "^1.0.0", "postcss": "^8.4.12", "rimraf": "^3.0.2", "rollup": "^2.70.2", @@ -103,6 +104,8 @@ "vite-plugin-compression": "^0.5.1", "vite-plugin-html": "^3.2.0", "vite-plugin-imagemin": "^0.6.1", + "vite-plugin-optimize-persist": "^0.1.2", + "vite-plugin-package-config": "^0.1.1", "vite-plugin-purge-icons": "^0.8.2", "vite-plugin-pwa": "^0.12.3", "vite-plugin-style-import": "^2.0.0", @@ -110,9 +113,7 @@ "vite-plugin-theme": "^0.8.6", "vite-plugin-windicss": "^1.8.7", "vue-eslint-parser": "^8.3.0", - "vue-tsc": "^1.0.9", - "vite-plugin-optimize-persist": "^0.1.2", - "vite-plugin-package-config": "^0.1.1" + "vue-tsc": "^1.0.9" }, "resolutions": { "bin-wrapper": "npm:bin-wrapper-china", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c3b1aa..3c20bca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,7 @@ specifiers: pinia: ^2.0.13 postcss: ^8.4.12 print-js: ^1.6.0 + qiankun: ^2.10.9 qrcode: ^1.5.0 qs: ^6.10.3 resize-observer-polyfill: ^1.5.1 @@ -118,6 +119,7 @@ dependencies: path-to-regexp: 6.2.1 pinia: 2.0.33_hmuptsblhheur2tugfgucj7gc4 print-js: 1.6.0 + qiankun: 2.10.9 qrcode: 1.5.1 qs: 6.11.1 resize-observer-polyfill: 1.5.1 @@ -5638,6 +5640,12 @@ packages: resolve-from: 4.0.0 dev: true + /import-html-entry/1.14.6: + resolution: {integrity: sha512-5MQkbwIr8n/bXOoE05M5/Nm0lnHO46vnb3D6svSvtVwpDqwhd/X14zjLcU31QWZ6gL8rUXNzj6vKHx4yOUL6gQ==} + dependencies: + '@babel/runtime': 7.21.0 + dev: false + /import-lazy/4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} @@ -7543,6 +7551,15 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} dev: true + /qiankun/2.10.9: + resolution: {integrity: sha512-+DOdKnmFMoMfIWZaoLaBVoHAPlEWg529ZtIBnoNLUTJq4CreHIhXjnqc0mlxCCg4axZKtcORDPMAk4kPG6IH3A==} + dependencies: + '@babel/runtime': 7.21.0 + import-html-entry: 1.14.6 + lodash: 4.17.21 + single-spa: 5.9.5 + dev: false + /qrcode/1.5.1: resolution: {integrity: sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==} engines: {node: '>=10.13.0'} @@ -8058,6 +8075,10 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /single-spa/5.9.5: + resolution: {integrity: sha512-9SQdmsyz4HSP+3gs6PJzhkaMEg+6zTlu9oxIghnwUX3eq+ajq4ft5egl0iyR55LAmO/UwvU8NgIWs/ZyQMa6dw==} + dev: false + /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} diff --git a/src/hooks/setting/index.ts b/src/hooks/setting/index.ts index 47e77e7..d58820f 100644 --- a/src/hooks/setting/index.ts +++ b/src/hooks/setting/index.ts @@ -18,7 +18,8 @@ export const useGlobSetting = (): Readonly => { VITE_GLOB_UPLOAD_URL, VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_SECRET, - VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET + VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET, + VITE_GLOB_APP_OPEN_QIANKUN, } = getAppEnvConfig(); if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { @@ -34,7 +35,8 @@ export const useGlobSetting = (): Readonly => { uploadUrl: VITE_GLOB_UPLOAD_URL, clientId: VITE_GLOB_CLIENT_ID, clientSecret: VITE_GLOB_CLIENT_SECRET, - gatewayAseEncodeSecret: VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET + gatewayAseEncodeSecret: VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET, + openQianKun: VITE_GLOB_APP_OPEN_QIANKUN }; return glob as Readonly; }; diff --git a/src/qiankun/apps.ts b/src/qiankun/apps.ts new file mode 100644 index 0000000..662ae7d --- /dev/null +++ b/src/qiankun/apps.ts @@ -0,0 +1,25 @@ +/** + *微应用apps + * @name: 微应用名称 - 具有唯一性 + * @entry: 微应用入口.必选 - 通过该地址加载微应用, + * @container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上 + * @activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用 + */ +import type { RegistrableApp } from 'qiankun'; +import { ComponentOptions } from '@vue/runtime-core'; + +//子应用列表 +const _apps: RegistrableApp[] = []; +for (const key in import.meta.env) { + if (key.includes('VITE_APP_SUB_')) { + const name = key.split('VITE_APP_SUB_')[1]; + const obj = { + name, + entry: import.meta.env[key], + container: '#content', + activeRule: name, + }; + _apps.push(obj); + } +} +export const apps = _apps; diff --git a/src/qiankun/index.ts b/src/qiankun/index.ts new file mode 100644 index 0000000..cd10873 --- /dev/null +++ b/src/qiankun/index.ts @@ -0,0 +1,68 @@ +/** + * qiankun配置 + */ +import { registerMicroApps, setDefaultMountApp, start, runAfterFirstMounted, addGlobalUncaughtErrorHandler } from 'qiankun'; +import { apps } from './apps'; +import { getProps, initGlState } from './state'; + +/** + * 重构apps + */ +function filterApps() { + apps.forEach((item) => { + //主应用需要传递给微应用的数据。 + item.props = getProps(); + //微应用触发的路由规则 + item.activeRule = genActiveRule('/' + item.activeRule); + }); + return apps; +} + +/** + * 路由监听 + * @param {*} routerPrefix 前缀 + */ +function genActiveRule(routerPrefix) { + return (location) => location.pathname.startsWith(routerPrefix); +} + +/** + * 微应用注册 + */ +function registerApps() { + const _apps = filterApps(); + registerMicroApps(_apps, { + beforeLoad: [ + (loadApp) => Promise.resolve(()=>{ + console.log('before load', loadApp); + }), + ], + beforeMount: [ + (mountApp) => Promise.resolve(()=>{ + console.log('before mount', mountApp); + }), + ], + afterMount: [ + (mountApp) => Promise.resolve(()=>{ + console.log('before mount', mountApp); + }), + ], + afterUnmount: [ + (unloadApp) => Promise.resolve(()=>{ + console.log('after unload', unloadApp); + }), + ], + }); + // 设置默认子应用,与 genActiveRule中的参数保持一致 + // setDefaultMountApp(); + // 第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。 + runAfterFirstMounted(() => console.log('开启监控')); + // 添加全局的未捕获异常处理器。 + addGlobalUncaughtErrorHandler((event) => console.log(event)); + // 定义全局状态 + initGlState(); + //启动qiankun + start({}); +} + +export default registerApps; diff --git a/src/qiankun/state.ts b/src/qiankun/state.ts new file mode 100644 index 0000000..138d238 --- /dev/null +++ b/src/qiankun/state.ts @@ -0,0 +1,39 @@ +/** + *公共数据 + */ +import { initGlobalState, RegistrableApp } from 'qiankun'; +import { store } from '/@/store'; +import { router } from '/@/router'; +import { getAccessToken } from '/@/utils/auth'; + +//定义传入子应用的数据 +export function getProps(){ + return { + data: { + publicPath: '/', + token: getAccessToken(), + store, + router, + }, + }; +} + +/** + * 定义全局状态,并返回通信方法,在主应用使用,微应用通过 props 获取通信方法。 + * @param state 主应用穿的公共数据 + */ +export function initGlState(info = { userName: 'admin' }) { + // 初始化state + const actions = initGlobalState(info); + // 设置新的值 + actions.setGlobalState(info); + // 注册 观察者 函数 - 响应 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); + } + }); +} diff --git a/src/utils/env.ts b/src/utils/env.ts index e090c29..1b6e476 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -36,7 +36,8 @@ export function getAppEnvConfig() { VITE_GLOB_UPLOAD_URL, VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_SECRET, - VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET + VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET, + VITE_GLOB_APP_OPEN_QIANKUN, } = ENV; if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) { @@ -53,7 +54,8 @@ export function getAppEnvConfig() { VITE_GLOB_UPLOAD_URL, VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_SECRET, - VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET + VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET, + VITE_GLOB_APP_OPEN_QIANKUN, }; } diff --git a/src/views/common/workflow/extension/form/helper/WorkflowFormDesign.vue b/src/views/common/workflow/extension/form/helper/WorkflowFormDesign.vue index df15b85..3ca6e05 100644 --- a/src/views/common/workflow/extension/form/helper/WorkflowFormDesign.vue +++ b/src/views/common/workflow/extension/form/helper/WorkflowFormDesign.vue @@ -4,67 +4,52 @@ @register="registerModal" @ok="handleSubmit" > - +