├── example
├── snapshot.png
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ └── logo.png
│ ├── views
│ │ ├── bar
│ │ │ └── index.vue
│ │ ├── foo
│ │ │ └── index.vue
│ │ ├── base
│ │ │ ├── elem
│ │ │ │ ├── index.vue
│ │ │ │ ├── EleDrawerExample.vue
│ │ │ │ └── EleExample.vue
│ │ │ ├── antd
│ │ │ │ ├── index.vue
│ │ │ │ ├── AntdDrawerExample.vue
│ │ │ │ └── AntdExample.vue
│ │ │ └── view
│ │ │ │ ├── index.vue
│ │ │ │ ├── ViewDrawerExample.vue
│ │ │ │ └── ViewExample.vue
│ │ └── match
│ │ │ └── index.vue
│ ├── components
│ │ ├── ExampleCard.vue
│ │ ├── Footer.vue
│ │ ├── Title.vue
│ │ └── HelloWorld.vue
│ ├── router.js
│ ├── main.js
│ └── App.vue
├── babel.config.js
├── vue.config.js
├── .eslintrc.js
├── .gitignore
├── package.json
└── README.md
├── babel.config.js
├── docs
├── .vuepress
│ ├── public
│ │ └── logo.png
│ └── config.js
├── README.md
└── guide
│ ├── README.md
│ ├── api.md
│ ├── install.md
│ ├── micro.md
│ └── use.md
├── micro-frontend-example
├── sub-app1
│ ├── .browserslistrc
│ ├── babel.config.js
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── assets
│ │ │ └── logo.png
│ │ ├── views
│ │ │ ├── 404.vue
│ │ │ ├── About.vue
│ │ │ └── Home.vue
│ │ ├── App.vue
│ │ ├── router
│ │ │ └── index.js
│ │ ├── main.js
│ │ └── components
│ │ │ └── HelloWorld.vue
│ ├── .gitignore
│ ├── .eslintrc.js
│ ├── package.json
│ └── vue.config.js
└── sub-app2
│ ├── .browserslistrc
│ ├── babel.config.js
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── assets
│ │ └── logo.png
│ ├── views
│ │ ├── 404.vue
│ │ ├── Home.vue
│ │ └── About.vue
│ ├── App.vue
│ ├── store
│ │ └── index.js
│ ├── components
│ │ └── HelloWorld.vue
│ ├── router
│ │ └── index.js
│ └── main.js
│ ├── .gitignore
│ ├── .eslintrc.js
│ ├── package.json
│ └── vue.config.js
├── README.md
├── .gitignore
├── rollup.config.js
├── src
├── modifyOptions.js
├── createCreateSlot.js
├── getSlotPayload.js
├── setGlobalHeader.js
├── locationMatcher.js
├── event.js
├── index.js
├── drawer.js
└── modal.js
├── deploy.sh
├── package.json
└── index.d.ts
/example/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/example/snapshot.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/example/src/assets/logo.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/docs/.vuepress/public/logo.png
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 5 versions
3 | Firefox ESR
4 | not dead
5 | not ie < 11
6 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 5 versions
3 | Firefox ESR
4 | not dead
5 | not ie < 11
6 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/example/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | publicPath:
3 | process.env.NODE_ENV === 'production' ? '/vue-create-dm/example/' : '/',
4 | };
5 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/micro-frontend-example/sub-app1/public/favicon.ico
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/micro-frontend-example/sub-app1/src/assets/logo.png
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/micro-frontend-example/sub-app2/public/favicon.ico
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hzyhbk/vue-create-dm/HEAD/micro-frontend-example/sub-app2/src/assets/logo.png
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 404
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 404
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 | 我是app2
3 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/example/src/views/bar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | page2
4 |
5 |
6 |
7 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | },
6 | extends: ['plugin:vue/essential', 'eslint:recommended'],
7 | rules: {
8 | 'no-debugger': 'off',
9 | 'no-empty': 'off',
10 | },
11 | parserOptions: {
12 | parser: 'babel-eslint',
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example/src/components/ExampleCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/src/views/foo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
我是foo页面
4 |
5 |
6 |
7 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | package-lock.json
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | package-lock.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-create-dm
2 |
3 | [](https://www.npmjs.com/package/vue-create-dm)
4 |
5 | 使用函数优雅地创建弹框抽屉
6 |
7 | ## [文档地址](https://hzyhbk.github.io/vue-create-dm/)
8 |
9 | ## [在线例子地址](https://hzyhbk.github.io/vue-create-dm/example/#/antd)
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | docs/.vuepress/dist
5 | docs/.vuepress/public/example
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | pnpm-debug.log*
16 |
17 | # Editor directories and files
18 | .idea
19 | .vscode
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | *.tgz
26 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 |
4 | Vue.use(Vuex);
5 |
6 | const store = new Vuex.Store({
7 | state: {
8 | count: 0,
9 | },
10 | mutations: {
11 | increment(state) {
12 | state.count++;
13 | },
14 | decrement(state) {
15 | state.count--;
16 | },
17 | },
18 | });
19 |
20 | export default store;
21 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | const { uglify } = require('rollup-plugin-uglify');
2 | const babel = require('rollup-plugin-babel');
3 |
4 | module.exports = {
5 | input: 'src/index.js',
6 | output: {
7 | file: 'dist/vue-create-dm.umd.min.js',
8 | name: 'vueCreateDM',
9 | format: 'umd',
10 | exports: 'named',
11 | },
12 | plugins: [
13 | babel({ runtimeHelpers: true, exclude: 'node_modules/**' }),
14 | uglify(),
15 | ],
16 | };
17 |
--------------------------------------------------------------------------------
/src/modifyOptions.js:
--------------------------------------------------------------------------------
1 | // arg1 {} arg2 ""
2 | // arg1 "" arg2 {}
3 | // return location "" coverBaseOption {}
4 | export function modifyOptions(baseOption, arg1 = {}, arg2 = '') {
5 | let coverBaseOption = arg1;
6 | let location = arg2;
7 | if (typeof arg1 === 'string') {
8 | location = arg1;
9 | coverBaseOption = arg2;
10 | }
11 | return {
12 | location,
13 | baseOption: { ...baseOption, ...coverBaseOption },
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
16 | 'vue/no-parsing-error': [2, { "x-invalid-end-tag": false }]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
16 | 'vue/no-parsing-error': [2, { "x-invalid-end-tag": false }]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我的子应用二的About页面
4 |
count:{{ $store.state.count }}
5 |
6 |
7 |
8 |
9 |
23 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{msg}}
4 |
5 |
6 |
7 |
15 |
16 |
17 |
33 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | Group title
7 |
8 |
9 | More
10 | Inner Card content
11 |
12 |
13 | More
14 | Inner Card content
15 |
16 |
17 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/example/src/views/base/elem/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/src/views/base/antd/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/src/views/base/view/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # 当发生错误时中止脚本
4 | set -e
5 |
6 | # cd到example
7 | cd example
8 |
9 | # 构建
10 | npm run build
11 |
12 | #复制 dist 到 docs 下
13 | cp -Rf dist/* ../docs/.vuepress/public/example/
14 |
15 | # 执行完退出
16 | cd -
17 |
18 | # 构建
19 | npm run docs:build
20 |
21 | # cd 到构建输出的目录下
22 | cd docs/.vuepress/dist
23 |
24 | # 部署到自定义域域名
25 | # echo 'www.example.com' > CNAME
26 |
27 | git init
28 | git add -A
29 | git commit -m 'deploy'
30 |
31 | # 部署到 https://.github.io
32 | # git push -f git@github.com:/.github.io.git master
33 |
34 | # 部署到 https://.github.io/
35 | git push -f git@github.com:hzyhbk/vue-create-dm.git master:gh-pages
36 |
37 | cd -
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import foo from './views/foo/index.vue';
4 | import bar from './views/bar/index.vue';
5 | import antd from './views/base/antd/index.vue';
6 | import view from './views/base/view/index.vue';
7 | import elem from './views/base/elem/index.vue';
8 | import match from './views/match/index.vue';
9 |
10 | Vue.use(VueRouter);
11 | const routes = [
12 | { path: '/foo', component: foo },
13 | { path: '/bar', component: bar },
14 | { path: '/antd', component: antd },
15 | { path: '/view', component: view },
16 | { path: '/elem', component: elem },
17 | { path: '/match', component: match },
18 | ];
19 | const router = new VueRouter({ routes });
20 |
21 | export default router;
22 |
--------------------------------------------------------------------------------
/example/src/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 自定义footer
4 |
{{ cancelText }}
5 |
6 |
{{
7 | okText
8 | }}
9 |
10 |
11 |
12 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | base: '/vue-create-dm/',
3 | title: 'VueCreateDM',
4 | themeConfig: {
5 | nav: [
6 | {
7 | text: '在线地址',
8 | link: 'https://hzyhbk.github.io/vue-create-dm/example/#/antd',
9 | },
10 | {
11 | text: 'Github',
12 | link: 'https://github.com/hzyhbk/vue-create-dm',
13 | },
14 | {
15 | text: 'ReactCreateDM',
16 | link: 'https://hzyhbk.github.io/react-create-dm',
17 | },
18 | ],
19 | sidebar: {
20 | '/guide/': [
21 | {
22 | title: '指南',
23 | collapsable: false,
24 | children: ['', 'install', 'use', 'api'],
25 | },
26 | {
27 | title: '进阶',
28 | collapsable: false,
29 | children: ['micro'],
30 | },
31 | ],
32 | },
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import Home from '../views/Home.vue';
4 |
5 | Vue.use(VueRouter);
6 |
7 | const routes = [
8 | {
9 | path: '/',
10 | name: 'Home',
11 | component: Home,
12 | },
13 | {
14 | path: '/about',
15 | name: 'About',
16 | // route level code-splitting
17 | // this generates a separate chunk (about.[hash].js) for this route
18 | // which is lazy-loaded when the route is visited.
19 | component: () =>
20 | import(/* webpackChunkName: "about" */ '../views/About.vue'),
21 | },
22 | {
23 | path: '*',
24 | component: () => import(/* webpackChunkName: "404" */ '../views/404.vue'),
25 | },
26 | ];
27 |
28 | const router = new VueRouter({
29 | mode: 'history',
30 | base: '/sub1',
31 | routes,
32 | });
33 |
34 | export default router;
35 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import Home from '../views/Home.vue';
4 |
5 | Vue.use(VueRouter);
6 |
7 | const routes = [
8 | {
9 | path: '/',
10 | name: 'Home',
11 | component: Home,
12 | },
13 | {
14 | path: '/about',
15 | name: 'About',
16 | // route level code-splitting
17 | // this generates a separate chunk (about.[hash].js) for this route
18 | // which is lazy-loaded when the route is visited.
19 | component: () =>
20 | import(/* webpackChunkName: "about" */ '../views/About.vue'),
21 | },
22 | {
23 | path: '*',
24 | component: () => import(/* webpackChunkName: "404" */ '../views/404.vue'),
25 | },
26 | ];
27 |
28 | const router = new VueRouter({
29 | mode: 'history',
30 | base: '/sub2',
31 | routes,
32 | });
33 |
34 | export default router;
35 |
--------------------------------------------------------------------------------
/example/src/components/Title.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
{{ title }}
7 |
hahahahahhahahha
8 |
9 |
10 |
11 |
35 |
36 |
47 |
--------------------------------------------------------------------------------
/src/createCreateSlot.js:
--------------------------------------------------------------------------------
1 | export function createDrawerSlot(createElement, slotVnMap, close) {
2 | return (options, slot = 'default') => {
3 | const slotElement = createElement(options.template, {
4 | ...options,
5 | on: {
6 | ...options.on,
7 | close,
8 | },
9 | slot,
10 | });
11 | slotVnMap[slot] = slotElement;
12 | return slotElement;
13 | };
14 | }
15 | export function createModalSlot(
16 | createElement,
17 | slotVnMap,
18 | confirmLoading,
19 | close,
20 | ok
21 | ) {
22 | return (options, slot = 'default') => {
23 | const slotElement = createElement(options.template, {
24 | ...options,
25 | props: {
26 | ...options.props,
27 | confirmLoading,
28 | },
29 | on: {
30 | ...options.on,
31 | close,
32 | ok,
33 | },
34 | slot,
35 | });
36 | slotVnMap[slot] = slotElement;
37 | return slotElement;
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-app1",
3 | "version": "0.1.0",
4 | "license": "ISC",
5 | "private": true,
6 | "scripts": {
7 | "serve": "vue-cli-service serve --port 8091",
8 | "build": "vue-cli-service build",
9 | "lint": "vue-cli-service lint"
10 | },
11 | "dependencies": {
12 | "ant-design-vue": "^1.6.5",
13 | "core-js": "^3.6.5",
14 | "single-spa-vue": "^1.8.2",
15 | "vue": "^2.6.11",
16 | "vue-create-dm": "^0.0.20",
17 | "vue-router": "^3.3.4",
18 | "vuex": "^3.4.0"
19 | },
20 | "devDependencies": {
21 | "@vue/cli-plugin-babel": "~4.4.0",
22 | "@vue/cli-plugin-eslint": "~4.4.0",
23 | "@vue/cli-service": "~4.4.0",
24 | "babel-eslint": "^10.1.0",
25 | "eslint": "^6.7.2",
26 | "eslint-plugin-vue": "^6.2.2",
27 | "less": "^3.0.4",
28 | "less-loader": "^5.0.0",
29 | "vue-template-compiler": "^2.6.11",
30 | "webpack-manifest-plugin": "^2.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-app2",
3 | "version": "0.1.0",
4 | "license": "ISC",
5 | "private": true,
6 | "scripts": {
7 | "serve": "vue-cli-service serve --port 8092",
8 | "build": "vue-cli-service build",
9 | "lint": "vue-cli-service lint"
10 | },
11 | "dependencies": {
12 | "ant-design-vue": "^1.6.5",
13 | "core-js": "^3.6.5",
14 | "single-spa-vue": "^1.8.2",
15 | "vue": "^2.6.11",
16 | "vue-create-dm": "^0.0.20",
17 | "vue-router": "^3.3.4",
18 | "vuex": "^3.4.0"
19 | },
20 | "devDependencies": {
21 | "@vue/cli-plugin-babel": "~4.4.0",
22 | "@vue/cli-plugin-eslint": "~4.4.0",
23 | "@vue/cli-service": "~4.4.0",
24 | "babel-eslint": "^10.1.0",
25 | "eslint": "^6.7.2",
26 | "eslint-plugin-vue": "^6.2.2",
27 | "less": "^3.0.4",
28 | "less-loader": "^5.0.0",
29 | "vue-template-compiler": "^2.6.11",
30 | "webpack-manifest-plugin": "^2.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/getSlotPayload.js:
--------------------------------------------------------------------------------
1 | // 获取子组件实例,调用约定的函数,获取返回值
2 | export async function getSlotPayload(slotVnMap, payloadSlot) {
3 | if (payloadSlot) {
4 | let slotInstance;
5 | // 传 true 默认从 default slot 取
6 | if (typeof payloadSlot === 'boolean') {
7 | slotInstance = slotVnMap.default;
8 | }
9 | if (typeof payloadSlot === 'string') {
10 | slotInstance = slotVnMap[payloadSlot];
11 | }
12 | if (slotInstance) {
13 | // 约定 子组件需要提供 providePayload 方法
14 | if (!slotInstance.componentInstance.providePayload) {
15 | console.warn('子组件需要提供 providePayload 方法来返回参数.');
16 | return null;
17 | } else {
18 | const fn = slotInstance.componentInstance.providePayload;
19 | let slotPayload;
20 | if (typeof fn.then === 'function') {
21 | slotPayload = await slotInstance.componentInstance.providePayload();
22 | } else {
23 | slotPayload = slotInstance.componentInstance.providePayload();
24 | }
25 | return slotPayload;
26 | }
27 | }
28 | return null;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | import spVue from 'single-spa-vue';
4 | import router from './router';
5 | import VueCreateDM from 'vue-create-dm';
6 | import Antd, { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
7 | import 'ant-design-vue/dist/antd.css';
8 | Vue.use(Antd);
9 | Vue.use(VueCreateDM, {
10 | antdModal,
11 | antdDrawer,
12 | });
13 |
14 | Vue.config.productionTip = false;
15 |
16 | const vueOptions = {
17 | render: (h) => h(App),
18 | router,
19 | };
20 |
21 | const lifecycles = spVue({
22 | Vue,
23 | appOptions: vueOptions,
24 | });
25 |
26 | export const bootstrap = function (props) {
27 | return lifecycles.bootstrap(props);
28 | };
29 |
30 | export const mount = function (props) {
31 | return lifecycles.mount(props);
32 | };
33 |
34 | export const unmount = function (props) {
35 | return lifecycles.unmount(props);
36 | };
37 |
38 | export const update = function (props) {
39 | console.error(JSON.stringify(props));
40 | return lifecycles.update(props);
41 | };
42 |
43 | export const $router = router;
44 | export const $Vue = new Vue();
45 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/vue.config.js:
--------------------------------------------------------------------------------
1 | const packageName = require('./package.json').name;
2 | const ManifestPlugin = require('webpack-manifest-plugin');
3 |
4 | module.exports = {
5 | publicPath:
6 | process.env.NODE_ENV === 'production'
7 | ? 'http://localhost:8091'
8 | : 'http://localhost:8091',
9 |
10 | configureWebpack: (config) => {
11 | config.output.libraryTarget = 'umd';
12 | config.output.library = packageName;
13 | config.output.jsonpFunction = `webpackJsonp_${packageName}`;
14 |
15 | config.optimization.splitChunks.cacheGroups = {};
16 |
17 | // 外部依赖,通用包从root模块加载
18 | config.externals = ['vue-router', 'vuex'];
19 | config.plugins.push(
20 | new ManifestPlugin({
21 | fileName: 'manifest-initial.json',
22 | filter: function (option) {
23 | return option.isInitial;
24 | },
25 | })
26 | );
27 | },
28 |
29 | devServer: {
30 | headers: {
31 | // 子应用如果与root应用不在同一个域名下,需要devops配置允许跨域
32 | 'Access-Control-Allow-Origin': '*',
33 | },
34 | },
35 |
36 | css: {
37 | extract: false,
38 | },
39 |
40 | // filenameHashing: false
41 | };
42 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/vue.config.js:
--------------------------------------------------------------------------------
1 | const packageName = require('./package.json').name;
2 | const ManifestPlugin = require('webpack-manifest-plugin');
3 |
4 | module.exports = {
5 | publicPath:
6 | process.env.NODE_ENV === 'production'
7 | ? 'http://localhost:8092'
8 | : 'http://localhost:8092',
9 |
10 | configureWebpack: (config) => {
11 | config.output.libraryTarget = 'umd';
12 | config.output.library = packageName;
13 | config.output.jsonpFunction = `webpackJsonp_${packageName}`;
14 |
15 | config.optimization.splitChunks.cacheGroups = {};
16 |
17 | // 外部依赖,通用包从root模块加载
18 | config.externals = ['vue-router', 'vuex'];
19 | config.plugins.push(
20 | new ManifestPlugin({
21 | fileName: 'manifest-initial.json',
22 | filter: function (option) {
23 | return option.isInitial;
24 | },
25 | })
26 | );
27 | },
28 |
29 | devServer: {
30 | headers: {
31 | // 子应用如果与root应用不在同一个域名下,需要devops配置允许跨域
32 | 'Access-Control-Allow-Origin': '*',
33 | },
34 | },
35 |
36 | css: {
37 | extract: false,
38 | },
39 |
40 | // filenameHashing: false
41 | };
42 |
--------------------------------------------------------------------------------
/example/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | import Antd from 'ant-design-vue';
4 | import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
5 | import { Modal as viewModal, Drawer as viewDrawer } from 'view-design';
6 | import { Dialog as eleModal, Drawer as eleDrawer } from 'element-ui';
7 | import Vuex from 'vuex';
8 | import router from './router';
9 | import 'ant-design-vue/dist/antd.css';
10 | import 'view-design/dist/styles/iview.css';
11 | import 'element-ui/lib/theme-chalk/index.css';
12 | import VueCreateDM from '../../src';
13 | import globalHeader from './components/Title';
14 |
15 | Vue.config.productionTip = false;
16 | Vue.use(Antd);
17 | Vue.use(Vuex);
18 | export const store = new Vuex.Store({
19 | state: {
20 | count: 0,
21 | },
22 | mutations: {
23 | increment(state) {
24 | state.count++;
25 | },
26 | },
27 | });
28 | Vue.use(VueCreateDM, {
29 | antdModal,
30 | antdDrawer,
31 | viewModal,
32 | viewDrawer,
33 | eleModal,
34 | eleDrawer,
35 | // store,
36 | // router,
37 | drawerGlobalHeader: globalHeader,
38 | });
39 |
40 | new Vue({
41 | render: (h) => h(App),
42 | store,
43 | router,
44 | }).$mount('#app');
45 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app2/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | import spVue from 'single-spa-vue';
4 | import router from './router';
5 | import store from './store';
6 | import VueCreateDM from 'vue-create-dm';
7 | import Antd, { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
8 | import 'ant-design-vue/dist/antd.css';
9 | Vue.use(Antd);
10 | Vue.use(VueCreateDM, {
11 | antdModal,
12 | antdDrawer,
13 | router,
14 | store,
15 | });
16 |
17 | Vue.config.productionTip = false;
18 |
19 | const vueOptions = {
20 | render: (h) => h(App),
21 | router,
22 | store,
23 | };
24 |
25 | const lifecycles = spVue({
26 | Vue,
27 | appOptions: vueOptions,
28 | });
29 |
30 | export const bootstrap = function(props) {
31 | return lifecycles.bootstrap(props);
32 | };
33 |
34 | export const mount = function(props) {
35 | return lifecycles.mount(props);
36 | };
37 |
38 | export const unmount = function(props) {
39 | return lifecycles.unmount(props);
40 | };
41 |
42 | export const update = function(props) {
43 | console.error(JSON.stringify(props));
44 | return lifecycles.update(props);
45 | };
46 |
47 | export const $router = router;
48 | export const $Vue = new Vue();
49 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-create-dm",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "ant-design-vue": "^1.6.3",
12 | "core-js": "^3.6.5",
13 | "element-ui": "^2.13.2",
14 | "view-design": "^4.3.2",
15 | "vue": "^2.6.11",
16 | "vue-create-dm": "^0.0.9",
17 | "vue-router": "^3.3.4",
18 | "vuex": "^3.5.1"
19 | },
20 | "devDependencies": {
21 | "@vue/cli-plugin-babel": "^4.4.0",
22 | "@vue/cli-plugin-eslint": "^4.4.0",
23 | "@vue/cli-service": "^4.4.0",
24 | "babel-eslint": "^10.1.0",
25 | "eslint": "^6.7.2",
26 | "eslint-plugin-vue": "^6.2.2",
27 | "vue-template-compiler": "^2.6.11"
28 | },
29 | "eslintConfig": {
30 | "root": true,
31 | "env": {
32 | "node": true
33 | },
34 | "extends": [
35 | "plugin:vue/essential",
36 | "eslint:recommended"
37 | ],
38 | "parserOptions": {
39 | "parser": "babel-eslint"
40 | },
41 | "rules": {}
42 | },
43 | "browserslist": [
44 | "> 1%",
45 | "last 2 versions",
46 | "not dead"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | heroText: VueCreateDM
4 | tagline: 使用函数优雅地创建弹框抽屉
5 | actionText: 了解更多 →
6 | actionLink: /guide/
7 | features:
8 | - title: 函数式编程
9 | details: 使用函数优雅地创建弹框抽屉,远离让人头疼的 isXXVisible。
10 | - title: 正常触发生命周期
11 | details: 弹框抽屉子组件的 created、mounted、destoryed 生命周期按照正常逻辑触发。
12 | - title: 与父组件通信
13 | details: 约定子组件提供 providePayload 函数来和创建它的父组件通信。
14 | ---
15 |
16 | ## 当前版本
17 | 
18 | ## 安装
19 |
20 | ``` bash
21 | yarn add vue-create-dm
22 | ```
23 | ## 使用
24 |
25 | ``` js
26 | import Vue from 'vue';
27 | import VueCreateDM from 'vue-create-dm';
28 | import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
29 | import { Modal as viewModal, Drawer as viewDrawer } from 'view-design';
30 | import { Dialog as eleModal, Drawer as eleDrawer } from 'element-ui';
31 | import store from './store'
32 | import router from './router';
33 | import modalGlobalHeader from './components/modalGlobalHeader';
34 | import drawerGlobalHeader from './components/drawerGlobalHeader';
35 |
36 | Vue.use(VueCreateDM, {
37 | antdModal,
38 | antdDrawer,
39 | viewModal,
40 | viewDrawer,
41 | eleModal,
42 | eleDrawer,
43 | store,
44 | router,
45 | modalGlobalHeader,
46 | drawerGlobalHeader,
47 | });
48 | ```
--------------------------------------------------------------------------------
/docs/guide/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 |
3 | 使用函数优雅地创建 [ant-design-vue](https://www.antdv.com/docs/vue/introduce-cn/)、[view-design](https://www.iviewui.com/)、[ElementUI](https://element.eleme.cn/#/zh-CN) 的 Drawer 和 Modal。
4 |
5 | ## 例子
6 | [在线地址](https://hzyhbk.github.io/vue-create-dm/example/#/antd)
7 |
8 | 代码见 `example` 文件夹。
9 |
10 | ## 特性
11 | * 通过函数来创建`Modal`或`Drawer`组件
12 | * `Modal`、`Drawer`的内容子组件的`created`、`mounted`、`destoryed`生命周期按照正常逻辑触发
13 | * `Modal`、`Drawer`支持分别注册全局头部组件(需要接收名为 `title` 的 `props`)
14 | * 支持传入 `title`、`content`、`footer` 插槽
15 | * 支持`Modal`、`Drawer`与父组件通信
16 | * 支持子组件获取 `this.$store` 和 `this.$router`
17 | * 支持传入路由来匹配**内容**组件,
18 | * 若传入`url`比如 https://www.baidu.com ,则以 `iframe`形式展示
19 | * 若传入 `相对路由`(比如 /foo, /bar),则获取匹配的路由组件展示
20 |
21 | ## 为什么
22 | 在使用弹窗抽屉组件的过程中,你是否也曾遇到过以下场景:
23 | 1. 一个项目里有许多的弹窗和抽屉类型的交互,有时甚至一个页面组件里就有许多弹窗和抽屉组件,原生的使用方式是先在父组件中写好弹框抽屉组件,然后通过`visible`变量来控制弹窗的显示隐藏,当弹窗抽屉一多,看着各种`xxVisible`让人感觉很混乱。
24 | 2. 弹窗抽屉内包含的子组件的生命周期并没有按我们预想的逻辑触发,我们想打开弹窗抽屉的时候才触发内容子组件的`created`和`mounted`生命周期,然而实际上却并不是;我们希望关闭的时候可以调用子组件的`destoryed`生命周期,可是目前的UI框架大多只是把组件设置为`display:none`了,并没有完全卸载子组件,
25 | 3. `antd`提供了`destroyOnClose`参数支持关闭时销毁子元素,但也没法解决上面说到的1,2两点问题。组件库虽然也有提供通过函数打开弹窗的方法,但那些都是一些简单的弹框,可配置的参数不多,自由度也不够高。
26 |
27 | 因此就有了`vue-create-dm`这个库,`dm`就是分别取了`Drawer`和`Modal`的第一个字母组合在一起(为什么不是`md`呢,因为`md`是`markdown`的缩写...)。目前内置支持了`ant-design-vue`、`view-design`和`ElementUI`三个组件库的的弹框抽屉组件,并且提供了各种工具函数可以自己支持其他组件库的弹框抽屉组件。
28 |
29 |
--------------------------------------------------------------------------------
/src/setGlobalHeader.js:
--------------------------------------------------------------------------------
1 | export function setGlobalHeader(baseOption, oldOptions) {
2 | //如果有此选项,设置全局header
3 | if (baseOption.globalHeader) {
4 | if (oldOptions.title) {
5 | // title 如果传 string,就用这个作为props
6 | if (typeof oldOptions.title === 'string') {
7 | return {
8 | ...oldOptions,
9 | title: {
10 | template: baseOption.globalHeader,
11 | props: {
12 | // 如果需要更多的props呢
13 | title: oldOptions.title,
14 | },
15 | },
16 | };
17 | }
18 | return oldOptions;
19 | }
20 | // 如果没传 title,而传了 modalProps.title 或 drawerProps.title
21 | // 取这个 title 作为props
22 | if (oldOptions.modalProps && oldOptions.modalProps.title) {
23 | return {
24 | ...oldOptions,
25 | title: {
26 | template: baseOption.globalHeader,
27 | props: {
28 | title: oldOptions.modalProps.title,
29 | },
30 | },
31 | };
32 | }
33 | if (oldOptions.drawerProps && oldOptions.drawerProps.title) {
34 | return {
35 | ...oldOptions,
36 | title: {
37 | template: baseOption.globalHeader,
38 | props: {
39 | title: oldOptions.drawerProps.title,
40 | },
41 | },
42 | };
43 | }
44 | return {
45 | ...oldOptions,
46 | title: {
47 | template: baseOption.globalHeader,
48 | },
49 | };
50 | }
51 | return oldOptions;
52 | }
53 |
--------------------------------------------------------------------------------
/src/locationMatcher.js:
--------------------------------------------------------------------------------
1 | const URL_REG = /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/;
2 |
3 | // 不要直接调用,this会获取不到
4 | export function locationMatcher(location, baseOptions, oldOptions) {
5 | if (location) {
6 | // 直接传链接
7 | if (URL_REG.test(location)) {
8 | return {
9 | ...oldOptions,
10 | content: {
11 | template: 'iframe',
12 | attrs: {
13 | src: location,
14 | frameborder: 0,
15 | },
16 | },
17 | };
18 | }
19 | // 传的是路由地址
20 | let getMatchedComponents;
21 | let router;
22 | // 这里的this是vm实例
23 | if (this.$router) {
24 | getMatchedComponents = this.$router.getMatchedComponents;
25 | router = this.$router;
26 | } else if (baseOptions.router) {
27 | getMatchedComponents = baseOptions.router.getMatchedComponents;
28 | router = baseOptions.router;
29 | }
30 | if (getMatchedComponents) {
31 | // 这里需要调 router 本身的 this
32 | const [template] = getMatchedComponents.call(router, location);
33 | if (template) {
34 | return {
35 | ...oldOptions,
36 | content: {
37 | ...oldOptions.content,
38 | template,
39 | },
40 | };
41 | }
42 | console.warn('[vue-create-dm]: 未匹配到路由对应的组件');
43 | return oldOptions;
44 | }
45 | console.warn(
46 | `[vue-create-dm]: 未获取到 getMatchedComponents 方法,检查是否正确使用 VueRouter 并且将 router 实例传入 VueCreateDM`
47 | );
48 | return oldOptions;
49 | }
50 | return oldOptions;
51 | }
52 |
--------------------------------------------------------------------------------
/docs/guide/api.md:
--------------------------------------------------------------------------------
1 |
2 | # 类型签名
3 |
4 | ```ts
5 | type SlotOption = { template: Component } & VNodeData;
6 |
7 | type ICallbackParams = {
8 | payload: any;
9 | slotPayload: any;
10 | };
11 |
12 | interface IBaseOption {
13 | title?: SlotOption | string;
14 | content?: SlotOption;
15 | beforeClose?: (params: ICallbackParams) => void | Promise;
16 | afterClose?: (params: ICallbackParams) => void | Promise;
17 | }
18 |
19 | interface IModalOption extends IBaseOption {
20 | modalProps: Object;
21 | footer?: SlotOption;
22 | onOk?: (params: ICallbackParams) => Boolean | Promise | void | Promise;
23 | payloadSlot?: boolean | 'default' | 'title' | 'footer';
24 | }
25 |
26 | interface IDrawerOption extends IBaseOption {
27 | drawerProps: Object;
28 | payloadSlot?: boolean | 'default' | 'title';
29 | }
30 |
31 | type IArgObj = {
32 | component: Component;
33 | globalHeader: Component;
34 | router: any;
35 | store: any;
36 | }
37 |
38 | type ICreateModalFn = (options: IModalOption, arg1?: IArgObj, arg2?: String) => VNode
39 | | (options: IModalOption, arg1?: string, arg2?: IArgObj) => VNode
40 |
41 | type ICreateDrawerFn = (options: IDrawerOption, arg1?: IArgObj, arg2?: String) => VNode
42 | | (options: IDrawerOption, arg1?: string, arg2?: IArgObj) => VNode
43 |
44 | declare module 'vue/types/vue' {
45 | interface Vue {
46 | $createAntdModal: ICreateModalFn;
47 | $createAntdDrawer: ICreateDrawerFn
48 | $createViewModal: ICreateModalFn;
49 | $createViewDrawer: ICreateDrawerFn
50 | $createEleModal: ICreateModalFn;
51 | $createEleDrawer: ICreateDrawerFn
52 | }
53 | }
54 | ```
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-create-dm",
3 | "version": "0.0.20",
4 | "author": "hzyhbk",
5 | "main": "./dist/vue-create-dm.umd.min.js",
6 | "files": [
7 | "src",
8 | "dist/*.js",
9 | "index.d.ts"
10 | ],
11 | "scripts": {
12 | "docs:dev": "vuepress dev docs",
13 | "docs:build": "vuepress build docs",
14 | "build": "vue-cli-service build --target lib --inline-vue --name vue-create-dm ./src/index.js",
15 | "build:rollup": "rimraf dist && rollup --config rollup.config.js"
16 | },
17 | "typings": "index.d.ts",
18 | "keywords": [
19 | "ant",
20 | "design",
21 | "antd",
22 | "vue",
23 | "vueComponent",
24 | "component",
25 | "components",
26 | "frontend",
27 | "iview",
28 | "viewui"
29 | ],
30 | "dependencies": {
31 | "core-js": "^3.6.5",
32 | "vue": "^2.6.11"
33 | },
34 | "devDependencies": {
35 | "@vue/cli-plugin-babel": "^4.4.0",
36 | "@vue/cli-plugin-eslint": "^4.4.0",
37 | "@vue/cli-service": "^4.4.0",
38 | "babel-eslint": "^10.1.0",
39 | "eslint": "^6.7.2",
40 | "eslint-plugin-vue": "^6.2.2",
41 | "rollup": "^2.23.0",
42 | "rollup-plugin-babel": "^4.4.0",
43 | "rollup-plugin-uglify": "^6.0.4",
44 | "vue-template-compiler": "^2.6.11",
45 | "vuepress": "^1.5.3"
46 | },
47 | "eslintConfig": {
48 | "root": true,
49 | "env": {
50 | "node": true
51 | },
52 | "extends": [
53 | "plugin:vue/essential",
54 | "eslint:recommended"
55 | ],
56 | "parserOptions": {
57 | "parser": "babel-eslint"
58 | },
59 | "rules": {
60 | "no-unused-vars": "off",
61 | "no-empty": "off",
62 | "no-undef": "off"
63 | }
64 | },
65 | "browserslist": [
66 | "> 1%",
67 | "last 2 versions",
68 | "not dead"
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/docs/guide/install.md:
--------------------------------------------------------------------------------
1 | # 安装
2 | ```bash
3 | yarn add vue-create-dm
4 | ```
5 |
6 | ## 统一注册
7 | ::: warning 注意
8 | 如果要在子组件内获取 `this.$store` 和 `this.$router` 请把 `VueCreateDM` 的注册放到 `Vuex` 和 `VueRouter` 实例生成之后,并且传入这两个实例
9 | :::
10 | ::: warning 注意
11 | 如果要自定义全局头部组件,请传入`modalGlobalHeader`,`drawerGlobalHeader`这两个参数,分别对应`Modal`组件的全局头部和`Drawer`组件的全局头部
12 | :::
13 | 下面演示如何进行全量注册:
14 | ```js
15 | import Vue from 'vue';
16 | import VueCreateDM from 'vue-create-dm';
17 | import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
18 | import { Modal as viewModal, Drawer as viewDrawer } from 'view-design';
19 | import { Dialog as eleModal, Drawer as eleDrawer } from 'element-ui';
20 | import store from './store'
21 | import router from './router';
22 | import modalGlobalHeader from './components/modalGlobalHeader';
23 | import drawerGlobalHeader from './components/drawerGlobalHeader';
24 |
25 | Vue.use(VueCreateDM, {
26 | antdModal,
27 | antdDrawer,
28 | viewModal,
29 | viewDrawer,
30 | eleModal,
31 | eleDrawer,
32 | store,
33 | router,
34 | modalGlobalHeader,
35 | drawerGlobalHeader,
36 | });
37 | ```
38 | ## 单个注册
39 | ::: warning 注意
40 | 如果要在子组件内获取 `this.$store` 和 `this.$router` 请把 `VueCreateDM` 的注册放到 `Vuex` 和 `VueRouter` 实例生成之后,并且传入这两个实例
41 | :::
42 | ::: warning 注意
43 | 如果要自定义全局头部组件,请传入`globalHeader`参数
44 | :::
45 |
46 | 下面演示如何单个注册,**其中`component`属性必传**,其余几个都是可选参数
47 | ```js
48 | import Vue from 'vue';
49 | import store from './store';
50 | import router from './router';
51 | import { createAntdDrawer } from 'vue-create-dm';
52 | import { Drawer } from 'ant-design-vue';
53 | import globalHeader from '../components/globalHeader';
54 |
55 | Vue.use(createAntdDrawer, {
56 | component: Drawer,
57 | router, // 子组件需要用到 this.$router 就传
58 | store // 子组件需要用到 this.$store 就传
59 | globalHeader,
60 | });
61 | ```
62 |
--------------------------------------------------------------------------------
/example/src/views/match/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 抽屉打开百度
5 |
6 |
7 |
8 |
9 | 抽屉打开foo页面
10 |
11 |
12 |
13 |
14 | 弹框打开百度
15 |
16 |
17 |
18 |
19 | 弹框打开foo页面
20 |
21 |
22 |
23 |
24 |
82 |
83 |
89 |
--------------------------------------------------------------------------------
/src/event.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export const event = new Vue();
4 | export const VCDM_OPEN_DRAWER = 'VCDM_OPEN_DRAWER';
5 | export const VCDM_OPEN_MODAL = 'VCDM_OPEN_MODAL';
6 |
7 | function open(item, options, methodName) {
8 | // 加载对应的子应用
9 | item.app().then((res) => {
10 | if (!res.$router || !res.$Vue) {
11 | console.warn(`子应用[${item.name}]请导出 $router 和 $Vue.`);
12 | return;
13 | }
14 | if (!res.$Vue[methodName]) {
15 | console.warn(
16 | `子应用[${item.name}]的 $Vue 上不存在 ${methodName} 方法,请检查子应用导出的$Vue.`
17 | );
18 | return;
19 | }
20 | // 通过导出的 router 实例上的 getMatchedComponents 方法获取到要打开的组件
21 | const [component] = res.$router.getMatchedComponents(options.path);
22 | const params = {
23 | content: {
24 | ...options.content,
25 | template: component,
26 | props: options.content.props || {},
27 | },
28 | };
29 | Object.assign(options, params);
30 | // 调用导出的 $Vue.$createAntdDrawer 或者 $Vue.$createAntdModal
31 | res.$Vue[methodName](options);
32 | });
33 | }
34 |
35 | export function listen(type, microAppConfig, methodName) {
36 | event.$on(type, (options) => {
37 | const { appName, path } = options;
38 | if (!appName) {
39 | console.warn(`请传入子应用名称`);
40 | return;
41 | }
42 | if (!path) {
43 | console.warn(`请传入要打开的子应用页面路由`);
44 | return;
45 | }
46 | microAppConfig.forEach((item) => {
47 | if (item.name === appName) {
48 | open(item, options, methodName);
49 | }
50 | });
51 | });
52 | }
53 | export function trigger(...args) {
54 | event.$emit(...args);
55 | }
56 |
57 | // 监听全局打开抽屉事件
58 | export function listenOpenDrawerAction(microAppConfig, methodName) {
59 | listen(VCDM_OPEN_DRAWER, microAppConfig, methodName);
60 | }
61 | // 监听全局打开弹框事件
62 | export function listenOpenModalAction(microAppConfig, methodName) {
63 | listen(VCDM_OPEN_MODAL, microAppConfig, methodName);
64 | }
65 | export function triggerOpenDrawerAction(props) {
66 | trigger(VCDM_OPEN_DRAWER, props);
67 | }
68 | export function triggerOpenModalAction(props) {
69 | trigger(VCDM_OPEN_MODAL, props);
70 | }
71 |
--------------------------------------------------------------------------------
/docs/guide/micro.md:
--------------------------------------------------------------------------------
1 | # 微前端中使用
2 |
3 | 基于single-spa,vue-create-dm也支持在微前端项目中**跨项目调用抽屉、弹框**。
4 |
5 | :::tip 前提条件
6 | 1. 需要互相调用抽屉、弹框的子项目**必须**都是 vue 技术栈
7 | 2. 并且都安装注册了vue-create-dm
8 | 3. 要跨项目调用的页面注册成路由页面
9 | :::
10 |
11 | ## 示例项目
12 | 参考 [micro-frontend-example](https://github.com/hzyhbk/vue-create-dm/tree/master/micro-frontend-example) 文件夹
13 |
14 | ## 主项目使用
15 | 1. 注册子应用的方式需要改成config配置文件的方式。
16 | ```js
17 | const config = [{
18 | name: 'sub1',
19 | app: () => loadSubApp('sub1', 'http://localhost:8081/manifest-initial.json'),
20 | activeWhen: (location) => location.pathname.startsWith('/sub1'),
21 | customProps: {
22 | domElement: '#app-sub-wrapper',
23 | },
24 | }]
25 | export default config;
26 | ```
27 | 2. main.js加入如下代码
28 | ```js
29 | import { listenOpenDrawerAction, triggerOpenDrawerAction } from 'vue-create-dm';
30 | import config from './micro-frontend/config/config';
31 | // 监听打开抽屉事件
32 | listenOpenDrawerAction(config, '$createAntdDrawer');
33 | // 子项目调用打开抽屉的函数
34 | window.triggerOpenDrawerAction = triggerOpenDrawerAction;
35 | ```
36 | ## 子项目使用
37 | 1. 除了 single-spa 要求导出的生命周期方法之外,子项目的`main.js`需要额外导出一个`router`实例和一个空的`vue`实例。
38 | ```js
39 | import Vue from 'vue';
40 | import router from './router';
41 | import store from './store';
42 | import VueCreateDM from 'vue-create-dm';
43 | import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
44 |
45 | Vue.use(VueCreateDM, {
46 | antdDrawer,
47 | antdModal,
48 | router,
49 | store,
50 | });
51 |
52 | export const bootstrap = function(){...}
53 | export const mount = function(){...}
54 | export const unmount = function(){...}
55 | export const update = function(){...}
56 | // 额外导出
57 | export const $router = router;
58 | export const $Vue = new Vue();
59 | ```
60 | 2. 需要被跨项目调用的组件,请在子项目的VueRouter路由配置文件中声明。
61 | 3. 子项目**触发**`openDrawer`、`openModal`事件
62 | ```js
63 | window.triggerOpenDrawerAction({
64 | appName: 'sub2',
65 | path: '/about',
66 | drawerProps: {
67 | title: '子应用二抽屉',
68 | width: '50%',
69 | },
70 | content: {},
71 | });
72 | ```
73 | ## API
74 | ```ts
75 | type IBaseTriggerOption = {
76 | appName: string; // 子应用名称
77 | path: string; // 子应用要被打开的页面
78 | }
79 | type IModalTriggerOption = IBaseTriggerOption & IModalOption;
80 | type IDrawerTriggerOption = IBaseTriggerOption & IDrawerOption;
81 |
82 | ```
--------------------------------------------------------------------------------
/example/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | VueCreateDM
5 |
11 |
12 | 基础示例
13 |
14 |
15 | Ant Design
16 |
17 |
18 |
19 | View UI
20 |
21 |
22 |
23 | Element UI
24 |
25 |
26 |
27 |
28 |
29 | 路由匹配
30 |
31 |
32 |
33 |
34 |
35 |
36 | {{ getSrcPath() }}
37 |
38 |
39 |
48 |
49 |
50 |
51 |
52 |
53 |
72 |
98 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | createAntdDrawer,
3 | createViewDrawer,
4 | createDrawer,
5 | createEleDrawer,
6 | } from './drawer';
7 | import {
8 | createAntdModal,
9 | createViewModal,
10 | createModal,
11 | createEleModal,
12 | } from './modal';
13 | import { getSlotPayload } from './getSlotPayload';
14 | import { createDrawerSlot, createModalSlot } from './createCreateSlot';
15 | import { locationMatcher } from './locationMatcher';
16 | import { setGlobalHeader } from './setGlobalHeader';
17 | import { modifyOptions } from './modifyOptions';
18 | import {
19 | event,
20 | VCDM_OPEN_DRAWER,
21 | VCDM_OPEN_MODAL,
22 | listen,
23 | trigger,
24 | listenOpenDrawerAction,
25 | listenOpenModalAction,
26 | triggerOpenDrawerAction,
27 | triggerOpenModalAction,
28 | } from './event';
29 |
30 | export {
31 | createAntdDrawer,
32 | createAntdModal,
33 | createViewDrawer,
34 | createViewModal,
35 | createEleDrawer,
36 | createEleModal,
37 | getSlotPayload,
38 | createDrawerSlot,
39 | createModalSlot,
40 | locationMatcher,
41 | setGlobalHeader,
42 | modifyOptions,
43 | createDrawer,
44 | createModal,
45 | VCDM_OPEN_DRAWER,
46 | VCDM_OPEN_MODAL,
47 | event,
48 | listen,
49 | trigger,
50 | listenOpenDrawerAction,
51 | listenOpenModalAction,
52 | triggerOpenDrawerAction,
53 | triggerOpenModalAction,
54 | };
55 |
56 | export default {
57 | install(
58 | Vue,
59 | {
60 | antdModal,
61 | antdDrawer,
62 | viewModal,
63 | viewDrawer,
64 | eleModal,
65 | eleDrawer,
66 | modalGlobalHeader,
67 | drawerGlobalHeader,
68 | ...restOptions
69 | }
70 | ) {
71 | const modalComponents = [
72 | {
73 | component: antdModal,
74 | plugin: createAntdModal,
75 | },
76 | {
77 | component: viewModal,
78 | plugin: createViewModal,
79 | },
80 |
81 | {
82 | component: eleModal,
83 | plugin: createEleModal,
84 | },
85 | ];
86 | const drawerComponents = [
87 | {
88 | component: antdDrawer,
89 | plugin: createAntdDrawer,
90 | },
91 | {
92 | component: viewDrawer,
93 | plugin: createViewDrawer,
94 | },
95 | {
96 | component: eleDrawer,
97 | plugin: createEleDrawer,
98 | },
99 | ];
100 |
101 | modalComponents.forEach((cpt) => {
102 | if (cpt.component) {
103 | cpt.plugin.install(Vue, {
104 | component: cpt.component,
105 | globalHeader: modalGlobalHeader,
106 | ...restOptions,
107 | });
108 | }
109 | });
110 | drawerComponents.forEach((cpt) => {
111 | if (cpt.component) {
112 | cpt.plugin.install(Vue, {
113 | component: cpt.component,
114 | globalHeader: drawerGlobalHeader,
115 | ...restOptions,
116 | });
117 | }
118 | });
119 | },
120 | };
121 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
打开普通抽屉
4 |
5 |
6 |
7 | 抽屉打开子应用二的页面
8 |
9 |
10 |
11 |
12 | 弹框打开子应用二的页面
13 |
14 |
15 |
16 | Cloud Database
17 |
18 |
19 | Prepaid
20 |
21 |
22 | YES
23 |
24 |
25 | 2018-04-24 18:00:00
26 |
27 |
28 | 2019-04-24 18:00:00
29 |
30 |
31 |
32 |
33 |
34 | $80.00
35 |
36 |
37 | $20.00
38 |
39 |
40 | $60.00
41 |
42 |
43 | Data disk type: MongoDB
44 |
45 | Database version: 3.4
46 |
47 | Package: dds.mongo.mid
48 |
49 | Storage space: 10 GB
50 |
51 | Replication factor: 3
52 |
53 | Region: East China 1
54 |
55 |
56 |
57 |
58 |
59 |
105 |
106 |
--------------------------------------------------------------------------------
/micro-frontend-example/sub-app1/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
子应用一的页面
4 |
{{ msg }}
5 |
6 | For a guide and recipes on how to configure / customize this project,
7 | check out the
8 | vue-cli documentation.
11 |
12 |
Installed CLI Plugins
13 |
31 |
Essential Links
32 |
55 |
Ecosystem
56 |
87 |
88 |
89 |
90 |
98 |
99 |
100 |
116 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # 通过函数创建 ant-design-vue 或者 view-design 的 drawer 和 modal
2 |
3 | ## 功能亮点
4 | - [x] 通过函数来创建 Modal 或者 Drawer 组件
5 | - [x] Modal、Drawer 的内容子组件的 created、mounted 生命周期按照正常逻辑触发
6 | - [x] 支持传入 title、content、footer 插槽
7 | - [x] 支持 Modal、Drawer 与父应用通信
8 |
9 | 
10 |
11 | ## createAntdDrawer、createViewDrawer
12 |
13 | * 会往 title 和 content 子组件内传入一个名为 close 的函数,在子组件内使用 `this.$emit('close', payload)` 触发关闭行为。
14 | * 如果传入 **payloadSlot** 参数,并且相应的子组件存在 `providePayload` 方法,在抽屉关闭时,会先调用此函数获取返回值,然后作为 `beforeClose` 和 `afterClose` 方法的参数传入
15 |
16 | ```js
17 | createAntdDrawer({
18 | // antd drawer 的所有props
19 | drawerProps: {},
20 | /*
21 | * 不传 或者 传false
22 | * 传 'title' 调用 标题子组件 的 providePayload 方法
23 | * 传 true 或者 'default' 调用 内容子组件 的 providePayload 方法
24 | */
25 | payloadSlot: true | false | 'title' | 'default',
26 | // title slot
27 | // drawerProps.title 和 title 同时存在的话,优先使用 title
28 | title: {
29 | template: Title,
30 | props: {
31 | title: '自定义标题组件',
32 | },
33 | },
34 | // default slot
35 | content: {
36 | template: HelloWorld,
37 | props: {
38 | msg: 'Welcome to Your Vue.js App',
39 | },
40 | },
41 | // 关闭前调用 支持async
42 | beforeClose: function({ payload, slotPayload }) {
43 | console.log('我要关闭了');
44 | },
45 | // 关闭后调用 支持async
46 | afterClose: function({ payload, slotPayload }) {
47 | console.log('我已经关闭了');
48 | },
49 | });
50 | ```
51 |
52 | ## createAntdModal、createViewModal
53 |
54 | * 会往 title、content 和 foter 子组件内传入 close 和 ok 函数和 confirmLoading props,在子组件内使用 `this.$emit('close', payload)` 触发关闭动作, 使用 `this.$emit('ok', payload)` 触发 onOk 回调。子组件内可以使用 confirmLoading 来管理确定按钮的 loading 状态
55 | * 如果传入 **payloadSlot** 参数,并且相应的子组件如果存在 `providePayload` 方法,在点击确定按钮时,会先调用此函数获取返回值,然后作为 `onOk` 方法的一个参数传入
56 |
57 | ```js
58 | createAntdModal({
59 | // antd modal 的所有props
60 | modalProps: {},
61 | /*
62 | * 不传 或者 传 false,不调用
63 | * 传 'title' 调用 标题子组件 的 providePayload 方法
64 | * 传 'footer' 调用 页脚子组件 的 providePayload 方法
65 | * 传 true 或者 'default' 调用 内容子组件 的 providePayload 方法
66 | */
67 | payloadSlot: true | false | 'title' | 'footer' | 'default',
68 | // title slot
69 | // modalProps.title 和 title 同时存在的话,优先使用 title
70 | title: {
71 | template: Title,
72 | props: {
73 | title: '自定义标题组件',
74 | },
75 | },
76 | // content slot
77 | content: {
78 | template: HelloWorld,
79 | props: {
80 | msg: 'Welcome to Your Vue.js App',
81 | },
82 | },
83 | // footer slot
84 | footer: {
85 | template: Footer,
86 | props: {
87 | cancelText: 'hahah取消',
88 | okText: 'hahah确定',
89 | },
90 | },
91 | // 关闭前调用 支持async
92 | beforeClose: function() {
93 | console.log('我要关闭了');
94 | },
95 | // 关闭后调用 支持async
96 | afterClose: function() {
97 | console.log('我已经关闭了');
98 | },
99 | // 确定按钮回调 支持async
100 | async onOk({ payload, slotPayload}) {
101 | await new Promise((resolve) => {
102 | setTimeout(() => {
103 | console.log('点了确定');
104 | resolve();
105 | }, 3000);
106 | });
107 | },
108 | });
109 | ```
--------------------------------------------------------------------------------
/example/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
{{ msg }}
5 |
6 | For a guide and recipes on how to configure / customize this project,
7 | check out the
8 | vue-cli documentation.
11 |
12 |
Installed CLI Plugins
13 |
31 |
Essential Links
32 |
55 |
Ecosystem
56 |
87 |
88 |
89 |
90 |
109 |
110 |
111 |
127 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Component,
3 | VNodeData,
4 | VNode,
5 | CreateElement,
6 | PluginFunction,
7 | } from 'vue';
8 | import { Vue as _Vue } from 'vue/types/vue';
9 |
10 | export type IPluginOption = {
11 | antdModal: Component;
12 | antdDrawer: Component;
13 | viewModal: Component;
14 | viewDrawer: Component;
15 | eleModal: Component;
16 | eleDrawer: Component;
17 | router: any;
18 | store: any;
19 | modalGlobalHeader: Component;
20 | drawerGlobalHeader: Component;
21 | };
22 |
23 | export type SlotOption = { template: Component } & VNodeData;
24 |
25 | export type ICallbackParams = {
26 | payload: any;
27 | slotPayload: any;
28 | };
29 |
30 | export interface IBaseOption {
31 | title?: SlotOption | string;
32 | content?: SlotOption;
33 | beforeClose?: (params: ICallbackParams) => void | Promise;
34 | afterClose?: (params: ICallbackParams) => void | Promise;
35 | }
36 | export interface IBaseCreateOption {
37 | component: Component;
38 | globalHeader: Component;
39 | titleSlotName: string; //原来组件提供的标题插槽名称
40 | visiblePropName: string; //原来控制抽屉组件显隐的属性名称
41 | router: any;
42 | store: any;
43 | }
44 | export interface ICreateDrawerOptions extends IBaseCreateOption {
45 | closeCbName: string; // 原来组件的关闭回调事件名称
46 | }
47 | export interface IDrawerOption extends IBaseOption {
48 | drawerProps: { [k: string]: any };
49 | payloadSlot?: boolean | 'default' | 'title';
50 | }
51 | export type createDrawer = (
52 | Vue: _Vue,
53 | createOptions: ICreateDrawerOptions,
54 | options: IDrawerOption
55 | ) => VNode;
56 |
57 | export interface IModalOption extends IBaseOption {
58 | modalProps: { [k: string]: any };
59 | footer?: SlotOption;
60 | onOk?: (params: ICallbackParams) => void | Promise;
61 | payloadSlot?: boolean | 'default' | 'title' | 'footer';
62 | }
63 | export interface ICreateModalOptions extends IBaseCreateOption {
64 | footerSlotName: string; //原来组件提供的footer插槽名称
65 | visiblePropName: string; //原来控制弹框组件显隐的属性名称
66 | cancelCbName: string; // 原来组件的关闭回调事件名称
67 | okCbName: string; // 原来组件的确定回调事件名称
68 | }
69 | export type createModal = (
70 | Vue: _Vue,
71 | createOptions: ICreateModalOptions,
72 | options: IModalOption
73 | ) => VNode;
74 |
75 | export type getSlotPayload = (
76 | slotVnMap: { [k: string]: VNode },
77 | payloadSlot: string
78 | ) => any;
79 |
80 | export type createDrawerSlot = (
81 | createElement: CreateElement,
82 | slotVnMap: { [k: string]: VNode },
83 | close: Promise
84 | ) => (option: SlotOption, slot: string) => VNode;
85 |
86 | export type createModalSlot = (
87 | createElement: CreateElement,
88 | slotVnMap: { [k: string]: VNode },
89 | confirmLoading: boolean,
90 | close: Promise,
91 | ok: Promise
92 | ) => (option: SlotOption, slot: string) => VNode;
93 |
94 | declare module 'vue/types/vue' {
95 | interface Vue {
96 | $createAntdModal: (options: IModalOption, location: string) => VNode;
97 | $createAntdDrawer: (options: IDrawerOption, location: string) => VNode;
98 | $createViewModal: (options: IModalOption, location: string) => VNode;
99 | $createViewDrawer: (options: IDrawerOption, location: string) => VNode;
100 | $createEleModal: (options: IModalOption, location: string) => VNode;
101 | $createEleDrawer: (options: IDrawerOption, location: string) => VNode;
102 | }
103 | }
104 |
105 | declare class VueCreateDM {
106 | static install: PluginFunction;
107 | }
108 |
109 | export default VueCreateDM;
110 |
--------------------------------------------------------------------------------
/example/src/views/base/antd/AntdDrawerExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建抽屉
6 |
7 |
8 |
9 |
10 | 自定义标题抽屉
11 |
12 |
13 |
14 |
15 |
数据项:{{ dataText }}
16 |
17 | 创建数据抽屉
18 |
19 |
20 |
21 |
22 | 从子组件取值
23 |
24 |
25 |
26 |
27 |
28 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/example/src/views/base/view/ViewDrawerExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建抽屉
6 |
7 |
8 |
9 |
10 | 自定义标题抽屉
11 |
12 |
13 |
14 |
15 |
数据项:{{ dataText }}
16 |
17 | 创建数据抽屉
18 |
19 |
20 |
21 |
22 | 从子组件取值
23 |
24 |
25 |
26 |
27 |
28 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/example/src/views/base/elem/EleDrawerExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建抽屉
6 |
7 |
8 |
9 |
10 | 自定义标题抽屉
11 |
12 |
13 |
14 |
15 |
数据项:{{ dataText }}
16 |
17 | 创建数据抽屉
18 |
19 |
20 |
21 |
22 | 从子组件取值
23 |
24 |
25 |
26 |
27 |
28 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/docs/guide/use.md:
--------------------------------------------------------------------------------
1 | # 使用
2 | ## 创建抽屉
3 | ### $createAntdDrawer、$createViewDrawer、$createEleDrawer
4 | ```js
5 | this.$createAntdDrawer(options, arg1, arg2);
6 | this.$createViewDrawer(options, arg1, arg2);
7 | this.$createEleDrawer(options, arg1, arg2);
8 | ```
9 | ::: tip 提示
10 | `title` 和 `content` 子组件内会被传入一个名为 `close` 的回调函数,在子组件内使用 `this.$emit('close', payload)` 触发关闭行为。
11 | :::
12 | ::: tip 提示
13 | 如果传入 **payloadSlot** 参数,并且相应的子组件存在 **`providePayload`** 方法,在抽屉关闭时,会先调用此函数获取返回值,然后作为 `beforeClose` 和 `afterClose` 方法的参数传入。
14 | :::
15 |
16 | ### options参数介绍
17 |
18 | | 参数 | 说明 | 类型 |
19 | | :-----| :---- | :---- |
20 | | drawerProps | Drawer 组件的所有 props
[Antd Drawer Props](https://www.antdv.com/components/drawer-cn/#API)
[iView Drawer props](https://www.iviewui.com/components/drawer#API)
[ElementUI Drawer props](https://element.eleme.cn/#/zh-CN/component/drawer) | Object | 是
21 | | payloadSlot | 是否需要和获取子组件的返回值,**子组件需要在methods中提供`providePayload`方法。**
- 不传或者传`false`,不调用;
- 传`'title'`调用**标题子组件**的`providePayload`方法;
- 传`true`或者`'default'`调用**内容子组件**的`providePayload`方法
| true \| false \| 'title' \| 'default' | 否
22 | | title | 标题自定义组件
- 如果注册了全局头部,优先使用全局头部组件。此时此项可直接传 String 类型;如果此项不传,那就使用`drawerProps.title`作为标题。如果传对象,那就不使用全局头部组件,走自定义头部的逻辑。
- 如果未注册全局头部,`title`需要传对象,其中`title.template`必填,类型是一个vue组件,`title`对象的其他参数可参考vue官方文档[深入数据对象](https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1)。如果 `drawerProps.title` 和 `title` 同时存在的话,优先使用 `title`。
| Object \| String |
23 | | content | 内容自定义组件。其中`content.template`必填,类型是一个vue组件,`content`对象的其他参数可参考vue官方文档[深入数据对象](https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1)。 | Object |
24 | | beforeClose | 抽屉关闭之前触发。其中`payload`代表调用子组件内调用`$eimt('close', params)`时携带的第二个参数;`slotPayload`表示从子组件中约定好的方法里获取到的返回值 | function({ payload, slotPayload }) |
25 | | afterClose | 抽屉关闭之前触发。 |function({ payload, slotPayload }) |
26 | |stopPropagation| 是否阻止原生`click`事件冒泡 | Boolean |
27 | |onClick| 原生`click`事件 | function(event) |
28 |
29 | ### 代码示例
30 | ```js
31 | this.$createAntdDrawer({
32 | drawerProps: {
33 | title: '标题',
34 | width: '500px',
35 | mask: false,
36 | },
37 | content: {
38 | template: HelloWorld,
39 | props: {
40 | msg: 'Welcome to Your Vue.js App',
41 | },
42 | },
43 | beforeClose: function() {
44 | console.log('我要关闭了');
45 | },
46 | afterClose: function() {
47 | console.log('我已经关闭了');
48 | },
49 | });
50 | ```
51 | ## 创建弹框
52 | ### $createAntdModal、$createViewModal、$createEleModal
53 | ```js
54 | this.$createAntdModal(options, arg1, arg2);
55 | this.$createViewModal(options, arg1, arg2);
56 | this.$createEleModal(options, arg1, arg2);
57 | ```
58 | ::: tip 提示
59 | `title`、`content`和`foter`子组件内会被传入`close`和`ok`两个回调函数和 一个名为`confirmLoading`的 props
60 | * 在子组件内使用 `this.$emit('close', payload)` 触发关闭动作
61 | * 使用 `this.$emit('ok', payload)` 触发 onOk 回调
62 | * 子组件内可以使用 `confirmLoading` 来管理确定按钮的 loading 状态
63 | * **antd的modal组件可以使用默认footer,iview需要自定义footer**
64 | :::
65 | ::: tip 提示
66 | 如果传入 **payloadSlot** 参数,并且相应的子组件如果存在 `providePayload` 方法,在点击确定按钮时,会先调用此函数获取返回值,然后作为 `onOk` 方法的一个参数传入
67 | :::
68 | ::: tip 提示
69 | 如果`onOk`函数返回`false`,并且是 antd 的 Modal 组件,会阻止关闭弹框;iView需要自定义 footer 组件来实现。
70 | :::
71 |
72 | ### options参数介绍
73 |
74 | 和上面说过的创建抽屉时传的差不多,只介绍以下几个不同的地方
75 | | 参数 | 说明 | 类型 |
76 | | :-----| :---- | :---- |
77 | |modalProps| Modal 的所有props
[Antd Modal](https://www.antdv.com/components/modal-cn/#API)
[iView Modal](https://www.iviewui.com/components/modal#API)
[ElementUI Dialog](https://element.eleme.cn/#/zh-CN/component/dialog)| Object |
78 | | payloadSlot | 同Drawer,多了一个 'footer' 选项 | true \| false \| 'title' \| 'footer' \| 'default'
79 | |footer| footer自定义组件 | Object |
80 | |onOk| 点击确认按钮的回调。
如果`onOk`函数返回`false`,并且是antd的Modal组件,会阻止关闭弹框;iView需要自定义footer组件来实现。 | function({ payload, slotPayload }) |
81 |
82 | ### 代码示例
83 |
84 | ```js
85 | this.$createAntdModal({
86 | modalProps: {
87 | title: '标题',
88 | width: '500px',
89 | mask: false,
90 | },
91 | content: {
92 | template: HelloWorld,
93 | props: {
94 | msg: 'Welcome to Your Vue.js App',
95 | },
96 | },
97 | beforeClose: function() {
98 | console.log('我要关闭了');
99 | },
100 | afterClose: function() {
101 | console.log('我已经关闭了');
102 | },
103 | async onOk() {
104 | const res = await new Promise((resolve) => {
105 | setTimeout(() => {
106 | console.log('点了确定');
107 | resolve(false);
108 | }, 3000);
109 | });
110 | return res;
111 | },
112 | });
113 | ```
114 |
--------------------------------------------------------------------------------
/example/src/views/base/antd/AntdExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建弹框
6 |
7 |
8 |
9 |
10 | 自定义标题弹框
11 |
12 |
13 |
14 |
15 | 自定义footer弹框
16 |
17 |
18 |
19 |
20 |
数据项:{{ dataText }}
21 |
22 | 创建数据弹框
23 |
24 |
25 |
26 |
27 | 从子组件取值
28 |
29 |
30 |
31 |
32 |
33 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/example/src/views/base/elem/EleExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建弹框
6 |
7 |
8 |
9 |
10 | 自定义标题弹框
11 |
12 |
13 |
14 |
15 | 自定义footer弹框
16 |
17 |
18 |
19 |
20 |
数据项:{{ dataText }}
21 |
22 | 创建数据弹框
23 |
24 |
25 |
26 |
27 | 从子组件取值
28 |
29 |
30 |
31 |
32 |
33 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/example/src/views/base/view/ViewExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 创建弹框
6 |
7 |
8 |
9 |
10 | 自定义标题弹框
11 |
12 |
13 |
14 |
15 | 自定义footer弹框
16 |
17 |
18 |
19 |
20 |
数据项:{{ dataText }}
21 |
22 | 创建数据弹框
23 |
24 |
25 |
26 |
27 | 从子组件取值
28 |
29 |
30 |
31 |
32 |
33 |
217 |
218 |
219 |
--------------------------------------------------------------------------------
/src/drawer.js:
--------------------------------------------------------------------------------
1 | import { createDrawerSlot } from './createCreateSlot';
2 | import { getSlotPayload } from './getSlotPayload';
3 | import { locationMatcher } from './locationMatcher';
4 | import { setGlobalHeader } from './setGlobalHeader';
5 | import { modifyOptions } from './modifyOptions';
6 |
7 | /**
8 | * drawerProps 就是组件库的 drawer 支持的props
9 | * title 和 content 都是对象,其中 template 属性代表组件,其他属性同 vue 的原生属性 https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
10 | */
11 | // 创建抽屉的主方法
12 | export function createDrawer(
13 | Vue,
14 | {
15 | component: Drawer,
16 | titleSlotName = 'title', //原来组件提供的标题插槽名称
17 | visiblePropName = 'visible', //原来控制抽屉组件显隐的属性名称
18 | closeCbName = 'close', // 原来组件的关闭回调事件名称
19 | router,
20 | store,
21 | },
22 | options
23 | ) {
24 | const {
25 | title,
26 | content,
27 | drawerProps,
28 | beforeClose,
29 | afterClose,
30 | payloadSlot, // 'default', 'title', false, true
31 | onClick,
32 | stopPropagation,
33 | } = options;
34 | const el = document.createElement('div');
35 | document.body.appendChild(el);
36 | let firstRender = true; // hack iview modal创建时没有动画的问题
37 |
38 | const vn = new Vue({
39 | data: {
40 | visible: false,
41 | slotVnMap: {},
42 | },
43 | render(createElement) {
44 | const self = this;
45 | if (firstRender) {
46 | setTimeout(() => {
47 | self.$data.visible = true;
48 | firstRender = false;
49 | }, 0);
50 | }
51 | const handleNativeClick = (event) => {
52 | if (stopPropagation) {
53 | event.stopPropagation();
54 | }
55 | onClick && onClick(event);
56 | };
57 | const handleClose = async function(payload) {
58 | const slotPayload = await getSlotPayload(
59 | self.$data.slotVnMap,
60 | payloadSlot
61 | );
62 | beforeClose && (await beforeClose({ payload, slotPayload }));
63 | self.$data.visible = false;
64 | // 因为antd关闭动画是 0.3s 所以稍微晚点再销毁组件
65 | setTimeout(async () => {
66 | self.$destroy();
67 |
68 | try {
69 | // 手动删除节点
70 | document.body.removeChild(self.$el);
71 | } catch (e) {}
72 |
73 | afterClose && (await afterClose({ payload, slotPayload }));
74 | }, 400);
75 | };
76 | const createSlot = createDrawerSlot(
77 | createElement,
78 | self.$data.slotVnMap,
79 | handleClose
80 | );
81 | const children = [];
82 | // 如果传了内容
83 | if (content && content.template) {
84 | children.push(createSlot(content));
85 | }
86 | // 如果title传了组件,默认用这个
87 | if (title && title.template) {
88 | // 如果是插槽的话,就要加slot
89 | children.push(createSlot(title, titleSlotName));
90 | drawerProps.title && delete drawerProps.title;
91 | }
92 | return createElement(
93 | Drawer,
94 | {
95 | props: {
96 | ...drawerProps,
97 | [visiblePropName]: self.$data.visible,
98 | },
99 | on: {
100 | [closeCbName]: handleClose,
101 | },
102 | nativeOn: {
103 | click: handleNativeClick,
104 | },
105 | },
106 | children
107 | );
108 | },
109 | router,
110 | store,
111 | }).$mount(el);
112 | return vn;
113 | }
114 |
115 | // 创建 antd drawer 的扩展方法
116 | export const createAntdDrawer = {
117 | install(Vue, originBaseOption) {
118 | Vue.prototype.$createAntdDrawer = function(options, argObj, argLocation) {
119 | const { location, baseOption } = modifyOptions(
120 | originBaseOption,
121 | argObj,
122 | argLocation
123 | );
124 | const newOptions = locationMatcher.call(
125 | this,
126 | location,
127 | baseOption,
128 | options
129 | );
130 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
131 | return createDrawer(
132 | Vue,
133 | {
134 | ...baseOption,
135 | titleSlotName: 'title',
136 | visiblePropName: 'visible',
137 | closeCbName: 'close',
138 | },
139 | optionsWithGH
140 | );
141 | };
142 | },
143 | };
144 | // 创建 iview drawer 的扩展方法
145 | export const createViewDrawer = {
146 | install(Vue, originBaseOption) {
147 | Vue.prototype.$createViewDrawer = function(options, argObj, argLocation) {
148 | const { location, baseOption } = modifyOptions(
149 | originBaseOption,
150 | argObj,
151 | argLocation
152 | );
153 | const newOptions = locationMatcher.call(
154 | this,
155 | location,
156 | baseOption,
157 | options
158 | );
159 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
160 | return createDrawer(
161 | Vue,
162 | {
163 | ...baseOption,
164 | titleSlotName: 'header',
165 | visiblePropName: 'value',
166 | closeCbName: 'on-close',
167 | },
168 | optionsWithGH
169 | );
170 | };
171 | },
172 | };
173 | // 创建 ele drawer 的扩展方法
174 | export const createEleDrawer = {
175 | install(Vue, originBaseOption) {
176 | Vue.prototype.$createEleDrawer = function(options, argObj, argLocation) {
177 | const { location, baseOption } = modifyOptions(
178 | originBaseOption,
179 | argObj,
180 | argLocation
181 | );
182 | const newOptions = locationMatcher.call(
183 | this,
184 | location,
185 | baseOption,
186 | options
187 | );
188 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
189 | return createDrawer(
190 | Vue,
191 | {
192 | ...baseOption,
193 | titleSlotName: 'title',
194 | visiblePropName: 'visible',
195 | closeCbName: 'close',
196 | },
197 | optionsWithGH
198 | );
199 | };
200 | },
201 | };
202 |
--------------------------------------------------------------------------------
/src/modal.js:
--------------------------------------------------------------------------------
1 | import { createModalSlot } from './createCreateSlot';
2 | import { getSlotPayload } from './getSlotPayload';
3 | import { locationMatcher } from './locationMatcher';
4 | import { setGlobalHeader } from './setGlobalHeader';
5 | import { modifyOptions } from './modifyOptions';
6 | /**
7 | * modalProps 就是组件库的 modal 支持的props
8 | * title、content、footer 都是对象,其中 template 属性代表组件,其他属性同 vue 的原生属性 https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
9 | */
10 | // 创建弹框的主方法
11 | export function createModal(
12 | Vue,
13 | {
14 | component: Modal,
15 | titleSlotName = 'title', //原来组件提供的标题插槽名称
16 | footerSlotName = 'footer', //原来组件提供的footer插槽名称
17 | visiblePropName = 'visible', //原来控制抽屉组件显隐的属性名称
18 | btnLoadingPropName = 'confirmLoading',
19 | cancelCbName = 'cancel', // 原来组件的关闭回调事件名称
20 | okCbName = 'ok', // 原来组件的确定回调事件名称
21 | router,
22 | store,
23 | },
24 | options
25 | ) {
26 | const {
27 | title,
28 | content,
29 | footer,
30 | modalProps,
31 | beforeClose,
32 | afterClose,
33 | onOk,
34 | payloadSlot,
35 | onClick,
36 | // 是否阻止整个modal组件的点击事件冒泡
37 | stopPropagation,
38 | } = options;
39 | const el = document.createElement('div');
40 | document.body.appendChild(el);
41 | let firstRender = true; // hack iview modal创建时没有动画的问题
42 |
43 | const vn = new Vue({
44 | data: {
45 | visible: false,
46 | confirmLoading: false,
47 | slotVnMap: {},
48 | },
49 | render(createElement) {
50 | const self = this;
51 | if (firstRender) {
52 | setTimeout(() => {
53 | self.$data.visible = true;
54 | firstRender = false;
55 | }, 0);
56 | }
57 | const handleNativeClick = (event) => {
58 | if (stopPropagation) {
59 | event.stopPropagation();
60 | }
61 | onClick && onClick(event);
62 | };
63 | const handleClose = async (payload) => {
64 | beforeClose && (await beforeClose(payload));
65 | self.$data.visible = false;
66 | setTimeout(async () => {
67 | self.$destroy();
68 |
69 | try {
70 | // 手动销毁dom
71 | document.body.removeChild(self.$el);
72 | } catch (e) {}
73 |
74 | afterClose && (await afterClose(payload));
75 | }, 400);
76 | };
77 | // 直接关闭不传slotPayload,通过ok关闭可以取到
78 | const handleOk = async (payload) => {
79 | const slotPayload = await getSlotPayload(
80 | self.$data.slotVnMap,
81 | payloadSlot
82 | );
83 | self.$data.confirmLoading = true;
84 | // 如果返回false表示不关闭,其他情况关闭
85 | const res = onOk && (await onOk({ payload, slotPayload }));
86 | self.$data.confirmLoading = false;
87 | if (res === false) {
88 | } else {
89 | await handleClose({ payload, slotPayload });
90 | }
91 | };
92 | const createSlot = createModalSlot(
93 | createElement,
94 | self.$data.slotVnMap,
95 | self.$data.confirmLoading,
96 | handleClose,
97 | handleOk
98 | );
99 | const children = [];
100 | // 如果传了内容
101 | if (content && content.template) {
102 | children.push(createSlot(content));
103 | }
104 | // 如果title传了组件,默认用这个
105 | if (title && title.template) {
106 | // 如果是插槽的话,就要加slot
107 | children.push(createSlot(title, titleSlotName));
108 | modalProps.title && delete modalProps.title;
109 | }
110 | // 如果title传了footer,用这个
111 | if (footer && footer.template) {
112 | children.push(createSlot(footer, footerSlotName));
113 | }
114 | return createElement(
115 | Modal,
116 | {
117 | props: {
118 | ...modalProps,
119 | [btnLoadingPropName]: self.$data.confirmLoading,
120 | [visiblePropName]: self.$data.visible,
121 | },
122 | on: {
123 | [cancelCbName]: handleClose,
124 | [okCbName]: handleOk,
125 | },
126 | nativeOn: {
127 | click: handleNativeClick,
128 | },
129 | },
130 | children
131 | );
132 | },
133 | router,
134 | store,
135 | }).$mount(el);
136 | return vn;
137 | }
138 | // 创建 antd modal 的扩展方法
139 | export const createAntdModal = {
140 | install(Vue, originBaseOption) {
141 | Vue.prototype.$createAntdModal = function(options, argObj, argLocation) {
142 | const { location, baseOption } = modifyOptions(
143 | originBaseOption,
144 | argObj,
145 | argLocation
146 | );
147 | const newOptions = locationMatcher.call(
148 | this,
149 | location,
150 | baseOption,
151 | options
152 | );
153 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
154 | return createModal(
155 | Vue,
156 | {
157 | ...baseOption,
158 | titleSlotName: 'title',
159 | footerSlotName: 'footer',
160 | visiblePropName: 'visible',
161 | btnLoadingPropName: 'confirmLoading',
162 | cancelCbName: 'cancel',
163 | okCbName: 'ok',
164 | },
165 | optionsWithGH
166 | );
167 | };
168 | },
169 | };
170 | // 创建 iview modal 的扩展方法
171 | export const createViewModal = {
172 | install(Vue, originBaseOption) {
173 | Vue.prototype.$createViewModal = function(options, argObj, argLocation) {
174 | const { location, baseOption } = modifyOptions(
175 | originBaseOption,
176 | argObj,
177 | argLocation
178 | );
179 | const newOptions = locationMatcher.call(
180 | this,
181 | location,
182 | baseOption,
183 | options
184 | );
185 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
186 | return createModal(
187 | Vue,
188 | {
189 | ...baseOption,
190 | titleSlotName: 'header',
191 | footerSlotName: 'footer',
192 | visiblePropName: 'value',
193 | btnLoadingPropName: 'loading',
194 | cancelCbName: 'on-cancel',
195 | okCbName: 'on-ok',
196 | },
197 | optionsWithGH
198 | );
199 | };
200 | },
201 | };
202 | // 创建 ele modal 的扩展方法
203 | export const createEleModal = {
204 | install(Vue, originBaseOption) {
205 | Vue.prototype.$createEleModal = function(options, argObj, argLocation) {
206 | const { location, baseOption } = modifyOptions(
207 | originBaseOption,
208 | argObj,
209 | argLocation
210 | );
211 | const newOptions = locationMatcher.call(
212 | this,
213 | location,
214 | baseOption,
215 | options
216 | );
217 | const optionsWithGH = setGlobalHeader(baseOption, newOptions);
218 | return createModal(
219 | Vue,
220 | {
221 | ...baseOption,
222 | titleSlotName: 'title',
223 | footerSlotName: 'footer',
224 | visiblePropName: 'visible',
225 | btnLoadingPropName: 'loading',
226 | cancelCbName: 'close',
227 | okCbName: 'ok',
228 | },
229 | optionsWithGH
230 | );
231 | };
232 | },
233 | };
234 |
--------------------------------------------------------------------------------