├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── dependabot.yml ├── release.yml └── workflows │ └── deploy.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── babel.config.js ├── gen-router.js ├── jsconfig.json ├── lint-staged.config.js ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── api │ └── dashboard.js ├── assets │ ├── icons │ │ ├── exit-fullscreen.svg │ │ ├── filter.svg │ │ ├── fullscreen.svg │ │ ├── h-bar.svg │ │ ├── liquid.svg │ │ └── note.svg │ ├── images │ │ └── apple-devices │ │ │ ├── ipadpro13.png │ │ │ ├── iphone14pro.png │ │ │ ├── iphone15pro.png │ │ │ └── mbp16.png │ ├── logo.png │ └── styles │ │ ├── common.scss │ │ ├── index.scss │ │ ├── layout.scss │ │ └── variables.scss ├── components │ ├── AsideToggler │ │ ├── holder.svg │ │ └── index.vue │ ├── Dashboard │ │ ├── DialogEdit.vue │ │ ├── css │ │ │ ├── dashboard.scss │ │ │ └── driverjs-theme.scss │ │ ├── index.vue │ │ ├── steps.js │ │ └── widgets │ │ │ └── example.vue │ ├── ElCardCollapse.vue │ ├── FullScreenToggler.vue │ ├── IgnoreMatchingPopover.vue │ ├── LoadingIndicator.vue │ └── SvgIcon.vue ├── directives │ └── overflow-tooltip.js ├── main.js ├── router.js └── views │ ├── apple-devices-preview.vue │ ├── aside-toggle-drag.vue │ ├── card-collapse.vue │ ├── code-diff.vue │ ├── custom-directive.vue │ ├── dashboard │ ├── index.vue │ └── widgets │ │ ├── charts │ │ ├── bar-test-types.vue │ │ └── liquid-activity-rate.vue │ │ └── daily │ │ └── hitokoto.vue │ ├── dom-to-image.vue │ ├── fullscreen.vue │ ├── guide.vue │ ├── home.vue │ ├── icons.vue │ ├── loading-indicator.vue │ ├── minder-editor.vue │ ├── overflow-tooltip.vue │ ├── set-height-adaptive.vue │ ├── sticky-fixed-col.vue │ ├── sticky-scroller.vue │ ├── sticky-sum.vue │ ├── translate-js.vue │ ├── vxe-table.vue │ └── vxe-table2.vue ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # 忽略 node_modules 目录 2 | node_modules/ 3 | 4 | # 忽略静态资源和打包后的文件 5 | build/ 6 | demo/ 7 | dist/ 8 | public/ 9 | src/assets/ 10 | 11 | # 忽略指定后缀的文件 12 | *.min.js 13 | *.scss 14 | *.css 15 | *.md 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/recommended', 8 | 'eslint:recommended', 9 | ], 10 | parserOptions: { 11 | parser: '@babel/eslint-parser', 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | quotes: ['error', 'single'], // 使用单引号 17 | 'comma-dangle': ['error', 'only-multiline'], // 多行时才可以使用尾随逗号 18 | semi: ['error', 'never'], // 不要尾随分号 19 | 'vue/max-attributes-per-line': ['error', { 20 | singleline: 5, 21 | multiline: 1, 22 | }], // 属性换行配置 23 | indent: ['error', 2, { SwitchCase: 1 }], // 强制执行一致的缩进样式 24 | 'vue/singleline-html-element-content-newline': 'off', // 单行元素内容换行 25 | 'no-unused-vars': [ 26 | 'warn', 27 | { 28 | vars: 'all', // 检测所有变量 29 | args: 'none', // 忽略函数参数 30 | caughtErrors: 'none', // 忽略 catch 语句的参数 31 | ignoreRestSiblings: true, // 忽略剩余子项 fn(...args) 32 | } 33 | ], 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | open-pull-requests-limit: 1000 13 | assignees: 14 | - "Lruihao" 15 | labels: 16 | - "dependencies" 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | authors: 6 | - dependabot 7 | categories: 8 | - title: New features 9 | labels: 10 | - new feature 11 | - title: Enhancement 12 | labels: 13 | - enhancement 14 | - title: Fixes 15 | labels: 16 | - bug 17 | - title: Other Changes 18 | labels: 19 | - "*" -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Deploy demo to GitHub Pages 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "main" branch 8 | push: 9 | branches: [main] 10 | paths: 11 | - 'src/**' 12 | - 'public/**' 13 | - 'package.json' 14 | - 'yarn.lock' 15 | # pull_request: 16 | # branches: [main] 17 | 18 | # Allows you to run this workflow manually from the Actions tab 19 | workflow_dispatch: 20 | 21 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 22 | jobs: 23 | # This workflow contains a single job called "build" 24 | build: 25 | # The type of runner that the job will run on 26 | runs-on: ubuntu-latest 27 | 28 | # Steps represent a sequence of tasks that will be executed as part of the job 29 | steps: 30 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | 34 | - name: Setup Node.js 35 | uses: actions/setup-node@v4 36 | with: 37 | node-version: 14 38 | 39 | - name: Install dependencies and Build demo 40 | run: | 41 | yarn install --frozen-lockfile 42 | yarn build 43 | touch dist/.nojekyll 44 | 45 | - name: Deploy to GitHub Pages 46 | uses: JamesIves/github-pages-deploy-action@v4 47 | with: 48 | # See https://github.com/JamesIves/github-pages-deploy-action#configuration- 49 | branch: gh-pages 50 | folder: dist 51 | clean: true 52 | single-commit: true 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /demo 5 | 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 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry="https://registry.npmjs.org/" 2 | message="Chore(release): %s" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Lruihao (https://lruihao.cn) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-el-demo 2 | 3 | Vue and element-ui related [demos](https://lruihao.github.io/vue-el-demo/). 4 | 5 | ## Project setup 6 | 7 | ```bash 8 | yarn install 9 | # Compiles and hot-reloads for development 10 | yarn serve 11 | # Compiles and minifies for production 12 | yarn build 13 | # Lints and fixes files 14 | yarn lint 15 | ``` 16 | 17 | Customize configuration see [Configuration Reference](https://cli.vuejs.org/config/). 18 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | plugins: [ 6 | '@babel/plugin-transform-private-methods', 7 | ], 8 | } 9 | -------------------------------------------------------------------------------- /gen-router.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const os = require('os') 3 | 4 | const vueDir = './src/views/' 5 | const routerFile = './src/router.js' 6 | const routes = [] 7 | 8 | const directories = fs.readdirSync(vueDir, { withFileTypes: true }) 9 | .filter((dirent) => dirent.isDirectory()) 10 | .map((dirent) => dirent.name) 11 | 12 | for (const subDir of directories) { 13 | getDirRoutes(`${vueDir}${subDir}/`, subDir) 14 | } 15 | 16 | getDirRoutes(vueDir) 17 | 18 | /** 19 | * Get routes from directory 20 | * @param {string} dir directory 21 | * @param {string} subDir sub directory 22 | */ 23 | function getDirRoutes(dir, subDir = '') { 24 | const files = fs.readdirSync(dir) 25 | for (const filename of files) { 26 | if (filename.indexOf('.') < 0) { 27 | continue 28 | } 29 | const [name, ext] = filename.split('.') 30 | if (ext !== 'vue') { 31 | continue 32 | } 33 | const pathname = subDir ? `${subDir}/${name}` : name 34 | const routeName = pathname.replace(/-(\w)|\/(\w)/g, (_, match1, match2) => (match1 || match2).toUpperCase()) 35 | let routeDescription = '' 36 | const contentFull = fs.readFileSync(`${dir}${filename}`, 'utf-8') 37 | // get route description from first line comment 38 | const match = //g.exec(contentFull.split(os.EOL)[0]) 39 | if (match) { 40 | routeDescription = match[1].trim() 41 | } 42 | routes.push(` { 43 | path: '/${name === 'home' ? '' : pathname}', 44 | name: '${routeName}',${routeDescription ? `\n meta: { description: '${routeDescription}' },` : ''} 45 | component: () => import(/* webpackChunkName: "${routeName}" */ '@/views/${pathname}'), 46 | },`) 47 | } 48 | } 49 | 50 | // move home route to first 51 | const homeIndex = routes.findIndex(route => route.indexOf('name: \'home\'') >= 0) 52 | if (homeIndex > 0) { 53 | routes.unshift(routes.splice(homeIndex, 1)[0]) 54 | } 55 | 56 | const result = 57 | `// This file is automatically generated by gen-router.js, please do not modify it manually! 58 | import VueRouter from 'vue-router' 59 | import Vue from 'vue' 60 | 61 | Vue.use(VueRouter) 62 | 63 | const routes = [ 64 | ${routes.join(os.EOL)} 65 | ] 66 | 67 | const router = new VueRouter({ 68 | mode: 'hash', 69 | routes, 70 | }) 71 | 72 | router.afterEach((to) => { 73 | document.title = \`\${to.meta?.description} - VUE-EL-DEMO\` 74 | }) 75 | 76 | export default router 77 | ` 78 | 79 | fs.writeFile(routerFile, result, 'utf-8', (err) => { 80 | if (err) throw err 81 | console.log(`✅ Router generated successfully in ${routerFile}`) 82 | }) 83 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,jsx,vue}': 'vue-cli-service lint', 3 | 'src/views/*.{vue}': [ 4 | 'npm run gr', 5 | 'git add', 6 | ], 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-el-demo", 3 | "version": "0.1.0", 4 | "description": "Vue and element-ui related demos.", 5 | "keywords": [ 6 | "vue", 7 | "vue2", 8 | "element-ui" 9 | ], 10 | "homepage": "https://lruihao.github.io/vue-el-demo/", 11 | "bugs": { 12 | "url": "https://github.com/Lruihao/vue-el-demo/issues" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/Lruihao/vue-el-demo.git" 17 | }, 18 | "author": "Lruihao (https://lruihao.cn)", 19 | "scripts": { 20 | "build": "npm run gr && vue-cli-service build", 21 | "gr": "node gen-router.js", 22 | "lint": "vue-cli-service lint", 23 | "serve": "npm run gr && vue-cli-service serve" 24 | }, 25 | "dependencies": { 26 | "@antv/g2plot": "^2.4.31", 27 | "@cell-x/el-table-sticky": "^1.0.3", 28 | "@interactjs/core": "1.10.2", 29 | "@interactjs/utils": "1.10.2", 30 | "@opd/g2plot-vue": "^3.6.7", 31 | "core-js": "^3.8.3", 32 | "dom-to-image": "^2.6.0", 33 | "dom-to-image-more": "^3.3.0", 34 | "driver.js": "^1.2.1", 35 | "element-ui": "^2.15.13", 36 | "i18n-jsautotranslate": "^3.12.0", 37 | "moment": "^2.29.4", 38 | "v-code-diff": "^1.9.0", 39 | "vue": "^2.6.14", 40 | "vue-fullscreen": "^2.6.1", 41 | "vue-grid-layout": "^2.4.0", 42 | "vue-minder-editor-extended": "^1.3.3", 43 | "vue-router": "^3.5.1", 44 | "vuex": "^3.6.2", 45 | "vxe-table": "^3.9.0" 46 | }, 47 | "devDependencies": { 48 | "@babel/core": "^7.12.16", 49 | "@babel/eslint-parser": "^7.12.16", 50 | "@babel/plugin-transform-private-methods": "^7.22.5", 51 | "@babel/runtime": "^7.13.0", 52 | "@vue/cli-plugin-babel": "~5.0.0", 53 | "@vue/cli-plugin-eslint": "~5.0.0", 54 | "@vue/cli-plugin-router": "~5.0.0", 55 | "@vue/cli-service": "~5.0.0", 56 | "eslint": "^7.32.0", 57 | "eslint-plugin-vue": "^8.0.3", 58 | "lint-staged": "^11.1.2", 59 | "sass": "^1.32.7", 60 | "sass-loader": "^12.0.0", 61 | "svg-sprite-loader": "^6.0.11", 62 | "svgo-loader": "^4.0.0", 63 | "vue-template-compiler": "^2.6.14", 64 | "webpack": "^5.0.0" 65 | }, 66 | "gitHooks": { 67 | "pre-commit": "lint-staged" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 104 | 105 | 144 | -------------------------------------------------------------------------------- /src/api/dashboard.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export default { 4 | /** 5 | * 通过 route 获取 dashboard 数组(后端自动获取组织 ID) 6 | * @param {Object} data 7 | * @param {String} data.route 仪表盘路由 8 | * @returns 9 | */ 10 | get: data => { 11 | return request({ 12 | url: '/dashboard/get', 13 | method: 'post', 14 | data 15 | }) 16 | }, 17 | /** 18 | * 新增 dashboard 19 | * @param {Object} data 20 | * @param {String} data.name 仪表盘名称 21 | * @param {String} data.route 仪表盘路由 22 | * @param {Array} data.layout 仪表盘布局 23 | * @param {String} data.aside 仪表盘侧边栏 left | right 24 | * @param {Boolean} data.compact 仪表盘是否垂直压缩高度 25 | * @returns 26 | */ 27 | add: data => { 28 | return request({ 29 | url: '/dashboard/add', 30 | method: 'post', 31 | data 32 | }) 33 | }, 34 | /** 35 | * 更新 dashboard 36 | * @param {Object} data 37 | * @param {Number} data.id 仪表盘 id 38 | * @param {String} data.name 仪表盘名称 39 | * @param {String} data.route 仪表盘路由 40 | * @param {Array} data.layout 仪表盘布局 41 | * @param {String} data.aside 仪表盘侧边栏 left | right 42 | * @param {Boolean} data.compact 仪表盘是否垂直压缩高度 43 | * @returns 44 | */ 45 | update: data => { 46 | return request({ 47 | url: '/dashboard/update', 48 | method: 'post', 49 | data 50 | }) 51 | }, 52 | /** 53 | * 删除 dashboard 54 | * @param {Number} id 仪表盘 id 55 | * @returns 56 | */ 57 | delete: id => { 58 | return request({ 59 | url: `/dashboard/delete/${id}`, 60 | method: 'post', 61 | }) 62 | }, 63 | } 64 | -------------------------------------------------------------------------------- /src/assets/icons/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/h-bar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/liquid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/note.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/apple-devices/ipadpro13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/images/apple-devices/ipadpro13.png -------------------------------------------------------------------------------- /src/assets/images/apple-devices/iphone14pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/images/apple-devices/iphone14pro.png -------------------------------------------------------------------------------- /src/assets/images/apple-devices/iphone15pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/images/apple-devices/iphone15pro.png -------------------------------------------------------------------------------- /src/assets/images/apple-devices/mbp16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/images/apple-devices/mbp16.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/styles/common.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lruihao/vue-el-demo/bcfbb36b72f2fba61afd00f51400509671464255/src/assets/styles/common.scss -------------------------------------------------------------------------------- /src/assets/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @import './layout.scss'; 3 | @import './common.scss'; 4 | -------------------------------------------------------------------------------- /src/assets/styles/layout.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | height: 100%; 7 | } 8 | 9 | body { 10 | height: 100%; 11 | margin: 0; 12 | overflow: hidden; 13 | 14 | #app { 15 | height: 100%; 16 | 17 | >.app-container { 18 | height: 100%; 19 | width: 100%; 20 | 21 | >.main-container { 22 | overflow: hidden auto; 23 | height: calc(100% - #{$headerHeight}); 24 | padding: 0; 25 | 26 | >.page-container { 27 | padding: 20px; 28 | position: relative; 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | .test-enter-active, .test-leave-active { 36 | transition: all .2s; 37 | } 38 | .test-enter, .test-leave-to { 39 | transform: translateX(-30px); 40 | opacity: 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/assets/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $headerHeight: 46px !default; 2 | -------------------------------------------------------------------------------- /src/components/AsideToggler/holder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/AsideToggler/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 145 | 146 | 204 | -------------------------------------------------------------------------------- /src/components/Dashboard/DialogEdit.vue: -------------------------------------------------------------------------------- 1 | 2 | 58 | 59 | 156 | 157 | 162 | -------------------------------------------------------------------------------- /src/components/Dashboard/css/dashboard.scss: -------------------------------------------------------------------------------- 1 | /* dashboard 相关样式 */ 2 | $dashboard-border-color: #e6e6e6 !default; 3 | $dashboard-box-shadow-color: rgba(0, 0, 0, 0.1) !default; 4 | $dashboard-sidebar-bg: #fafbfc !default; 5 | $dashboard-placeholder-bg: #fec171 !default; 6 | 7 | .dashboard-container { 8 | width: 100%; 9 | position: relative; 10 | display: flex; 11 | 12 | // 隐藏 dashboard 内的滚动条 13 | *::-webkit-scrollbar { 14 | width: 0; 15 | height: 0; 16 | } 17 | 18 | // 侧栏部分样式 19 | %aside-part { 20 | border-right: 1px solid $dashboard-border-color; 21 | box-shadow: 1px 0 12px 0 $dashboard-box-shadow-color; 22 | } 23 | 24 | // 侧栏在右边 25 | &[data-direction="right"] { 26 | flex-direction: row-reverse; 27 | 28 | %aside-part { 29 | border-left: 1px solid $dashboard-border-color; 30 | box-shadow: -1px 0 12px 0 $dashboard-box-shadow-color; 31 | } 32 | } 33 | 34 | // 展开时的样式 35 | &[data-is-expand] { 36 | .vue-grid-layout { 37 | // 背景显示网格辅助线(宽:1/24 高:40+8px) 38 | background-image: linear-gradient(90deg, rgba(60, 10, 30, 0.04) 1px, transparent 0), 39 | linear-gradient(1turn, rgba(60, 10, 30, 0.04) 1px, transparent 0); 40 | background-size: calc((100% - 8px) / 24) 48px; 41 | background-position: 4px 4px; 42 | } 43 | } 44 | 45 | // dashboard 侧边栏 46 | .dashboard-aside { 47 | height: 100%; 48 | overflow-y: hidden; 49 | display: flex; 50 | flex-direction: column; 51 | background-color: $dashboard-sidebar-bg; 52 | z-index: 10; 53 | @extend %aside-part; 54 | 55 | .dashboard-aside-header { 56 | font-size: 1.125rem; 57 | font-weight: 500; 58 | border-bottom: 1px solid $dashboard-border-color; 59 | padding: 10px 1rem; 60 | position: sticky; 61 | top: 0; 62 | z-index: 1; 63 | background-color: darken($dashboard-sidebar-bg, 1%); 64 | display: flex; 65 | align-items: center; 66 | justify-content: space-between; 67 | user-select: none; 68 | cursor: grab; 69 | 70 | .dashboard-close-btn { 71 | border: none; 72 | cursor: pointer; 73 | color: inherit; 74 | transform: scale(1.2); 75 | background-color: transparent; 76 | font-size: inherit; 77 | &:hover { 78 | color: #606266; 79 | } 80 | } 81 | } 82 | 83 | .dashboard-aside-main { 84 | display: flex; 85 | overflow-y: auto; 86 | flex-grow: 1; 87 | } 88 | 89 | .dashboard-categories { 90 | width: auto; 91 | overflow-y: auto; 92 | padding-block: 6px; 93 | background-color: darken($dashboard-sidebar-bg, 1%); 94 | 95 | .el-menu-item { 96 | padding: 0 10px !important; 97 | cursor: default; 98 | height: 40px; 99 | line-height: 40px; 100 | 101 | span { 102 | display: inline-block; 103 | line-height: 18px; 104 | vertical-align: middle; 105 | padding: 8px; 106 | width: 100%; 107 | border-radius: 4px; 108 | cursor: pointer; 109 | } 110 | 111 | &:focus, 112 | &:hover { 113 | background-color: transparent; 114 | } 115 | 116 | &:hover span { 117 | background-color: darken($dashboard-sidebar-bg, 4%); 118 | } 119 | 120 | &.is-active { 121 | span { 122 | background-color: #e8f1fd; 123 | } 124 | } 125 | } 126 | } 127 | 128 | // 有侧边栏时,组件列表的样式 129 | .aside-toggler-container + .dashboard-widgets { 130 | padding-left: 14px; 131 | } 132 | 133 | .dashboard-widgets { 134 | list-style: none; 135 | margin-block: 0; 136 | padding: 2px 10px; 137 | overflow-y: auto; 138 | 139 | .dashboard-widget { 140 | margin: 8px 0; 141 | cursor: move; 142 | background-color: darken($dashboard-sidebar-bg, 3%); 143 | border-radius: 4px; 144 | width: 200px; 145 | height: 60px; 146 | border: 1px solid $dashboard-border-color; 147 | padding-inline: 10px; 148 | display: flex; 149 | align-items: center; 150 | justify-content: space-between; 151 | gap: 0.5rem; 152 | position: relative; 153 | box-sizing: border-box; 154 | 155 | .widget-info { 156 | overflow: hidden; 157 | text-overflow: ellipsis; 158 | white-space: nowrap; 159 | 160 | .widget-icon { 161 | transform: scale(1.5); 162 | margin-left: 0.25rem; 163 | } 164 | .widget-name { 165 | font-size: 14px; 166 | margin-left: 0.5rem; 167 | } 168 | } 169 | 170 | .widget-category { 171 | position: absolute; 172 | bottom: 6px; 173 | left: 10px; 174 | transform: scale(0.5) translate(-50%, 50%); 175 | font-size: 16px; 176 | line-height: 1; 177 | } 178 | } 179 | 180 | .dashboard-widget-empty { 181 | cursor: not-allowed; 182 | @extend .dashboard-widget; 183 | } 184 | } 185 | } 186 | 187 | // dashboard 主体部分 188 | .dashboard-main { 189 | padding: 0; 190 | height: 100% !important; 191 | overflow: hidden auto; 192 | position: relative; 193 | 194 | .vue-grid-layout { 195 | min-height: 100%; 196 | 197 | &.dragging { 198 | z-index: 1; 199 | } 200 | } 201 | 202 | .dashboard-layout-empty { 203 | height: 100%; 204 | width: 100%; 205 | position: absolute; 206 | top: 0; 207 | } 208 | 209 | // dashboard 组件拖拽占位符的样式 210 | .vue-grid-item.vue-grid-placeholder { 211 | background: $dashboard-placeholder-bg; 212 | } 213 | 214 | .vue-grid-item { 215 | // 禁止默认触摸行为 216 | touch-action: none; 217 | 218 | // dashboard 组件的通用样式 219 | .dashboard-item { 220 | height: 100%; 221 | width: 100%; 222 | 223 | &:not(.el-card) { 224 | border-radius: 4px; 225 | border: 1px solid #ebeef5; 226 | background-color: #fff; 227 | overflow: hidden; 228 | color: #303133; 229 | transition: 0.3s; 230 | --box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 231 | 232 | &.bg-transparent { 233 | background-color: transparent; 234 | border: none; 235 | } 236 | &[shadow="always"], 237 | &:not([shadow]) { 238 | box-shadow: var(--box-shadow); 239 | } 240 | &[shadow="hover"]:hover { 241 | box-shadow: var(--box-shadow); 242 | } 243 | } 244 | } 245 | // dashboard 组件的操作按钮 246 | .item-operation { 247 | display: none; 248 | position: absolute; 249 | top: 2px; 250 | right: 2px; 251 | z-index: 10; 252 | .el-button { 253 | padding: 2px 4px; 254 | } 255 | } 256 | // hover 时显示操作按钮 257 | &:hover .item-operation { 258 | display: block; 259 | } 260 | } 261 | } 262 | } 263 | 264 | // 注入到面包屑左/右侧的操作 265 | .dashboard-operations { 266 | font-size: 1.25rem; 267 | 268 | .dashboard-operation-icon { 269 | cursor: pointer; 270 | &.el-icon-s-operation { 271 | font-size: 1.25rem; 272 | } 273 | } 274 | 275 | .el-dropdown .el-button-group { 276 | display: inline-block; 277 | } 278 | } 279 | 280 | .dashboard-operation-menu { 281 | .el-dropdown-menu__item { 282 | text-align: center; 283 | cursor: default; 284 | 285 | &:nth-child(2) { 286 | padding-block: 0.5rem; 287 | } 288 | 289 | &:hover, 290 | &:focus { 291 | background-color: inherit; 292 | } 293 | .el-button i { 294 | margin-right: 0; 295 | } 296 | .el-button--text { 297 | padding: 0; 298 | } 299 | } 300 | 301 | .dashboard-operation-btn { 302 | padding: 6px; 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/components/Dashboard/css/driverjs-theme.scss: -------------------------------------------------------------------------------- 1 | // For details, please refer to https://driverjs.com/docs/theming 2 | 3 | .driver-popover.driverjs-theme { 4 | --popover-bg-color: #fde047; 5 | background-color: var(--popover-bg-color); 6 | color: #000; 7 | 8 | .driver-popover-title { 9 | font-size: 20px; 10 | } 11 | 12 | .driver-popover-title, 13 | .driver-popover-description, 14 | .driver-popover-progress-text { 15 | color: #000; 16 | } 17 | 18 | .driver-popover-navigation-btns { 19 | justify-content: space-between; 20 | gap: 1.5rem; 21 | 22 | button { 23 | flex: 1; 24 | text-align: center; 25 | text-shadow: none; 26 | font-size: 14px; 27 | padding: 5px 8px; 28 | border-radius: 6px; 29 | background-color: var(--btn-bg-color); 30 | border: 2px solid var(--btn-bg-color); 31 | 32 | & + button { 33 | margin-left: 0; 34 | } 35 | } 36 | 37 | .driver-popover-prev-btn { 38 | color: #000; 39 | --btn-bg-color: #fff; 40 | 41 | &:hover { 42 | color: #111; 43 | --btn-bg-color: #e8edf5; 44 | } 45 | } 46 | 47 | .driver-popover-next-btn { 48 | color: #fff; 49 | --btn-bg-color: #000; 50 | 51 | &:hover { 52 | color: #eee; 53 | --btn-bg-color: #424242; 54 | } 55 | } 56 | } 57 | 58 | .driver-popover-close-btn { 59 | color: #777; 60 | 61 | &:hover { 62 | color: #000; 63 | } 64 | } 65 | 66 | .driver-popover-arrow-side-left.driver-popover-arrow { 67 | border-left-color: var(--popover-bg-color); 68 | } 69 | 70 | .driver-popover-arrow-side-right.driver-popover-arrow { 71 | border-right-color: var(--popover-bg-color); 72 | } 73 | 74 | .driver-popover-arrow-side-top.driver-popover-arrow { 75 | border-top-color: var(--popover-bg-color); 76 | } 77 | 78 | .driver-popover-arrow-side-bottom.driver-popover-arrow { 79 | border-bottom-color: var(--popover-bg-color); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/components/Dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 297 | 298 | 912 | 913 | 938 | -------------------------------------------------------------------------------- /src/components/Dashboard/steps.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | element: '.dashboard-operations:last-child', 4 | popover: { 5 | title: '展开组件列表', 6 | description: '点击管理组件按钮展开侧栏组件列表', 7 | showButtons: ['next', 'close'], 8 | } 9 | }, 10 | { 11 | element: '.dashboard-container .dashboard-aside', 12 | popover: { 13 | title: '添加组件', 14 | description: `点击组件的添加按钮 15 | 18 | 或者拖拽组件放置到容器中即可。`, 19 | } 20 | }, 21 | { 22 | element: '.dashboard-container .dashboard-main', 23 | popover: { 24 | title: '改变位置', 25 | description: '鼠标 按住组件即可拖拽调整组件位置。', 26 | } 27 | }, 28 | { 29 | element: '.dashboard-container .vue-grid-item:first-child', 30 | popover: { 31 | title: '调整大小', 32 | description: '鼠标移动到组件右下角,鼠标会变成可调整大小的图标 ,此时按住鼠标左键即可调整组件大小。', 33 | align: 'end', 34 | } 35 | }, 36 | { 37 | element: '.dashboard-container .vue-grid-item:first-child', 38 | popover: { 39 | title: '配置组件', 40 | description: `鼠标移动到组件上,组件右上角会出现操作按钮组 41 |
42 | 45 | 48 | 51 |
。`, 52 | align: 'start', 53 | }, 54 | }, 55 | { 56 | element: '.dashboard-container', 57 | popover: { 58 | title: '侧栏位置', 59 | description: '鼠标按住侧栏任意空白处,拖拽侧栏到页面左侧或右侧然后放下即可改变侧栏位置。', 60 | } 61 | }, 62 | { 63 | element: '.dashboard-operations:last-child', 64 | popover: { 65 | title: '更多操作', 66 | description: '鼠标移动到图标上 时,会出现紧凑布局开关、刷新、清空等更多操作,点击最右侧按钮可全屏显示。', 67 | } 68 | }, 69 | { 70 | element: '.dashboard-container .dashboard-aside .dashboard-close-btn', 71 | popover: { 72 | title: '恭喜完成 🎉', 73 | description: '现在你可以尝试自定义一个仪表盘啦!', 74 | showButtons: ['next'], 75 | } 76 | } 77 | ] 78 | -------------------------------------------------------------------------------- /src/components/Dashboard/widgets/example.vue: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 199 | 200 | 213 | -------------------------------------------------------------------------------- /src/components/ElCardCollapse.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 39 | 73 | -------------------------------------------------------------------------------- /src/components/FullScreenToggler.vue: -------------------------------------------------------------------------------- 1 | 2 | 92 | 93 | 98 | 99 | 111 | -------------------------------------------------------------------------------- /src/components/IgnoreMatchingPopover.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 109 | 110 | 154 | -------------------------------------------------------------------------------- /src/components/LoadingIndicator.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 63 | -------------------------------------------------------------------------------- /src/components/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 37 | 38 | 53 | -------------------------------------------------------------------------------- /src/directives/overflow-tooltip.js: -------------------------------------------------------------------------------- 1 | const setTooltip = (el, binding) => { 2 | // 设置内容 3 | el.innerText = binding.value 4 | 5 | const elComputed = document.defaultView.getComputedStyle(el, '') 6 | const padding = parseInt(elComputed.paddingLeft.replace('px', '')) + parseInt(elComputed.paddingRight.replace('px', '')) 7 | const range = document.createRange() 8 | range.setStart(el, 0) 9 | range.setEnd(el, el.childNodes.length) 10 | const rangeWidth = range.getBoundingClientRect().width 11 | const isEllipsis = rangeWidth + padding > el.offsetWidth || el.scrollWidth > el.offsetWidth 12 | 13 | // 鼠标移入时,将浮层元素插入到 body 中 14 | el.onmouseenter = function(e) { 15 | if (!isEllipsis) { return } 16 | // 创建浮层元素并设置样式 17 | const vcTooltipDom = document.createElement('div') 18 | // 设置 id 方便寻找 19 | vcTooltipDom.setAttribute('id', 'vc-tooltip') 20 | // 将浮层插入到 body 中 21 | document.body.appendChild(vcTooltipDom) 22 | // 浮层中的文字 通过属性值传递动态的显示文案 23 | document.getElementById('vc-tooltip').innerHTML = binding.value 24 | } 25 | // 鼠标移动时,动态修改浮层的位置属性 26 | el.onmousemove = function(e) { 27 | if (!isEllipsis) { return } 28 | const vcTooltipDom = document.getElementById('vc-tooltip') 29 | if (!vcTooltipDom) { return } 30 | const padding = 5 31 | let offsetX = e.clientX + 15 32 | let offsetY = e.clientY + 15 33 | // 判断是否超出视窗边界(横向) 34 | if (offsetX + vcTooltipDom.offsetWidth > document.documentElement.clientWidth) { 35 | offsetX = document.documentElement.clientWidth - vcTooltipDom.offsetWidth - padding 36 | } 37 | if (offsetX <= 0) { 38 | offsetX = padding 39 | vcTooltipDom.style.width = document.documentElement.clientWidth - padding * 2 + 'px' 40 | } 41 | // 判断是否超出视窗边界(纵向) 42 | if (offsetY + vcTooltipDom.offsetHeight > document.documentElement.clientHeight) { 43 | offsetY = document.documentElement.clientHeight - vcTooltipDom.offsetHeight - padding 44 | } 45 | if (offsetY <= 0) { 46 | offsetY = padding 47 | vcTooltipDom.style.height = document.documentElement.clientHeight - padding * 2 + 'px' 48 | } 49 | vcTooltipDom.style.left = offsetX + 'px' 50 | vcTooltipDom.style.top = offsetY + 'px' 51 | // 注:当浮层元素和窗口大小差不多时,浮层会覆盖原本的内容,导致浮层闪一下就不见了 52 | } 53 | // 鼠标移出时将浮层元素销毁 54 | el.onmouseleave = function() { 55 | if (!isEllipsis) { return } 56 | // 找到浮层元素并移出 57 | const vcTooltipDom = document.getElementById('vc-tooltip') 58 | vcTooltipDom && document.body.removeChild(vcTooltipDom) 59 | } 60 | } 61 | 62 | const insertStyle = () => { 63 | if (document.getElementById('vc-tooltip-style')) { return } 64 | const style = document.createElement('style') 65 | style.id = 'vc-tooltip-style' 66 | style.innerHTML = ` 67 | :root { 68 | --vc-tooltip-bg: #303133; 69 | --vc-tooltip-color: #fff; 70 | } 71 | #vc-tooltip { 72 | position: absolute; 73 | background: var(--vc-tooltip-bg); 74 | color: var(--vc-tooltip-color); 75 | font-size: 12px; 76 | z-index: 6000; 77 | padding: 10px; 78 | border-radius: 4px; 79 | line-height: 1.2; 80 | min-height: 10px; 81 | word-wrap: break-word; 82 | } 83 | #vc-tooltip::before { 84 | content: ""; 85 | position: absolute; 86 | width: 0; 87 | height: 0; 88 | border: 5px solid transparent; 89 | border-bottom-color: var(--vc-tooltip-bg); 90 | left: 0; 91 | bottom: 100%; 92 | margin-left: 15px; 93 | } 94 | `.replace(/\s+/g, ' ').trim() 95 | document.head.appendChild(style) 96 | } 97 | 98 | const plugin = { 99 | install(Vue) { 100 | Vue.directive('overflow-tooltip', { 101 | inserted: (el, binding) => { 102 | el.binding = binding 103 | // 设置元素样式 104 | Object.assign(el.style, { 105 | overflow: 'hidden', 106 | textOverflow: 'ellipsis', 107 | whiteSpace: 'nowrap', 108 | }) 109 | // 监控元素可见性变化 110 | const observer = new IntersectionObserver((entries) => { 111 | if (entries[0].isIntersecting) { 112 | setTooltip(el, el.binding) 113 | } 114 | }) 115 | observer.observe(el) 116 | // 监控元素宽度变化 117 | const resizeObserver = new ResizeObserver(() => { 118 | setTooltip(el, el.binding) 119 | }) 120 | resizeObserver.observe(el) 121 | // 插入样式 122 | insertStyle() 123 | // 设置浮层内容 124 | setTooltip(el, binding) 125 | }, 126 | update: (el, binding) => { 127 | el.binding = binding 128 | // 更新浮层内容 129 | setTooltip(el, binding) 130 | }, 131 | unbind: (el) => { 132 | el.onmouseenter = null 133 | el.onmousemove = null 134 | el.onmouseleave = null 135 | }, 136 | }) 137 | } 138 | } 139 | 140 | let GlobalVue = null 141 | if (typeof window !== 'undefined') { 142 | GlobalVue = window.Vue 143 | } else if (typeof global !== 'undefined') { 144 | GlobalVue = global.Vue 145 | } 146 | 147 | if (GlobalVue) { 148 | GlobalVue.use(plugin) 149 | } 150 | 151 | export default plugin 152 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | import '@/assets/styles/index.scss' 5 | import App from './App.vue' 6 | import router from '@/router' 7 | import SvgIcon from '@/components/SvgIcon.vue' 8 | import elTableSticky from '@cell-x/el-table-sticky' 9 | import vueMinderEditor from 'vue-minder-editor-extended' 10 | import ElCardCollapse from '@/components/ElCardCollapse.vue' 11 | import Clickoutside from 'element-ui/src/utils/clickoutside' 12 | import overflowTooltip from '@/directives/overflow-tooltip' 13 | import VxeUI from 'vxe-pc-ui' 14 | import 'vxe-pc-ui/lib/style.css' 15 | import VxeUITable from 'vxe-table' 16 | import 'vxe-table/lib/style.css' 17 | 18 | // register svg component globally 19 | Vue.component('SvgIcon', SvgIcon) 20 | // require all svg 21 | const requireAll = (requireContext) => requireContext.keys().map(requireContext) 22 | const req = require.context('@/assets/icons', false, /\.svg$/) 23 | requireAll(req) 24 | 25 | Vue.use(ElementUI) 26 | Vue.config.productionTip = false 27 | Vue.prototype.$fullRouter = router 28 | Vue.prototype.$homeRoute = router.options.routes.find(route => route.name === 'home') 29 | Vue.use(elTableSticky) 30 | Vue.use(vueMinderEditor) 31 | Vue.component('ElCardCollapse', ElCardCollapse) 32 | // v-clickoutside 指令,用于点击元素外部触发事件 33 | Vue.directive('clickoutside', Clickoutside) 34 | // v-focus 指令,用于元素聚焦 35 | Vue.directive('focus', { 36 | inserted: function (el) { 37 | el.focus() 38 | } 39 | }) 40 | // v-overflow-tooltip 指令,用于元素内容溢出时显示 tooltip 41 | Vue.use(overflowTooltip) 42 | Vue.use(VxeUI) 43 | Vue.use(VxeUITable) 44 | 45 | new Vue({ 46 | router, 47 | render: h => h(App) 48 | }).$mount('#app') 49 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | // This file is automatically generated by gen-router.js, please do not modify it manually! 2 | import VueRouter from 'vue-router' 3 | import Vue from 'vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | name: 'home', 11 | meta: { description: '首页' }, 12 | component: () => import(/* webpackChunkName: "home" */ '@/views/home'), 13 | }, 14 | { 15 | path: '/dashboard/index', 16 | name: 'dashboardIndex', 17 | meta: { description: '拖拽式仪表盘' }, 18 | component: () => import(/* webpackChunkName: "dashboardIndex" */ '@/views/dashboard/index'), 19 | }, 20 | { 21 | path: '/apple-devices-preview', 22 | name: 'appleDevicesPreview', 23 | meta: { description: '网站预览图生成' }, 24 | component: () => import(/* webpackChunkName: "appleDevicesPreview" */ '@/views/apple-devices-preview'), 25 | }, 26 | { 27 | path: '/aside-toggle-drag', 28 | name: 'asideToggleDrag', 29 | meta: { description: '侧栏折叠和托拽组件' }, 30 | component: () => import(/* webpackChunkName: "asideToggleDrag" */ '@/views/aside-toggle-drag'), 31 | }, 32 | { 33 | path: '/card-collapse', 34 | name: 'cardCollapse', 35 | meta: { description: '可折叠的 el-card' }, 36 | component: () => import(/* webpackChunkName: "cardCollapse" */ '@/views/card-collapse'), 37 | }, 38 | { 39 | path: '/code-diff', 40 | name: 'codeDiff', 41 | meta: { description: 'code diff 测试' }, 42 | component: () => import(/* webpackChunkName: "codeDiff" */ '@/views/code-diff'), 43 | }, 44 | { 45 | path: '/custom-directive', 46 | name: 'customDirective', 47 | meta: { description: '自定义指令测试' }, 48 | component: () => import(/* webpackChunkName: "customDirective" */ '@/views/custom-directive'), 49 | }, 50 | { 51 | path: '/dom-to-image', 52 | name: 'domToImage', 53 | meta: { description: 'dom-to-image vs dom-to-image-more' }, 54 | component: () => import(/* webpackChunkName: "domToImage" */ '@/views/dom-to-image'), 55 | }, 56 | { 57 | path: '/fullscreen', 58 | name: 'fullscreen', 59 | meta: { description: '全屏切换方案' }, 60 | component: () => import(/* webpackChunkName: "fullscreen" */ '@/views/fullscreen'), 61 | }, 62 | { 63 | path: '/guide', 64 | name: 'guide', 65 | meta: { description: 'driver.js demo' }, 66 | component: () => import(/* webpackChunkName: "guide" */ '@/views/guide'), 67 | }, 68 | { 69 | path: '/icons', 70 | name: 'icons', 71 | meta: { description: 'icon 使用范例' }, 72 | component: () => import(/* webpackChunkName: "icons" */ '@/views/icons'), 73 | }, 74 | { 75 | path: '/loading-indicator', 76 | name: 'loadingIndicator', 77 | meta: { description: 'CSS 实现 Postman loading 效果' }, 78 | component: () => import(/* webpackChunkName: "loadingIndicator" */ '@/views/loading-indicator'), 79 | }, 80 | { 81 | path: '/minder-editor', 82 | name: 'minderEditor', 83 | meta: { description: '脑图编辑器 demo' }, 84 | component: () => import(/* webpackChunkName: "minderEditor" */ '@/views/minder-editor'), 85 | }, 86 | { 87 | path: '/overflow-tooltip', 88 | name: 'overflowTooltip', 89 | meta: { description: 'v-overflow-tooltip 指令' }, 90 | component: () => import(/* webpackChunkName: "overflowTooltip" */ '@/views/overflow-tooltip'), 91 | }, 92 | { 93 | path: '/set-height-adaptive', 94 | name: 'setHeightAdaptive', 95 | meta: { description: 'v-height-adaptive 高度自适应的表格' }, 96 | component: () => import(/* webpackChunkName: "setHeightAdaptive" */ '@/views/set-height-adaptive'), 97 | }, 98 | { 99 | path: '/sticky-fixed-col', 100 | name: 'stickyFixedCol', 101 | meta: { description: 'v-sticky-header 固定列表格' }, 102 | component: () => import(/* webpackChunkName: "stickyFixedCol" */ '@/views/sticky-fixed-col'), 103 | }, 104 | { 105 | path: '/sticky-scroller', 106 | name: 'stickyScroller', 107 | meta: { description: 'v-sticky-scroller 固定横向滚动条' }, 108 | component: () => import(/* webpackChunkName: "stickyScroller" */ '@/views/sticky-scroller'), 109 | }, 110 | { 111 | path: '/sticky-sum', 112 | name: 'stickySum', 113 | meta: { description: 'v-sticky-footer 表尾合计行' }, 114 | component: () => import(/* webpackChunkName: "stickySum" */ '@/views/sticky-sum'), 115 | }, 116 | { 117 | path: '/translate-js', 118 | name: 'translateJs', 119 | meta: { description: '测试在 Vue 中使用 translate.js' }, 120 | component: () => import(/* webpackChunkName: "translateJs" */ '@/views/translate-js'), 121 | }, 122 | { 123 | path: '/vxe-table', 124 | name: 'vxeTable', 125 | meta: { description: 'vxe-table' }, 126 | component: () => import(/* webpackChunkName: "vxeTable" */ '@/views/vxe-table'), 127 | }, 128 | { 129 | path: '/vxe-table2', 130 | name: 'vxeTable2', 131 | meta: { description: 'vxe-table custom col' }, 132 | component: () => import(/* webpackChunkName: "vxeTable2" */ '@/views/vxe-table2'), 133 | }, 134 | ] 135 | 136 | const router = new VueRouter({ 137 | mode: 'hash', 138 | routes, 139 | }) 140 | 141 | router.afterEach((to) => { 142 | document.title = `${to.meta?.description} - VUE-EL-DEMO` 143 | }) 144 | 145 | export default router 146 | -------------------------------------------------------------------------------- /src/views/apple-devices-preview.vue: -------------------------------------------------------------------------------- 1 | 2 |