├── apps └── README.md ├── packages ├── micro-app-tools │ ├── listener.ts │ ├── .npmignore │ ├── types │ │ ├── global.d.ts │ │ └── exports.d.ts │ ├── react18 │ │ ├── utils.tsx │ │ └── index.ts │ ├── tsconfig.json │ ├── vue2 │ │ ├── ReactComponent.vue │ │ └── index.ts │ ├── package.json │ ├── vue3 │ │ ├── ReactComponent.vue │ │ ├── utils.tsx │ │ └── renderComponent.ts │ └── data.ts └── shared │ └── package.json ├── src ├── style │ ├── overflow.scss │ ├── element-plus.cover.scss │ ├── reset.scss │ ├── index.scss │ └── common.scss ├── components │ ├── use-tinymce │ │ ├── README.md │ │ └── index.vue │ ├── use-svg │ │ ├── README.md │ │ ├── index.vue │ │ └── loader.ts │ ├── use-schema-render │ │ ├── index.vue │ │ ├── config.ts │ │ └── index.tsx │ ├── use-suspense │ │ ├── README.md │ │ └── index.vue │ └── el-dialog │ │ └── index.vue ├── assets │ ├── images │ │ ├── admin.png │ │ └── defaultAvatar.png │ └── svg │ │ ├── menu.svg │ │ ├── arrow-bottom.svg │ │ ├── vue.svg │ │ ├── zhanwei.svg │ │ ├── docs-question.svg │ │ ├── Github.svg │ │ └── react.svg ├── layouts │ ├── hook.ts │ ├── HeadLayout.vue │ ├── components │ │ ├── RouteInfoBar.vue │ │ ├── MenuItem.vue │ │ ├── Head.vue │ │ └── LinkCardList.vue │ └── index.vue ├── pages │ ├── 403.vue │ ├── empty.vue │ ├── components │ │ ├── LoginCard.vue │ │ └── LoginDialog.vue │ ├── demo │ │ ├── vueFlow │ │ │ ├── ValueNode.vue │ │ │ ├── OperatorNode.vue │ │ │ ├── Icon.vue │ │ │ └── ResultNode.vue │ │ ├── lowcodeEngine.vue │ │ ├── lottie.vue │ │ ├── reactComponent.vue │ │ ├── frameLessUI.vue │ │ └── reactCompDemo1.tsx │ ├── SubMicroApp.vue │ ├── 404.vue │ ├── noMenu.vue │ └── login.vue ├── utils │ ├── index.test.ts │ ├── CONSTS.ts │ ├── Config.ts │ └── request.ts ├── api │ ├── menu.ts │ └── pageSchema.ts ├── hooks │ ├── index.ts │ └── router.ts ├── type.d.ts ├── router │ └── interceptor.test.ts ├── versionUpdateCheck_worker.js ├── versionUpdateCheck.js ├── store │ └── index.ts └── App.vue ├── .prettierignore ├── public ├── b983d603f445c18c471ed1a0b8871c0a.txt ├── favicon.ico ├── tinymce │ ├── langs │ │ └── README.md │ ├── license.md │ ├── skins │ │ ├── ui │ │ │ ├── oxide │ │ │ │ ├── skin.shadowdom.min.css │ │ │ │ └── skin.shadowdom.js │ │ │ ├── tinymce-5 │ │ │ │ ├── skin.shadowdom.min.css │ │ │ │ └── skin.shadowdom.js │ │ │ ├── oxide-dark │ │ │ │ ├── skin.shadowdom.min.css │ │ │ │ └── skin.shadowdom.js │ │ │ └── tinymce-5-dark │ │ │ │ ├── skin.shadowdom.min.css │ │ │ │ └── skin.shadowdom.js │ │ └── content │ │ │ ├── default │ │ │ ├── content.min.css │ │ │ └── content.js │ │ │ ├── tinymce-5 │ │ │ ├── content.min.css │ │ │ └── content.js │ │ │ ├── writer │ │ │ ├── content.min.css │ │ │ └── content.js │ │ │ ├── dark │ │ │ ├── content.min.css │ │ │ └── content.js │ │ │ ├── tinymce-5-dark │ │ │ ├── content.min.css │ │ │ └── content.js │ │ │ └── document │ │ │ ├── content.min.css │ │ │ └── content.js │ └── plugins │ │ ├── code │ │ └── plugin.min.js │ │ ├── visualblocks │ │ └── plugin.min.js │ │ ├── nonbreaking │ │ └── plugin.min.js │ │ ├── save │ │ └── plugin.min.js │ │ ├── pagebreak │ │ └── plugin.min.js │ │ ├── preview │ │ └── plugin.min.js │ │ ├── autoresize │ │ └── plugin.min.js │ │ ├── anchor │ │ └── plugin.min.js │ │ ├── insertdatetime │ │ └── plugin.min.js │ │ ├── help │ │ └── js │ │ │ └── i18n │ │ │ └── keynav │ │ │ ├── zh_CN.js │ │ │ └── zh_TW.js │ │ ├── autolink │ │ └── plugin.min.js │ │ └── autosave │ │ └── plugin.min.js └── empty.html ├── docs ├── src │ ├── public │ │ ├── favicon.ico │ │ ├── images │ │ │ └── 1-1.png │ │ └── svgs │ │ │ ├── Vue.svg │ │ │ ├── React.svg │ │ │ ├── Nginx.svg │ │ │ ├── UnoCSS.svg │ │ │ ├── TS.svg │ │ │ ├── JS.svg │ │ │ ├── frame-less-ui.svg │ │ │ ├── DispatchComponent.svg │ │ │ ├── Webpack.svg │ │ │ ├── Vite.svg │ │ │ ├── Node.svg │ │ │ ├── RouterComponent.svg │ │ │ └── ChatGPT.svg │ ├── assets │ │ └── images │ │ │ ├── 1-1.png │ │ │ ├── 群二维码.jpg │ │ │ ├── 群主微信二维码.jpg │ │ │ └── obsidian │ │ │ ├── 主应用路由拦截.png │ │ │ └── 子应用路由拦截逻辑.png │ ├── Docs │ │ ├── 项目规范 │ │ │ ├── 主应用.md │ │ │ ├── 子应用.md │ │ │ └── 通用规范.md │ │ ├── 3.封装特性 │ │ │ ├── 低代码设计器.md │ │ │ ├── _子应用独立调试.md │ │ │ ├── 低代码与源代码混合开发.md │ │ │ ├── 低代码渲染器.md │ │ │ ├── 组件共享方案1-路由组件.md │ │ │ ├── 组件共享方案3-跨框架组件库.md │ │ │ └── 组件共享方案2-派发组件.md │ │ ├── 2.基础功能 │ │ │ ├── 预加载.md │ │ │ └── 资源复用.md │ │ ├── 4.不同框架差异 │ │ │ ├── 路由组件注册.md │ │ │ └── 定义组件.md │ │ └── 1.指南 │ │ │ ├── 模版介绍.md │ │ │ └── 微前端介绍.md │ ├── About │ │ ├── discussion.md │ │ └── history.md │ └── index.md ├── README.md ├── scripts │ └── build_for_serveless.ts ├── package.json ├── .vitepress │ └── config.mts ├── .gitignore ├── s.master.yaml └── nginx.conf ├── .prettierrc ├── vitest.config.ts ├── commitlint.config.ts ├── scripts └── build_for_serveless.ts ├── eslint.config.js ├── .vscode └── settings.json ├── uno.config.ts ├── .gitignore ├── s.master.yaml ├── uno.common.ts ├── tsconfig.json ├── index.html ├── package.json ├── README.md └── nginx.conf /apps/README.md: -------------------------------------------------------------------------------- 1 | > 子应用项目全部放这个目录下 2 | -------------------------------------------------------------------------------- /packages/micro-app-tools/listener.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/style/overflow.scss: -------------------------------------------------------------------------------- 1 | micro-app-main { 2 | } 3 | -------------------------------------------------------------------------------- /src/components/use-tinymce/README.md: -------------------------------------------------------------------------------- 1 | ## 富文本组件 tinymce 2 | -------------------------------------------------------------------------------- /packages/micro-app-tools/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | *.tsx 3 | !*.d.ts -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | public 2 | publicOrigin 3 | src/pages/introduce.vue -------------------------------------------------------------------------------- /public/b983d603f445c18c471ed1a0b8871c0a.txt: -------------------------------------------------------------------------------- 1 | 5722edd085d7fa6143491e533d486555169a4440 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /docs/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/src/assets/images/admin.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 90, 5 | "singleAttributePerLine": true 6 | } -------------------------------------------------------------------------------- /docs/src/assets/images/1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/assets/images/1-1.png -------------------------------------------------------------------------------- /docs/src/assets/images/群二维码.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/assets/images/群二维码.jpg -------------------------------------------------------------------------------- /docs/src/public/images/1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/public/images/1-1.png -------------------------------------------------------------------------------- /docs/src/assets/images/群主微信二维码.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/assets/images/群主微信二维码.jpg -------------------------------------------------------------------------------- /src/assets/images/defaultAvatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/src/assets/images/defaultAvatar.png -------------------------------------------------------------------------------- /src/layouts/hook.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | 3 | /** 存储引导页target ref */ 4 | export const tourStepsRefs = ref>([]); 5 | -------------------------------------------------------------------------------- /src/pages/403.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/src/Docs/项目规范/主应用.md: -------------------------------------------------------------------------------- 1 | ## svg 资源 2 | 3 | 要在 UseSvg 使用的 svg 放在/assets/svg/下,如果 svg 太大(大于 10kb)不建议通过 UseSvg 使用 4 | 体积较大的 svg 资源不要放/assets/svg/下 5 | -------------------------------------------------------------------------------- /docs/src/assets/images/obsidian/主应用路由拦截.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/assets/images/obsidian/主应用路由拦截.png -------------------------------------------------------------------------------- /docs/src/assets/images/obsidian/子应用路由拦截逻辑.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAMMUpro/micro-admin-template/HEAD/docs/src/assets/images/obsidian/子应用路由拦截逻辑.png -------------------------------------------------------------------------------- /public/tinymce/langs/README.md: -------------------------------------------------------------------------------- 1 | This is where language files should be placed. 2 | 3 | Please DO NOT translate these directly, use this service instead: https://crowdin.com/project/tinymce 4 | -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/低代码设计器.md: -------------------------------------------------------------------------------- 1 | ## 低代码设计器(待完善) 2 | 3 | 低代码设计器不属于后台模板功能,是一个独立的项目 4 | 在设计器内可视化编辑页面(节点),编辑完成后可以在后台通过[低代码渲染器](./低代码渲染器.md)使用该页面(节点) 5 | 6 | [🔗点击体验在线设计器](https://ali-lowcode.lammu.cn/) -------------------------------------------------------------------------------- /src/utils/index.test.ts: -------------------------------------------------------------------------------- 1 | import { isMobile } from './index'; 2 | 3 | describe('测试utils/index.ts', () => { 4 | test('isMobile', () => { 5 | expect(isMobile()).toBe(false); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/api/menu.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | /** 4 | * 获取菜单树 5 | */ 6 | export function getMenuTree(params: any = {}) { 7 | return request('GET', '/menu/tree', params); 8 | } 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## vitepress ssg文档 2 | 3 | 配置文件: /.vitepress/config.mts 4 | 5 | .md文件写在 /src/下 6 | 7 | 建议使用Typora编辑/预览.md文件 8 | 9 | # 安装依赖(忽略版本冲突)主目录下运行, 会安装packages/*下项目的所有依赖 10 | yarn install --ignore-engines 11 | -------------------------------------------------------------------------------- /docs/src/Docs/2.基础功能/预加载.md: -------------------------------------------------------------------------------- 1 | ### 预加载 2 | 3 | ```TSX 4 | /** 5 | * 预加载子应用所需资源 6 | */ 7 | microApp.preFetch([ 8 | { name: 'tinymce', url: 'https://tinymce.boshiyun.com.cn/tinymce2/tinymce.min.js', level: 1 }, // 加载资源并解析 9 | ], 2000) 10 | ``` -------------------------------------------------------------------------------- /packages/micro-app-tools/types/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue'; 5 | const component: DefineComponent<{}, {}, any>; 6 | export default component; 7 | } 8 | -------------------------------------------------------------------------------- /public/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/src/About/discussion.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: false 3 | footer: false 4 | prev: false 5 | --- 6 | 7 | # 讨论群 8 | 9 | **微信群** 10 | 11 | ![微信群](/assets/images/群二维码.jpg) 12 | 13 | 14 | **如果过期或超200人, 加群主微信拉你入群, 备注:微前端后台** 15 | 16 | ![群主微信](/assets/images/群主微信二维码.jpg) -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vitest/config'; 2 | import viteConfig from './vite.config'; 3 | 4 | export default mergeConfig( 5 | viteConfig, 6 | defineConfig({ 7 | test: { 8 | globals: true, 9 | environment: 'happy-dom', 10 | } 11 | }) 12 | ) -------------------------------------------------------------------------------- /public/tinymce/license.md: -------------------------------------------------------------------------------- 1 | # Software License Agreement 2 | 3 | **TinyMCE** – [](https://github.com/tinymce/tinymce) 4 | Copyright (c) 2024, Ephox Corporation DBA Tiny Technologies, Inc. 5 | 6 | Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). 7 | -------------------------------------------------------------------------------- /src/assets/svg/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/_子应用独立调试.md: -------------------------------------------------------------------------------- 1 | ## 子应用独立调试 2 | 3 | 主应用包含头部,菜单等 UI;子应用只需要编写具体业务的 UI,无需再重复编写头部和菜单。 4 | 5 | 正常情况下,在开发子应用的时候,我们需要运行主应用和对应的子应用。 6 | 7 | 但是,我们可以通过一种“取巧”的方式,让子应用开发无需启动主应用 8 | 9 | > 这在跨团队开发时很有用, 让子应用可以独立调试,可以不需要把主应用的仓库权限下放给非核心团队 10 | 11 | - 子应用独立运行时,通过反向嵌套原本的主应用来加载菜单 / 头部 12 | - 同时复用主应用原有的登录 / 鉴权 / 路由拦截器 13 | - 由于主 / 子应用关系的调换,需要兼容处理一些判断逻辑 14 | -------------------------------------------------------------------------------- /src/assets/svg/arrow-bottom.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared", 3 | "version": "1.0.0", 4 | "description": "局内共享", 5 | "main": "index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "dependencies": { 11 | 12 | }, 13 | "keywords": [], 14 | "author": "lammu", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /src/style/element-plus.cover.scss: -------------------------------------------------------------------------------- 1 | // 修改element-plus样式的命名空间,需要从vite.config.ts导入此文件! 2 | @forward 'element-plus/theme-chalk/src/mixins/config.scss' with ($namespace: 'main-el' 3 | ); 4 | 5 | :root { 6 | 7 | // 调整菜单文本溢出宽度 8 | .main-el-menu:not(.main-el-menu--collapse) .main-el-sub-menu__title { 9 | padding-right: calc(var(--main-el-menu-base-level-padding) + 14px); 10 | } 11 | } -------------------------------------------------------------------------------- /docs/src/public/svgs/Vue.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/src/Docs/项目规范/子应用.md: -------------------------------------------------------------------------------- 1 | ## 基础路由 2 | - 中转页 3 | - 404页面不存在提示页 4 | - 403无权限提示页 5 | - 登录页(兼容子应用独立运行) 6 | - 其它【导出路由】... 7 | 8 | ### 导出路由 9 | - 导出组件注册的路由 10 | - 其它固定的页面 11 | 12 | ## 用户路由 13 | - 兜底路由(404路由) 14 | 15 | ## 路由拦截 16 | 17 | ![子应用路由拦截](/assets/images/obsidian/子应用路由拦截逻辑.png) 18 | 19 | ## 样式 20 | - 子应用容器不滚动最大高度为 var(--sub-app-container-height) 21 | - 主应用common.scss定义了一些公共样式,子应用可直接使用,部分以-m-开头 -------------------------------------------------------------------------------- /src/layouts/HeadLayout.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /src/pages/empty.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /docs/src/Docs/4.不同框架差异/路由组件注册.md: -------------------------------------------------------------------------------- 1 | 2 | ## 路由组件路由注册 3 | ### vue3 4 | ```tsx 5 | import { generateExportComponent } from '@/microapp'; 6 | 7 | { 8 | path: '/ExportComponent/xxxDetailByID', 9 | name: 'ExportComponent_xxxDetailByID', 10 | component: () => generateExportComponent(defineAsyncComponent(() => import('@/pages/xxxDetailByID.vue'))), 11 | meta: { hidden: true, title: 'xx详情' }, 12 | }, 13 | ``` 14 | 15 | ### vue2 -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/conventional-changelog/commitlint 2 | export default { 3 | extends: ['@commitlint/config-conventional'], 4 | rules: { 5 | 'type-enum': [ 6 | 2, 7 | 'always', 8 | ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'], 9 | ], 10 | 'subject-full-stop': [0, 'never'], // 不允许有结束符号 . 11 | 'subject-case': [0, 'never'], // 必须以小写开头 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref } from 'vue'; 2 | 3 | /** 记录弹窗的zIndex,每次会加一 */ 4 | const zIndex = ref(2000); 5 | 6 | /** 7 | * 获取组件最高定位层级 8 | * ps: 该方法调用一次之后,就会累加一次层级 9 | */ 10 | export function usezIndex() { 11 | return zIndex.value++; 12 | } 13 | 14 | /** 是否移动端尺寸(小于等于768px) */ 15 | export const isPhone = ref(false); 16 | 17 | /** 是否桌面端尺寸(大于768px) */ 18 | export const isDesktop = computed(() => !isPhone.value); 19 | -------------------------------------------------------------------------------- /src/utils/CONSTS.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 静态常量,node环境下也可使用 3 | */ 4 | export default { 5 | /** 路由前缀,用于router/打包路径/判断url是否为本应用页面 */ 6 | PREFIX_URL: 'micromain', 7 | /** 必须以micro-app-开头,不允许大写字母 */ 8 | microAppTagName: 'micro-app-admin', 9 | /** 文档标题前缀 */ 10 | PREFIX_DOCUMENT_TITLE: '微后台', 11 | /** 启动端口号 */ 12 | PORT: 1314, 13 | /** 子应用路由name前缀, 子应用路由name统一为`${前缀}${子应用name}` */ 14 | subAppRouteNamePrefix: 'subApp_', 15 | }; 16 | -------------------------------------------------------------------------------- /src/pages/components/LoginCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /packages/micro-app-tools/types/exports.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 此文件无法合并到global.d.ts,合并后会导致使用global.d.ts里面的类型需要手动导入才能使用 3 | */ 4 | import { MicroAppWindow, Window_microApp } from '.'; 5 | 6 | /** 提升一些全局ts */ 7 | declare global { 8 | interface Window extends Partial { 9 | /** 微前端应用挂载 */ 10 | mount: () => void; 11 | /** 微前端应用卸载 */ 12 | unmount: () => void; 13 | 14 | /** 微前端 */ 15 | microApp?: Window_microApp; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scripts/build_for_serveless.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目部署步骤 3 | * 1. 运行vite build打包命令 4 | * 2. 复制nginx.config到/build/下 5 | */ 6 | import { default as tools } from 'local-tool'; 7 | 8 | const { copyFileOrDir, setSymlink, deleteFileOrDir, execSync } = tools; 9 | 10 | function main() { 11 | console.log('>>> 打包中...'); 12 | execSync('yarn build'); 13 | copyFileOrDir('./nginx.conf', './build/nginx.conf'); 14 | console.log('>>> 打包成功...'); 15 | } 16 | 17 | main(); 18 | -------------------------------------------------------------------------------- /src/type.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /** 基础对象 */ 4 | interface BaseObj { 5 | [key: string]: T; 6 | } 7 | 8 | /** 9 | * `JavaScript`类型 10 | * - 这里只枚举一些常见类型,后续根据使用场景自行添加即可 11 | */ 12 | type JavaScriptTypes = 13 | | 'string' 14 | | 'number' 15 | | 'array' 16 | | 'object' 17 | | 'boolean' 18 | | 'function' 19 | | 'null' 20 | | 'undefined' 21 | | 'regexp' 22 | | 'promise' 23 | | 'formdata'; 24 | -------------------------------------------------------------------------------- /docs/scripts/build_for_serveless.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目部署步骤 3 | * 1. 运行vite build打包命令 4 | * 2. 复制nginx.config到/build/下 5 | */ 6 | import { default as tools } from 'local-tool'; 7 | 8 | const { copyFileOrDir, setSymlink, deleteFileOrDir, execSync } = tools; 9 | 10 | function main() { 11 | console.log('>>> 打包中...'); 12 | execSync('yarn build'); 13 | copyFileOrDir('./nginx.conf', './build/nginx.conf'); 14 | console.log('>>> 打包成功...'); 15 | } 16 | 17 | main(); 18 | -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/低代码与源代码混合开发.md: -------------------------------------------------------------------------------- 1 | ## 低代码与源代码混合开发(待完善) 2 | 3 | ### 背景 4 | 引入低代码的目的不是为了取代源代码开发,只是提供了一种选择,你可以通过[🔗低代码开发](https://ali-lowcode.lammu.cn/)某个页面(甚至某个很小的区域),同时你也可以直接编写源代码来开发这个页面 5 | 6 | ### 目标 7 | 基于该背景,我们的低代码更多时候是面向前端开发者的而不是小白(当然可以再封装简化),尽量消除低代码和源代码之间的体验差异,支持低代码与源代码之间的无缝通信,真正做到高复用,组件一次编写,随处使用 8 | 9 | ### 特色 10 | 该模板支持[🔗主应用渲染低代码页面](https://micro-admin-template.lammu.cn/micromain/demo/lowcodeEngine), react/vue3/vue2子应用渲染低代码页面的同时还支持在低代码内部渲染子应用页面或组件,这样可以穿插调用 -------------------------------------------------------------------------------- /packages/micro-app-tools/react18/utils.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState, forwardRef, useImperativeHandle, ForwardRefExoticComponent } from 'react'; 2 | 3 | /** 4 | * 对于forwardRef组件再包装一层供其它框架获取实例 5 | */ 6 | export function forwardRefWrap(ForwardRefComponent: ForwardRefExoticComponent) { 7 | return function (props: any) { 8 | const ref = useRef(null); 9 | props.setReactCtxOnMounted?.(ref); 10 | return 11 | } 12 | } -------------------------------------------------------------------------------- /src/components/use-svg/README.md: -------------------------------------------------------------------------------- 1 | # UseSvg 2 | 3 | ## 建议注册为全局组件 4 | 5 | ```TSX 6 | import UseSvg from '@/components/use-svg/index.vue'; 7 | // 注册全局组件: `use-svg` 8 | app.component('use-svg', UseSvg); 9 | ``` 10 | 11 | ## vite.config.ts 配置 12 | 13 | ```TSX 14 | import { svgBuilder } from './src/components/use-svg/loader'; 15 | 16 | plugins: [ 17 | /** svg处理 */ 18 | svgBuilder('./src/assets/svg/'), 19 | ] 20 | ``` 21 | 22 | ## 使用方法 23 | 24 | ```TSX 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/oxide/skin.shadowdom.min.css: -------------------------------------------------------------------------------- 1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} 2 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.css: -------------------------------------------------------------------------------- 1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} 2 | -------------------------------------------------------------------------------- /src/assets/svg/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css: -------------------------------------------------------------------------------- 1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} 2 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.css: -------------------------------------------------------------------------------- 1 | body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} 2 | -------------------------------------------------------------------------------- /src/api/pageSchema.ts: -------------------------------------------------------------------------------- 1 | import { env, host } from '@/components/use-schema-render/config'; 2 | 3 | /** 4 | * 查找页面schema 5 | */ 6 | export async function findPageSchemaByNodeId(params: { nodeId: number }) { 7 | const response = await fetch( 8 | `${env === 'localhost' ? '/nest' : host}/page-schema/${params.nodeId}`, 9 | { 10 | method: 'get', 11 | } 12 | ); 13 | const res = await response.json(); 14 | if (res.code == 1) { 15 | res.data.package = JSON.parse(res.data.package || '[]'); 16 | res.data.schema = JSON.parse(res.data.schema || '{}'); 17 | } 18 | return res; 19 | } 20 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-admin-docs", 3 | "version": "1.0.0", 4 | "description": "微前端后台模板", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "vitepress dev", 9 | "build": "vitepress build", 10 | "preview": "vitepress preview --port 5152", 11 | "deploy": "s deploy -t s.master.yaml --use-local" 12 | }, 13 | "dependencies": { 14 | "vitepress": "^1.3.3", 15 | "fs-extra": "^10.0.0" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^20.14.8", 19 | "@types/fs-extra": "^9.0.13", 20 | "local-tool": "^1.1.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import pluginJs from "@eslint/js"; 2 | import pluginTs from "typescript-eslint"; 3 | import pluginVue from "eslint-plugin-vue"; 4 | 5 | export default [ 6 | /** .js */ 7 | pluginJs.configs.recommended, 8 | /** .ts */ 9 | ...pluginTs.configs.recommended, 10 | /** .vue */ 11 | ...pluginVue.configs["flat/essential"], 12 | { 13 | files: ['*.vue', '**/*.vue'], 14 | languageOptions: { 15 | parserOptions: { 16 | parser: '@typescript-eslint/parser' 17 | } 18 | }, 19 | rules: { 20 | "vue/multi-word-component-names": "off", 21 | } 22 | } 23 | ]; -------------------------------------------------------------------------------- /docs/src/Docs/4.不同框架差异/定义组件.md: -------------------------------------------------------------------------------- 1 | ## 注册组件 2 | ### vue3 3 | ```tsx 4 | defineComponent({ 5 | setup() { 6 | return () => 7 | h(MicroComponent, { 8 | _is: 'Page404', 9 | msg: '组件参数', 10 | }); 11 | }, 12 | }) 13 | ``` 14 | 15 | ### vue2 16 | ```tsx 17 | { 18 | render(createElement) { 19 | return createElement(MicroComponent, { 20 | props: { 21 | _is: 'Page404', 22 | }, 23 | /** 如果需要传组件参数需要通过attrs传递 */ 24 | attrs: { 25 | msg: '组件参数', 26 | }, 27 | }); 28 | }, 29 | } 30 | ``` 31 | 32 | ### react18 33 | ```tsx 34 | // TODO 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/src/public/svgs/React.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/oxide/skin.shadowdom.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('ui/default/skin.shadowdom.css', "body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}") 2 | //# sourceMappingURL=skin.shadowdom.js.map 3 | -------------------------------------------------------------------------------- /src/pages/components/LoginDialog.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/oxide-dark/skin.shadowdom.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('ui/dark/skin.shadowdom.css', "body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}") 2 | //# sourceMappingURL=skin.shadowdom.js.map 3 | -------------------------------------------------------------------------------- /public/tinymce/skins/ui/tinymce-5/skin.shadowdom.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('ui/tinymce-5/skin.shadowdom.css', "body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}") 2 | //# sourceMappingURL=skin.shadowdom.js.map 3 | -------------------------------------------------------------------------------- /src/components/use-schema-render/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 24 | -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress'; 2 | import themeConfig from './themeConfig.mjs'; 3 | 4 | /** [docs](https://vitepress.dev/reference/site-config) */ 5 | export default defineConfig({ 6 | title: "微前端后台模板", 7 | description: "微前端后台模板", 8 | srcDir: './src', 9 | outDir: './build', 10 | cacheDir: './.vite', 11 | /** 网页favicon */ 12 | head: [ 13 | ['link', { rel: 'icon', href: '/favicon.ico' }], 14 | ], 15 | // 路由前缀 16 | base: '/docs/', 17 | /** vite相关配置 */ 18 | vite: { 19 | server: { 20 | port: 5151, 21 | }, 22 | }, 23 | /** 路由配置 / 主题配置 */ 24 | themeConfig: themeConfig, 25 | }) 26 | -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/低代码渲染器.md: -------------------------------------------------------------------------------- 1 | ## 低代码渲染器(待完善) 2 | 3 | `\src\components\use-schema-render\index.vue`封装了一个低代码的渲染器,支持在主/子应用使用 4 | 5 | [🔗点击查看在线示例](https://micro-admin-template.lammu.cn/micromain/demo/lowcodeEngine) 6 | 7 | 8 | ### 主应用下使用 9 | > 页面编辑/节点id可通过[低代码设计器](./低代码设计器.md)获取 10 | ```TSX 11 | import UseSchemaRender from '@/components/use-schema-render/index.vue'; 12 | 13 | 16 | ``` 17 | 18 | ### 子应用下使用 19 | 20 | ```TSX 21 | import MicroComponent from 'micro-app-utils/react18/MicroComponent'; 22 | 23 | 27 | ``` -------------------------------------------------------------------------------- /public/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('ui/tinymce-5-dark/skin.shadowdom.css', "body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}") 2 | //# sourceMappingURL=skin.shadowdom.js.map 3 | -------------------------------------------------------------------------------- /src/pages/demo/vueFlow/ValueNode.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/组件共享方案1-路由组件.md: -------------------------------------------------------------------------------- 1 | # 路由组件 2 | 3 | ## 💡由来 4 | 5 | 微前端可以用来加载其它应用的页面。那将组件注册为页面,通过微前端来加载其它项目的组件是否可行呢? 6 | 经过测试,答案是可以的 7 | 8 | ## 使用场景 9 | 路由组件建议用在比较复杂的区块组件上(例如业务详情),因为通过微前端加载组件的方式是比较耗时耗资源的 10 | 11 | ## 用法 12 | 13 | ```TSX 14 | import MicroApp from 'micro-app-utils/vue3/MicroApp.vue'; 15 | 16 | // 使用_router-mode="pure"能避免url出现不必要的路由信息 17 | 22 | ``` 23 | 24 | ## 潜在的问题 25 | 26 | 1. 无限循环嵌套 27 | 28 | MicroApp组件默认支持嵌套3层,可以规避无限循环的问题,如果需要嵌套更多层,可以给MicroApp传递_forceInit参数 29 | 30 | 2. 无法双向绑定 31 | 32 | 3. 无法调用组件方法 33 | 34 | 现阶段先避免组件方法这种使用方式 35 | 36 | 后续可能会解决这个问题 -------------------------------------------------------------------------------- /src/style/reset.scss: -------------------------------------------------------------------------------- 1 | /** 重置样式表 */ 2 | *, 3 | *:before, 4 | *:after { 5 | box-sizing: border-box; 6 | padding: 0; 7 | margin: 0; 8 | } 9 | 10 | body { 11 | font-family: '微软雅黑'; 12 | } 13 | 14 | a, 15 | a:visited, 16 | a:focus, 17 | a:hover, 18 | a:link, 19 | a:active { 20 | color: inherit; 21 | outline: none; 22 | text-decoration: none; 23 | } 24 | 25 | button, 26 | input { 27 | border: none; 28 | background-color: transparent; 29 | outline: none; 30 | border-radius: 0; 31 | font-size: inherit; 32 | } 33 | 34 | a, 35 | button { 36 | cursor: pointer; 37 | } 38 | 39 | ul, 40 | li, 41 | ol { 42 | list-style: none; 43 | } 44 | 45 | :focus-visible { 46 | outline: none; 47 | } -------------------------------------------------------------------------------- /src/components/use-suspense/README.md: -------------------------------------------------------------------------------- 1 | ## 延迟渲染组件 2 | 3 | 说明:对于一些一开始不显示的元素可以设置成不挂载,第一次显示即挂载,之后不会重新挂载 4 | 已注册为全局组件 5 | 6 | ## 注意,如果包裹的子元素是有动画的,并且子元素的显示到和 use-suspense 用同一个变量的,必须使用 v-model 7 | 8 | ### 使用 9 | 10 | ```vue 11 | 26 | 30 | ``` 31 | -------------------------------------------------------------------------------- /src/pages/SubMicroApp.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 29 | -------------------------------------------------------------------------------- /src/hooks/router.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态变量统一不要和函数混在一起,否则会报错:Cannot access 'globalStore' before initialization 3 | */ 4 | 5 | import { MenuItemType } from '@/types/common'; 6 | import { ref } from 'vue'; 7 | 8 | // TODO类型 9 | export const currentRouteInfo = ref(); 10 | 11 | /** 路由初始化时信息对象 */ 12 | export const routerTo = { 13 | path: '', 14 | query: {}, 15 | }; 16 | 17 | /** 是否已添加动态路由 */ 18 | export let isAddedAsyncRoutes = false; 19 | export function updateIsAddedAsyncRoutes(value: boolean) { 20 | isAddedAsyncRoutes = value; 21 | } 22 | 23 | /** 动态路由暂存 */ 24 | export let asyncRoutes: Array = []; // TODO类型 25 | export function updateAsyncRoutes(value: Array) { 26 | asyncRoutes = value; 27 | } 28 | -------------------------------------------------------------------------------- /docs/src/public/svgs/Nginx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/src/public/svgs/UnoCSS.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "prettier.enable": true, 5 | "eslint.enable": true, 6 | "eslint.format.enable": true, 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": true 9 | }, 10 | "eslint.validate": [ 11 | "javascript", 12 | "typescript" 13 | ], 14 | "eslint.codeAction.showDocumentation": { 15 | "enable": true 16 | }, 17 | "eslint.probe": [ 18 | "astro", 19 | "javascript", 20 | "javascriptreact", 21 | "typescript", 22 | "typescriptreact", 23 | "html", 24 | "mdx", 25 | "vue", 26 | "markdown", 27 | "json", 28 | "jsonc" 29 | ], 30 | "eslint.debug": true, 31 | "eslint.lintTask.enable": true, 32 | "eslint.onIgnoredFiles": "off", 33 | "eslint.experimental.useFlatConfig": false, 34 | } -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/组件共享方案3-跨框架组件库.md: -------------------------------------------------------------------------------- 1 | # frame-less-ui(跨框架组件库) 2 | 3 | > 项目已开源在https://github.com/LAMMUpro/frame-less-ui.git 4 | 5 | ## 💡由来 6 | 7 | js原生支持web component(自定义组件),天然跨框架,而vue3支持将vue组件编译成web component 8 | 9 | ## 使用场景 10 | 11 | 常用于一些跨框架的业务/复杂组件,例如`成员选择器` / `部门选择器` / `虚拟列表树`,可以在任何前端项目中使用,可以基于element-plus进行二次封装。 12 | 13 | ## 使用 14 | 15 | ```TSX 16 | import FlPagingSelectV3 from 'frame-less-ui/vue3/paging-select'; 17 | 18 | 26 | 27 | ``` 28 | 29 | ## 潜在的问题 30 | 31 | 1. web component使用了shadow dom,不能使用样式穿透来修改组件样式了 32 | 33 | 2. 针对不同框架需要额外包装一层来解决各框架特定的问题,会造成一些工作量 34 | 35 | 3. 对web component的开发各方还未处于成熟阶段,某些api/特性可能还不兼容 36 | 37 | -------------------------------------------------------------------------------- /public/tinymce/plugins/code/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",(e=>((e=>{e.addCommand("mceCodeEditor",(()=>{(e=>{const o=(e=>e.getContent({source_view:!0}))(e);e.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:o},onSubmit:o=>{((e,o)=>{e.focus(),e.undoManager.transact((()=>{e.setContent(o)})),e.selection.setCursorLocation(),e.nodeChanged()})(e,o.getData().code),o.close()}})})(e)}))})(e),(e=>{const o=()=>e.execCommand("mceCodeEditor");e.ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:o}),e.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:o})})(e),{})))}(); -------------------------------------------------------------------------------- /docs/src/public/svgs/TS.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/router/interceptor.test.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router'; 2 | import { baseRoutes } from '.'; 3 | import CONSTS from '@/utils/CONSTS'; 4 | // import { initRouteInterceptor } from './interceptor'; 5 | // import { createPinia } from 'pinia'; 6 | // import { createApp } from 'vue'; 7 | // import App from '../App.vue'; 8 | 9 | describe('测试路由拦截', () => { 10 | const router = createRouter({ 11 | history: createWebHistory(`/${CONSTS.PREFIX_URL}/`), 12 | routes: baseRoutes, 13 | }); 14 | 15 | // const pinia = createPinia(); 16 | // const app = createApp(App); 17 | // app.use(pinia); 18 | 19 | // initRouteInterceptor(router); 20 | 21 | test('路由拦截', async () => { 22 | expect(router.currentRoute.value.fullPath).toBe('/'); 23 | await router.push({ path: '/empty' }); 24 | expect(router.currentRoute.value.fullPath).toBe('/empty'); 25 | expect(false).toBe(false); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # 缓存文件 2 | .cache 3 | # serveless缓存 4 | .s 5 | # 编译产物 6 | build/* 7 | # 编译产物 8 | build_*/* 9 | # 编译产物 10 | dist 11 | target 12 | # 第三方包 13 | node_modules 14 | # yarn版本锁定 15 | yarn.lock 16 | # pnpm版本锁定 17 | pnpm-lock.yaml 18 | # package版本锁定 19 | package-lock.json 20 | # 生成的文档 21 | swagger.yaml 22 | # 后端配置文件 23 | .env 24 | # 后端配置文件 25 | .env.* 26 | #日志文件 27 | logs 28 | # 日志文件 29 | npm-debug.log* 30 | # 日志文件 31 | pnpm-debug.log* 32 | # 日志文件 33 | yarn-debug.log* 34 | # 日志文件 35 | yarn-error.log* 36 | # mac特有 37 | .DS_Store 38 | # storybook日志文件 39 | storybook.log 40 | # storybook编译产物 41 | storybook-static 42 | 43 | # 字体图标多余文件 44 | src/assets/iconfont/*demo* 45 | src/assets/iconfont/iconfont.js 46 | src/assets/iconfont/iconfont.json 47 | 48 | # element自动导入生成的文件 49 | auto-imports.d.ts 50 | components.d.ts 51 | 52 | .vite 53 | .vitepress/* 54 | !.vitepress/config.mts 55 | !.vitepress/themeConfig.mts 56 | 57 | # obsidian 软件配置 58 | obsidian/.obsidian 59 | 60 | # 忽略idea编译文件 61 | .idea 62 | -------------------------------------------------------------------------------- /docs/src/Docs/项目规范/通用规范.md: -------------------------------------------------------------------------------- 1 | ## 图片资源 2 | 3 | 图片资源按需裁切文件尺寸 / 压缩一下文件体积, [在线图片裁切/压缩](https://www.iloveimg.com/zh-cn/compress-image) 4 | 5 | ## 空白路由 6 | 7 | 子应用需要新增一个空白路由作为页面中转站, 用于做默认路由, 同一页面刷新中转路由 8 | 9 | ```tsx 10 | { 11 | path: '/empty', 12 | name: 'empty', 13 | component: PageEmpty, 14 | meta: { hidden: true, title: '空白中转路由' }, 15 | }, 16 | ``` 17 | 18 | ## 全局状态存储 19 | 20 | ## 路由配置 21 | 22 | subAppName 保持和子应用路由前缀一致 23 | 24 | ## 微前端配置 25 | 26 | 由于 micro-app 的 tagName 属性不能含大写,所以 mirco-app-serviceAdmin 要改为 micro-app-service-admin 27 | 28 | ## location 使用 29 | 30 | > 子应用不要直接操作 location 对象,当成只读对象来用 31 | 32 | ```TSX 33 | // prefect 34 | (window.microApp || window).location.href 35 | ``` 36 | 37 | ## 布局 38 | 39 | 子应用尽量不要使用 fixed 布局,fixed 布局相对的是父应用窗口 40 | 41 | ## 样式 42 | 43 | 子应用不要给父应用挂样式,,比如:root、body 44 | 45 | ## \使用 46 | 47 | - 路由组件指定\_router-mode="pure",否则频繁切换可能会报错:超出了最大调用堆栈大小 48 | - 默认主应用和第一层子应用可以使用路由组件(\),二层及以上的子应用默认不能使用路由组件 49 | - 如果二层及以上子应用需要使用路由组件,需要传递\_forceInit 参数 50 | -------------------------------------------------------------------------------- /uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, presetAttributify, presetUno } from 'unocss'; 2 | import { common } from './uno.common'; 3 | import { transformerDirectives } from 'unocss'; 4 | import { transformerVariantGroup } from 'unocss'; 5 | 6 | export default defineConfig({ 7 | /** 启用这个配置会覆盖掉默认配置, 需要把官方的默认加回来 */ 8 | presets: [ 9 | /** 默认预设 */ 10 | presetUno(), 11 | /** 自定义预设 */ 12 | common, 13 | ], 14 | transformers: [ 15 | /** 指令转换器 .css内 @screen lt-md { html { --uno: font-size-12px; } } */ 16 | transformerDirectives(), 17 | /** 变体组 @example className='md:(font-size-90px c-#34579a)' */ 18 | transformerVariantGroup(), 19 | ], 20 | theme: { 21 | /** 22 | * 断点, 只能设置min-width, 但可以用 at-, lt- 23 | * @example className='font-size-16px lt-xs:font-size-12px md:font-size-12px' 24 | */ 25 | breakpoints: { 26 | xs: '320px', 27 | sm: '640px', 28 | md: '768px', 29 | lg: '1024px', 30 | xl: '1280px', 31 | xxl: '1536px', 32 | }, 33 | }, 34 | }) -------------------------------------------------------------------------------- /src/components/use-tinymce/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 45 | -------------------------------------------------------------------------------- /src/layouts/components/RouteInfoBar.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 缓存文件 2 | .cache 3 | # serveless缓存 4 | .s 5 | # 编译产物 6 | build 7 | # 编译产物 8 | ./build_* 9 | # 编译产物 10 | dist 11 | target 12 | # 第三方包 13 | node_modules 14 | # yarn版本锁定 15 | yarn.lock 16 | # pnpm版本锁定 17 | pnpm-lock.yaml 18 | # package版本锁定 19 | package-lock.json 20 | # 生成的文档 21 | swagger.yaml 22 | # 后端配置文件 23 | .env 24 | # 后端配置文件 25 | .env.* 26 | # 日志文件 27 | logs 28 | # 日志文件 29 | npm-debug.log* 30 | # 日志文件 31 | pnpm-debug.log* 32 | # 日志文件 33 | yarn-debug.log* 34 | # 日志文件 35 | yarn-error.log* 36 | # mac特有 37 | .DS_Store 38 | # storybook日志文件 39 | storybook.log 40 | # storybook编译产物 41 | storybook-static 42 | 43 | # 字体图标多余文件 44 | src/assets/iconfont/*demo* 45 | src/assets/iconfont/iconfont.js 46 | src/assets/iconfont/iconfont.json 47 | 48 | # element自动导入生成的文件 49 | auto-imports.d.ts 50 | components.d.ts 51 | 52 | # 忽略打包文件 53 | .idea 54 | 55 | # 忽略子应用(按理说子应用是git仓库会自动忽略的) 56 | apps/* 57 | !apps/README.md 58 | 59 | # 工具库tsc编译后的产物 60 | packages/micro-app-tools/**/*.js 61 | # 这个配置后续还需要优化 62 | packages/micro-app-tools/**/*.d.ts 63 | packages/micro-app-tools/**/*.jsx -------------------------------------------------------------------------------- /docs/src/public/svgs/JS.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/micro-app-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "es6", 6 | "useDefineForClassFields": true, 7 | "module": "ES6", 8 | "lib": [ 9 | "ES2020", 10 | "DOM", 11 | "DOM.Iterable" 12 | ], 13 | "skipLibCheck": true, 14 | /* Bundler mode */ 15 | "moduleResolution": "bundler", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "moduleDetection": "force", 19 | "noEmit": false, 20 | "jsx": "preserve", 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": true, 24 | "noUnusedParameters": true, 25 | "noFallthroughCasesInSwitch": true, 26 | "allowSyntheticDefaultImports": true, 27 | "types": [ 28 | "node", 29 | ], 30 | "baseUrl": "./", 31 | "paths": { 32 | "@/*": [ 33 | "src/*" 34 | ] 35 | }, 36 | }, 37 | "include": [ 38 | "**/*.ts", 39 | "**/*.tsx", 40 | "**/*.vue", 41 | ] 42 | } -------------------------------------------------------------------------------- /docs/src/public/svgs/frame-less-ui.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/demo/vueFlow/OperatorNode.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 42 | -------------------------------------------------------------------------------- /src/pages/demo/lowcodeEngine.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 33 | 34 | 40 | -------------------------------------------------------------------------------- /docs/src/Docs/2.基础功能/资源复用.md: -------------------------------------------------------------------------------- 1 | 2 | ## 主应用加载后传给子应用 3 | 4 | 子应用加载完资源后,挂载到window下,子应用通过rawWindow引用 或者 通过事件传递给子应用 5 | 6 | ps:这种方式主应用和子应用使用的是同一个对象,有可能存在副作用(慎用) 7 | 8 | 例:加载tinyMce 9 | 10 | ## 打包资源共享 11 | 12 | > 共用url方式,统一使用esm打包产物 13 | 14 | ```tsx 15 | external: ['vue', 'vue-router', 'element-plus', '@micro-zoe/micro-app'], 16 | output: { 17 | format: 'esm', // 打包模式 18 | /** 19 | * 从对应网络路径中加载依赖 20 | * 对于external排除的依赖,直接从'vue'导入是无效的路径,所以需要配置对应资源路径 21 | */ 22 | paths: { 23 | 'vue': '/kchadmin/vue.cd73.js', 24 | 'vue-router': '/kchadmin/vue-router.4bcc.js', 25 | '@micro-zoe/micro-app': '/kchadmin/micro-app.4e9a.js', 26 | 'element-plus': '/kchadmin/element-plus.bd36.js', 27 | }, 28 | } 29 | ``` 30 | 31 | 32 | ### 强缓存 33 | 34 | > nginx对带hash值的静态文件做强缓存,可以不需要等待浏览器响应 35 | > 36 | > ps: 协商缓存本身速度很快(内容下载ms级别,浏览器响应也是ms级别),但是当网络拥堵时,浏览器响应时间可能很长,几秒钟都可以(强缓存可以避免这种极端情况) 37 | 38 | 配置nginx为指定目录下的指定文件格式`.[a-z0-9]{8}_h.(常见文件后缀)`的文件设置强缓存 39 | 40 | ```nginx 41 | location ~ "/(js|css|img|font)/.*?\.[a-z0-9]{8}_h\.(js|mjs|css|ttf|woff|woff2|png|jpg|jpeg|webp|gif|svg|ico)$" { 42 | expires 365d; 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /s.master.yaml: -------------------------------------------------------------------------------- 1 | extend: s.yaml 2 | 3 | ## 公共配置(基本不用改) 4 | commonConfig: 5 | functionName: env_master # 函数名 6 | 7 | services: 8 | moduleName: # 服务/模块名, 不参与部署 9 | props: 10 | function: # 函数配置 11 | name: ${commonConfig.functionName} 12 | customDomains: # [自定义域名](https://docs.serverless-devs.com/fc/yaml/customDomains) 13 | - domainName: ${env.DomainName_master} # 域名(.env配置)(不带http[s]://),需要预先手动配置域名解析!!!, 如果是auto取值,系统则会默认分配域名 14 | protocol: HTTPS # 协议,取值:HTTP | HTTPS | HTTP,HTTPS (如果不配https,把这个改为HTTP) 15 | certId: ${env.CertId_master} # 域名证书Id(.env配置)(Id到数字证书管理服务中心, 证书详情里面找)(如果不配https,把项注释掉) 16 | tlsConfig: #(如果不配https,把项注释掉) 17 | minVersion: TLSv1.2 # TLS协议版本 18 | maxVersion: TLSv1.2 19 | cipherSuites: ${commonConfig.cipherSuites} 20 | routeConfigs: # 路由配置 21 | - path: /* # 路径 22 | serviceName: ${projectConfig.serviceName} # 服务名 23 | functionName: ${commonConfig.functionName} # 函数名 24 | qualifier: LATEST 25 | methods: # 前端要个GET就行了 26 | - GET 27 | -------------------------------------------------------------------------------- /docs/s.master.yaml: -------------------------------------------------------------------------------- 1 | extend: s.yaml 2 | 3 | ## 公共配置(基本不用改) 4 | commonConfig: 5 | functionName: env_master # 函数名 6 | 7 | services: 8 | moduleName: # 服务/模块名, 不参与部署 9 | props: 10 | function: # 函数配置 11 | name: ${commonConfig.functionName} 12 | # customDomains: # [自定义域名](https://docs.serverless-devs.com/fc/yaml/customDomains) 13 | # - domainName: ${env.DomainName_master} # 域名(.env配置)(不带http[s]://),需要预先手动配置域名解析!!!, 如果是auto取值,系统则会默认分配域名 14 | # protocol: HTTPS # 协议,取值:HTTP | HTTPS | HTTP,HTTPS (如果不配https,把这个改为HTTP) 15 | # certId: ${env.CertId_master} # 域名证书Id(.env配置)(Id到数字证书管理服务中心, 证书详情里面找)(如果不配https,把项注释掉) 16 | # tlsConfig: #(如果不配https,把项注释掉) 17 | # minVersion: TLSv1.2 # TLS协议版本 18 | # maxVersion: TLSv1.2 19 | # cipherSuites: ${commonConfig.cipherSuites} 20 | # routeConfigs: # 路由配置 21 | # - path: /* # 路径 22 | # serviceName: ${projectConfig.serviceName} # 服务名 23 | # functionName: ${commonConfig.functionName} # 函数名 24 | # qualifier: LATEST 25 | # methods: # 前端要个GET就行了 26 | # - GET -------------------------------------------------------------------------------- /src/pages/404.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 45 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/default/content.min.css: -------------------------------------------------------------------------------- 1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/tinymce-5/content.min.css: -------------------------------------------------------------------------------- 1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /src/pages/demo/lottie.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/writer/content.min.css: -------------------------------------------------------------------------------- 1 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /uno.common.ts: -------------------------------------------------------------------------------- 1 | import { Preset } from 'unocss'; 2 | 3 | export const common: Preset = { 4 | name: 'common', 5 | /** 自定义预设 */ 6 | rules: [ 7 | /* 溢出...显示 当前节点生效 */ 8 | [ 9 | 'ellipsis', 10 | { overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap' }, 11 | ], 12 | [ 13 | 'ellipsis-1', 14 | { 15 | '-webkit-line-clamp': '1', 16 | display: '-webkit-box', 17 | '-webkit-box-orient': 'vertical', 18 | overflow: 'hidden', 19 | }, 20 | ], 21 | [ 22 | 'ellipsis-2', 23 | { 24 | '-webkit-line-clamp': '2', 25 | display: '-webkit-box', 26 | '-webkit-box-orient': 'vertical', 27 | overflow: 'hidden', 28 | }, 29 | ], 30 | [ 31 | 'ellipsis-3', 32 | { 33 | '-webkit-line-clamp': '3', 34 | display: '-webkit-box', 35 | '-webkit-box-orient': 'vertical', 36 | overflow: 'hidden', 37 | }, 38 | ], 39 | [ 40 | /** 动态化 */ 41 | /^m-([\.\d]+)$/, 42 | ([_, num]) => ({ margin: `${num}px` }), 43 | ], 44 | ], 45 | /** 缩写, */ 46 | shortcuts: [{ fcc: 'flex justify-center items-center' }], 47 | }; 48 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/dark/content.min.css: -------------------------------------------------------------------------------- 1 | body{background-color:#222f3e;color:#fff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/tinymce-5-dark/content.min.css: -------------------------------------------------------------------------------- 1 | body{background-color:#2f3742;color:#dfe0e4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /public/tinymce/plugins/visualblocks/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const s=(t,s,o)=>{t.dom.toggleClass(t.getBody(),"mce-visualblocks"),o.set(!o.get()),((t,s)=>{t.dispatch("VisualBlocks",{state:s})})(t,o.get())},o=("visualblocks_default_state",t=>t.options.get("visualblocks_default_state"));const e=(t,s)=>o=>{o.setActive(s.get());const e=t=>o.setActive(t.state);return t.on("VisualBlocks",e),()=>t.off("VisualBlocks",e)};t.add("visualblocks",((t,l)=>{(t=>{(0,t.options.register)("visualblocks_default_state",{processor:"boolean",default:!1})})(t);const a=(t=>{let s=!1;return{get:()=>s,set:t=>{s=t}}})();((t,o,e)=>{t.addCommand("mceVisualBlocks",(()=>{s(t,0,e)}))})(t,0,a),((t,s)=>{const o=()=>t.execCommand("mceVisualBlocks");t.ui.registry.addToggleButton("visualblocks",{icon:"visualblocks",tooltip:"Show blocks",onAction:o,onSetup:e(t,s)}),t.ui.registry.addToggleMenuItem("visualblocks",{text:"Show blocks",icon:"visualblocks",onAction:o,onSetup:e(t,s)})})(t,a),((t,e,l)=>{t.on("PreviewFormats AfterPreviewFormats",(s=>{l.get()&&t.dom.toggleClass(t.getBody(),"mce-visualblocks","afterpreviewformats"===s.type)})),t.on("init",(()=>{o(t)&&s(t,0,l)}))})(t,0,a)}))}(); -------------------------------------------------------------------------------- /src/pages/demo/reactComponent.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/default/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/default/content.css', "body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/document/content.min.css: -------------------------------------------------------------------------------- 1 | @media screen{html{background:#f4f4f4;min-height:100%}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(100vh - 1rem);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} 2 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/tinymce-5/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/tinymce-5/content.css', "body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /src/components/use-svg/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 44 | 45 | 62 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/writer/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/writer/content.css', "body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /packages/micro-app-tools/vue2/ReactComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 59 | -------------------------------------------------------------------------------- /docs/src/Docs/1.指南/模版介绍.md: -------------------------------------------------------------------------------- 1 | ## 微前端后台模版介绍 2 | 3 | 该模板使用vue3 + vite作为主应用,分别适配了`vue3+vite` / `vue2+vite` / `vue2+webpack` / `react+vite`子应用,同时各个子应用均测试了`派发组件`/`路由组件`/`跨框架组件` 4 | 5 | ⚠️: 本项目只`测试`一些第三方库是否`适配`, 不会提供很全的后台功能(github已经有很多优秀的模版了); 如果你的项目想引入微前端, 最好的做法是基于主应用,参考性的将该项目的部分功能移植到你的项目中去而不是直接用这个项目. 6 | 7 | ## 适配清单 8 | 9 | 1. 样式: 适配PC端 & 移动端 10 | 11 | 2. 浏览器:兼容微信浏览器 / Chrome / 手机自带浏览器 (包括iphone/小米/vivo/oppo/真我) 12 | 13 | 3. 前端框架:兼容vue3/react/vue2 14 | 15 | ## 模版有哪些特色功能 16 | 17 | - 三种跨框架的组件共享方案:`派发组件`、`路由组件`、`fl组件库` 18 | 19 | 1. 派发组件``支持主应用`下发组件`给子应用使用, `兼容vue3/vue2/react`, 同时`支持作用域插槽`,常用于业务组件 20 | 21 | 2. 路由组件``支持组件注册为路由给其它应用使用,常用于较大区域的业务模块 22 | 23 | 3. `fl组件库`是我另一个[单独的项目库](https://www.npmjs.com/package/frame-less-ui),可以在任何前端项目中使用,常用于业务组件 24 | 25 | - ``组件支持在`vue的环境下直接使用React组件`, 包括阿里`低代码渲染器`这样复杂的React组件 26 | 27 | - 微前端和低代码`混合开发`, 在代码中使用低代码渲染器渲染低代码页面, 再低代码中可以反向通过\加载子应用 28 | 29 | ## 模版还做了什么? 30 | 31 | 模版内有个`micro-app-tools子包`,它在micro-app的基础上进行了`二次封装`, 通过提前配置子应用, 减少了大量配置参数及命名唯一性问题。 让用户使用``加载子应用最少只需要传入子应用名称`_name`以及目标路径`_path`即可, 同时_path支持动态变化, 页面会自动刷新 32 | 33 | ```tsx 34 | // eg: 加载子应用appA的/pageA页面 35 | import MicroApp from 'micro-app-tools/vue3/MicroApp'; 36 | 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/dark/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/dark/content.css', "body{background-color:#222f3e;color:#fff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /src/pages/demo/vueFlow/Icon.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 54 | -------------------------------------------------------------------------------- /packages/micro-app-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-app-tools", 3 | "version": "0.1.9", 4 | "description": "基于micro-app封装的工具函数", 5 | "main": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": "./index.js", 9 | "./data": "./data.js", 10 | "./types": "./types/index.d.ts", 11 | "./types/exports.d.ts": "./types/exports.d.ts", 12 | "./types/global.d.ts": "./types/global.d.ts", 13 | "./listener": "./listener.js", 14 | "./vue3/index": "./vue3/index.js", 15 | "./vue3/ReactComponent.vue": "./vue3/ReactComponent.vue", 16 | "./vue3/MicroApp.vue": "./vue3/MicroApp.vue", 17 | "./vue3/MicroComponent.vue": "./vue3/MicroComponent.vue", 18 | "./vue3/renderComponent": "./vue3/renderComponent.js", 19 | "./vue2/index": "./vue2/index.js", 20 | "./vue2/ReactComponent.vue": "./vue2/ReactComponent.vue", 21 | "./vue2/MicroApp.vue": "./vue2/MicroApp.vue", 22 | "./vue2/MicroComponent.vue": "./vue2/MicroComponent.vue", 23 | "./react18/index": "./react18/index.js", 24 | "./react18/MicroComponent": "./react18/MicroComponent.jsx", 25 | "./react18/MicroApp": "./react18/MicroApp.jsx", 26 | "./react18/utils": "./react18/utils.jsx" 27 | }, 28 | "scripts": { 29 | "build": "tsc", 30 | "tsc": "tsc" 31 | }, 32 | "keywords": [], 33 | "author": "lammu", 34 | "license": "ISC" 35 | } -------------------------------------------------------------------------------- /public/tinymce/skins/content/tinymce-5-dark/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/tinymce-5-dark/content.css', "body{background-color:#2f3742;color:#dfe0e4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /public/tinymce/skins/content/document/content.js: -------------------------------------------------------------------------------- 1 | tinymce.Resource.add('content/document/content.css', "@media screen{html{background:#f4f4f4;min-height:100%}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(100vh - 1rem);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border=\"0\"]):not([style*=border-width]) td,table[border]:not([border=\"0\"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border=\"0\"]):not([style*=border-style]) td,table[border]:not([border=\"0\"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border=\"0\"]):not([style*=border-color]) td,table[border]:not([border=\"0\"]):not([style*=border-color]) th{border-color:#ccc}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}") 2 | //# sourceMappingURL=content.js.map 3 | -------------------------------------------------------------------------------- /docs/src/public/svgs/DispatchComponent.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/svg/zhanwei.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ESNext", 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "lib": [ 9 | "ES2020", 10 | "DOM", 11 | "DOM.Iterable" 12 | ], 13 | "skipLibCheck": true, 14 | /* Bundler mode */ 15 | "moduleResolution": "bundler", 16 | "allowImportingTsExtensions": true, 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "moduleDetection": "force", 20 | "noEmit": true, 21 | "jsx": "preserve", 22 | /* Linting */ 23 | "strict": true, 24 | "noUnusedLocals": true, 25 | "noUnusedParameters": true, 26 | "noFallthroughCasesInSwitch": true, 27 | "allowSyntheticDefaultImports": true, 28 | "types": [ 29 | "node", 30 | "element-plus/global", 31 | "vitest/globals", 32 | "frame-less-ui/global.d.ts", 33 | "micro-app-tools/types/global.d.ts" 34 | ], 35 | "baseUrl": "./", 36 | "paths": { 37 | "@/*": [ 38 | "src/*" 39 | ] 40 | } 41 | }, 42 | "include": [ 43 | "vite.config.ts", 44 | "src/**/*.ts", 45 | "src/**/*.tsx", 46 | "src/**/*.vue", 47 | "micro-app-tools/**/*.vue", 48 | "micro-app-tools/**/*.ts", 49 | "micro-app-tools/**/*.tsx", 50 | "packages/**/*" 51 | ] 52 | } -------------------------------------------------------------------------------- /docs/src/public/svgs/Webpack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/svg/docs-question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/src/About/history.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: false 3 | footer: false 4 | prev: false 5 | --- 6 | 7 | # 更新历史 8 | 9 | ## 1.3.0(排期中) 10 | - 低代码架构优化 11 | - 升级react版本到18 12 | - 升级antd版本到5 13 | - 结合antd / element plus组件? 14 | - 路由组件支持方法调用 15 | 16 | ## 1.2.0(开发中) 17 | - 用户系统 / 权限控制 18 | - 动态路由 19 | - 优化路由拦截器 20 | - 当前菜单信息回显 21 | - 低代码深度集成 22 | - 优化渲染 23 | - 优化菜单类型, 直接支持低代码页面 24 | - 低代码组件样式隔离 25 | - 主应用入口文件体积优化 26 | - 组件分包 27 | - 延迟初始化 28 | - 父子应用打包优化, 资源复用 29 | - 引入unocss 30 | - 样式兼容移动端 31 | - 检查设备兼容性 32 | 33 | ## 1.1.0(已完成) 34 | - 项目解耦 ✔ 35 | - 将micro-app-utils独立成一个npm库 ✔ 36 | - 动态菜单 ✔ 37 | - [wwj]react18子应用 ✔ 38 | - 接入路由 ✔ 39 | - 路由拦截 ✔ 40 | - 主应用支持将Element组件注册成派发组件(按需加载) ✔ 41 | - vue2子应用 ✔ 42 | - 路由 ✔ 43 | - vue3子应用 ✔ 44 | - 路由 ✔ 45 | - 线上demo ✔ 46 | - 子应用复用主应用的404/403/login页面(派发组件形式) ✔ 47 | - 输入url在线预览网址 ✔ 48 | - 路由组件优化 ✔ 49 | - 命名唯一性 ✔ 50 | - 解决循环嵌套问题 ✔ 51 | - 加载状态 ✔ 52 | - 加载失败处理 ✔ 53 | - 派发组件优化 ✔ 54 | - 插槽渲染 ✔ 55 | - 插槽参数传递 ✔ 56 | - 插槽内数据响应式 ✔ 57 | - 微前端多层嵌套下的事件穿透 ✔ 58 | - react插槽局部更新 ✔ 59 | - 销毁时机 ✔ 60 | - 路由拦截 ✔ 61 | - 富文本组件(tinymce) ✔ 62 | 63 | ## 1.0.0(已完成) 64 | - 项目初始化 ✔ 65 | - vitest 测试框架 ✔ 66 | - monorepo 共享 microapp 工具 / shared ... ✔ 67 | - micro-app-utils 基于 micro-app 的二次封装 ✔ 68 | - shared 常用模块共享 ✔ 69 | - sass ✔ 70 | - router ✔ 71 | - 公共组件 ✔ 72 | - use-svg ✔ 73 | - Prettier 文件格式化(需要 vscode 安装 Prettier) ✔ 74 | - eslint 代码规范(需要 vscode 安装 ESLint 2.4.4) ✔ 75 | - element-plus (组件及样式按需引入) ✔ 76 | - react18 子应用 ✔ 77 | -------------------------------------------------------------------------------- /src/versionUpdateCheck_worker.js: -------------------------------------------------------------------------------- 1 | import CONSTS from '@/utils/CONSTS'; 2 | 3 | /** 定时器 */ 4 | let timer; 5 | 6 | /** 取消定时器 */ 7 | let timer_clear; 8 | 9 | /** 应用入口html */ 10 | let indexHtml = ''; 11 | 12 | /** 13 | * 获取应用入口html 14 | */ 15 | async function getIndexHtml() { 16 | /** 不加随机参数会使用disk cache */ 17 | const res = await fetch(`/${CONSTS.PREFIX_URL}/index.html?ts=${Date.now()}`); 18 | return await res.text(); 19 | } 20 | 21 | /** 22 | * 接收到页面请求 23 | */ 24 | self.onmessage = async function (event) { 25 | /** 首次进入先获取一次 */ 26 | indexHtml = await getIndexHtml(); 27 | 28 | /** 29 | * 事件类型: 30 | * 'page-hidden' - 页面隐藏 31 | * 'page-visible' - 页面显示 32 | */ 33 | const eventType = event.data.type; 34 | if (eventType === 'page-hidden') { 35 | /** 36 | * 页面隐藏,取消轮询(延迟1s,如果再1s内重新切换回同源的标签页,可以取消这个定时器) 37 | */ 38 | timer_clear = setTimeout(() => { 39 | clearInterval(timer); 40 | timer = undefined; 41 | }, 1000); 42 | } else if (eventType === 'page-visible') { 43 | /** 44 | * 页面显示 45 | */ 46 | if (timer_clear) { 47 | clearTimeout(timer_clear); 48 | timer_clear = undefined; 49 | } 50 | if (!timer) { 51 | timer = setInterval(async () => { 52 | /** 版本改变了,清空定时器,给所有标签页发送事件 */ 53 | if ((await getIndexHtml()) !== indexHtml) { 54 | clearInterval(timer); 55 | timer = undefined; 56 | postMessage({ type: 'version-change' }); 57 | } 58 | }, 5000); 59 | } 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /src/style/index.scss: -------------------------------------------------------------------------------- 1 | html { 2 | --uno: font-size-16px; 3 | } 4 | 5 | @screen lt-md { 6 | html { 7 | --uno: font-size-14px; 8 | } 9 | } 10 | 11 | :root { 12 | /** 防止子应用导致的高度溢出 */ 13 | overflow: hidden; 14 | } 15 | 16 | micro-app-body, 17 | .render-root { 18 | height: 100%; 19 | } 20 | 21 | #__micro-app-main { 22 | width: 100vw; 23 | 24 | // 路由组件样式 25 | .__micro-app-container { 26 | &.__content, 27 | .__content { 28 | display: contents; 29 | } 30 | 31 | .__micro-app { 32 | height: 100%; 33 | } 34 | 35 | .__tip-msg { 36 | width: 100%; 37 | font-size: 20px; 38 | text-align: center; 39 | padding: 24px; 40 | border-radius: 3px; 41 | margin-bottom: 5px; 42 | 43 | &.__error, 44 | &.__config, 45 | &.__loading { 46 | background: repeating-linear-gradient( 47 | 45deg, 48 | white, 49 | white 5px, 50 | #d9d9d9 5px, 51 | #d9d9d9 10px 52 | ); 53 | } 54 | 55 | &.__error { 56 | color: #ff4949; 57 | } 58 | 59 | &.__config { 60 | color: #ffba00; 61 | } 62 | 63 | &.__loading { 64 | color: #1890ff; 65 | } 66 | 67 | .__reload-btn, 68 | .__close-btn { 69 | cursor: pointer; 70 | } 71 | } 72 | } 73 | } 74 | 75 | /** 重置样式表 */ 76 | @import '@/style/reset.scss'; 77 | 78 | /** 在这导入团队用的样式 */ 79 | @import '@/style/common.scss'; 80 | 81 | /** 覆盖子应用样式 */ 82 | @import '@/style/overflow.scss'; 83 | -------------------------------------------------------------------------------- /src/components/use-suspense/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 64 | 65 | 68 | -------------------------------------------------------------------------------- /public/tinymce/plugins/nonbreaking/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";var n=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=n=>e=>typeof e===n,o=e("boolean"),a=e("number"),t=n=>e=>e.options.get(n),i=t("nonbreaking_force_tab"),s=t("nonbreaking_wrap"),r=(n,e)=>{let o="";for(let a=0;a{const o=s(n)||n.plugins.visualchars?`${r(" ",e)}`:r(" ",e);n.undoManager.transact((()=>n.insertContent(o)))};var l=tinymce.util.Tools.resolve("tinymce.util.VK");const u=n=>e=>{const o=()=>{e.setEnabled(n.selection.isEditable())};return n.on("NodeChange",o),o(),()=>{n.off("NodeChange",o)}};n.add("nonbreaking",(n=>{(n=>{const e=n.options.register;e("nonbreaking_force_tab",{processor:n=>o(n)?{value:n?3:0,valid:!0}:a(n)?{value:n,valid:!0}:{valid:!1,message:"Must be a boolean or number."},default:!1}),e("nonbreaking_wrap",{processor:"boolean",default:!0})})(n),(n=>{n.addCommand("mceNonBreaking",(()=>{c(n,1)}))})(n),(n=>{const e=()=>n.execCommand("mceNonBreaking");n.ui.registry.addButton("nonbreaking",{icon:"non-breaking",tooltip:"Nonbreaking space",onAction:e,onSetup:u(n)}),n.ui.registry.addMenuItem("nonbreaking",{icon:"non-breaking",text:"Nonbreaking space",onAction:e,onSetup:u(n)})})(n),(n=>{const e=i(n);e>0&&n.on("keydown",(o=>{if(o.keyCode===l.TAB&&!o.isDefaultPrevented()){if(o.shiftKey)return;o.preventDefault(),o.stopImmediatePropagation(),c(n,e)}}))})(n)}))}(); -------------------------------------------------------------------------------- /src/components/el-dialog/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 41 | 42 | 63 | -------------------------------------------------------------------------------- /src/pages/noMenu.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 40 | 41 | 70 | -------------------------------------------------------------------------------- /docs/src/public/svgs/Vite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/tinymce/plugins/save/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const o=("function",e=>"function"==typeof e);var n=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),t=tinymce.util.Tools.resolve("tinymce.util.Tools");const a=e=>o=>o.options.get(e),c=a("save_enablewhendirty"),i=a("save_onsavecallback"),s=a("save_oncancelcallback"),r=(e,o)=>{e.notificationManager.open({text:o,type:"error"})},l=e=>o=>{const n=()=>{o.setEnabled(!c(e)||e.isDirty())};return n(),e.on("NodeChange dirty",n),()=>e.off("NodeChange dirty",n)};e.add("save",(e=>{(e=>{const o=e.options.register;o("save_enablewhendirty",{processor:"boolean",default:!0}),o("save_onsavecallback",{processor:"function"}),o("save_oncancelcallback",{processor:"function"})})(e),(e=>{e.ui.registry.addButton("save",{icon:"save",tooltip:"Save",enabled:!1,onAction:()=>e.execCommand("mceSave"),onSetup:l(e),shortcut:"Meta+S"}),e.ui.registry.addButton("cancel",{icon:"cancel",tooltip:"Cancel",enabled:!1,onAction:()=>e.execCommand("mceCancel"),onSetup:l(e)}),e.addShortcut("Meta+S","","mceSave")})(e),(e=>{e.addCommand("mceSave",(()=>{(e=>{const t=n.DOM.getParent(e.id,"form");if(c(e)&&!e.isDirty())return;e.save();const a=i(e);if(o(a))return a.call(e,e),void e.nodeChanged();t?(e.setDirty(!1),t.onsubmit&&!t.onsubmit()||("function"==typeof t.submit?t.submit():r(e,"Error: Form submit field collision.")),e.nodeChanged()):r(e,"Error: No form element found.")})(e)})),e.addCommand("mceCancel",(()=>{(e=>{const n=t.trim(e.startContent),a=s(e);o(a)?a.call(e,e):e.resetContent(n)})(e)}))})(e)}))}(); -------------------------------------------------------------------------------- /packages/micro-app-tools/vue3/ReactComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 71 | -------------------------------------------------------------------------------- /public/tinymce/plugins/pagebreak/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env");const t=e=>a=>a.options.get(e),n=t("pagebreak_separator"),o=t("pagebreak_split_block"),r="mce-pagebreak",s=e=>{const t=``;return e?`

${t}

`:t},c=e=>a=>{const t=()=>{a.setEnabled(e.selection.isEditable())};return e.on("NodeChange",t),t(),()=>{e.off("NodeChange",t)}};e.add("pagebreak",(e=>{(e=>{const a=e.options.register;a("pagebreak_separator",{processor:"string",default:"\x3c!-- pagebreak --\x3e"}),a("pagebreak_split_block",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mcePageBreak",(()=>{e.insertContent(s(o(e)))}))})(e),(e=>{const a=()=>e.execCommand("mcePageBreak");e.ui.registry.addButton("pagebreak",{icon:"page-break",tooltip:"Page break",onAction:a,onSetup:c(e)}),e.ui.registry.addMenuItem("pagebreak",{text:"Page break",icon:"page-break",onAction:a,onSetup:c(e)})})(e),(e=>{const a=n(e),t=()=>o(e),c=new RegExp(a.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,(e=>"\\"+e)),"gi");e.on("BeforeSetContent",(e=>{e.content=e.content.replace(c,s(t()))})),e.on("PreInit",(()=>{e.serializer.addNodeFilter("img",(n=>{let o,s,c=n.length;for(;c--;)if(o=n[c],s=o.attr("class"),s&&-1!==s.indexOf(r)){const n=o.parent;if(n&&e.schema.getBlockElements()[n.name]&&t()){n.type=3,n.value=a,n.raw=!0,o.remove();continue}o.type=3,o.value=a,o.raw=!0}}))}))})(e),(e=>{e.on("ResolveName",(a=>{"IMG"===a.target.nodeName&&e.dom.hasClass(a.target,r)&&(a.name="pagebreak")}))})(e)}))}(); -------------------------------------------------------------------------------- /src/style/common.scss: -------------------------------------------------------------------------------- 1 | // 公共样式,子应用可以直接使用,以-m-开头 2 | 3 | .-m-card { 4 | border-radius: 4px; 5 | padding: 18px; 6 | 7 | &:not(:last-child) { 8 | margin-bottom: 14px; 9 | } 10 | 11 | color: #1C1E21; 12 | background-color: white; 13 | line-height: 1.6em; 14 | 15 | &.lightblue { 16 | background-color: lightblue; 17 | } 18 | 19 | &.lightcoral { 20 | background-color: lightcoral; 21 | } 22 | 23 | &.lightcyan { 24 | background-color: lightcyan; 25 | } 26 | 27 | &.lightgoldenrodyellow { 28 | background-color: lightgoldenrodyellow; 29 | } 30 | 31 | &.lightgray { 32 | background-color: lightgray; 33 | } 34 | 35 | &.lightgreen { 36 | background-color: lightgreen; 37 | } 38 | 39 | &.lightgrey { 40 | background-color: lightgrey; 41 | } 42 | 43 | &.lightgrey { 44 | background-color: lightgrey; 45 | } 46 | 47 | &.lightpink { 48 | background-color: lightpink; 49 | } 50 | 51 | &.lightsalmon { 52 | background-color: lightsalmon; 53 | } 54 | 55 | &.lightseagreen { 56 | background-color: lightseagreen; 57 | } 58 | 59 | &.lightskyblue { 60 | background-color: lightskyblue; 61 | } 62 | 63 | .-m-title { 64 | display: block; 65 | position: relative; 66 | font-size: 18px; 67 | color: #0e2441; 68 | font-weight: 600; 69 | padding-left: 8px; 70 | margin-bottom: 8px; 71 | 72 | &::after { 73 | position: absolute; 74 | content: ''; 75 | top: 50%; 76 | left: 0; 77 | width: 3px; 78 | height: 15px; 79 | transform: translate(0, -50%); 80 | background-color: #358cff; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /docs/src/Docs/3.封装特性/组件共享方案2-派发组件.md: -------------------------------------------------------------------------------- 1 | # 派发组件 2 | 3 | ## 💡由来 4 | 5 | 由于主应用document.querySelector可以直接获取到子应用的dom, 有了dom就可以通过createApp来渲染主应用的组件 6 | 7 | 利用此原理,可以让子应用将目标节点id通过事件发送给父应用,在父应用调用createApp来渲染主应用的组件,这样把渲染组件的任务抽离到主应用,就能做到跨框架了,无论子应用用的是什么框架(甚至不用框架),只需要向父应用发送通知,便可以“调用”主应用的组件 8 | 9 | ## 使用场景 10 | 11 | 常用于一些跨框架的业务/复杂组件,例如`成员选择器` / `部门选择器` / `虚拟列表树`,可以在主应用写一套,所有子应用都可以直接使用 12 | 13 | ## 注册 14 | 15 | 例:注册一个 UseSvg 图标组件(vue3), 该组件的原生用法只需要传入一个 name 属性, 值为图标名称 16 | 17 | > 具体参数/方法看对应组件源码或文档 18 | 19 | ```TSX 20 | // 主应用main.ts 初始化微前端时加入需要派发的组件 21 | MicroAppInit({ 22 | /** ...其它配置 */ 23 | MicroComponentMap: { 24 | /** 注册UseSvg组件 */ 25 | UseSvg: () => import('@/components/use-svg/index.vue'), // 传入导入函数 26 | // UseSvg: UseSvg, // 也可以直接传一个组件过去 27 | }, 28 | }); 29 | ``` 30 | 31 | ## 使用 32 | 33 | 主应用注册的组件可以在任意子应用中使用, 目前已兼容 vue2/vue3/react 34 | 35 | ```TSX 36 | import MicroComponent from 'micro-app-utils/react18/MicroComponent'; 37 | 38 | // _开头的属性是配置相关的, 用_is而不是is是为了兼容vue2, 同时考虑可能存在的重名问题 39 | 40 | 41 | // 派发组件在写法上参考了动态组件的写法 42 | 43 | ``` 44 | 45 | ## 潜在的问题 46 | 47 | 1. 不同框架的作用域插槽的兼容方案有差异,可能有潜在bug 48 | 49 | 2. 不同框架的写法略有不同,比如双向绑定 50 | 51 | 52 | ## 注意事项 53 | 54 | ### vue2 55 | 56 | 1. vue2 使用 MicroComponent 默认插槽时, 外层必须用``包裹, 否则响应式会丢失(原因未知) 57 | 2. 双向绑定 58 | - v-model 对应简写也是 v-model, 完整写法是:modelValue + @update:modelValue 59 | - v-model:show 对应简写是 :show.sync, 完整写法是:show + @update:show 60 | 61 | ### react 62 | 63 | - 插槽通过属性来传递,不要使用 children 来传递默认插槽 64 | - 双向绑定 65 | - react 没有语法糖,所以只能写完整写法,同时事件需要以 on+首字母大写开头 66 | -------------------------------------------------------------------------------- /src/utils/Config.ts: -------------------------------------------------------------------------------- 1 | import { subAppLocation } from 'micro-app-tools'; 2 | 3 | /** 不同环境url包含的字符串, 用于判断当前是测试环境还是正式环境, 要求不能相互匹配!!! */ 4 | const urlSubStringInfo = { 5 | test: 'test.micro-admin-template.lammu.cn', 6 | pre: 'pre.micro-admin-template.lammu.cn', 7 | master: 'micro-admin-template.lammu.cn', 8 | }; 9 | 10 | /** 不同环境对应的origin */ 11 | const originMap = { 12 | test: 'https://test.micro-admin-template.lammu.cn', 13 | pre: 'https://pre.micro-admin-template.lammu.cn', 14 | master: 'https://micro-admin-template.lammu.cn', 15 | }; 16 | 17 | export default { 18 | /** 是否本地开发环境 */ 19 | isLocalhost: import.meta.env.DEV, 20 | /** 是否测试环境 */ 21 | get isTest() { 22 | return this.env === 'test'; 23 | }, 24 | /** 是否预发环境 */ 25 | get isPre() { 26 | return this.env === 'pre'; 27 | }, 28 | /** 是否生产环境 */ 29 | get isMaster() { 30 | return this.env === 'master'; 31 | }, 32 | /** hostname */ 33 | hostname: subAppLocation?.hostname, 34 | /** 不同环境origin */ 35 | originMap: originMap, 36 | /** token名称 */ 37 | get tokenKey() { 38 | return 'tokenKey'; 39 | }, 40 | /** 运行环境 */ 41 | get env(): 'localhost' | 'test' | 'pre' | 'master' { 42 | const hostname = subAppLocation.hostname; 43 | if (this.isLocalhost) { 44 | return 'localhost'; 45 | } else if (hostname.includes(urlSubStringInfo['test'])) { 46 | return 'test'; 47 | } else if (hostname.includes(urlSubStringInfo['pre'])) { 48 | return 'pre'; 49 | } else if (hostname.includes(urlSubStringInfo['master'])) { 50 | return 'master'; 51 | } else { 52 | console.error('判断不出当前代码运行环境,请检查配置!!!'); 53 | return 'localhost'; 54 | } 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /docs/src/public/svgs/Node.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/tinymce/plugins/preview/plugin.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE version 7.1.2 (TBD) 3 | */ 4 | !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=e=>t=>t.options.get(e),i=n("content_style"),s=n("content_css_cors"),c=n("body_class"),r=n("body_id");e.add("preview",(e=>{(e=>{e.addCommand("mcePreview",(()=>{(e=>{const n=(e=>{var n;let l="";const a=e.dom.encode,d=null!==(n=i(e))&&void 0!==n?n:"";l+='';const m=s(e)?' crossorigin="anonymous"':"";o.each(e.contentCSS,(t=>{l+='"})),d&&(l+='");const y=r(e),u=c(e),v=' 53 | 54 | 73 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | MicroAdmin - 微后台 9 | 10 | 11 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "micro-admin docs" 7 | text: "微前端后台模板" 8 | tagline: 基于京东micro-app微前端框架的后台模板,让你更快上手微前端,支持vue3/vue2/react子应用 9 | image: 10 | src: /images/1-1.png 11 | alt: x 12 | actions: 13 | - theme: alt 14 | text: ⚡快速开始 15 | link: /Docs/1.指南/微前端介绍 16 | - theme: brand 17 | text: 🐛常见问题总结 18 | link: /Docs/常见问题总结/常见问题 19 | - theme: alt 20 | text: 🚧历史更新 21 | link: /About/history 22 | - theme: brand 23 | text: 🔗在线预览 24 | link: https://micro-admin-template.lammu.cn/micromain/introduce 25 | 26 | features: 27 | - icon: 28 | src: /favicon.ico 29 | title: MicroApp 30 | details: 使用京东微前端框架,更稳定,支持特性更多 31 | - icon: 32 | src: /svgs/RouterComponent.svg 33 | title: 组件共享1-路由组件 34 | details: 利用微前端支持应用嵌套的特性,将组件注册成路由供其它应用使用(跨框架组件) 35 | - icon: 36 | src: /svgs/DispatchComponent.svg 37 | title: 组件共享2-派发组件 38 | details: 利用微前端支持应用嵌套的特性,将组件注册成路由供其它应用使用(跨框架组件) 39 | - icon: 40 | src: /svgs/frame-less-ui.svg 41 | title: 组件共享3-跨框架组件库 42 | details: frame-less-ui组件库使用vue3开发web componenet,产物可在vue3/vue2/react/原生环境下使用 43 | - icon: 44 | src: /svgs/UnoCSS.svg 45 | title: UnoCSS 46 | details: 样式兼容移动端 47 | - icon: 48 | src: /svgs/Vue.svg 49 | title: Vue3 50 | details: 优先支持Vue3 / Vue2 / React18, 主应用使用的是Vue3 51 | - icon: 52 | src: /svgs/Vite.svg 53 | title: Vite 54 | details: 优先支持Vite / webpack, 主应用使用的是Vite 55 | - icon: 56 | src: /svgs/React.svg 57 | title: React组件渲染器 58 | details: vue环境下可直接渲染react组件 59 | 60 | - icon: 61 | src: /svgs/JS.svg 62 | title: 项目拆分 63 | details: 单一项目可拆分成多个子应用,减少单模块代码体积,子应用可使用不同技术栈,方便集成其它技术栈应用 64 | - icon: 65 | src: /svgs/TS.svg 66 | title: 低代码与源代码混合开发 67 | details: 支持使用低代码或源代码进行混合开发,相互通信 68 | --- 69 | 70 | 71 | ### iframe在线预览 72 | 73 | > 也点击右上角「在线预览↗」进行全屏预览 74 | 75 |