├── .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 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
41 |
48 |
53 |
57 |
58 |
59 |
68 |
69 | {{ row.meta && row.meta.description }}
70 |
71 |
72 |
73 |
74 |
75 |
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 |
2 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
145 |
146 |
204 |
--------------------------------------------------------------------------------
/src/components/Dashboard/DialogEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 紧凑布局
27 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 左侧
43 | 右侧
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 取 消
54 | 确 定
55 |
56 |
57 |
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 |
4 |
5 |
129 |
136 |
146 |
150 |
151 |
152 |
159 |
164 | {{ item.category }} ({{ item.components.length }})
165 |
166 |
167 |
168 |
169 |
203 |
204 |
205 |
206 |
219 |
220 |
240 |
241 |
242 |
252 |
253 |
254 |
261 |
268 |
275 |
282 |
283 |
284 |
285 |
290 | 新手引导
291 |
292 |
293 |
294 |
295 |
296 |
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 |
16 |
17 |
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 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
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 |
3 |
4 | {{ params.content }}
5 |
10 |
17 |
18 |
19 |
20 |
21 |
22 | 取消
23 | 保存
24 |
25 |
26 |
27 |
28 |
29 |
199 |
200 |
213 |
--------------------------------------------------------------------------------
/src/components/ElCardCollapse.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
39 |
73 |
--------------------------------------------------------------------------------
/src/components/FullScreenToggler.vue:
--------------------------------------------------------------------------------
1 |
2 |
92 |
93 |
98 |
99 |
111 |
--------------------------------------------------------------------------------
/src/components/IgnoreMatchingPopover.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ keyword }}
27 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
109 |
110 |
154 |
--------------------------------------------------------------------------------
/src/components/LoadingIndicator.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
16 |
17 |
63 |
--------------------------------------------------------------------------------
/src/components/SvgIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
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 |
3 |
30 |
31 |
32 |
150 |
151 |
262 |
--------------------------------------------------------------------------------
/src/views/aside-toggle-drag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 左侧栏内容
6 |
7 |
8 |
9 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Tempora perspiciatis sunt nam mollitia suscipit totam! Libero cupiditate eum suscipit adipisci eos, rerum sit iure ducimus facilis corrupti ut sint a.
10 | Dignissimos fuga voluptatibus, vel, dolores doloremque quis aliquid quasi, unde rem asperiores veniam officia cumque libero ducimus perferendis? Cum consequuntur quibusdam officiis quo nam aliquam, aut architecto. Recusandae, tenetur aperiam.
11 | Praesentium recusandae aliquid dolores, dignissimos sequi veritatis totam aliquam error similique laborum nemo explicabo commodi ex, eos dolore tempora. Aperiam aliquid necessitatibus, veritatis totam ipsum quia id perspiciatis voluptatibus? Aliquid?
12 | Autem, sed dicta illo atque quas voluptatem dolor itaque illum quaerat nihil iusto laborum, eius ad aliquam. At consequuntur nesciunt dolores expedita libero veniam quos animi repellendus, in, ad ratione?
13 | Qui, dolores praesentium. Dolorum, vero ipsam exercitationem excepturi placeat nihil nobis. Quas, nostrum nemo. Harum earum accusantium blanditiis eligendi veritatis architecto reprehenderit quo sapiente odio, eaque laborum, ullam possimus error?
14 | Labore dolorum tenetur exercitationem! Cupiditate assumenda consequatur tempora accusamus. Perspiciatis expedita ipsum deleniti laudantium alias in, voluptates reiciendis sunt porro labore odit error adipisci quo, ea sed repellendus accusantium illum.
15 | Dignissimos, error inventore. Officia necessitatibus fugit neque magni reiciendis placeat vel voluptate assumenda, ducimus, modi rerum dicta id tempore quae error, eaque et sit illum culpa quasi ratione eveniet doloribus?
16 | Dicta ut porro, totam perferendis cupiditate illum dignissimos quam necessitatibus culpa, iste molestiae aliquid, quis quidem inventore aspernatur. Veniam similique, placeat quo deleniti aut cum modi provident magnam libero nostrum.
17 | Excepturi, labore! Nisi aliquam eaque, officiis eveniet et quia rem cum libero ut culpa aut voluptas perferendis sed dignissimos laudantium repudiandae a optio repellat? Placeat assumenda numquam reiciendis nobis magni?
18 | Dicta maiores deserunt numquam, laudantium a distinctio nisi quidem, culpa totam amet rem aspernatur magnam doloribus obcaecati aut reprehenderit temporibus ab sint possimus aliquid? Dolorum numquam minima voluptatibus? Ex, facere.
19 | Eius totam quia expedita atque quae suscipit quam. Optio ad alias soluta neque vitae! Possimus, itaque ullam quia vel, ipsum distinctio aliquid expedita fugiat voluptatum totam non consectetur laudantium quidem!
20 | Officia voluptatem atque aliquid! Ratione unde, tempora ipsam porro doloribus quasi provident, harum voluptatem, reprehenderit quos sunt voluptates quia! Earum, praesentium! In nesciunt optio iusto fugit ab quasi sunt libero.
21 | Vitae, amet. Voluptatem reprehenderit sint, officiis dicta suscipit voluptates corporis expedita pariatur, unde laudantium in blanditiis deserunt magnam at! Voluptate iste vitae exercitationem tempore perferendis eaque commodi aut laudantium hic?
22 | Aliquam dolore voluptatum impedit officiis itaque ab alias accusantium, corporis qui repudiandae sit cum nihil unde ratione beatae. Debitis repellendus facilis soluta quos sit esse, suscipit vitae beatae tempora tempore!
23 | Optio at id sint facere minus officia eveniet eius quod sapiente doloribus perspiciatis sunt, quos, exercitationem delectus, nisi dolor nesciunt? Facilis odit officia tempora neque repudiandae unde accusantium explicabo vero!
24 | Velit doloribus debitis odio inventore cum beatae quod eligendi laboriosam ut! Soluta ratione aperiam tempora optio ea placeat, recusandae officia veniam eligendi, amet iusto nihil, provident blanditiis dignissimos facere aspernatur.
25 | Ullam quae beatae inventore, dolore minima numquam aut eum consequatur perferendis odio delectus aliquam corporis ipsam omnis cupiditate, amet, nam quaerat commodi totam doloremque obcaecati quibusdam iure? Minima, labore quos!
26 | Ex obcaecati quidem, molestias, delectus possimus ipsa nulla consectetur autem sequi iste vitae dolorum, veniam quam. Quia provident ducimus exercitationem libero inventore, iusto ratione voluptate dolorem beatae. Aperiam, sed recusandae!
27 | A labore natus rem fuga, omnis neque perferendis maiores iusto nemo similique, assumenda expedita accusamus reiciendis corrupti numquam excepturi, deserunt nihil. Magnam, mollitia recusandae a iure cumque dolorem ipsa consequuntur?
28 | Dolorem voluptates ad consectetur repudiandae sit vero accusantium debitis veniam odio corporis, iste odit, quidem cumque? Omnis est illo ea, quasi, voluptates illum, beatae officiis iure blanditiis optio enim obcaecati!
29 | Sit eveniet molestiae in vitae enim tempore culpa vel aliquid laborum eaque expedita commodi reprehenderit architecto veniam perferendis aspernatur repudiandae obcaecati, doloribus odit. Nobis cupiditate consectetur rem ut quos repellendus!
30 | Numquam quis, sit amet autem in et tenetur sunt. Itaque autem repellendus iure nostrum labore qui laboriosam sed esse quibusdam, dignissimos, sit hic eum ea! A tempora molestias illo nam?
31 | Nostrum beatae doloribus magni vero ducimus dolorem optio necessitatibus ullam, facere quae qui quaerat nobis non ad cupiditate dignissimos quo et. Itaque animi asperiores minus delectus corporis? Laboriosam, ducimus provident?
32 | Facilis maxime, corporis doloribus molestiae corrupti nisi sed nemo. Incidunt suscipit id ea minima blanditiis asperiores accusantium tenetur delectus, repellendus, veritatis vero non, explicabo repudiandae quaerat laboriosam harum fugiat expedita.
33 | Natus nobis reprehenderit alias amet vel blanditiis ea? Eaque accusamus est saepe nihil illum. Illo veniam temporibus, repudiandae mollitia totam sint nihil nam quo, explicabo maiores vel, aliquid minima nulla.
34 | Omnis velit facere, repudiandae magnam suscipit tenetur distinctio esse laborum quidem, natus quia libero necessitatibus totam quos nesciunt iste doloribus ullam! Consequuntur accusamus aliquid nisi eveniet incidunt, rem nesciunt adipisci.
35 | Voluptatibus veritatis iste provident repellendus dolorem nihil unde quasi quam, eveniet veniam, laudantium in laboriosam officiis iusto, nemo id sapiente eius voluptatum? Nobis, vel quasi possimus eos et temporibus a.
36 | Nobis nostrum, est aspernatur veritatis ducimus distinctio, a temporibus eum dolore commodi vero excepturi. Veritatis illum necessitatibus optio nulla quam assumenda voluptatum culpa nam id? Aut accusantium ipsum nulla excepturi.
37 | Reiciendis, sed? Id eos vero neque asperiores nihil dolorem repudiandae quia nostrum laboriosam ratione quos sit voluptatum ex blanditiis, quasi accusamus officia. Inventore velit repellat necessitatibus laudantium qui omnis perspiciatis?
38 | Fugiat aliquam cupiditate eveniet quibusdam. Molestias omnis iure ratione assumenda alias expedita, numquam, culpa temporibus quis vitae voluptatum quibusdam amet facere. Dignissimos ipsum molestiae, omnis culpa excepturi explicabo laborum porro.
39 |
40 |
41 |
42 | 右侧栏内容
43 |
44 |
45 |
46 |
47 |
63 |
64 |
78 |
--------------------------------------------------------------------------------
/src/views/card-collapse.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 卡片名称
7 | 操作按钮
8 |
9 |
10 | {{ '列表内容 ' + o }}
11 |
12 |
13 |
14 |
15 |
16 |
26 |
27 |
46 |
--------------------------------------------------------------------------------
/src/views/code-diff.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
15 |
16 |
23 |
24 | +{{ stat.additionsNum }} 增
25 | -{{ stat.deletionsNum }} 减
26 | ±{{ stat.ignoreAdditionsNum + stat.ignoreDeletionsNum }} 忽略
30 | ignoreMatchingLines = val"
33 | />
34 |
35 |
36 |
37 |
38 |
39 |
58 |
59 |
66 |
--------------------------------------------------------------------------------
/src/views/custom-directive.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 | 点我
12 |
13 |
{{ message }}
14 |
15 |
16 |
17 |
42 |
43 |
46 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
32 |
--------------------------------------------------------------------------------
/src/views/dashboard/widgets/charts/bar-test-types.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
34 |
35 |
36 |
45 |
46 |
47 |
57 |
58 |
59 |
60 | 取 消
61 | 确 定
62 |
63 |
64 |
65 |
66 |
67 |
295 |
296 |
331 |
--------------------------------------------------------------------------------
/src/views/dashboard/widgets/charts/liquid-activity-rate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
32 |
33 |
41 |
42 |
配置组件
43 |
{{ title }}
44 |
45 |
46 |
47 |
55 |
61 |
62 |
63 |
64 |
74 |
75 |
76 |
77 | 取 消
78 | 确 定
79 |
80 |
81 |
82 |
83 |
84 |
358 |
359 |
390 |
--------------------------------------------------------------------------------
/src/views/dashboard/widgets/daily/hitokoto.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
{{ sentence.hitokoto }}
19 |
23 | {{ sentence.from }}
24 | {{ sentence.from_who }}
25 |
26 |
27 |
28 |
36 |
37 |
配置组件
38 |
{{ title }}
39 |
40 |
41 |
42 |
43 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 取 消
68 | 确 定
69 |
70 |
71 |
72 |
73 |
74 |
195 |
196 |
309 |
--------------------------------------------------------------------------------
/src/views/dom-to-image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
dom-to-image vs dom-to-image-more
5 |
6 | Select a PNG image 👇
7 | view source
8 |
9 |
10 |
Download
11 |
12 |
Download More
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
63 |
--------------------------------------------------------------------------------
/src/views/fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
53 |
54 |
88 |
--------------------------------------------------------------------------------
/src/views/guide.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | Nav Item 1
10 | Nav Item 2
11 | Nav Item 3
12 |
13 |
14 |
21 |
24 |
25 |
26 |
27 |
53 |
54 |
116 |
--------------------------------------------------------------------------------
/src/views/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
16 |
20 |
21 |
22 |
31 |
32 | {{ row.meta && row.meta.description }}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
56 |
--------------------------------------------------------------------------------
/src/views/icons.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Svg Icon
5 |
6 |
7 |
8 |
9 |
10 |
15 |
--------------------------------------------------------------------------------
/src/views/loading-indicator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
--------------------------------------------------------------------------------
/src/views/minder-editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
69 |
--------------------------------------------------------------------------------
/src/views/overflow-tooltip.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
v-overflow-tooltip
6 |
7 |
文本溢出(100px):
8 |
12 |
13 |
14 |
15 |
未溢出(1000px):
16 |
20 |
21 |
22 |
el-tooltip
23 |
26 | {{ content }}
27 |
28 |
29 |
30 |
31 |
41 |
--------------------------------------------------------------------------------
/src/views/set-height-adaptive.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 表格设置 height
属性自带固定表头和横向滚动条效果,配合 v-height-adaptive
指令实现表格高度自适应
7 |
8 |
9 |
16 |
21 |
26 |
30 |
31 |
36 |
37 | v-height-adaptive
的 offsetBottom
支持响应式
38 |
39 |
40 |
41 |
42 |
43 |
63 |
--------------------------------------------------------------------------------
/src/views/sticky-fixed-col.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
16 |
22 |
29 |
34 |
39 |
44 |
49 |
54 |
55 |
56 | 查看
57 |
58 |
59 | 编辑
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
94 |
104 |
--------------------------------------------------------------------------------
/src/views/sticky-scroller.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
16 |
22 |
29 |
34 |
39 |
44 |
49 |
54 |
55 |
56 | 查看
57 |
58 |
59 | 编辑
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
94 |
104 |
--------------------------------------------------------------------------------
/src/views/sticky-sum.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
17 |
22 |
27 |
32 |
37 |
38 |
39 |
40 |
41 |
89 |
--------------------------------------------------------------------------------
/src/views/translate-js.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
本地语言: {{ localLanguage }}
5 |
当前语言: {{ currentLanguage }}
6 |
7 |
8 | 博客(英语:Blog)是一种在线日记型式的个人网站,借由张帖子章、图片或视频来记录生活、抒发情感或分享信息。博客上的文章通常根据张贴时间,以倒序方式由新到旧排列。
9 |
10 |
11 | 按钮切换语言:
12 |
20 | {{ lang.label }}
21 |
22 |
28 | 重置
29 |
30 |
31 | select 选择框切换语言:
32 |
33 |
34 |
35 |
36 |
73 |
74 |
77 |
--------------------------------------------------------------------------------
/src/views/vxe-table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 新增
7 | 删除选中
8 | 获取选中
9 | 获取新增
10 | 获取删除
11 | 获取修改
12 | 还原
13 |
14 |
15 |
16 |
28 |
29 |
30 |
31 |
32 | custom: {{ row.name }}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
96 |
97 |
100 |
--------------------------------------------------------------------------------
/src/views/vxe-table2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
47 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | const path = require('path')
3 |
4 | function resolve(dir) {
5 | return path.join(__dirname, dir)
6 | }
7 |
8 | module.exports = defineConfig({
9 | publicPath: process.env.NODE_ENV === 'production' ? '/vue-el-demo/' : '/',
10 | transpileDependencies: true,
11 | chainWebpack: (config) => {
12 | // set svg-sprite-loader
13 | const svgPath = resolve('src/assets/icons')
14 | config.module
15 | .rule('svg')
16 | .exclude.add(svgPath)
17 | .end()
18 | config.module
19 | .rule('svg-icon')
20 | .test(/.svg$/)
21 | .include.add(svgPath)
22 | .end()
23 | .use('svg-sprite-loader')
24 | .loader('svg-sprite-loader')
25 | .options({
26 | symbolId: 'icon-[name]',
27 | })
28 | .end()
29 | // remove origin svg fill attr
30 | .use('svgo-loader')
31 | .loader('svgo-loader')
32 | .tap((options) => ({
33 | ...options,
34 | // 删除 svg 中 fill 和 fill-rule
35 | plugins: [{ name: 'removeAttrs', params: { attrs: 'fill|fill-rule' } }],
36 | }))
37 | .end()
38 | },
39 | })
40 |
--------------------------------------------------------------------------------