├── .gitignore ├── .npmrc ├── Dockerfile ├── LICENSE ├── README.md ├── arya.config.sample.js ├── commands ├── clear-commit-msg.sh ├── deploy.sh └── preview.js ├── jest.config.js ├── nginx.conf ├── package.json ├── public ├── favicon.ico ├── img │ └── icons │ │ ├── android-chrome-96x96.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg ├── index.html ├── manifest.json ├── mockup.png ├── robots.txt ├── service-worker.js └── site.webmanifest ├── src ├── App.vue ├── assets │ ├── icons │ │ ├── adjust.svg │ │ ├── author.svg │ │ ├── document.svg │ │ ├── download.svg │ │ ├── explore.svg │ │ ├── full-screen.svg │ │ ├── github.svg │ │ ├── homepage.svg │ │ ├── index.js │ │ ├── preview.svg │ │ ├── set-style.svg │ │ ├── setting.svg │ │ ├── upload.svg │ │ ├── wechat.svg │ │ └── x.svg │ ├── images │ │ ├── logo.png │ │ ├── markdown-white.png │ │ └── markdown.png │ └── styles │ │ ├── common.less │ │ ├── element.less │ │ ├── element │ │ ├── alert.css │ │ ├── aside.css │ │ ├── autocomplete.css │ │ ├── badge.css │ │ ├── base.css │ │ ├── breadcrumb-item.css │ │ ├── breadcrumb.css │ │ ├── button-group.css │ │ ├── button.css │ │ ├── card.css │ │ ├── carousel-item.css │ │ ├── carousel.css │ │ ├── cascader.css │ │ ├── checkbox-button.css │ │ ├── checkbox-group.css │ │ ├── checkbox.css │ │ ├── col.css │ │ ├── collapse-item.css │ │ ├── collapse.css │ │ ├── color-picker.css │ │ ├── container.css │ │ ├── date-picker.css │ │ ├── dialog.css │ │ ├── display.css │ │ ├── dropdown-item.css │ │ ├── dropdown-menu.css │ │ ├── dropdown.css │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ ├── footer.css │ │ ├── form-item.css │ │ ├── form.css │ │ ├── header.css │ │ ├── icon.css │ │ ├── index.css │ │ ├── input-number.css │ │ ├── input.css │ │ ├── loading.css │ │ ├── main.css │ │ ├── menu-item-group.css │ │ ├── menu-item.css │ │ ├── menu.css │ │ ├── message-box.css │ │ ├── message.css │ │ ├── notification.css │ │ ├── option-group.css │ │ ├── option.css │ │ ├── pagination.css │ │ ├── popover.css │ │ ├── popper.css │ │ ├── progress.css │ │ ├── radio-button.css │ │ ├── radio-group.css │ │ ├── radio.css │ │ ├── rate.css │ │ ├── reset.css │ │ ├── row.css │ │ ├── scrollbar.css │ │ ├── select-dropdown.css │ │ ├── select.css │ │ ├── slider.css │ │ ├── spinner.css │ │ ├── step.css │ │ ├── steps.css │ │ ├── submenu.css │ │ ├── switch.css │ │ ├── tab-pane.css │ │ ├── table-column.css │ │ ├── table.css │ │ ├── tabs.css │ │ ├── tag.css │ │ ├── time-picker.css │ │ ├── time-select.css │ │ ├── tooltip.css │ │ ├── transfer.css │ │ ├── tree.css │ │ └── upload.css │ │ ├── mixins.less │ │ ├── style.less │ │ └── variables.less ├── components │ ├── Advertisement.vue │ ├── Icon.vue │ ├── PreviewVditor.vue │ └── icons │ │ └── Arrow.vue ├── config │ ├── aboutArya.js │ ├── constant.js │ └── default.js ├── global.js ├── helper │ ├── document.js │ ├── export.js │ ├── index.js │ ├── lodash.js │ └── utils.js ├── main.js ├── mixins │ └── metaMixin.js ├── pages │ ├── About.vue │ ├── ExportImage.vue │ ├── ExportPPT.vue │ ├── ExportPdf.vue │ ├── Main.vue │ └── partials │ │ ├── Frame.vue │ │ ├── HeaderNav.vue │ │ └── NotFound.vue ├── registerServiceWorker.js └── router │ ├── beforeEachHooks.js │ ├── commonRoutes.js │ ├── index.js │ └── routes │ ├── index.js │ └── main.js ├── tests └── unit │ ├── .eslintrc.js │ └── Arrow.spec.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | arya.config.js 6 | 7 | # package-lock 8 | package-lock.json 9 | 10 | # ignore json 11 | size-plugin.json 12 | 13 | # local env files 14 | .env.local 15 | .env.*.local 16 | 17 | # Log files 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Editor directories and files 23 | .idea 24 | .vscode 25 | *.suo 26 | *.ntvs* 27 | *.njsproj 28 | *.sln 29 | *.sw* 30 | 31 | # Test 32 | tests/coverage 33 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry="https://registry.npmmirror.com" -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | COPY dist /usr/share/nginx/html 4 | COPY nginx.conf /etc/nginx/conf.d/default.conf 5 | 6 | EXPOSE 80 7 | 8 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JadeYang(杨琼璞) 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 |

2 | 3 |

4 | Arya - 在线 Markdown 编辑器 5 |

6 | 7 |
8 | 9 | 📝 基于 Vue2Vditor,所构建的在线 Markdown 编辑器,支持绘制流程图、甘特图、时序图、任务列表、Echarts 图表、五线谱,以及 PPT 预览、视频音频解析、HTML 自动转换为 Markdown 等功能。markdown.lovejade.cn。 10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 | GitHub package.json version 18 | 19 | 20 | LICENSE 21 | 22 | 23 | Prettier 24 | 25 | 26 | Gitmoji 30 | 31 | 32 | Arya - 在线 Markdown 编辑器 33 | 34 | 35 | Chat On V2ex 36 | 37 | 38 | Author nicejade 39 | 40 |
41 | 42 | ## 背景初衷 43 | 44 | 早期就有关注到由`黑客派`所出品的 [Vditor](https://github.com/Vanessa219/vditor):一款为未来而构建的下一代 Markdown 编辑器。然,现而今市面上所存在的 Markdown 编辑器,或多或少都存在一些问题(或功能不全,或高级功能收费...),因此基于自身所需,加之 [Vditor](https://github.com/Vanessa219/vditor) 的强大,就诞生了做一款[在线 Markdown 编辑器](https://markdown.lovejade.cn/?ref=github.com) 的念头;取其名曰 [`Arya`(二丫)](https://quickapp.lovejade.cn/talking-game-of-thrones/?ref=github.com)。 45 | 46 | ## 功能支持 47 | 48 | - [x] 🎉 通常 `Markdown` 解析器自带的基本功能; 49 | - [x] 🍀 支持**流程图**、**甘特图**、**时序图**、**任务列表**; 50 | - [x] 🏁 支持粘贴 HTML 自动转换为 Markdown; 51 | - [x] 💃🏻 支持插入原生 Emoji、设置常用表情列表; 52 | - [x] 🚑 支持编辑内容保存**本地存储**,防止意外丢失; 53 | - [x] 📝 支持**实时预览**,主窗口大小拖拽,字符计数; 54 | - [x] 🛠 支持常用快捷键(**Tab**),及代码块添加复制; 55 | - [x] ✨ 支持**导出**携带样式的 PDF、PNG、JPEG 等; 56 | - [x] ✨ 升级 Vditor,新增对 `echarts` 图表的支持; 57 | - [x] ✨ 注入 [RevealJs](https://revealjs.com/#/),增设对 `PPT` 预览支持; 58 | - [x] 👏 支持检查并格式化 Markdown 语法,使其专业; 59 | - [x] 🦑 支持五线谱、及[部分站点、视频、音频解析](https://github.com/Vanessa219/vditor); 60 | - [x] 🌟 增加对**所见即所得**编辑模式的支持(`⌘-⇧-M`); 61 | - [x] 🌟 新增复制到微信公众号等周边功能; 62 | - [x] 🌟 支持导入本地 Markdown(`*.md`) 文件; 63 | 64 | ## 如何使用 65 | 66 | 清空目前 [Arya](https://markdown.lovejade.cn/?ref=github.com) 编辑区默认文档,即可使用。 67 | 68 | 默认为[所见即所得](https://b3log.org/vditor/)模式,可通过 `⌘-⇧-M`(`Ctrl-⇧-M`)进行切换;或通过以下方式: 69 | 70 | - 所见即所得:`⌘-⌥-7`(`Ctrl-alt-7`); 71 | - 即时渲染:`⌘-⌥-8`(`Ctrl-alt-8`); 72 | - 分屏渲染:`⌘-⌥-9`(`Ctrl-alt-9`); 73 | 74 | ### PPT 预览 75 | 76 | 如果您用作 `PPT` 预览(入口在`设置`中),需要注意,这里暂还不能支持各种图表的渲染;您可以使用 `---` 来定义水平方向上幻灯片,用 `--` 来定义垂直幻灯片;更多设定可以参见 [Reveal.js Markdown 文档](https://revealjs.com/markdown/)。 77 | 78 | ## 如何部署? 79 | 80 | ### 采用 [pm2](https://pm2.keymetrics.io/) 部署 81 | 82 | PM2 是一个强大的生产环境进程管理器,它不仅支持通过命令行启动应用,还可以使用配置文件(通常名为 `ecosystem.config.js`)来管理复杂的部署场景。您可以通过执行如下命令实现快速部署: 83 | 84 | ```bash 85 | # 🎉 克隆项目 86 | git clone https://github.com/nicejade/markdown-online-editor.git 87 | cd markdown-online-editor 88 | 89 | # ➕ 安装依赖 90 | yarn 91 | yarn global add pm2 92 | 93 | # 🔧 构建产物 94 | yarn build 95 | 96 | # 🚀 部署服务 97 | cd dist 98 | npx pm2 start "npx http-server -p 8866" --name "markdown-editor" 99 | ``` 100 | 101 | ### Docker 自托管 102 | 103 | 已将最新版本使用 [Docker](https://docs.docker.com/engine/install/)  打包镜像并上传至  [Docker Hub](https://hub.docker.com/r/nicejade/markdown-online-editor),可通过如下方式进行使用: 104 | 105 | ```shell 106 | docker run -d -p [Your-Specified-Port]:80 nicejade/markdown-online-editor:[tagname] 107 | 108 | # 示例: 109 | docker run -d -p 8866:80 nicejade/markdown-online-editor:latest 110 | ``` 111 | 112 | 基于如上示例,如果您在本地执行,只需打开网址——[http://localhost:8866](http://localhost:8866/) 即可访问。如果在服务器运行,可以通过 http://[Server-IP]:8866 来访问。构建 Docker 镜像过程,可参见:[如何为 markdown-online-editor 服务构建 docker 镜像?](https://memo.lovejade.cn/m/49a7b493bddeed71)。您也可以通过 **Cloudflare Pages** 或 Github Pages,托管编译产物(`dist` 目录下内容),从而实现**无需服务器情况下快速部署**。 113 | 114 | 或者**使用 [docker compose](https://docs.docker.com/compose/)**: 115 | 116 | 创建一个 `docker-compose.yml` 文件,并在其中定义服务(其中 version: '3' 指定了 Docker Compose 文件的版本;您可以根据实际情况进行调整): 117 | 118 | ```yaml 119 | version: '3' 120 | services: 121 | markdown-editor: 122 | image: nicejade/markdown-online-editor:latest 123 | ports: 124 | - '8866:80' 125 | restart: always 126 | ``` 127 | 128 | 在包含 `docker-compose.yml` 文件的目录中,运行以下命令启动服务: 129 | 130 | ```bash 131 | docker-compose up -d 132 | ``` 133 | 134 | 这将在后台启动服务,并且效果与下面的  `docker run`  命令相同。使用 Docker Compose 可以更方便地管理多个容器,并且配置更易读和维护。 135 | 136 | ## 如何开发 137 | 138 | ### 先决条件 139 | 140 | 说明用户在安装和使用前,需要准备的一些先决条件,譬如:您需要安装或升级  [Node.js](https://nodejs.org/en/)(>= `16.*`,< `18.*`),推荐使用  [Pnpm](https://pnpm.io/)  或  [Yarn](https://www.jeffjade.com/2017/12/30/135-npm-vs-yarn-detial-memo/)  作为首选包管理工具。 141 | 142 | ```bash 143 | # 🎉 克隆项目 144 | git clone https://github.com/nicejade/markdown-online-editor.git 145 | cd markdown-online-editor 146 | 147 | # ➕ 安装依赖 148 | yarn 149 | 150 | # 🚧 开始开发 151 | yarn start 152 | 153 | # 🚀 部署 Github Pages(需修改 commands/deploy.sh) 154 | yarn deploy 155 | ``` 156 | 157 | ## 特别鸣谢 158 | 159 | [Arya](https://markdown.lovejade.cn/?utm_source=github.com) 的产生,得益于 [Vditor](https://github.com/b3log/vditor):一款浏览器端的 Markdown 编辑器,同时也离不开 [Vue、Reveal.js 等开源库](https://github.com/nicejade/markdown-online-editor/blob/master/package.json#L25-L64)的支持,感谢 🙌。 160 | 161 | ## 相关链接 162 | 163 | - [清风明月轩](https://www.thebettersites.com/?ref=github.com) 164 | - [逍遥自在轩](https://www.niceshare.site/?ref=github.com) 165 | - [晚晴幽草轩](https://www.jeffjade.com/nicelinks?ref=github.com) 166 | - [缘知随心庭](https://fine.niceshare.site/?ref=github.com) 167 | - [静轩之别苑](https://quickapp.lovejade.cn/?ref=github.com) 168 | - [悠然宜想亭](https://forum.lovejade.cn/?ref=github.com) 169 | - [SegmentFault](https://segmentfault.com/u/jeffjade) 170 | - [X(Twitter)](https://x.com/MarshalXuan) 171 | - [@MarshalXuan](https://www.youtube.com/@MarshalXuan) 172 | 173 | ## License 174 | 175 | [MIT](http://opensource.org/licenses/MIT) 176 | 177 | Copyright (c) 2018-present, [nicejade](https://www.thebettersites.com). 178 | -------------------------------------------------------------------------------- /arya.config.sample.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | /** 4 | * @desc executablePath: Path to a Chromium or Chrome executable to run instead of the bundled Chromium. 5 | * https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions 6 | * 此项目 prerender-spa-plugin 来预渲染 HTML;而此库基于 Puppeteer 所完成; 7 | * 因此在安装时需要下载 Chromium,详见:https://www.jeffjade.com/2019/06/14/156-puppeteer-robot/#%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85 8 | * 推荐手动下载 Chromium,通过设定 executablePath 来使用;在根目录下创建 arya.config.js,其格式如下示例; 9 | */ 10 | exports.executablePath = '/Users/${YOUR-PATH}/Chromium.app/Contents/MacOS/Chromium' 11 | -------------------------------------------------------------------------------- /commands/clear-commit-msg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | git checkout --orphan latest_branch 6 | 7 | git add -A 8 | 9 | git commit -am "✨ initial create & commit" 10 | 11 | git branch -D master 12 | 13 | git branch -m master 14 | 15 | git push -f origin master 16 | -------------------------------------------------------------------------------- /commands/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 确保脚本抛出遇到的错误 4 | set -e 5 | 6 | # 生成静态文件 7 | yarn run build 8 | 9 | # 进入生成的文件夹 10 | cd ./dist 11 | 12 | # 如果是发布到自定义域名 13 | echo 'markdown.lovejade.cn' > CNAME 14 | 15 | git init 16 | git add -A 17 | git commit -m '🎉 update release' 18 | 19 | git push -f git@github.com:nicejade/markdown-online-editor.git main:gh-pages 20 | cd - -------------------------------------------------------------------------------- /commands/preview.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const chalk = require('chalk') 3 | const childProcess = require('child_process') 4 | const fs = require('fs') 5 | const path = require('path') 6 | const opn = require('opn') 7 | 8 | const app = express() 9 | 10 | const resolveRealPath = (dir) => { 11 | return path.join(__dirname, dir) 12 | } 13 | 14 | const entryFilePath = resolveRealPath('../dist/index.html') 15 | 16 | const openStaticServer = () => { 17 | app.use('/js', express.static(resolveRealPath('../dist/js/'))) 18 | app.use('/css', express.static(resolveRealPath('../dist/css/'))) 19 | app.use('/img', express.static(resolveRealPath('../dist/img/'))) 20 | app.use('/fonts', express.static(resolveRealPath('../dist/fonts/'))) 21 | 22 | app.get('*', function (req, res) { 23 | const content = fs.readFileSync(entryFilePath, 'utf8') 24 | res.send(content) 25 | }) 26 | 27 | app.listen(3000, function () { 28 | console.log(chalk.cyan('Example app listening on port 3000!\n')) 29 | console.log(chalk.yellow('You Can Visit: ') + chalk.green('http://localhost:3000/')) 30 | opn('http://localhost:3000') 31 | }) 32 | } 33 | 34 | const main = () => { 35 | const isExist = fs.existsSync(entryFilePath) 36 | if (isExist) { 37 | openStaticServer() 38 | } else { 39 | const commandStr = `vue-cli-service build` 40 | const output = childProcess.execSync(commandStr, { 41 | cwd: process.cwd(), 42 | timeout: 60000, 43 | encoding: 'utf8' 44 | }) 45 | openStaticServer() 46 | console.log(output) 47 | } 48 | } 49 | 50 | main() 51 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | const path = require('path') 3 | 4 | module.exports = { 5 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 6 | transform: { 7 | '^.+\\.vue$': 'vue-jest', 8 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 9 | 'jest-transform-stub', 10 | '^.+\\.jsx?$': 'babel-jest' 11 | }, 12 | moduleNameMapper: { 13 | '^@/(.*)$': '/src/$1' 14 | }, 15 | snapshotSerializers: ['jest-serializer-vue'], 16 | testMatch: [ 17 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 18 | ], 19 | collectCoverage: true, 20 | collectCoverageFrom: ['src/**/*.{js,vue}', '!src/assets/**'], 21 | coverageReporters: ['html', 'text-summary'], 22 | coverageDirectory: path.resolve(__dirname, './tests/coverage') 23 | } 24 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name _; 4 | root /usr/share/nginx/html; 5 | index index.html; 6 | 7 | # 禁止缓存,确保始终从服务器获取最新内容 8 | add_header Cache-Control no-cache; 9 | 10 | # 处理所有路由 11 | location / { 12 | try_files $uri $uri/ /index.html; 13 | 14 | # 允许所有跨域请求 15 | add_header Access-Control-Allow-Origin *; 16 | add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; 17 | add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; 18 | } 19 | 20 | # 配置静态资源缓存 21 | location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { 22 | expires 1y; 23 | add_header Cache-Control "public, no-transform"; 24 | } 25 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-online-editor", 3 | "version": "3.0.0", 4 | "author": "nicejade", 5 | "private": true, 6 | "description": "Arya,是一款基于 Vue、Vditor,为未来而构建的在线 Markdown 编辑器;轻量且强大:内置粘贴 HTML 自动转换为 Markdown,支持 PPT 预览,以及绘制流程图、甘特图、时序图、任务列表、五线谱,可导出携带样式的图片、PDF、微信公众号特制的 HTML 等。", 7 | "scripts": { 8 | "start": "yarn serve", 9 | "serve": "vue-cli-service serve --open", 10 | "build": "vue-cli-service build", 11 | "watcher": "onchange \"src/**/**/*.{js,css,scss,vue}\" -- prettier --write {{changed}}", 12 | "prettier": "prettier --write \"src/**/**/*.{js,css,scss,vue}\"", 13 | "lint": "vue-cli-service lint", 14 | "test": "vue-cli-service test:unit", 15 | "testbuild": "vue-cli-service build --mode development", 16 | "deploy": "bash ./commands/deploy.sh", 17 | "analyz": "NODE_ENV=production npm_config_report=true npm run build", 18 | "clear-commit-msg": "bash ./commands/clear-commit-msg.sh", 19 | "eslint-fix": "eslint src/**/**/*.vue --fix", 20 | "format-code": "prettier-eslint --write \"src/**/*.{js,vue,css,scss}\"", 21 | "precommit-msg": "echo '🐉 Start pre-commit checks...' && exit 0", 22 | "preview": "node ./commands/preview.js" 23 | }, 24 | "dependencies": { 25 | "canvg": "^2.0.0", 26 | "element-ui": "^2.12.0", 27 | "hint.css": "^2.6.0", 28 | "html2canvas": "^1.0.0-rc.3", 29 | "html2pdf.js": "^0.9.1", 30 | "lodash": "4.17.15", 31 | "lodash.assign": "^4.2.0", 32 | "lodash.clonedeep": "^4.5.0", 33 | "register-service-worker": "^1.0.0", 34 | "reveal.js": "^3.8.0", 35 | "vditor": "3.10.8", 36 | "vue": "^2.5.17", 37 | "vue-meta": "^2.2.2", 38 | "vue-router": "^3.1.3" 39 | }, 40 | "devDependencies": { 41 | "@vue/cli-plugin-babel": "^3.11.0", 42 | "@vue/cli-plugin-eslint": "^3.11.0", 43 | "@vue/cli-plugin-pwa": "^3.11.0", 44 | "@vue/cli-plugin-unit-jest": "^3.11.0", 45 | "@vue/cli-service": "^3.11.0", 46 | "@vue/test-utils": "^1.0.0-beta.29", 47 | "babel-core": "7.0.0-bridge.0", 48 | "babel-jest": "^24.9.0", 49 | "css-loader": "^3.4.2", 50 | "eslint-config-prettier": "^6.3.0", 51 | "eslint-plugin-prettier": "^3.1.0", 52 | "eslint-plugin-vue": "^5.2.3", 53 | "husky": "^3.0.5", 54 | "less": "^3.11.1", 55 | "less-loader": "^5.0.0", 56 | "lint-staged": "^9.2.5", 57 | "onchange": "^6.1.0", 58 | "prettier-eslint-cli": "^5.0.0", 59 | "size-plugin": "^2.0.0", 60 | "svg-sprite-loader": "^4.1.6", 61 | "svgo-loader": "2.2.1", 62 | "vue-svg-loader": "^0.12.0", 63 | "vue-template-compiler": "^2.5.17", 64 | "webpack-bundle-analyzer": "3.5.0" 65 | }, 66 | "babel": { 67 | "presets": [ 68 | "@vue/app" 69 | ] 70 | }, 71 | "eslintConfig": { 72 | "globals": { 73 | "L": true 74 | }, 75 | "root": true, 76 | "env": { 77 | "node": true, 78 | "es6": true 79 | }, 80 | "rules": { 81 | "no-console": 0, 82 | "no-useless-escape": 0, 83 | "no-multiple-empty-lines": [ 84 | 2, 85 | { 86 | "max": 3 87 | } 88 | ], 89 | "prettier/prettier": [ 90 | "error", 91 | { 92 | "singleQuote": true, 93 | "semi": false, 94 | "trailingComma": "none", 95 | "bracketSpacing": true, 96 | "jsxBracketSameLine": true, 97 | "insertPragma": true, 98 | "requirePragma": false 99 | } 100 | ] 101 | }, 102 | "plugins": [], 103 | "extends": [ 104 | "plugin:vue/essential", 105 | "plugin:prettier/recommended", 106 | "eslint:recommended" 107 | ], 108 | "parserOptions": { 109 | "parser": "babel-eslint" 110 | } 111 | }, 112 | "prettier": { 113 | "singleQuote": true, 114 | "semi": false, 115 | "printWidth": 100, 116 | "proseWrap": "never" 117 | }, 118 | "postcss": { 119 | "plugins": { 120 | "autoprefixer": {} 121 | } 122 | }, 123 | "browserslist": [ 124 | "> 1%", 125 | "last 2 versions", 126 | "not ie <= 8" 127 | ], 128 | "eslintIgnore": [ 129 | "package.json" 130 | ], 131 | "husky": { 132 | "hooks": { 133 | "pre-commit": "yarn run precommit-msg && lint-staged" 134 | } 135 | }, 136 | "lint-staged": { 137 | "**/**.{js,json,pcss,vue,css,scss}": [ 138 | "prettier --write" 139 | ] 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/img/icons/android-chrome-96x96.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #00aba9 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Arya - 在线 Markdown 编辑器 11 | 13 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Markdown \u7f16\u8f91\u5668", 3 | "short_name": "Markdown \u7f16\u8f91\u5668", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-96x96.png", 7 | "sizes": "96x96", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone", 14 | "start_url": "https://markdown.lovejade.cn/?utm_source=device-screen" 15 | } -------------------------------------------------------------------------------- /public/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/public/mockup.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/service-worker.js: -------------------------------------------------------------------------------- 1 | const HTMLToCache = '/' 2 | const version = 'v2.1.2' 3 | 4 | self.addEventListener('install', (event) => { 5 | event.waitUntil( 6 | caches.open(version).then((cache) => { 7 | cache.add(HTMLToCache).then(self.skipWaiting()) 8 | }) 9 | ) 10 | }) 11 | 12 | self.addEventListener('activate', (event) => { 13 | event.waitUntil( 14 | caches 15 | .keys() 16 | .then((cacheNames) => 17 | Promise.all( 18 | cacheNames.map((cacheName) => { 19 | if (version !== cacheName) return caches.delete(cacheName) 20 | }) 21 | ) 22 | ) 23 | .then(self.clients.claim()) 24 | ) 25 | }) 26 | 27 | self.addEventListener('fetch', (event) => { 28 | const requestToFetch = event.request.clone() 29 | event.respondWith( 30 | caches.match(event.request.clone()).then((cached) => { 31 | // We don't return cached HTML (except if fetch failed) 32 | if (cached) { 33 | const resourceType = cached.headers.get('content-type') 34 | // We only return non css/js/html cached response e.g images 35 | if (!hasHash(event.request.url) && !/text\/html/.test(resourceType)) { 36 | return cached 37 | } 38 | 39 | // If the CSS/JS didn't change since it's been cached, return the cached version 40 | if (hasHash(event.request.url) && hasSameHash(event.request.url, cached.url)) { 41 | return cached 42 | } 43 | } 44 | return fetch(requestToFetch) 45 | .then((response) => { 46 | const clonedResponse = response.clone() 47 | const contentType = clonedResponse.headers.get('content-type') 48 | 49 | if ( 50 | !clonedResponse || 51 | clonedResponse.status !== 200 || 52 | clonedResponse.type !== 'basic' || 53 | /\/sockjs\//.test(event.request.url) 54 | ) { 55 | return response 56 | } 57 | 58 | if (/html/.test(contentType)) { 59 | caches.open(version).then((cache) => cache.put(HTMLToCache, clonedResponse)) 60 | } else { 61 | // Delete old version of a file 62 | if (hasHash(event.request.url)) { 63 | caches.open(version).then((cache) => 64 | cache.keys().then((keys) => 65 | keys.forEach((asset) => { 66 | if (new RegExp(removeHash(event.request.url)).test(removeHash(asset.url))) { 67 | cache.delete(asset) 68 | } 69 | }) 70 | ) 71 | ) 72 | } 73 | 74 | if (requestToFetch.url.startsWith('http')) { 75 | caches.open(version).then((cache) => cache.put(event.request, clonedResponse)) 76 | } 77 | } 78 | return response 79 | }) 80 | .catch(() => { 81 | if (hasHash(event.request.url)) return caches.match(event.request.url) 82 | // If the request URL hasn't been served from cache and isn't sockjs we suppose it's HTML 83 | else if (!/\/sockjs\//.test(event.request.url)) return caches.match(HTMLToCache) 84 | // Only for sockjs 85 | return new Response('No connection to the server', { 86 | status: 503, 87 | statusText: 'No connection to the server', 88 | headers: new Headers({ 'Content-Type': 'text/plain' }), 89 | }) 90 | }) 91 | }) 92 | ) 93 | }) 94 | 95 | function removeHash(element) { 96 | if (typeof element === 'string') return element.split('?hash=')[0] 97 | } 98 | 99 | function hasHash(element) { 100 | if (typeof element === 'string') return /\?hash=.*/.test(element) 101 | } 102 | 103 | function hasSameHash(firstUrl, secondUrl) { 104 | if (typeof firstUrl === 'string' && typeof secondUrl === 'string') { 105 | return /\?hash=(.*)/.exec(firstUrl)[1] === /\?hash=(.*)/.exec(secondUrl)[1] 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Markdown \u7f16\u8f91\u5668", 3 | "short_name": "Markdown \u7f16\u8f91\u5668", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-96x96.png", 7 | "sizes": "96x96", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone", 14 | "start_url": "https://markdown.lovejade.cn/?utm_source=device-screen" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 16 | 17 | 30 | -------------------------------------------------------------------------------- /src/assets/icons/adjust.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/assets/icons/author.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/assets/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 11 | 13 | 15 | 18 | 24 | 32 | 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 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/assets/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/assets/icons/full-screen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 11 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 28 | 29 | 30 | 31 | 32 | 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 | 60 | -------------------------------------------------------------------------------- /src/assets/icons/homepage.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const files = require.context('.', true, /\.svg$/) 4 | const modules = {} 5 | 6 | files.keys().forEach(key => { 7 | modules[key.replace(/(\.\/|\.svg)/g, '')] = files(key) 8 | }) 9 | 10 | export default modules 11 | -------------------------------------------------------------------------------- /src/assets/icons/preview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/assets/icons/set-style.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/assets/icons/setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 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 | -------------------------------------------------------------------------------- /src/assets/icons/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/assets/icons/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/markdown-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/images/markdown-white.png -------------------------------------------------------------------------------- /src/assets/images/markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/images/markdown.png -------------------------------------------------------------------------------- /src/assets/styles/common.less: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | font-family: Open Sans; 6 | } 7 | 8 | *:focus { 9 | outline: none !important; 10 | } 11 | 12 | a { 13 | color: @brand; 14 | text-decoration: none; 15 | } 16 | 17 | html, 18 | body { 19 | font-size: 14px; 20 | background-color: #ffffff !important; 21 | } 22 | // export page css 23 | .export-page { 24 | width: 100%; 25 | height: 100%; 26 | min-height: 100vh; 27 | background-color: @white; 28 | .flex-box-center(column); 29 | .button-group { 30 | width: 100%; 31 | margin: 20px auto; 32 | } 33 | #export-vditor { 34 | max-width: 960px; 35 | height: 100%; 36 | min-height: 100vh; 37 | margin-bottom: 20px; 38 | text-align: left; 39 | } 40 | .vditor-toolbar { 41 | display: none; 42 | } 43 | .vditor { 44 | border: 0; 45 | } 46 | .vditor-preview { 47 | padding: 3 * @size-factor; 48 | margin: 3 * @size-factor; 49 | box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1); 50 | } 51 | .vditor-reset h1 { 52 | text-align: center; 53 | } 54 | } 55 | 56 | @media (max-width: 768px) { 57 | .export-page { 58 | #export-vditor { 59 | width: 100% !important; 60 | margin: 0 !important; 61 | } 62 | .vditor-preview { 63 | padding: 0 10px; 64 | } 65 | } 66 | } 67 | 68 | #khaleesi { 69 | text-align: left; 70 | } 71 | -------------------------------------------------------------------------------- /src/assets/styles/element.less: -------------------------------------------------------------------------------- 1 | .el-message { 2 | z-index: @el-message-zindex !important; 3 | } 4 | .el-dropdown-menu__item { 5 | a { 6 | color: #606266; 7 | } 8 | } 9 | .el-dropdown-menu__item.is-disabled { 10 | a { 11 | color: #bbbbbb; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/styles/element/alert.css: -------------------------------------------------------------------------------- 1 | .el-alert { 2 | width: 100%; 3 | padding: 8px 16px; 4 | margin: 0; 5 | -webkit-box-sizing: border-box; 6 | box-sizing: border-box; 7 | border-radius: 4px; 8 | position: relative; 9 | background-color: #fff; 10 | overflow: hidden; 11 | opacity: 1; 12 | display: -webkit-box; 13 | display: -ms-flexbox; 14 | display: flex; 15 | -webkit-box-align: center; 16 | -ms-flex-align: center; 17 | align-items: center; 18 | -webkit-transition: opacity 0.2s; 19 | transition: opacity 0.2s; 20 | } 21 | .el-alert.is-center { 22 | -webkit-box-pack: center; 23 | -ms-flex-pack: center; 24 | justify-content: center; 25 | } 26 | .el-alert--success { 27 | background-color: #f0f9eb; 28 | color: #67c23a; 29 | } 30 | .el-alert--success .el-alert__description { 31 | color: #67c23a; 32 | } 33 | .el-alert--info { 34 | background-color: #f4f4f5; 35 | color: #909399; 36 | } 37 | .el-alert--info .el-alert__description { 38 | color: #909399; 39 | } 40 | .el-alert--warning { 41 | background-color: #fdf6ec; 42 | color: #e6a23c; 43 | } 44 | .el-alert--warning .el-alert__description { 45 | color: #e6a23c; 46 | } 47 | .el-alert--error { 48 | background-color: #fef0f0; 49 | color: #f56c6c; 50 | } 51 | .el-alert--error .el-alert__description { 52 | color: #f56c6c; 53 | } 54 | .el-alert__content { 55 | display: table-cell; 56 | padding: 0 8px; 57 | } 58 | .el-alert__icon { 59 | font-size: 16px; 60 | width: 16px; 61 | } 62 | .el-alert__icon.is-big { 63 | font-size: 28px; 64 | width: 28px; 65 | } 66 | .el-alert__title { 67 | font-size: 13px; 68 | line-height: 18px; 69 | } 70 | .el-alert__title.is-bold { 71 | font-weight: 700; 72 | } 73 | .el-alert .el-alert__description { 74 | font-size: 12px; 75 | margin: 5px 0 0; 76 | } 77 | .el-alert__closebtn { 78 | font-size: 12px; 79 | color: #c0c4cc; 80 | opacity: 1; 81 | position: absolute; 82 | top: 12px; 83 | right: 15px; 84 | cursor: pointer; 85 | } 86 | .el-alert__closebtn.is-customed { 87 | font-style: normal; 88 | font-size: 13px; 89 | top: 9px; 90 | } 91 | .el-alert-fade-enter, 92 | .el-alert-fade-leave-active { 93 | opacity: 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/assets/styles/element/aside.css: -------------------------------------------------------------------------------- 1 | .el-aside { 2 | overflow: auto; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | -ms-flex-negative: 0; 6 | flex-shrink: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/element/badge.css: -------------------------------------------------------------------------------- 1 | .el-badge { 2 | position: relative; 3 | vertical-align: middle; 4 | display: inline-block; 5 | } 6 | .el-badge__content { 7 | background-color: #f56c6c; 8 | border-radius: 10px; 9 | color: #fff; 10 | display: inline-block; 11 | font-size: 12px; 12 | height: 18px; 13 | line-height: 18px; 14 | padding: 0 6px; 15 | text-align: center; 16 | white-space: nowrap; 17 | border: 1px solid #fff; 18 | } 19 | .el-badge__content.is-fixed { 20 | position: absolute; 21 | top: 0; 22 | right: 10px; 23 | -webkit-transform: translateY(-50%) translateX(100%); 24 | transform: translateY(-50%) translateX(100%); 25 | } 26 | .el-badge__content.is-fixed.is-dot { 27 | right: 5px; 28 | } 29 | .el-badge__content.is-dot { 30 | height: 8px; 31 | width: 8px; 32 | padding: 0; 33 | right: 0; 34 | border-radius: 50%; 35 | } 36 | -------------------------------------------------------------------------------- /src/assets/styles/element/breadcrumb-item.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/breadcrumb-item.css -------------------------------------------------------------------------------- /src/assets/styles/element/breadcrumb.css: -------------------------------------------------------------------------------- 1 | .el-breadcrumb { 2 | font-size: 14px; 3 | line-height: 1; 4 | } 5 | .el-breadcrumb::after, 6 | .el-breadcrumb::before { 7 | display: table; 8 | content: ''; 9 | } 10 | .el-breadcrumb::after { 11 | clear: both; 12 | } 13 | .el-breadcrumb__separator { 14 | margin: 0 9px; 15 | font-weight: 700; 16 | color: #c0c4cc; 17 | } 18 | .el-breadcrumb__separator[class*='icon'] { 19 | margin: 0 6px; 20 | font-weight: 400; 21 | } 22 | .el-breadcrumb__item { 23 | float: left; 24 | } 25 | .el-breadcrumb__inner { 26 | color: #606266; 27 | } 28 | .el-breadcrumb__inner a, 29 | .el-breadcrumb__inner.is-link { 30 | font-weight: 700; 31 | text-decoration: none; 32 | -webkit-transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); 33 | transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); 34 | color: #303133; 35 | } 36 | .el-breadcrumb__inner a:hover, 37 | .el-breadcrumb__inner.is-link:hover { 38 | color: #000000; 39 | cursor: pointer; 40 | } 41 | .el-breadcrumb__item:last-child .el-breadcrumb__inner, 42 | .el-breadcrumb__item:last-child .el-breadcrumb__inner a, 43 | .el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover, 44 | .el-breadcrumb__item:last-child .el-breadcrumb__inner:hover { 45 | font-weight: 400; 46 | color: #606266; 47 | cursor: text; 48 | } 49 | .el-breadcrumb__item:last-child .el-breadcrumb__separator { 50 | display: none; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/styles/element/button-group.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/button-group.css -------------------------------------------------------------------------------- /src/assets/styles/element/card.css: -------------------------------------------------------------------------------- 1 | .el-card { 2 | border-radius: 4px; 3 | border: 1px solid #ebeef5; 4 | background-color: #fff; 5 | overflow: hidden; 6 | color: #303133; 7 | -webkit-transition: 0.3s; 8 | transition: 0.3s; 9 | } 10 | .el-card.is-always-shadow, 11 | .el-card.is-hover-shadow:focus, 12 | .el-card.is-hover-shadow:hover { 13 | -webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 14 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 15 | } 16 | .el-card__header { 17 | padding: 18px 20px; 18 | border-bottom: 1px solid #ebeef5; 19 | -webkit-box-sizing: border-box; 20 | box-sizing: border-box; 21 | } 22 | .el-card__body { 23 | padding: 20px; 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/styles/element/carousel-item.css: -------------------------------------------------------------------------------- 1 | .el-carousel__item, 2 | .el-carousel__mask { 3 | position: absolute; 4 | height: 100%; 5 | top: 0; 6 | left: 0; 7 | } 8 | .el-carousel__item { 9 | width: 100%; 10 | display: inline-block; 11 | overflow: hidden; 12 | z-index: 0; 13 | } 14 | .el-carousel__item.is-active { 15 | z-index: 2; 16 | } 17 | .el-carousel__item.is-animating { 18 | -webkit-transition: -webkit-transform 0.4s ease-in-out; 19 | transition: -webkit-transform 0.4s ease-in-out; 20 | transition: transform 0.4s ease-in-out; 21 | transition: transform 0.4s ease-in-out, -webkit-transform 0.4s ease-in-out; 22 | } 23 | .el-carousel__item--card { 24 | width: 50%; 25 | -webkit-transition: -webkit-transform 0.4s ease-in-out; 26 | transition: -webkit-transform 0.4s ease-in-out; 27 | transition: transform 0.4s ease-in-out; 28 | transition: transform 0.4s ease-in-out, -webkit-transform 0.4s ease-in-out; 29 | } 30 | .el-carousel__item--card.is-in-stage { 31 | cursor: pointer; 32 | z-index: 1; 33 | } 34 | .el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask, 35 | .el-carousel__item--card.is-in-stage:hover .el-carousel__mask { 36 | opacity: 0.12; 37 | } 38 | .el-carousel__item--card.is-active { 39 | z-index: 2; 40 | } 41 | .el-carousel__mask { 42 | width: 100%; 43 | background-color: #fff; 44 | opacity: 0.24; 45 | -webkit-transition: 0.2s; 46 | transition: 0.2s; 47 | } 48 | -------------------------------------------------------------------------------- /src/assets/styles/element/carousel.css: -------------------------------------------------------------------------------- 1 | .el-carousel { 2 | overflow-x: hidden; 3 | position: relative; 4 | } 5 | .el-carousel__container { 6 | position: relative; 7 | height: 300px; 8 | } 9 | .el-carousel__arrow { 10 | border: none; 11 | outline: 0; 12 | padding: 0; 13 | margin: 0; 14 | height: 36px; 15 | width: 36px; 16 | cursor: pointer; 17 | -webkit-transition: 0.3s; 18 | transition: 0.3s; 19 | border-radius: 50%; 20 | background-color: rgba(31, 45, 61, 0.11); 21 | color: #fff; 22 | position: absolute; 23 | top: 50%; 24 | z-index: 10; 25 | -webkit-transform: translateY(-50%); 26 | transform: translateY(-50%); 27 | text-align: center; 28 | font-size: 12px; 29 | } 30 | .el-carousel__arrow--left { 31 | left: 16px; 32 | } 33 | .el-carousel__arrow--right { 34 | right: 16px; 35 | } 36 | .el-carousel__arrow:hover { 37 | background-color: rgba(31, 45, 61, 0.23); 38 | } 39 | .el-carousel__arrow i { 40 | cursor: pointer; 41 | } 42 | .el-carousel__indicators { 43 | position: absolute; 44 | list-style: none; 45 | bottom: 0; 46 | left: 50%; 47 | -webkit-transform: translateX(-50%); 48 | transform: translateX(-50%); 49 | margin: 0; 50 | padding: 0; 51 | z-index: 2; 52 | } 53 | .el-carousel__indicators--outside { 54 | bottom: 26px; 55 | text-align: center; 56 | position: static; 57 | -webkit-transform: none; 58 | transform: none; 59 | } 60 | .el-carousel__indicators--outside .el-carousel__indicator:hover button { 61 | opacity: 0.64; 62 | } 63 | .el-carousel__indicators--outside button { 64 | background-color: #c0c4cc; 65 | opacity: 0.24; 66 | } 67 | .el-carousel__indicators--labels { 68 | left: 0; 69 | right: 0; 70 | -webkit-transform: none; 71 | transform: none; 72 | text-align: center; 73 | } 74 | .el-carousel__indicators--labels .el-carousel__button { 75 | height: auto; 76 | width: auto; 77 | padding: 2px 18px; 78 | font-size: 12px; 79 | } 80 | .el-carousel__indicators--labels .el-carousel__indicator { 81 | padding: 6px 4px; 82 | } 83 | .el-carousel__indicator { 84 | display: inline-block; 85 | background-color: transparent; 86 | padding: 12px 4px; 87 | cursor: pointer; 88 | } 89 | .el-carousel__indicator:hover button { 90 | opacity: 0.72; 91 | } 92 | .el-carousel__indicator.is-active button { 93 | opacity: 1; 94 | } 95 | .el-carousel__button { 96 | display: block; 97 | opacity: 0.48; 98 | width: 30px; 99 | height: 2px; 100 | background-color: #fff; 101 | border: none; 102 | outline: 0; 103 | padding: 0; 104 | margin: 0; 105 | cursor: pointer; 106 | -webkit-transition: 0.3s; 107 | transition: 0.3s; 108 | } 109 | .carousel-arrow-left-enter, 110 | .carousel-arrow-left-leave-active { 111 | -webkit-transform: translateY(-50%) translateX(-10px); 112 | transform: translateY(-50%) translateX(-10px); 113 | opacity: 0; 114 | } 115 | .carousel-arrow-right-enter, 116 | .carousel-arrow-right-leave-active { 117 | -webkit-transform: translateY(-50%) translateX(10px); 118 | transform: translateY(-50%) translateX(10px); 119 | opacity: 0; 120 | } 121 | -------------------------------------------------------------------------------- /src/assets/styles/element/checkbox-button.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/checkbox-button.css -------------------------------------------------------------------------------- /src/assets/styles/element/checkbox-group.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/checkbox-group.css -------------------------------------------------------------------------------- /src/assets/styles/element/collapse-item.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/collapse-item.css -------------------------------------------------------------------------------- /src/assets/styles/element/collapse.css: -------------------------------------------------------------------------------- 1 | .el-collapse { 2 | border-top: 1px solid #ebeef5; 3 | border-bottom: 1px solid #ebeef5; 4 | } 5 | .el-collapse-item__header { 6 | height: 48px; 7 | line-height: 48px; 8 | background-color: #fff; 9 | color: #303133; 10 | cursor: pointer; 11 | border-bottom: 1px solid #ebeef5; 12 | font-size: 13px; 13 | font-weight: 500; 14 | -webkit-transition: border-bottom-color 0.3s; 15 | transition: border-bottom-color 0.3s; 16 | outline: 0; 17 | } 18 | .el-collapse-item__arrow { 19 | margin-right: 8px; 20 | -webkit-transition: -webkit-transform 0.3s; 21 | transition: -webkit-transform 0.3s; 22 | transition: transform 0.3s; 23 | transition: transform 0.3s, -webkit-transform 0.3s; 24 | float: right; 25 | line-height: 48px; 26 | font-weight: 300; 27 | } 28 | .el-collapse-item__arrow.is-active { 29 | -webkit-transform: rotate(90deg); 30 | transform: rotate(90deg); 31 | } 32 | .el-collapse-item__header.focusing:focus:not(:hover) { 33 | color: #000000; 34 | } 35 | .el-collapse-item__header.is-active { 36 | border-bottom-color: transparent; 37 | } 38 | .el-collapse-item__wrap { 39 | will-change: height; 40 | background-color: #fff; 41 | overflow: hidden; 42 | -webkit-box-sizing: border-box; 43 | box-sizing: border-box; 44 | border-bottom: 1px solid #ebeef5; 45 | } 46 | .el-collapse-item__content { 47 | padding-bottom: 25px; 48 | font-size: 13px; 49 | color: #303133; 50 | line-height: 1.769230769230769; 51 | } 52 | .el-collapse-item:last-child { 53 | margin-bottom: -1px; 54 | } 55 | -------------------------------------------------------------------------------- /src/assets/styles/element/container.css: -------------------------------------------------------------------------------- 1 | .el-container { 2 | display: -webkit-box; 3 | display: -ms-flexbox; 4 | display: flex; 5 | -webkit-box-orient: horizontal; 6 | -webkit-box-direction: normal; 7 | -ms-flex-direction: row; 8 | flex-direction: row; 9 | -webkit-box-flex: 1; 10 | -ms-flex: 1; 11 | flex: 1; 12 | -ms-flex-preferred-size: auto; 13 | flex-basis: auto; 14 | -webkit-box-sizing: border-box; 15 | box-sizing: border-box; 16 | min-width: 0; 17 | } 18 | .el-container.is-vertical { 19 | -webkit-box-orient: vertical; 20 | -webkit-box-direction: normal; 21 | -ms-flex-direction: column; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /src/assets/styles/element/dialog.css: -------------------------------------------------------------------------------- 1 | .v-modal-enter { 2 | -webkit-animation: v-modal-in 0.2s ease; 3 | animation: v-modal-in 0.2s ease; 4 | } 5 | .v-modal-leave { 6 | -webkit-animation: v-modal-out 0.2s ease forwards; 7 | animation: v-modal-out 0.2s ease forwards; 8 | } 9 | @-webkit-keyframes v-modal-in { 10 | 0% { 11 | opacity: 0; 12 | } 13 | } 14 | @keyframes v-modal-in { 15 | 0% { 16 | opacity: 0; 17 | } 18 | } 19 | @-webkit-keyframes v-modal-out { 20 | 100% { 21 | opacity: 0; 22 | } 23 | } 24 | @keyframes v-modal-out { 25 | 100% { 26 | opacity: 0; 27 | } 28 | } 29 | .v-modal { 30 | position: fixed; 31 | left: 0; 32 | top: 0; 33 | width: 100%; 34 | height: 100%; 35 | opacity: 0.5; 36 | background: #000; 37 | } 38 | .el-popup-parent--hidden { 39 | overflow: hidden; 40 | } 41 | .el-dialog { 42 | position: relative; 43 | margin: 0 auto 50px; 44 | background: #fff; 45 | border-radius: 2px; 46 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); 47 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); 48 | -webkit-box-sizing: border-box; 49 | box-sizing: border-box; 50 | width: 50%; 51 | } 52 | .el-dialog.is-fullscreen { 53 | width: 100%; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | height: 100%; 57 | overflow: auto; 58 | } 59 | .el-dialog__wrapper { 60 | position: fixed; 61 | top: 0; 62 | right: 0; 63 | bottom: 0; 64 | left: 0; 65 | overflow: auto; 66 | margin: 0; 67 | } 68 | .el-dialog__header { 69 | padding: 20px 20px 10px; 70 | } 71 | .el-dialog__headerbtn { 72 | position: absolute; 73 | top: 20px; 74 | right: 20px; 75 | padding: 0; 76 | background: 0 0; 77 | border: none; 78 | outline: 0; 79 | cursor: pointer; 80 | font-size: 16px; 81 | } 82 | .el-dialog__headerbtn .el-dialog__close { 83 | color: #909399; 84 | } 85 | .el-dialog__headerbtn:focus .el-dialog__close, 86 | .el-dialog__headerbtn:hover .el-dialog__close { 87 | color: #000000; 88 | } 89 | .el-dialog__title { 90 | line-height: 24px; 91 | font-size: 18px; 92 | color: #303133; 93 | } 94 | .el-dialog__body { 95 | padding: 30px 20px; 96 | color: #606266; 97 | font-size: 14px; 98 | } 99 | .el-dialog__footer { 100 | padding: 10px 20px 20px; 101 | text-align: right; 102 | -webkit-box-sizing: border-box; 103 | box-sizing: border-box; 104 | } 105 | .el-dialog--center { 106 | text-align: center; 107 | } 108 | .el-dialog--center .el-dialog__body { 109 | text-align: initial; 110 | padding: 25px 25px 30px; 111 | } 112 | .el-dialog--center .el-dialog__footer { 113 | text-align: inherit; 114 | } 115 | .dialog-fade-enter-active { 116 | -webkit-animation: dialog-fade-in 0.3s; 117 | animation: dialog-fade-in 0.3s; 118 | } 119 | .dialog-fade-leave-active { 120 | -webkit-animation: dialog-fade-out 0.3s; 121 | animation: dialog-fade-out 0.3s; 122 | } 123 | @-webkit-keyframes dialog-fade-in { 124 | 0% { 125 | -webkit-transform: translate3d(0, -20px, 0); 126 | transform: translate3d(0, -20px, 0); 127 | opacity: 0; 128 | } 129 | 100% { 130 | -webkit-transform: translate3d(0, 0, 0); 131 | transform: translate3d(0, 0, 0); 132 | opacity: 1; 133 | } 134 | } 135 | @keyframes dialog-fade-in { 136 | 0% { 137 | -webkit-transform: translate3d(0, -20px, 0); 138 | transform: translate3d(0, -20px, 0); 139 | opacity: 0; 140 | } 141 | 100% { 142 | -webkit-transform: translate3d(0, 0, 0); 143 | transform: translate3d(0, 0, 0); 144 | opacity: 1; 145 | } 146 | } 147 | @-webkit-keyframes dialog-fade-out { 148 | 0% { 149 | -webkit-transform: translate3d(0, 0, 0); 150 | transform: translate3d(0, 0, 0); 151 | opacity: 1; 152 | } 153 | 100% { 154 | -webkit-transform: translate3d(0, -20px, 0); 155 | transform: translate3d(0, -20px, 0); 156 | opacity: 0; 157 | } 158 | } 159 | @keyframes dialog-fade-out { 160 | 0% { 161 | -webkit-transform: translate3d(0, 0, 0); 162 | transform: translate3d(0, 0, 0); 163 | opacity: 1; 164 | } 165 | 100% { 166 | -webkit-transform: translate3d(0, -20px, 0); 167 | transform: translate3d(0, -20px, 0); 168 | opacity: 0; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/assets/styles/element/display.css: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width: 767px) { 2 | .hidden-xs-only { 3 | display: none !important; 4 | } 5 | } 6 | @media only screen and (min-width: 768px) { 7 | .hidden-sm-and-up { 8 | display: none !important; 9 | } 10 | } 11 | @media only screen and (min-width: 768px) and (max-width: 991px) { 12 | .hidden-sm-only { 13 | display: none !important; 14 | } 15 | } 16 | @media only screen and (max-width: 991px) { 17 | .hidden-sm-and-down { 18 | display: none !important; 19 | } 20 | } 21 | @media only screen and (min-width: 992px) { 22 | .hidden-md-and-up { 23 | display: none !important; 24 | } 25 | } 26 | @media only screen and (min-width: 992px) and (max-width: 1199px) { 27 | .hidden-md-only { 28 | display: none !important; 29 | } 30 | } 31 | @media only screen and (max-width: 1199px) { 32 | .hidden-md-and-down { 33 | display: none !important; 34 | } 35 | } 36 | @media only screen and (min-width: 1200px) { 37 | .hidden-lg-and-up { 38 | display: none !important; 39 | } 40 | } 41 | @media only screen and (min-width: 1200px) and (max-width: 1919px) { 42 | .hidden-lg-only { 43 | display: none !important; 44 | } 45 | } 46 | @media only screen and (max-width: 1919px) { 47 | .hidden-lg-and-down { 48 | display: none !important; 49 | } 50 | } 51 | @media only screen and (min-width: 1920px) { 52 | .hidden-xl-only { 53 | display: none !important; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/assets/styles/element/dropdown-item.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/dropdown-item.css -------------------------------------------------------------------------------- /src/assets/styles/element/dropdown-menu.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/dropdown-menu.css -------------------------------------------------------------------------------- /src/assets/styles/element/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/assets/styles/element/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/assets/styles/element/footer.css: -------------------------------------------------------------------------------- 1 | .el-footer { 2 | padding: 0 20px; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | -ms-flex-negative: 0; 6 | flex-shrink: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/element/form-item.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/form-item.css -------------------------------------------------------------------------------- /src/assets/styles/element/form.css: -------------------------------------------------------------------------------- 1 | .el-form--inline .el-form-item, 2 | .el-form--inline .el-form-item__content { 3 | display: inline-block; 4 | vertical-align: top; 5 | } 6 | .el-form-item::after, 7 | .el-form-item__content::after { 8 | clear: both; 9 | } 10 | .el-form--label-left .el-form-item__label { 11 | text-align: left; 12 | } 13 | .el-form--label-top .el-form-item__label { 14 | float: none; 15 | display: inline-block; 16 | text-align: left; 17 | padding: 0 0 10px; 18 | } 19 | .el-form--inline .el-form-item { 20 | margin-right: 10px; 21 | } 22 | .el-form--inline .el-form-item__label { 23 | float: none; 24 | display: inline-block; 25 | } 26 | .el-form-item__content .el-input-group, 27 | .el-form-item__label { 28 | vertical-align: middle; 29 | } 30 | .el-form--inline.el-form--label-top .el-form-item__content { 31 | display: block; 32 | } 33 | .el-form-item { 34 | margin-bottom: 22px; 35 | } 36 | .el-form-item::after, 37 | .el-form-item::before { 38 | display: table; 39 | content: ''; 40 | } 41 | .el-form-item .el-form-item { 42 | margin-bottom: 0; 43 | } 44 | .el-form-item--mini.el-form-item, 45 | .el-form-item--small.el-form-item { 46 | margin-bottom: 18px; 47 | } 48 | .el-form-item .el-input__validateIcon { 49 | display: none; 50 | } 51 | .el-form-item--medium .el-form-item__content, 52 | .el-form-item--medium .el-form-item__label { 53 | line-height: 36px; 54 | } 55 | .el-form-item--small .el-form-item__content, 56 | .el-form-item--small .el-form-item__label { 57 | line-height: 32px; 58 | } 59 | .el-form-item--small .el-form-item__error { 60 | padding-top: 2px; 61 | } 62 | .el-form-item--mini .el-form-item__content, 63 | .el-form-item--mini .el-form-item__label { 64 | line-height: 28px; 65 | } 66 | .el-form-item--mini .el-form-item__error { 67 | padding-top: 1px; 68 | } 69 | .el-form-item__label { 70 | text-align: right; 71 | float: left; 72 | font-size: 14px; 73 | color: #606266; 74 | line-height: 40px; 75 | padding: 0 12px 0 0; 76 | -webkit-box-sizing: border-box; 77 | box-sizing: border-box; 78 | } 79 | .el-form-item__content { 80 | line-height: 40px; 81 | position: relative; 82 | font-size: 14px; 83 | } 84 | .el-form-item__content::after, 85 | .el-form-item__content::before { 86 | display: table; 87 | content: ''; 88 | } 89 | .el-form-item__error { 90 | color: #f56c6c; 91 | font-size: 12px; 92 | line-height: 1; 93 | padding-top: 4px; 94 | position: absolute; 95 | top: 100%; 96 | left: 0; 97 | } 98 | .el-form-item__error--inline { 99 | position: relative; 100 | top: auto; 101 | left: auto; 102 | display: inline-block; 103 | margin-left: 10px; 104 | } 105 | .el-form-item.is-required .el-form-item__label:before { 106 | content: '*'; 107 | color: #f56c6c; 108 | margin-right: 4px; 109 | } 110 | .el-form-item.is-error .el-input__inner, 111 | .el-form-item.is-error .el-input__inner:focus, 112 | .el-form-item.is-error .el-textarea__inner, 113 | .el-form-item.is-error .el-textarea__inner:focus { 114 | border-color: #f56c6c; 115 | } 116 | .el-form-item.is-error .el-input-group__append .el-input__inner, 117 | .el-form-item.is-error .el-input-group__prepend .el-input__inner { 118 | border-color: transparent; 119 | } 120 | .el-form-item.is-error .el-input__validateIcon { 121 | color: #f56c6c; 122 | } 123 | .el-form-item.is-success .el-input__inner, 124 | .el-form-item.is-success .el-input__inner:focus, 125 | .el-form-item.is-success .el-textarea__inner, 126 | .el-form-item.is-success .el-textarea__inner:focus { 127 | border-color: #67c23a; 128 | } 129 | .el-form-item.is-success .el-input-group__append .el-input__inner, 130 | .el-form-item.is-success .el-input-group__prepend .el-input__inner { 131 | border-color: transparent; 132 | } 133 | .el-form-item.is-success .el-input__validateIcon { 134 | color: #67c23a; 135 | } 136 | .el-form-item--feedback .el-input__validateIcon { 137 | display: inline-block; 138 | } 139 | -------------------------------------------------------------------------------- /src/assets/styles/element/header.css: -------------------------------------------------------------------------------- 1 | .el-header { 2 | padding: 0 20px; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | -ms-flex-negative: 0; 6 | flex-shrink: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/element/icon.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: element-icons; 3 | src: url(fonts/element-icons.woff) format('woff'), url(fonts/element-icons.ttf) format('truetype'); 4 | font-weight: 400; 5 | font-style: normal; 6 | } 7 | [class*=' el-icon-'], 8 | [class^='el-icon-'] { 9 | font-family: element-icons !important; 10 | speak: none; 11 | font-style: normal; 12 | font-weight: 400; 13 | font-variant: normal; 14 | text-transform: none; 15 | line-height: 1; 16 | vertical-align: baseline; 17 | display: inline-block; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | } 21 | .el-icon-info:before { 22 | content: '\e61a'; 23 | } 24 | .el-icon-error:before { 25 | content: '\e62c'; 26 | } 27 | .el-icon-success:before { 28 | content: '\e62d'; 29 | } 30 | .el-icon-warning:before { 31 | content: '\e62e'; 32 | } 33 | .el-icon-question:before { 34 | content: '\e634'; 35 | } 36 | .el-icon-back:before { 37 | content: '\e606'; 38 | } 39 | .el-icon-arrow-left:before { 40 | content: '\e600'; 41 | } 42 | .el-icon-arrow-down:before { 43 | content: '\e603'; 44 | } 45 | .el-icon-arrow-right:before { 46 | content: '\e604'; 47 | } 48 | .el-icon-arrow-up:before { 49 | content: '\e605'; 50 | } 51 | .el-icon-caret-left:before { 52 | content: '\e60a'; 53 | } 54 | .el-icon-caret-bottom:before { 55 | content: '\e60b'; 56 | } 57 | .el-icon-caret-top:before { 58 | content: '\e60c'; 59 | } 60 | .el-icon-caret-right:before { 61 | content: '\e60e'; 62 | } 63 | .el-icon-d-arrow-left:before { 64 | content: '\e610'; 65 | } 66 | .el-icon-d-arrow-right:before { 67 | content: '\e613'; 68 | } 69 | .el-icon-minus:before { 70 | content: '\e621'; 71 | } 72 | .el-icon-plus:before { 73 | content: '\e62b'; 74 | } 75 | .el-icon-remove:before { 76 | content: '\e635'; 77 | } 78 | .el-icon-circle-plus:before { 79 | content: '\e601'; 80 | } 81 | .el-icon-remove-outline:before { 82 | content: '\e63c'; 83 | } 84 | .el-icon-circle-plus-outline:before { 85 | content: '\e602'; 86 | } 87 | .el-icon-close:before { 88 | content: '\e60f'; 89 | } 90 | .el-icon-check:before { 91 | content: '\e611'; 92 | } 93 | .el-icon-circle-close:before { 94 | content: '\e607'; 95 | } 96 | .el-icon-circle-check:before { 97 | content: '\e639'; 98 | } 99 | .el-icon-circle-close-outline:before { 100 | content: '\e609'; 101 | } 102 | .el-icon-circle-check-outline:before { 103 | content: '\e63e'; 104 | } 105 | .el-icon-zoom-out:before { 106 | content: '\e645'; 107 | } 108 | .el-icon-zoom-in:before { 109 | content: '\e641'; 110 | } 111 | .el-icon-d-caret:before { 112 | content: '\e615'; 113 | } 114 | .el-icon-sort:before { 115 | content: '\e640'; 116 | } 117 | .el-icon-sort-down:before { 118 | content: '\e630'; 119 | } 120 | .el-icon-sort-up:before { 121 | content: '\e631'; 122 | } 123 | .el-icon-tickets:before { 124 | content: '\e63f'; 125 | } 126 | .el-icon-document:before { 127 | content: '\e614'; 128 | } 129 | .el-icon-goods:before { 130 | content: '\e618'; 131 | } 132 | .el-icon-sold-out:before { 133 | content: '\e63b'; 134 | } 135 | .el-icon-news:before { 136 | content: '\e625'; 137 | } 138 | .el-icon-message:before { 139 | content: '\e61b'; 140 | } 141 | .el-icon-date:before { 142 | content: '\e608'; 143 | } 144 | .el-icon-printer:before { 145 | content: '\e62f'; 146 | } 147 | .el-icon-time:before { 148 | content: '\e642'; 149 | } 150 | .el-icon-bell:before { 151 | content: '\e622'; 152 | } 153 | .el-icon-mobile-phone:before { 154 | content: '\e624'; 155 | } 156 | .el-icon-service:before { 157 | content: '\e63a'; 158 | } 159 | .el-icon-view:before { 160 | content: '\e643'; 161 | } 162 | .el-icon-menu:before { 163 | content: '\e620'; 164 | } 165 | .el-icon-more:before { 166 | content: '\e646'; 167 | } 168 | .el-icon-more-outline:before { 169 | content: '\e626'; 170 | } 171 | .el-icon-star-on:before { 172 | content: '\e637'; 173 | } 174 | .el-icon-star-off:before { 175 | content: '\e63d'; 176 | } 177 | .el-icon-location:before { 178 | content: '\e61d'; 179 | } 180 | .el-icon-location-outline:before { 181 | content: '\e61f'; 182 | } 183 | .el-icon-phone:before { 184 | content: '\e627'; 185 | } 186 | .el-icon-phone-outline:before { 187 | content: '\e628'; 188 | } 189 | .el-icon-picture:before { 190 | content: '\e629'; 191 | } 192 | .el-icon-picture-outline:before { 193 | content: '\e62a'; 194 | } 195 | .el-icon-delete:before { 196 | content: '\e612'; 197 | } 198 | .el-icon-search:before { 199 | content: '\e619'; 200 | } 201 | .el-icon-edit:before { 202 | content: '\e61c'; 203 | } 204 | .el-icon-edit-outline:before { 205 | content: '\e616'; 206 | } 207 | .el-icon-rank:before { 208 | content: '\e632'; 209 | } 210 | .el-icon-refresh:before { 211 | content: '\e633'; 212 | } 213 | .el-icon-share:before { 214 | content: '\e636'; 215 | } 216 | .el-icon-setting:before { 217 | content: '\e638'; 218 | } 219 | .el-icon-upload:before { 220 | content: '\e60d'; 221 | } 222 | .el-icon-upload2:before { 223 | content: '\e644'; 224 | } 225 | .el-icon-download:before { 226 | content: '\e617'; 227 | } 228 | .el-icon-loading:before { 229 | content: '\e61e'; 230 | } 231 | .el-icon-loading { 232 | -webkit-animation: rotating 2s linear infinite; 233 | animation: rotating 2s linear infinite; 234 | } 235 | .el-icon--right { 236 | margin-left: 5px; 237 | } 238 | .el-icon--left { 239 | margin-right: 5px; 240 | } 241 | @-webkit-keyframes rotating { 242 | 0% { 243 | -webkit-transform: rotateZ(0); 244 | transform: rotateZ(0); 245 | } 246 | 100% { 247 | -webkit-transform: rotateZ(360deg); 248 | transform: rotateZ(360deg); 249 | } 250 | } 251 | @keyframes rotating { 252 | 0% { 253 | -webkit-transform: rotateZ(0); 254 | transform: rotateZ(0); 255 | } 256 | 100% { 257 | -webkit-transform: rotateZ(360deg); 258 | transform: rotateZ(360deg); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/assets/styles/element/loading.css: -------------------------------------------------------------------------------- 1 | .el-loading-parent--relative { 2 | position: relative !important; 3 | } 4 | .el-loading-parent--hidden { 5 | overflow: hidden !important; 6 | } 7 | .el-loading-mask { 8 | position: absolute; 9 | z-index: 2000; 10 | background-color: rgba(255, 255, 255, 0.9); 11 | margin: 0; 12 | top: 0; 13 | right: 0; 14 | bottom: 0; 15 | left: 0; 16 | -webkit-transition: opacity 0.3s; 17 | transition: opacity 0.3s; 18 | } 19 | .el-loading-mask.is-fullscreen { 20 | position: fixed; 21 | } 22 | .el-loading-mask.is-fullscreen .el-loading-spinner { 23 | margin-top: -25px; 24 | } 25 | .el-loading-mask.is-fullscreen .el-loading-spinner .circular { 26 | height: 50px; 27 | width: 50px; 28 | } 29 | .el-loading-spinner { 30 | top: 50%; 31 | margin-top: -21px; 32 | width: 100%; 33 | text-align: center; 34 | position: absolute; 35 | } 36 | .el-loading-spinner .el-loading-text { 37 | color: #000000; 38 | margin: 3px 0; 39 | font-size: 14px; 40 | } 41 | .el-loading-spinner .circular { 42 | height: 42px; 43 | width: 42px; 44 | -webkit-animation: loading-rotate 2s linear infinite; 45 | animation: loading-rotate 2s linear infinite; 46 | } 47 | .el-loading-spinner .path { 48 | -webkit-animation: loading-dash 1.5s ease-in-out infinite; 49 | animation: loading-dash 1.5s ease-in-out infinite; 50 | stroke-dasharray: 90, 150; 51 | stroke-dashoffset: 0; 52 | stroke-width: 2; 53 | stroke: #000000; 54 | stroke-linecap: round; 55 | } 56 | .el-loading-spinner i { 57 | color: #000000; 58 | } 59 | .el-loading-fade-enter, 60 | .el-loading-fade-leave-active { 61 | opacity: 0; 62 | } 63 | @-webkit-keyframes loading-rotate { 64 | 100% { 65 | -webkit-transform: rotate(360deg); 66 | transform: rotate(360deg); 67 | } 68 | } 69 | @keyframes loading-rotate { 70 | 100% { 71 | -webkit-transform: rotate(360deg); 72 | transform: rotate(360deg); 73 | } 74 | } 75 | @-webkit-keyframes loading-dash { 76 | 0% { 77 | stroke-dasharray: 1, 200; 78 | stroke-dashoffset: 0; 79 | } 80 | 50% { 81 | stroke-dasharray: 90, 150; 82 | stroke-dashoffset: -40px; 83 | } 84 | 100% { 85 | stroke-dasharray: 90, 150; 86 | stroke-dashoffset: -120px; 87 | } 88 | } 89 | @keyframes loading-dash { 90 | 0% { 91 | stroke-dasharray: 1, 200; 92 | stroke-dashoffset: 0; 93 | } 94 | 50% { 95 | stroke-dasharray: 90, 150; 96 | stroke-dashoffset: -40px; 97 | } 98 | 100% { 99 | stroke-dasharray: 90, 150; 100 | stroke-dashoffset: -120px; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/assets/styles/element/main.css: -------------------------------------------------------------------------------- 1 | .el-main { 2 | -webkit-box-flex: 1; 3 | -ms-flex: 1; 4 | flex: 1; 5 | -ms-flex-preferred-size: auto; 6 | flex-basis: auto; 7 | overflow: auto; 8 | -webkit-box-sizing: border-box; 9 | box-sizing: border-box; 10 | padding: 20px; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/styles/element/menu-item-group.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/menu-item-group.css -------------------------------------------------------------------------------- /src/assets/styles/element/menu-item.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/menu-item.css -------------------------------------------------------------------------------- /src/assets/styles/element/message.css: -------------------------------------------------------------------------------- 1 | .el-message__closeBtn:focus, 2 | .el-message__content:focus { 3 | outline-width: 0; 4 | } 5 | .el-message { 6 | min-width: 380px; 7 | -webkit-box-sizing: border-box; 8 | box-sizing: border-box; 9 | border-radius: 4px; 10 | border-width: 1px; 11 | border-style: solid; 12 | border-color: #ebeef5; 13 | position: fixed; 14 | left: 50%; 15 | top: 20px; 16 | -webkit-transform: translateX(-50%); 17 | transform: translateX(-50%); 18 | background-color: #edf2fc; 19 | -webkit-transition: opacity 0.3s, -webkit-transform 0.4s; 20 | transition: opacity 0.3s, -webkit-transform 0.4s; 21 | transition: opacity 0.3s, transform 0.4s; 22 | transition: opacity 0.3s, transform 0.4s, -webkit-transform 0.4s; 23 | overflow: hidden; 24 | padding: 15px 15px 15px 20px; 25 | display: -webkit-box; 26 | display: -ms-flexbox; 27 | display: flex; 28 | -webkit-box-align: center; 29 | -ms-flex-align: center; 30 | align-items: center; 31 | } 32 | .el-message.is-center { 33 | -webkit-box-pack: center; 34 | -ms-flex-pack: center; 35 | justify-content: center; 36 | } 37 | .el-message.is-closable .el-message__content { 38 | padding-right: 16px; 39 | } 40 | .el-message p { 41 | margin: 0; 42 | } 43 | .el-message--info .el-message__content { 44 | color: #909399; 45 | } 46 | .el-message--success { 47 | background-color: #f0f9eb; 48 | border-color: #e1f3d8; 49 | } 50 | .el-message--success .el-message__content { 51 | color: #67c23a; 52 | } 53 | .el-message--warning { 54 | background-color: #fdf6ec; 55 | border-color: #faecd8; 56 | } 57 | .el-message--warning .el-message__content { 58 | color: #e6a23c; 59 | } 60 | .el-message--error { 61 | background-color: #fef0f0; 62 | border-color: #fde2e2; 63 | } 64 | .el-message--error .el-message__content { 65 | color: #f56c6c; 66 | } 67 | .el-message__icon { 68 | margin-right: 10px; 69 | } 70 | .el-message__content { 71 | padding: 0; 72 | font-size: 14px; 73 | line-height: 1; 74 | } 75 | .el-message__closeBtn { 76 | position: absolute; 77 | top: 50%; 78 | right: 15px; 79 | -webkit-transform: translateY(-50%); 80 | transform: translateY(-50%); 81 | cursor: pointer; 82 | color: #c0c4cc; 83 | font-size: 16px; 84 | } 85 | .el-message__closeBtn:hover { 86 | color: #909399; 87 | } 88 | .el-message .el-icon-success { 89 | color: #67c23a; 90 | } 91 | .el-message .el-icon-error { 92 | color: #f56c6c; 93 | } 94 | .el-message .el-icon-info { 95 | color: #909399; 96 | } 97 | .el-message .el-icon-warning { 98 | color: #e6a23c; 99 | } 100 | .el-message-fade-enter, 101 | .el-message-fade-leave-active { 102 | opacity: 0; 103 | -webkit-transform: translate(-50%, -100%); 104 | transform: translate(-50%, -100%); 105 | } 106 | -------------------------------------------------------------------------------- /src/assets/styles/element/notification.css: -------------------------------------------------------------------------------- 1 | .el-notification { 2 | display: -webkit-box; 3 | display: -ms-flexbox; 4 | display: flex; 5 | width: 330px; 6 | padding: 14px 26px 14px 13px; 7 | border-radius: 8px; 8 | -webkit-box-sizing: border-box; 9 | box-sizing: border-box; 10 | border: 1px solid #ebeef5; 11 | position: fixed; 12 | background-color: #fff; 13 | -webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 14 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 15 | -webkit-transition: opacity 0.3s, left 0.3s, right 0.3s, top 0.4s, bottom 0.3s, 16 | -webkit-transform 0.3s; 17 | transition: opacity 0.3s, left 0.3s, right 0.3s, top 0.4s, bottom 0.3s, -webkit-transform 0.3s; 18 | transition: opacity 0.3s, transform 0.3s, left 0.3s, right 0.3s, top 0.4s, bottom 0.3s; 19 | transition: opacity 0.3s, transform 0.3s, left 0.3s, right 0.3s, top 0.4s, bottom 0.3s, 20 | -webkit-transform 0.3s; 21 | overflow: hidden; 22 | } 23 | .el-notification.right { 24 | right: 16px; 25 | } 26 | .el-notification.left { 27 | left: 16px; 28 | } 29 | .el-notification__group { 30 | margin-left: 13px; 31 | } 32 | .el-notification__title { 33 | font-weight: 700; 34 | font-size: 16px; 35 | color: #303133; 36 | margin: 0; 37 | } 38 | .el-notification__content { 39 | font-size: 14px; 40 | line-height: 21px; 41 | margin: 6px 0 0; 42 | color: #606266; 43 | text-align: justify; 44 | } 45 | .el-notification__content p { 46 | margin: 0; 47 | } 48 | .el-notification__icon { 49 | height: 24px; 50 | width: 24px; 51 | font-size: 24px; 52 | } 53 | .el-notification__closeBtn { 54 | position: absolute; 55 | top: 18px; 56 | right: 15px; 57 | cursor: pointer; 58 | color: #909399; 59 | font-size: 16px; 60 | } 61 | .el-notification__closeBtn:hover { 62 | color: #606266; 63 | } 64 | .el-notification .el-icon-success { 65 | color: #67c23a; 66 | } 67 | .el-notification .el-icon-error { 68 | color: #f56c6c; 69 | } 70 | .el-notification .el-icon-info { 71 | color: #909399; 72 | } 73 | .el-notification .el-icon-warning { 74 | color: #e6a23c; 75 | } 76 | .el-notification-fade-enter.right { 77 | right: 0; 78 | -webkit-transform: translateX(100%); 79 | transform: translateX(100%); 80 | } 81 | .el-notification-fade-enter.left { 82 | left: 0; 83 | -webkit-transform: translateX(-100%); 84 | transform: translateX(-100%); 85 | } 86 | .el-notification-fade-leave-active { 87 | opacity: 0; 88 | } 89 | -------------------------------------------------------------------------------- /src/assets/styles/element/option-group.css: -------------------------------------------------------------------------------- 1 | .el-select-group { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | .el-select-group__wrap { 6 | position: relative; 7 | list-style: none; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | .el-select-group__wrap:not(:last-of-type) { 12 | padding-bottom: 24px; 13 | } 14 | .el-select-group__wrap:not(:last-of-type)::after { 15 | content: ''; 16 | position: absolute; 17 | display: block; 18 | left: 20px; 19 | right: 20px; 20 | bottom: 12px; 21 | height: 1px; 22 | background: #e4e7ed; 23 | } 24 | .el-select-group__title { 25 | padding-left: 20px; 26 | font-size: 12px; 27 | color: #909399; 28 | line-height: 30px; 29 | } 30 | .el-select-group .el-select-dropdown__item { 31 | padding-left: 20px; 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/styles/element/option.css: -------------------------------------------------------------------------------- 1 | .el-select-dropdown__item { 2 | font-size: 14px; 3 | padding: 0 20px; 4 | position: relative; 5 | white-space: nowrap; 6 | overflow: hidden; 7 | text-overflow: ellipsis; 8 | color: #606266; 9 | height: 34px; 10 | line-height: 34px; 11 | -webkit-box-sizing: border-box; 12 | box-sizing: border-box; 13 | cursor: pointer; 14 | } 15 | .el-select-dropdown__item.is-disabled { 16 | color: #c0c4cc; 17 | cursor: not-allowed; 18 | } 19 | .el-select-dropdown__item.is-disabled:hover { 20 | background-color: #fff; 21 | } 22 | .el-select-dropdown__item.hover, 23 | .el-select-dropdown__item:hover { 24 | background-color: #f5f7fa; 25 | } 26 | .el-select-dropdown__item.selected { 27 | color: #000000; 28 | font-weight: 700; 29 | } 30 | .el-select-dropdown__item span { 31 | line-height: 34px !important; 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/styles/element/popover.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow, 2 | .el-popper .popper__arrow::after { 3 | position: absolute; 4 | display: block; 5 | width: 0; 6 | height: 0; 7 | border-color: transparent; 8 | border-style: solid; 9 | } 10 | .el-popper .popper__arrow { 11 | border-width: 6px; 12 | -webkit-filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 13 | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 14 | } 15 | .el-popper .popper__arrow::after { 16 | content: ' '; 17 | border-width: 6px; 18 | } 19 | .el-popper[x-placement^='top'] { 20 | margin-bottom: 12px; 21 | } 22 | .el-popper[x-placement^='top'] .popper__arrow { 23 | bottom: -6px; 24 | left: 50%; 25 | margin-right: 3px; 26 | border-top-color: #ebeef5; 27 | border-bottom-width: 0; 28 | } 29 | .el-popper[x-placement^='top'] .popper__arrow::after { 30 | bottom: 1px; 31 | margin-left: -6px; 32 | border-top-color: #fff; 33 | border-bottom-width: 0; 34 | } 35 | .el-popper[x-placement^='bottom'] { 36 | margin-top: 12px; 37 | } 38 | .el-popper[x-placement^='bottom'] .popper__arrow { 39 | top: -6px; 40 | left: 50%; 41 | margin-right: 3px; 42 | border-top-width: 0; 43 | border-bottom-color: #ebeef5; 44 | } 45 | .el-popper[x-placement^='bottom'] .popper__arrow::after { 46 | top: 1px; 47 | margin-left: -6px; 48 | border-top-width: 0; 49 | border-bottom-color: #fff; 50 | } 51 | .el-popper[x-placement^='right'] { 52 | margin-left: 12px; 53 | } 54 | .el-popper[x-placement^='right'] .popper__arrow { 55 | top: 50%; 56 | left: -6px; 57 | margin-bottom: 3px; 58 | border-right-color: #ebeef5; 59 | border-left-width: 0; 60 | } 61 | .el-popper[x-placement^='right'] .popper__arrow::after { 62 | bottom: -6px; 63 | left: 1px; 64 | border-right-color: #fff; 65 | border-left-width: 0; 66 | } 67 | .el-popper[x-placement^='left'] { 68 | margin-right: 12px; 69 | } 70 | .el-popper[x-placement^='left'] .popper__arrow { 71 | top: 50%; 72 | right: -6px; 73 | margin-bottom: 3px; 74 | border-right-width: 0; 75 | border-left-color: #ebeef5; 76 | } 77 | .el-popper[x-placement^='left'] .popper__arrow::after { 78 | right: 1px; 79 | bottom: -6px; 80 | margin-left: -6px; 81 | border-right-width: 0; 82 | border-left-color: #fff; 83 | } 84 | .el-popover { 85 | position: absolute; 86 | background: #fff; 87 | min-width: 150px; 88 | border-radius: 4px; 89 | border: 1px solid #ebeef5; 90 | padding: 12px; 91 | z-index: 2000; 92 | color: #606266; 93 | line-height: 1.4; 94 | text-align: justify; 95 | font-size: 14px; 96 | -webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 97 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 98 | } 99 | .el-popover--plain { 100 | padding: 18px 20px; 101 | } 102 | .el-popover__title { 103 | color: #303133; 104 | font-size: 16px; 105 | line-height: 1; 106 | margin-bottom: 12px; 107 | } 108 | .el-popover:focus, 109 | .el-popover:focus:active, 110 | .el-popover__reference:focus:hover, 111 | .el-popover__reference:focus:not(.focusing) { 112 | outline-width: 0; 113 | } 114 | -------------------------------------------------------------------------------- /src/assets/styles/element/popper.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow, 2 | .el-popper .popper__arrow::after { 3 | position: absolute; 4 | display: block; 5 | width: 0; 6 | height: 0; 7 | border-color: transparent; 8 | border-style: solid; 9 | } 10 | .el-popper .popper__arrow { 11 | border-width: 6px; 12 | -webkit-filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 13 | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 14 | } 15 | .el-popper .popper__arrow::after { 16 | content: ' '; 17 | border-width: 6px; 18 | } 19 | .el-popper[x-placement^='top'] { 20 | margin-bottom: 12px; 21 | } 22 | .el-popper[x-placement^='top'] .popper__arrow { 23 | bottom: -6px; 24 | left: 50%; 25 | margin-right: 3px; 26 | border-top-color: #ebeef5; 27 | border-bottom-width: 0; 28 | } 29 | .el-popper[x-placement^='top'] .popper__arrow::after { 30 | bottom: 1px; 31 | margin-left: -6px; 32 | border-top-color: #fff; 33 | border-bottom-width: 0; 34 | } 35 | .el-popper[x-placement^='bottom'] { 36 | margin-top: 12px; 37 | } 38 | .el-popper[x-placement^='bottom'] .popper__arrow { 39 | top: -6px; 40 | left: 50%; 41 | margin-right: 3px; 42 | border-top-width: 0; 43 | border-bottom-color: #ebeef5; 44 | } 45 | .el-popper[x-placement^='bottom'] .popper__arrow::after { 46 | top: 1px; 47 | margin-left: -6px; 48 | border-top-width: 0; 49 | border-bottom-color: #fff; 50 | } 51 | .el-popper[x-placement^='right'] { 52 | margin-left: 12px; 53 | } 54 | .el-popper[x-placement^='right'] .popper__arrow { 55 | top: 50%; 56 | left: -6px; 57 | margin-bottom: 3px; 58 | border-right-color: #ebeef5; 59 | border-left-width: 0; 60 | } 61 | .el-popper[x-placement^='right'] .popper__arrow::after { 62 | bottom: -6px; 63 | left: 1px; 64 | border-right-color: #fff; 65 | border-left-width: 0; 66 | } 67 | .el-popper[x-placement^='left'] { 68 | margin-right: 12px; 69 | } 70 | .el-popper[x-placement^='left'] .popper__arrow { 71 | top: 50%; 72 | right: -6px; 73 | margin-bottom: 3px; 74 | border-right-width: 0; 75 | border-left-color: #ebeef5; 76 | } 77 | .el-popper[x-placement^='left'] .popper__arrow::after { 78 | right: 1px; 79 | bottom: -6px; 80 | margin-left: -6px; 81 | border-right-width: 0; 82 | border-left-color: #fff; 83 | } 84 | -------------------------------------------------------------------------------- /src/assets/styles/element/progress.css: -------------------------------------------------------------------------------- 1 | .el-progress { 2 | position: relative; 3 | line-height: 1; 4 | } 5 | .el-progress__text { 6 | font-size: 14px; 7 | color: #606266; 8 | display: inline-block; 9 | vertical-align: middle; 10 | margin-left: 10px; 11 | line-height: 1; 12 | } 13 | .el-progress__text i { 14 | vertical-align: middle; 15 | display: block; 16 | } 17 | .el-progress--circle { 18 | display: inline-block; 19 | } 20 | .el-progress--circle .el-progress__text { 21 | position: absolute; 22 | top: 50%; 23 | left: 0; 24 | width: 100%; 25 | text-align: center; 26 | margin: 0; 27 | -webkit-transform: translate(0, -50%); 28 | transform: translate(0, -50%); 29 | } 30 | .el-progress--circle .el-progress__text i { 31 | vertical-align: middle; 32 | display: inline-block; 33 | } 34 | .el-progress--without-text .el-progress__text { 35 | display: none; 36 | } 37 | .el-progress--without-text .el-progress-bar { 38 | padding-right: 0; 39 | margin-right: 0; 40 | display: block; 41 | } 42 | .el-progress-bar, 43 | .el-progress-bar__inner::after, 44 | .el-progress-bar__innerText { 45 | display: inline-block; 46 | vertical-align: middle; 47 | } 48 | .el-progress--text-inside .el-progress-bar { 49 | padding-right: 0; 50 | margin-right: 0; 51 | } 52 | .el-progress.is-success .el-progress-bar__inner { 53 | background-color: #67c23a; 54 | } 55 | .el-progress.is-success .el-progress__text { 56 | color: #67c23a; 57 | } 58 | .el-progress.is-exception .el-progress-bar__inner { 59 | background-color: #f56c6c; 60 | } 61 | .el-progress.is-exception .el-progress__text { 62 | color: #f56c6c; 63 | } 64 | .el-progress-bar { 65 | padding-right: 50px; 66 | width: 100%; 67 | margin-right: -55px; 68 | -webkit-box-sizing: border-box; 69 | box-sizing: border-box; 70 | } 71 | .el-progress-bar__outer { 72 | height: 6px; 73 | border-radius: 100px; 74 | background-color: #ebeef5; 75 | overflow: hidden; 76 | position: relative; 77 | vertical-align: middle; 78 | } 79 | .el-progress-bar__inner { 80 | position: absolute; 81 | left: 0; 82 | top: 0; 83 | height: 100%; 84 | background-color: #000000; 85 | text-align: right; 86 | border-radius: 100px; 87 | line-height: 1; 88 | white-space: nowrap; 89 | } 90 | .el-progress-bar__inner::after { 91 | content: ''; 92 | height: 100%; 93 | } 94 | .el-progress-bar__innerText { 95 | color: #fff; 96 | font-size: 12px; 97 | margin: 0 5px; 98 | } 99 | @-webkit-keyframes progress { 100 | 0% { 101 | background-position: 0 0; 102 | } 103 | 100% { 104 | background-position: 32px 0; 105 | } 106 | } 107 | @keyframes progress { 108 | 0% { 109 | background-position: 0 0; 110 | } 111 | 100% { 112 | background-position: 32px 0; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/assets/styles/element/radio-button.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | .el-radio-button, 3 | .el-radio-button__inner { 4 | display: inline-block; 5 | position: relative; 6 | outline: 0; 7 | } 8 | .el-radio-button__inner { 9 | line-height: 1; 10 | white-space: nowrap; 11 | vertical-align: middle; 12 | background: #fff; 13 | border: 1px solid #dcdfe6; 14 | font-weight: 500; 15 | border-left: 0; 16 | color: #606266; 17 | -webkit-appearance: none; 18 | text-align: center; 19 | -webkit-box-sizing: border-box; 20 | box-sizing: border-box; 21 | margin: 0; 22 | cursor: pointer; 23 | -webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); 24 | transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); 25 | padding: 12px 20px; 26 | font-size: 14px; 27 | border-radius: 0; 28 | } 29 | .el-radio-button__inner.is-round { 30 | padding: 12px 20px; 31 | } 32 | .el-radio-button__inner:hover { 33 | color: #000000; 34 | } 35 | .el-radio-button__inner [class*='el-icon-'] { 36 | line-height: 0.9; 37 | } 38 | .el-radio-button__inner [class*='el-icon-'] + span { 39 | margin-left: 5px; 40 | } 41 | .el-radio-button:first-child .el-radio-button__inner { 42 | border-left: 1px solid #dcdfe6; 43 | border-radius: 4px 0 0 4px; 44 | -webkit-box-shadow: none !important; 45 | box-shadow: none !important; 46 | } 47 | .el-radio-button__orig-radio { 48 | opacity: 0; 49 | outline: 0; 50 | position: absolute; 51 | z-index: -1; 52 | } 53 | .el-radio-button__orig-radio:checked + .el-radio-button__inner { 54 | color: #fff; 55 | background-color: #000000; 56 | border-color: #000000; 57 | -webkit-box-shadow: -1px 0 0 0 #000000; 58 | box-shadow: -1px 0 0 0 #000000; 59 | } 60 | .el-radio-button__orig-radio:disabled + .el-radio-button__inner { 61 | color: #c0c4cc; 62 | cursor: not-allowed; 63 | background-image: none; 64 | background-color: #fff; 65 | border-color: #ebeef5; 66 | -webkit-box-shadow: none; 67 | box-shadow: none; 68 | } 69 | .el-radio-button__orig-radio:disabled:checked + .el-radio-button__inner { 70 | background-color: #f2f6fc; 71 | } 72 | .el-radio-button:last-child .el-radio-button__inner { 73 | border-radius: 0 4px 4px 0; 74 | } 75 | .el-radio-button:first-child:last-child .el-radio-button__inner { 76 | border-radius: 4px; 77 | } 78 | .el-radio-button--medium .el-radio-button__inner { 79 | padding: 10px 20px; 80 | font-size: 14px; 81 | border-radius: 0; 82 | } 83 | .el-radio-button--medium .el-radio-button__inner.is-round { 84 | padding: 10px 20px; 85 | } 86 | .el-radio-button--small .el-radio-button__inner { 87 | padding: 9px 15px; 88 | font-size: 12px; 89 | border-radius: 0; 90 | } 91 | .el-radio-button--small .el-radio-button__inner.is-round { 92 | padding: 9px 15px; 93 | } 94 | .el-radio-button--mini .el-radio-button__inner { 95 | padding: 7px 15px; 96 | font-size: 12px; 97 | border-radius: 0; 98 | } 99 | .el-radio-button--mini .el-radio-button__inner.is-round { 100 | padding: 7px 15px; 101 | } 102 | .el-radio-button:focus:not(.is-focus):not(:active) { 103 | -webkit-box-shadow: 0 0 2px 2px #000000; 104 | box-shadow: 0 0 2px 2px #000000; 105 | } 106 | -------------------------------------------------------------------------------- /src/assets/styles/element/radio-group.css: -------------------------------------------------------------------------------- 1 | .el-radio-group { 2 | display: inline-block; 3 | line-height: 1; 4 | vertical-align: middle; 5 | font-size: 0; 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/styles/element/radio.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | .el-radio, 3 | .el-radio--medium.is-bordered .el-radio__label { 4 | font-size: 14px; 5 | } 6 | .el-radio, 7 | .el-radio__input { 8 | white-space: nowrap; 9 | line-height: 1; 10 | outline: 0; 11 | } 12 | .el-radio, 13 | .el-radio__inner, 14 | .el-radio__input { 15 | position: relative; 16 | display: inline-block; 17 | } 18 | .el-radio { 19 | color: #606266; 20 | font-weight: 500; 21 | cursor: pointer; 22 | -moz-user-select: none; 23 | -webkit-user-select: none; 24 | -ms-user-select: none; 25 | } 26 | .el-radio.is-bordered { 27 | padding: 12px 20px 0 10px; 28 | border-radius: 4px; 29 | border: 1px solid #dcdfe6; 30 | -webkit-box-sizing: border-box; 31 | box-sizing: border-box; 32 | height: 40px; 33 | } 34 | .el-radio.is-bordered.is-checked { 35 | border-color: #000000; 36 | } 37 | .el-radio.is-bordered.is-disabled { 38 | cursor: not-allowed; 39 | border-color: #ebeef5; 40 | } 41 | .el-radio__input.is-disabled .el-radio__inner, 42 | .el-radio__input.is-disabled.is-checked .el-radio__inner { 43 | background-color: #f5f7fa; 44 | border-color: #e4e7ed; 45 | } 46 | .el-radio.is-bordered + .el-radio.is-bordered { 47 | margin-left: 10px; 48 | } 49 | .el-radio--medium.is-bordered { 50 | padding: 10px 20px 0 10px; 51 | border-radius: 4px; 52 | height: 36px; 53 | } 54 | .el-radio--mini.is-bordered .el-radio__label, 55 | .el-radio--small.is-bordered .el-radio__label { 56 | font-size: 12px; 57 | } 58 | .el-radio--medium.is-bordered .el-radio__inner { 59 | height: 14px; 60 | width: 14px; 61 | } 62 | .el-radio--small.is-bordered { 63 | padding: 8px 15px 0 10px; 64 | border-radius: 3px; 65 | height: 32px; 66 | } 67 | .el-radio--small.is-bordered .el-radio__inner { 68 | height: 12px; 69 | width: 12px; 70 | } 71 | .el-radio--mini.is-bordered { 72 | padding: 6px 15px 0 10px; 73 | border-radius: 3px; 74 | height: 28px; 75 | } 76 | .el-radio--mini.is-bordered .el-radio__inner { 77 | height: 12px; 78 | width: 12px; 79 | } 80 | .el-radio + .el-radio { 81 | margin-left: 30px; 82 | } 83 | .el-radio__input { 84 | cursor: pointer; 85 | vertical-align: middle; 86 | } 87 | .el-radio__input.is-disabled .el-radio__inner { 88 | cursor: not-allowed; 89 | } 90 | .el-radio__input.is-disabled .el-radio__inner::after { 91 | cursor: not-allowed; 92 | background-color: #f5f7fa; 93 | } 94 | .el-radio__input.is-disabled .el-radio__inner + .el-radio__label { 95 | cursor: not-allowed; 96 | } 97 | .el-radio__input.is-disabled.is-checked .el-radio__inner::after { 98 | background-color: #c0c4cc; 99 | } 100 | .el-radio__input.is-disabled + span.el-radio__label { 101 | color: #c0c4cc; 102 | cursor: not-allowed; 103 | } 104 | .el-radio__input.is-checked .el-radio__inner { 105 | border-color: #000000; 106 | background: #000000; 107 | } 108 | .el-radio__input.is-checked .el-radio__inner::after { 109 | -webkit-transform: translate(-50%, -50%) scale(1); 110 | transform: translate(-50%, -50%) scale(1); 111 | } 112 | .el-radio__input.is-checked + .el-radio__label { 113 | color: #000000; 114 | } 115 | .el-radio__input.is-focus .el-radio__inner { 116 | border-color: #000000; 117 | } 118 | .el-radio__inner { 119 | border: 1px solid #dcdfe6; 120 | border-radius: 100%; 121 | width: 14px; 122 | height: 14px; 123 | background-color: #fff; 124 | cursor: pointer; 125 | -webkit-box-sizing: border-box; 126 | box-sizing: border-box; 127 | } 128 | .el-radio__inner:hover { 129 | border-color: #000000; 130 | } 131 | .el-radio__inner::after { 132 | width: 4px; 133 | height: 4px; 134 | border-radius: 100%; 135 | background-color: #fff; 136 | content: ''; 137 | position: absolute; 138 | left: 50%; 139 | top: 50%; 140 | -webkit-transform: translate(-50%, -50%) scale(0); 141 | transform: translate(-50%, -50%) scale(0); 142 | -webkit-transition: -webkit-transform 0.15s ease-in; 143 | transition: -webkit-transform 0.15s ease-in; 144 | transition: transform 0.15s ease-in; 145 | transition: transform 0.15s ease-in, -webkit-transform 0.15s ease-in; 146 | } 147 | .el-radio__original { 148 | opacity: 0; 149 | outline: 0; 150 | position: absolute; 151 | z-index: -1; 152 | top: 0; 153 | left: 0; 154 | right: 0; 155 | bottom: 0; 156 | margin: 0; 157 | } 158 | .el-radio:focus:not(.is-focus):not(:active) .el-radio__inner { 159 | -webkit-box-shadow: 0 0 2px 2px #000000; 160 | box-shadow: 0 0 2px 2px #000000; 161 | } 162 | .el-radio__label { 163 | font-size: 14px; 164 | padding-left: 10px; 165 | } 166 | -------------------------------------------------------------------------------- /src/assets/styles/element/rate.css: -------------------------------------------------------------------------------- 1 | .el-rate__icon, 2 | .el-rate__item { 3 | position: relative; 4 | display: inline-block; 5 | } 6 | .el-rate { 7 | height: 20px; 8 | line-height: 1; 9 | } 10 | .el-rate:active, 11 | .el-rate:focus { 12 | outline-width: 0; 13 | } 14 | .el-rate__item { 15 | font-size: 0; 16 | vertical-align: middle; 17 | } 18 | .el-rate__icon { 19 | font-size: 18px; 20 | margin-right: 6px; 21 | color: #c0c4cc; 22 | -webkit-transition: 0.3s; 23 | transition: 0.3s; 24 | } 25 | .el-rate__decimal, 26 | .el-rate__icon .path2 { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | } 31 | .el-rate__icon.hover { 32 | -webkit-transform: scale(1.15); 33 | transform: scale(1.15); 34 | } 35 | .el-rate__decimal { 36 | display: inline-block; 37 | overflow: hidden; 38 | } 39 | .el-rate__text { 40 | font-size: 14px; 41 | vertical-align: middle; 42 | } 43 | -------------------------------------------------------------------------------- /src/assets/styles/element/reset.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { 3 | font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 4 | '微软雅黑', Arial, sans-serif; 5 | font-weight: 400; 6 | font-size: 14px; 7 | color: #000; 8 | -webkit-font-smoothing: antialiased; 9 | } 10 | a { 11 | color: #000000; 12 | text-decoration: none; 13 | } 14 | a:focus, 15 | a:hover { 16 | color: rgb(51, 51, 51); 17 | } 18 | a:active { 19 | color: rgb(0, 0, 0); 20 | } 21 | h1, 22 | h2, 23 | h3, 24 | h4, 25 | h5, 26 | h6 { 27 | color: #606266; 28 | font-weight: inherit; 29 | } 30 | h1:first-child, 31 | h2:first-child, 32 | h3:first-child, 33 | h4:first-child, 34 | h5:first-child, 35 | h6:first-child, 36 | p:first-child { 37 | margin-top: 0; 38 | } 39 | h1:last-child, 40 | h2:last-child, 41 | h3:last-child, 42 | h4:last-child, 43 | h5:last-child, 44 | h6:last-child, 45 | p:last-child { 46 | margin-bottom: 0; 47 | } 48 | h1 { 49 | font-size: 20px; 50 | } 51 | h2 { 52 | font-size: 18px; 53 | } 54 | h3 { 55 | font-size: 16px; 56 | } 57 | h4, 58 | h5, 59 | h6, 60 | p { 61 | font-size: inherit; 62 | } 63 | p { 64 | line-height: 1.8; 65 | } 66 | sub, 67 | sup { 68 | font-size: 13px; 69 | } 70 | small { 71 | font-size: 12px; 72 | } 73 | hr { 74 | margin-top: 20px; 75 | margin-bottom: 20px; 76 | border: 0; 77 | border-top: 1px solid #eee; 78 | } 79 | -------------------------------------------------------------------------------- /src/assets/styles/element/row.css: -------------------------------------------------------------------------------- 1 | .el-row { 2 | position: relative; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | .el-row::after, 7 | .el-row::before { 8 | display: table; 9 | content: ''; 10 | } 11 | .el-row::after { 12 | clear: both; 13 | } 14 | .el-row--flex { 15 | display: -webkit-box; 16 | display: -ms-flexbox; 17 | display: flex; 18 | } 19 | .el-row--flex:after, 20 | .el-row--flex:before { 21 | display: none; 22 | } 23 | .el-row--flex.is-justify-center { 24 | -webkit-box-pack: center; 25 | -ms-flex-pack: center; 26 | justify-content: center; 27 | } 28 | .el-row--flex.is-justify-end { 29 | -webkit-box-pack: end; 30 | -ms-flex-pack: end; 31 | justify-content: flex-end; 32 | } 33 | .el-row--flex.is-justify-space-between { 34 | -webkit-box-pack: justify; 35 | -ms-flex-pack: justify; 36 | justify-content: space-between; 37 | } 38 | .el-row--flex.is-justify-space-around { 39 | -ms-flex-pack: distribute; 40 | justify-content: space-around; 41 | } 42 | .el-row--flex.is-align-middle { 43 | -webkit-box-align: center; 44 | -ms-flex-align: center; 45 | align-items: center; 46 | } 47 | .el-row--flex.is-align-bottom { 48 | -webkit-box-align: end; 49 | -ms-flex-align: end; 50 | align-items: flex-end; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/styles/element/scrollbar.css: -------------------------------------------------------------------------------- 1 | .el-scrollbar { 2 | overflow: hidden; 3 | position: relative; 4 | } 5 | .el-scrollbar:active > .el-scrollbar__bar, 6 | .el-scrollbar:focus > .el-scrollbar__bar, 7 | .el-scrollbar:hover > .el-scrollbar__bar { 8 | opacity: 1; 9 | -webkit-transition: opacity 340ms ease-out; 10 | transition: opacity 340ms ease-out; 11 | } 12 | .el-scrollbar__wrap { 13 | overflow: scroll; 14 | height: 100%; 15 | } 16 | .el-scrollbar__wrap--hidden-default::-webkit-scrollbar { 17 | width: 0; 18 | height: 0; 19 | } 20 | .el-scrollbar__thumb { 21 | position: relative; 22 | display: block; 23 | width: 0; 24 | height: 0; 25 | cursor: pointer; 26 | border-radius: inherit; 27 | background-color: rgba(144, 147, 153, 0.3); 28 | -webkit-transition: 0.3s background-color; 29 | transition: 0.3s background-color; 30 | } 31 | .el-scrollbar__thumb:hover { 32 | background-color: rgba(144, 147, 153, 0.5); 33 | } 34 | .el-scrollbar__bar { 35 | position: absolute; 36 | right: 2px; 37 | bottom: 2px; 38 | z-index: 1; 39 | border-radius: 4px; 40 | opacity: 0; 41 | -webkit-transition: opacity 120ms ease-out; 42 | transition: opacity 120ms ease-out; 43 | } 44 | .el-scrollbar__bar.is-vertical { 45 | width: 6px; 46 | top: 2px; 47 | } 48 | .el-scrollbar__bar.is-vertical > div { 49 | width: 100%; 50 | } 51 | .el-scrollbar__bar.is-horizontal { 52 | height: 6px; 53 | left: 2px; 54 | } 55 | .el-scrollbar__bar.is-horizontal > div { 56 | height: 100%; 57 | } 58 | -------------------------------------------------------------------------------- /src/assets/styles/element/select-dropdown.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow, 2 | .el-popper .popper__arrow::after { 3 | position: absolute; 4 | display: block; 5 | width: 0; 6 | height: 0; 7 | border-color: transparent; 8 | border-style: solid; 9 | } 10 | .el-popper .popper__arrow { 11 | border-width: 6px; 12 | -webkit-filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 13 | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); 14 | } 15 | .el-popper .popper__arrow::after { 16 | content: ' '; 17 | border-width: 6px; 18 | } 19 | .el-popper[x-placement^='top'] { 20 | margin-bottom: 12px; 21 | } 22 | .el-popper[x-placement^='top'] .popper__arrow { 23 | bottom: -6px; 24 | left: 50%; 25 | margin-right: 3px; 26 | border-top-color: #ebeef5; 27 | border-bottom-width: 0; 28 | } 29 | .el-popper[x-placement^='top'] .popper__arrow::after { 30 | bottom: 1px; 31 | margin-left: -6px; 32 | border-top-color: #fff; 33 | border-bottom-width: 0; 34 | } 35 | .el-popper[x-placement^='bottom'] { 36 | margin-top: 12px; 37 | } 38 | .el-popper[x-placement^='bottom'] .popper__arrow { 39 | top: -6px; 40 | left: 50%; 41 | margin-right: 3px; 42 | border-top-width: 0; 43 | border-bottom-color: #ebeef5; 44 | } 45 | .el-popper[x-placement^='bottom'] .popper__arrow::after { 46 | top: 1px; 47 | margin-left: -6px; 48 | border-top-width: 0; 49 | border-bottom-color: #fff; 50 | } 51 | .el-popper[x-placement^='right'] { 52 | margin-left: 12px; 53 | } 54 | .el-popper[x-placement^='right'] .popper__arrow { 55 | top: 50%; 56 | left: -6px; 57 | margin-bottom: 3px; 58 | border-right-color: #ebeef5; 59 | border-left-width: 0; 60 | } 61 | .el-popper[x-placement^='right'] .popper__arrow::after { 62 | bottom: -6px; 63 | left: 1px; 64 | border-right-color: #fff; 65 | border-left-width: 0; 66 | } 67 | .el-popper[x-placement^='left'] { 68 | margin-right: 12px; 69 | } 70 | .el-popper[x-placement^='left'] .popper__arrow { 71 | top: 50%; 72 | right: -6px; 73 | margin-bottom: 3px; 74 | border-right-width: 0; 75 | border-left-color: #ebeef5; 76 | } 77 | .el-popper[x-placement^='left'] .popper__arrow::after { 78 | right: 1px; 79 | bottom: -6px; 80 | margin-left: -6px; 81 | border-right-width: 0; 82 | border-left-color: #fff; 83 | } 84 | .el-select-dropdown { 85 | position: absolute; 86 | z-index: 1001; 87 | border: 1px solid #e4e7ed; 88 | border-radius: 4px; 89 | background-color: #fff; 90 | -webkit-box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 91 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); 92 | -webkit-box-sizing: border-box; 93 | box-sizing: border-box; 94 | margin: 5px 0; 95 | } 96 | .el-select-dropdown.is-multiple .el-select-dropdown__item.selected { 97 | color: #000000; 98 | background-color: #fff; 99 | } 100 | .el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover { 101 | background-color: #f5f7fa; 102 | } 103 | .el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after { 104 | position: absolute; 105 | right: 20px; 106 | font-family: element-icons; 107 | content: '\E611'; 108 | font-size: 12px; 109 | font-weight: 700; 110 | -webkit-font-smoothing: antialiased; 111 | -moz-osx-font-smoothing: grayscale; 112 | } 113 | .el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list { 114 | padding: 0; 115 | } 116 | .el-select-dropdown__empty { 117 | padding: 10px 0; 118 | margin: 0; 119 | text-align: center; 120 | color: #999; 121 | font-size: 14px; 122 | } 123 | .el-select-dropdown__wrap { 124 | max-height: 274px; 125 | } 126 | .el-select-dropdown__list { 127 | list-style: none; 128 | padding: 6px 0; 129 | margin: 0; 130 | -webkit-box-sizing: border-box; 131 | box-sizing: border-box; 132 | } 133 | -------------------------------------------------------------------------------- /src/assets/styles/element/spinner.css: -------------------------------------------------------------------------------- 1 | .el-time-spinner { 2 | width: 100%; 3 | white-space: nowrap; 4 | } 5 | .el-spinner { 6 | display: inline-block; 7 | vertical-align: middle; 8 | } 9 | .el-spinner-inner { 10 | -webkit-animation: rotate 2s linear infinite; 11 | animation: rotate 2s linear infinite; 12 | width: 50px; 13 | height: 50px; 14 | } 15 | .el-spinner-inner .path { 16 | stroke: #ececec; 17 | stroke-linecap: round; 18 | -webkit-animation: dash 1.5s ease-in-out infinite; 19 | animation: dash 1.5s ease-in-out infinite; 20 | } 21 | @-webkit-keyframes rotate { 22 | 100% { 23 | -webkit-transform: rotate(360deg); 24 | transform: rotate(360deg); 25 | } 26 | } 27 | @keyframes rotate { 28 | 100% { 29 | -webkit-transform: rotate(360deg); 30 | transform: rotate(360deg); 31 | } 32 | } 33 | @-webkit-keyframes dash { 34 | 0% { 35 | stroke-dasharray: 1, 150; 36 | stroke-dashoffset: 0; 37 | } 38 | 50% { 39 | stroke-dasharray: 90, 150; 40 | stroke-dashoffset: -35; 41 | } 42 | 100% { 43 | stroke-dasharray: 90, 150; 44 | stroke-dashoffset: -124; 45 | } 46 | } 47 | @keyframes dash { 48 | 0% { 49 | stroke-dasharray: 1, 150; 50 | stroke-dashoffset: 0; 51 | } 52 | 50% { 53 | stroke-dasharray: 90, 150; 54 | stroke-dashoffset: -35; 55 | } 56 | 100% { 57 | stroke-dasharray: 90, 150; 58 | stroke-dashoffset: -124; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/assets/styles/element/step.css: -------------------------------------------------------------------------------- 1 | .el-step { 2 | position: relative; 3 | -ms-flex-negative: 1; 4 | flex-shrink: 1; 5 | } 6 | .el-step:last-of-type .el-step__line { 7 | display: none; 8 | } 9 | .el-step:last-of-type.is-flex { 10 | -ms-flex-preferred-size: auto !important; 11 | flex-basis: auto !important; 12 | -ms-flex-negative: 0; 13 | flex-shrink: 0; 14 | -webkit-box-flex: 0; 15 | -ms-flex-positive: 0; 16 | flex-grow: 0; 17 | } 18 | .el-step:last-of-type .el-step__description, 19 | .el-step:last-of-type .el-step__main { 20 | padding-right: 0; 21 | } 22 | .el-step__head { 23 | position: relative; 24 | width: 100%; 25 | } 26 | .el-step__head.is-process { 27 | color: #303133; 28 | border-color: #303133; 29 | } 30 | .el-step__head.is-wait { 31 | color: #c0c4cc; 32 | border-color: #c0c4cc; 33 | } 34 | .el-step__head.is-success { 35 | color: #67c23a; 36 | border-color: #67c23a; 37 | } 38 | .el-step__head.is-error { 39 | color: #f56c6c; 40 | border-color: #f56c6c; 41 | } 42 | .el-step__head.is-finish { 43 | color: #000000; 44 | border-color: #000000; 45 | } 46 | .el-step__icon { 47 | position: relative; 48 | z-index: 1; 49 | display: -webkit-inline-box; 50 | display: -ms-inline-flexbox; 51 | display: inline-flex; 52 | -webkit-box-pack: center; 53 | -ms-flex-pack: center; 54 | justify-content: center; 55 | -webkit-box-align: center; 56 | -ms-flex-align: center; 57 | align-items: center; 58 | width: 24px; 59 | height: 24px; 60 | font-size: 14px; 61 | -webkit-box-sizing: border-box; 62 | box-sizing: border-box; 63 | background: #fff; 64 | -webkit-transition: 0.15s ease-out; 65 | transition: 0.15s ease-out; 66 | } 67 | .el-step__icon.is-text { 68 | border-radius: 50%; 69 | border: 2px solid; 70 | border-color: inherit; 71 | } 72 | .el-step__icon.is-icon { 73 | width: 40px; 74 | } 75 | .el-step__icon-inner { 76 | display: inline-block; 77 | -webkit-user-select: none; 78 | -moz-user-select: none; 79 | -ms-user-select: none; 80 | user-select: none; 81 | text-align: center; 82 | font-weight: 700; 83 | line-height: 1; 84 | color: inherit; 85 | } 86 | .el-step__icon-inner[class*='el-icon']:not(.is-status) { 87 | font-size: 25px; 88 | font-weight: 400; 89 | } 90 | .el-step__icon-inner.is-status { 91 | -webkit-transform: translateY(1px); 92 | transform: translateY(1px); 93 | } 94 | .el-step__line { 95 | position: absolute; 96 | border-color: inherit; 97 | background-color: #c0c4cc; 98 | } 99 | .el-step__line-inner { 100 | display: block; 101 | border-width: 1px; 102 | border-style: solid; 103 | border-color: inherit; 104 | -webkit-transition: 0.15s ease-out; 105 | transition: 0.15s ease-out; 106 | -webkit-box-sizing: border-box; 107 | box-sizing: border-box; 108 | width: 0; 109 | height: 0; 110 | } 111 | .el-step__main { 112 | white-space: normal; 113 | text-align: left; 114 | } 115 | .el-step__title { 116 | font-size: 16px; 117 | line-height: 38px; 118 | } 119 | .el-step__title.is-process { 120 | font-weight: 700; 121 | color: #303133; 122 | } 123 | .el-step__title.is-wait { 124 | color: #c0c4cc; 125 | } 126 | .el-step__title.is-success { 127 | color: #67c23a; 128 | } 129 | .el-step__title.is-error { 130 | color: #f56c6c; 131 | } 132 | .el-step__title.is-finish { 133 | color: #000000; 134 | } 135 | .el-step__description { 136 | padding-right: 10%; 137 | margin-top: -5px; 138 | font-size: 12px; 139 | line-height: 20px; 140 | font-weight: 400; 141 | } 142 | .el-step__description.is-process { 143 | color: #303133; 144 | } 145 | .el-step__description.is-wait { 146 | color: #c0c4cc; 147 | } 148 | .el-step__description.is-success { 149 | color: #67c23a; 150 | } 151 | .el-step__description.is-error { 152 | color: #f56c6c; 153 | } 154 | .el-step__description.is-finish { 155 | color: #000000; 156 | } 157 | .el-step.is-horizontal { 158 | display: inline-block; 159 | } 160 | .el-step.is-horizontal .el-step__line { 161 | height: 2px; 162 | top: 11px; 163 | left: 0; 164 | right: 0; 165 | } 166 | .el-step.is-vertical { 167 | display: -webkit-box; 168 | display: -ms-flexbox; 169 | display: flex; 170 | } 171 | .el-step.is-vertical .el-step__head { 172 | -webkit-box-flex: 0; 173 | -ms-flex-positive: 0; 174 | flex-grow: 0; 175 | width: 24px; 176 | } 177 | .el-step.is-vertical .el-step__main { 178 | padding-left: 10px; 179 | -webkit-box-flex: 1; 180 | -ms-flex-positive: 1; 181 | flex-grow: 1; 182 | } 183 | .el-step.is-vertical .el-step__title { 184 | line-height: 24px; 185 | padding-bottom: 8px; 186 | } 187 | .el-step.is-vertical .el-step__line { 188 | width: 2px; 189 | top: 0; 190 | bottom: 0; 191 | left: 11px; 192 | } 193 | .el-step.is-vertical .el-step__icon.is-icon { 194 | width: 24px; 195 | } 196 | .el-step.is-center .el-step__head, 197 | .el-step.is-center .el-step__main { 198 | text-align: center; 199 | } 200 | .el-step.is-center .el-step__description { 201 | padding-left: 20%; 202 | padding-right: 20%; 203 | } 204 | .el-step.is-center .el-step__line { 205 | left: 50%; 206 | right: -50%; 207 | } 208 | .el-step.is-simple { 209 | display: -webkit-box; 210 | display: -ms-flexbox; 211 | display: flex; 212 | -webkit-box-align: center; 213 | -ms-flex-align: center; 214 | align-items: center; 215 | } 216 | .el-step.is-simple .el-step__head { 217 | width: auto; 218 | font-size: 0; 219 | padding-right: 10px; 220 | } 221 | .el-step.is-simple .el-step__icon { 222 | background: 0 0; 223 | width: 16px; 224 | height: 16px; 225 | font-size: 12px; 226 | } 227 | .el-step.is-simple .el-step__icon-inner[class*='el-icon']:not(.is-status) { 228 | font-size: 18px; 229 | } 230 | .el-step.is-simple .el-step__icon-inner.is-status { 231 | -webkit-transform: scale(0.8) translateY(1px); 232 | transform: scale(0.8) translateY(1px); 233 | } 234 | .el-step.is-simple .el-step__main { 235 | position: relative; 236 | display: -webkit-box; 237 | display: -ms-flexbox; 238 | display: flex; 239 | -webkit-box-align: stretch; 240 | -ms-flex-align: stretch; 241 | align-items: stretch; 242 | -webkit-box-flex: 1; 243 | -ms-flex-positive: 1; 244 | flex-grow: 1; 245 | } 246 | .el-step.is-simple .el-step__title { 247 | font-size: 16px; 248 | line-height: 20px; 249 | } 250 | .el-step.is-simple:not(:last-of-type) .el-step__title { 251 | max-width: 50%; 252 | word-break: break-all; 253 | } 254 | .el-step.is-simple .el-step__arrow { 255 | -webkit-box-flex: 1; 256 | -ms-flex-positive: 1; 257 | flex-grow: 1; 258 | display: -webkit-box; 259 | display: -ms-flexbox; 260 | display: flex; 261 | -webkit-box-align: center; 262 | -ms-flex-align: center; 263 | align-items: center; 264 | -webkit-box-pack: center; 265 | -ms-flex-pack: center; 266 | justify-content: center; 267 | } 268 | .el-step.is-simple .el-step__arrow::after, 269 | .el-step.is-simple .el-step__arrow::before { 270 | content: ''; 271 | display: inline-block; 272 | position: absolute; 273 | height: 15px; 274 | width: 1px; 275 | background: #c0c4cc; 276 | } 277 | .el-step.is-simple .el-step__arrow::before { 278 | -webkit-transform: rotate(-45deg) translateY(-4px); 279 | transform: rotate(-45deg) translateY(-4px); 280 | -webkit-transform-origin: 0 0; 281 | transform-origin: 0 0; 282 | } 283 | .el-step.is-simple .el-step__arrow::after { 284 | -webkit-transform: rotate(45deg) translateY(4px); 285 | transform: rotate(45deg) translateY(4px); 286 | -webkit-transform-origin: 100% 100%; 287 | transform-origin: 100% 100%; 288 | } 289 | .el-step.is-simple:last-of-type .el-step__arrow { 290 | display: none; 291 | } 292 | -------------------------------------------------------------------------------- /src/assets/styles/element/steps.css: -------------------------------------------------------------------------------- 1 | .el-steps { 2 | display: -webkit-box; 3 | display: -ms-flexbox; 4 | display: flex; 5 | } 6 | .el-steps--simple { 7 | padding: 13px 8%; 8 | border-radius: 4px; 9 | background: #f5f7fa; 10 | } 11 | .el-steps--horizontal { 12 | white-space: nowrap; 13 | } 14 | .el-steps--vertical { 15 | height: 100%; 16 | -webkit-box-orient: vertical; 17 | -webkit-box-direction: normal; 18 | -ms-flex-flow: column; 19 | flex-flow: column; 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/styles/element/submenu.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/submenu.css -------------------------------------------------------------------------------- /src/assets/styles/element/switch.css: -------------------------------------------------------------------------------- 1 | .el-switch { 2 | display: -webkit-inline-box; 3 | display: -ms-inline-flexbox; 4 | display: inline-flex; 5 | -webkit-box-align: center; 6 | -ms-flex-align: center; 7 | align-items: center; 8 | position: relative; 9 | font-size: 14px; 10 | line-height: 20px; 11 | height: 20px; 12 | vertical-align: middle; 13 | } 14 | .el-switch.is-disabled .el-switch__core, 15 | .el-switch.is-disabled .el-switch__label { 16 | cursor: not-allowed; 17 | } 18 | .el-switch__core, 19 | .el-switch__label { 20 | display: inline-block; 21 | cursor: pointer; 22 | vertical-align: middle; 23 | } 24 | .el-switch__label { 25 | -webkit-transition: 0.2s; 26 | transition: 0.2s; 27 | height: 20px; 28 | font-size: 14px; 29 | font-weight: 500; 30 | color: #303133; 31 | } 32 | .el-switch__label.is-active { 33 | color: #000000; 34 | } 35 | .el-switch__label--left { 36 | margin-right: 10px; 37 | } 38 | .el-switch__label--right { 39 | margin-left: 10px; 40 | } 41 | .el-switch__label * { 42 | line-height: 1; 43 | font-size: 14px; 44 | display: inline-block; 45 | } 46 | .el-switch__input { 47 | position: absolute; 48 | width: 0; 49 | height: 0; 50 | opacity: 0; 51 | margin: 0; 52 | } 53 | .el-switch__input:focus ~ .el-switch__core { 54 | outline: #000000 solid 1px; 55 | } 56 | .el-switch__core { 57 | margin: 0; 58 | position: relative; 59 | width: 40px; 60 | height: 20px; 61 | border: 1px solid #dcdfe6; 62 | outline: 0; 63 | border-radius: 10px; 64 | -webkit-box-sizing: border-box; 65 | box-sizing: border-box; 66 | background: #dcdfe6; 67 | -webkit-transition: border-color 0.3s, background-color 0.3s; 68 | transition: border-color 0.3s, background-color 0.3s; 69 | } 70 | .el-switch__core:after { 71 | content: ''; 72 | position: absolute; 73 | top: 1px; 74 | left: 1px; 75 | border-radius: 100%; 76 | -webkit-transition: all 0.3s; 77 | transition: all 0.3s; 78 | width: 16px; 79 | height: 16px; 80 | background-color: #fff; 81 | } 82 | .el-switch.is-checked .el-switch__core { 83 | border-color: #000000; 84 | background-color: #000000; 85 | } 86 | .el-switch.is-checked .el-switch__core::after { 87 | left: 100%; 88 | margin-left: -17px; 89 | } 90 | .el-switch.is-disabled { 91 | opacity: 0.6; 92 | } 93 | .el-switch--wide .el-switch__label.el-switch__label--left span { 94 | left: 10px; 95 | } 96 | .el-switch--wide .el-switch__label.el-switch__label--right span { 97 | right: 10px; 98 | } 99 | .el-switch .label-fade-enter, 100 | .el-switch .label-fade-leave-active { 101 | opacity: 0; 102 | } 103 | -------------------------------------------------------------------------------- /src/assets/styles/element/tab-pane.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicejade/markdown-online-editor/58c38df82615de4b816bddbb53031df8c1347e02/src/assets/styles/element/tab-pane.css -------------------------------------------------------------------------------- /src/assets/styles/element/tag.css: -------------------------------------------------------------------------------- 1 | .el-tag { 2 | background-color: rgba(64, 158, 255, 0.1); 3 | display: inline-block; 4 | padding: 0 10px; 5 | height: 32px; 6 | line-height: 30px; 7 | font-size: 12px; 8 | color: #000000; 9 | border-radius: 4px; 10 | -webkit-box-sizing: border-box; 11 | box-sizing: border-box; 12 | border: 1px solid rgba(64, 158, 255, 0.2); 13 | white-space: nowrap; 14 | } 15 | .el-tag .el-icon-close { 16 | border-radius: 50%; 17 | text-align: center; 18 | position: relative; 19 | cursor: pointer; 20 | font-size: 12px; 21 | height: 16px; 22 | width: 16px; 23 | line-height: 16px; 24 | vertical-align: middle; 25 | top: -1px; 26 | right: -5px; 27 | color: #000000; 28 | } 29 | .el-tag .el-icon-close::before { 30 | display: block; 31 | } 32 | .el-tag .el-icon-close:hover { 33 | background-color: #000000; 34 | color: #fff; 35 | } 36 | .el-tag--info, 37 | .el-tag--info .el-tag__close { 38 | color: #909399; 39 | } 40 | .el-tag--info { 41 | background-color: rgba(144, 147, 153, 0.1); 42 | border-color: rgba(144, 147, 153, 0.2); 43 | } 44 | .el-tag--info.is-hit { 45 | border-color: #909399; 46 | } 47 | .el-tag--info .el-tag__close:hover { 48 | background-color: #909399; 49 | color: #fff; 50 | } 51 | .el-tag--success { 52 | background-color: rgba(103, 194, 58, 0.1); 53 | border-color: rgba(103, 194, 58, 0.2); 54 | color: #67c23a; 55 | } 56 | .el-tag--success.is-hit { 57 | border-color: #67c23a; 58 | } 59 | .el-tag--success .el-tag__close { 60 | color: #67c23a; 61 | } 62 | .el-tag--success .el-tag__close:hover { 63 | background-color: #67c23a; 64 | color: #fff; 65 | } 66 | .el-tag--warning { 67 | background-color: rgba(230, 162, 60, 0.1); 68 | border-color: rgba(230, 162, 60, 0.2); 69 | color: #e6a23c; 70 | } 71 | .el-tag--warning.is-hit { 72 | border-color: #e6a23c; 73 | } 74 | .el-tag--warning .el-tag__close { 75 | color: #e6a23c; 76 | } 77 | .el-tag--warning .el-tag__close:hover { 78 | background-color: #e6a23c; 79 | color: #fff; 80 | } 81 | .el-tag--danger { 82 | background-color: rgba(245, 108, 108, 0.1); 83 | border-color: rgba(245, 108, 108, 0.2); 84 | color: #f56c6c; 85 | } 86 | .el-tag--danger.is-hit { 87 | border-color: #f56c6c; 88 | } 89 | .el-tag--danger .el-tag__close { 90 | color: #f56c6c; 91 | } 92 | .el-tag--danger .el-tag__close:hover { 93 | background-color: #f56c6c; 94 | color: #fff; 95 | } 96 | .el-tag--medium { 97 | height: 28px; 98 | line-height: 26px; 99 | } 100 | .el-tag--medium .el-icon-close { 101 | -webkit-transform: scale(0.8); 102 | transform: scale(0.8); 103 | } 104 | .el-tag--small { 105 | height: 24px; 106 | padding: 0 8px; 107 | line-height: 22px; 108 | } 109 | .el-tag--small .el-icon-close { 110 | -webkit-transform: scale(0.8); 111 | transform: scale(0.8); 112 | } 113 | .el-tag--mini { 114 | height: 20px; 115 | padding: 0 5px; 116 | line-height: 19px; 117 | } 118 | .el-tag--mini .el-icon-close { 119 | margin-left: -3px; 120 | -webkit-transform: scale(0.7); 121 | transform: scale(0.7); 122 | } 123 | -------------------------------------------------------------------------------- /src/assets/styles/element/tooltip.css: -------------------------------------------------------------------------------- 1 | .el-tooltip:focus:hover, 2 | .el-tooltip:focus:not(.focusing) { 3 | outline-width: 0; 4 | } 5 | .el-tooltip__popper { 6 | position: absolute; 7 | border-radius: 4px; 8 | padding: 10px; 9 | z-index: 2000; 10 | font-size: 12px; 11 | line-height: 1.2; 12 | min-width: 10px; 13 | } 14 | .el-tooltip__popper .popper__arrow, 15 | .el-tooltip__popper .popper__arrow::after { 16 | position: absolute; 17 | display: block; 18 | width: 0; 19 | height: 0; 20 | border-color: transparent; 21 | border-style: solid; 22 | } 23 | .el-tooltip__popper .popper__arrow { 24 | border-width: 6px; 25 | } 26 | .el-tooltip__popper .popper__arrow::after { 27 | content: ' '; 28 | border-width: 5px; 29 | } 30 | .el-tooltip__popper[x-placement^='top'] { 31 | margin-bottom: 12px; 32 | } 33 | .el-tooltip__popper[x-placement^='top'] .popper__arrow { 34 | bottom: -6px; 35 | border-top-color: #303133; 36 | border-bottom-width: 0; 37 | } 38 | .el-tooltip__popper[x-placement^='top'] .popper__arrow::after { 39 | bottom: 1px; 40 | margin-left: -5px; 41 | border-top-color: #303133; 42 | border-bottom-width: 0; 43 | } 44 | .el-tooltip__popper[x-placement^='bottom'] { 45 | margin-top: 12px; 46 | } 47 | .el-tooltip__popper[x-placement^='bottom'] .popper__arrow { 48 | top: -6px; 49 | border-top-width: 0; 50 | border-bottom-color: #303133; 51 | } 52 | .el-tooltip__popper[x-placement^='bottom'] .popper__arrow::after { 53 | top: 1px; 54 | margin-left: -5px; 55 | border-top-width: 0; 56 | border-bottom-color: #303133; 57 | } 58 | .el-tooltip__popper[x-placement^='right'] { 59 | margin-left: 12px; 60 | } 61 | .el-tooltip__popper[x-placement^='right'] .popper__arrow { 62 | left: -6px; 63 | border-right-color: #303133; 64 | border-left-width: 0; 65 | } 66 | .el-tooltip__popper[x-placement^='right'] .popper__arrow::after { 67 | bottom: -5px; 68 | left: 1px; 69 | border-right-color: #303133; 70 | border-left-width: 0; 71 | } 72 | .el-tooltip__popper[x-placement^='left'] { 73 | margin-right: 12px; 74 | } 75 | .el-tooltip__popper[x-placement^='left'] .popper__arrow { 76 | right: -6px; 77 | border-right-width: 0; 78 | border-left-color: #303133; 79 | } 80 | .el-tooltip__popper[x-placement^='left'] .popper__arrow::after { 81 | right: 1px; 82 | bottom: -5px; 83 | margin-left: -5px; 84 | border-right-width: 0; 85 | border-left-color: #303133; 86 | } 87 | .el-tooltip__popper.is-dark { 88 | background: #303133; 89 | color: #fff; 90 | } 91 | .el-tooltip__popper.is-light { 92 | background: #fff; 93 | border: 1px solid #303133; 94 | } 95 | .el-tooltip__popper.is-light[x-placement^='top'] .popper__arrow { 96 | border-top-color: #303133; 97 | } 98 | .el-tooltip__popper.is-light[x-placement^='top'] .popper__arrow::after { 99 | border-top-color: #fff; 100 | } 101 | .el-tooltip__popper.is-light[x-placement^='bottom'] .popper__arrow { 102 | border-bottom-color: #303133; 103 | } 104 | .el-tooltip__popper.is-light[x-placement^='bottom'] .popper__arrow::after { 105 | border-bottom-color: #fff; 106 | } 107 | .el-tooltip__popper.is-light[x-placement^='left'] .popper__arrow { 108 | border-left-color: #303133; 109 | } 110 | .el-tooltip__popper.is-light[x-placement^='left'] .popper__arrow::after { 111 | border-left-color: #fff; 112 | } 113 | .el-tooltip__popper.is-light[x-placement^='right'] .popper__arrow { 114 | border-right-color: #303133; 115 | } 116 | .el-tooltip__popper.is-light[x-placement^='right'] .popper__arrow::after { 117 | border-right-color: #fff; 118 | } 119 | -------------------------------------------------------------------------------- /src/assets/styles/element/tree.css: -------------------------------------------------------------------------------- 1 | .el-tree { 2 | position: relative; 3 | cursor: default; 4 | background: #fff; 5 | color: #606266; 6 | } 7 | .el-tree__empty-block { 8 | position: relative; 9 | min-height: 60px; 10 | text-align: center; 11 | width: 100%; 12 | height: 100%; 13 | } 14 | .el-tree__empty-text { 15 | position: absolute; 16 | left: 50%; 17 | top: 50%; 18 | -webkit-transform: translate(-50%, -50%); 19 | transform: translate(-50%, -50%); 20 | color: #6f7180; 21 | } 22 | .el-tree__drop-indicator { 23 | position: absolute; 24 | left: 0; 25 | right: 0; 26 | height: 1px; 27 | background-color: #000000; 28 | } 29 | .el-tree-node { 30 | white-space: nowrap; 31 | outline: 0; 32 | } 33 | .el-tree-node:focus > .el-tree-node__content { 34 | background-color: #f5f7fa; 35 | } 36 | .el-tree-node.is-drop-inner > .el-tree-node__content .el-tree-node__label { 37 | background-color: #000000; 38 | color: #fff; 39 | } 40 | .el-tree-node__content { 41 | display: -webkit-box; 42 | display: -ms-flexbox; 43 | display: flex; 44 | -webkit-box-align: center; 45 | -ms-flex-align: center; 46 | align-items: center; 47 | height: 26px; 48 | cursor: pointer; 49 | } 50 | .el-tree-node__content > .el-tree-node__expand-icon { 51 | padding: 6px; 52 | } 53 | .el-tree-node__content > .el-checkbox { 54 | margin-right: 8px; 55 | } 56 | .el-tree-node__content:hover { 57 | background-color: #f5f7fa; 58 | } 59 | .el-tree.is-dragging .el-tree-node__content { 60 | cursor: move; 61 | } 62 | .el-tree.is-dragging .el-tree-node__content * { 63 | pointer-events: none; 64 | } 65 | .el-tree.is-dragging.is-drop-not-allow .el-tree-node__content { 66 | cursor: not-allowed; 67 | } 68 | .el-tree-node__expand-icon { 69 | cursor: pointer; 70 | color: #c0c4cc; 71 | font-size: 12px; 72 | -webkit-transform: rotate(0); 73 | transform: rotate(0); 74 | -webkit-transition: -webkit-transform 0.3s ease-in-out; 75 | transition: -webkit-transform 0.3s ease-in-out; 76 | transition: transform 0.3s ease-in-out; 77 | transition: transform 0.3s ease-in-out, -webkit-transform 0.3s ease-in-out; 78 | } 79 | .el-tree-node__expand-icon.expanded { 80 | -webkit-transform: rotate(90deg); 81 | transform: rotate(90deg); 82 | } 83 | .el-tree-node__expand-icon.is-leaf { 84 | color: transparent; 85 | cursor: default; 86 | } 87 | .el-tree-node__label { 88 | font-size: 14px; 89 | } 90 | .el-tree-node__loading-icon { 91 | margin-right: 8px; 92 | font-size: 14px; 93 | color: #c0c4cc; 94 | } 95 | .el-tree-node > .el-tree-node__children { 96 | overflow: hidden; 97 | background-color: transparent; 98 | } 99 | .el-tree-node.is-expanded > .el-tree-node__children { 100 | display: block; 101 | } 102 | .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content { 103 | background-color: #f0f7ff; 104 | } 105 | -------------------------------------------------------------------------------- /src/assets/styles/mixins.less: -------------------------------------------------------------------------------- 1 | /* Clearfix 2 | For modern browsers 3 | 1. The space content is one way to avoid an Opera bug when the 4 | contenteditable attribute is included anywhere else in the document. 5 | Otherwise it causes space to appear at the top and bottom of elements 6 | that are clearfixed. 7 | 2. The use of `table` rather than `block` is only necessary if using 8 | `:before` to contain the top-margins of child elements. 9 | Source: http://nicolasgallagher.com/micro-clearfix-hack */ 10 | .clearfix() { 11 | &:before, 12 | &:after { 13 | content: ' '; 14 | display: table; 15 | } 16 | &:after { 17 | clear: both; 18 | } 19 | } 20 | 21 | // (Responsive image)Keep images from scaling beyond the width of their parents. 22 | .img-responsive(@display: block) { 23 | display: @display; 24 | max-width: 100%; 25 | height: auto; 26 | } 27 | 28 | // Resize anything 29 | .resizable(@direction) { 30 | resize: @direction; 31 | overflow: auto; 32 | } 33 | 34 | // Text overflow 35 | // Requires inline-block or block for proper styling 36 | .text-overflow() { 37 | overflow: hidden; 38 | text-overflow: ellipsis; 39 | white-space: nowrap; 40 | } 41 | 42 | // Center-align a block level element 43 | .center-block() { 44 | display: block; 45 | margin-left: auto; 46 | margin-right: auto; 47 | } 48 | 49 | .absolute-center() { 50 | position: absolute; 51 | top: 50%; 52 | left: 50%; 53 | transform: translate(-50%, -50%); 54 | } 55 | 56 | .fixed-center() { 57 | position: fixed; 58 | top: 50%; 59 | left: 50%; 60 | transform: translate(-50%, -50%); 61 | } 62 | 63 | .flex-box-center(@direction: row, @justify: center, @align-items: center) { 64 | flex-direction: @direction; 65 | display: -webkit-box; 66 | display: -moz-box; 67 | display: -webkit-flex; 68 | display: -ms-flex; 69 | display: flex; 70 | -webkit-box-align: center; 71 | -moz-box-align: center; 72 | -ms-box-align: center; 73 | -webkit-align-items: @align-items; 74 | -ms-align-items: @align-items; 75 | align-items: @align-items; 76 | -webkit-box-pack: center; 77 | -webkit-justify-content: center; 78 | justify-content: @justify; 79 | } 80 | -------------------------------------------------------------------------------- /src/assets/styles/style.less: -------------------------------------------------------------------------------- 1 | @import 'variables.less'; 2 | @import 'element.less'; 3 | @import 'mixins.less'; 4 | @import 'common.less'; 5 | -------------------------------------------------------------------------------- /src/assets/styles/variables.less: -------------------------------------------------------------------------------- 1 | @size-factor: 0.5rem; 2 | 3 | @brand: #03a9f4; 4 | 5 | @bg-grey: #fafcff; 6 | @input-grey: #f2f4f7; 7 | @text-grey: #6a8bad; 8 | @icon-grey: #707780; 9 | 10 | @border-grey: #d7dce5; 11 | @border-black: #7a8ba9; 12 | 13 | @deep-black: #1a2a3a; 14 | @black: #2a3a4a; 15 | @liht-black: #3a4a5a; 16 | 17 | @white: #ffffff; 18 | @red: #fa0101; 19 | @grey: #e8ebf1; 20 | @orange: #6b5900; 21 | @yellow: #ffe564; 22 | 23 | @font-large: 2.1rem; 24 | @font-medium: 1.3rem; 25 | @font-small: 1rem; 26 | 27 | @header-height: 60px; 28 | @max-body-width: 1440px; 29 | 30 | @el-message-zindex: 10000; 31 | @hint-css-zindex: 9999; 32 | -------------------------------------------------------------------------------- /src/components/Advertisement.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 57 | 58 | 85 | -------------------------------------------------------------------------------- /src/components/Icon.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 35 | 36 | 43 | -------------------------------------------------------------------------------- /src/components/PreviewVditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 61 | 62 | 131 | -------------------------------------------------------------------------------- /src/components/icons/Arrow.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 38 | 39 | 64 | -------------------------------------------------------------------------------- /src/config/aboutArya.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export default `# [Arya](https://markdown.lovejade.cn/?utm_source=markdown.lovejade.cn&pid=about-arya) - 在线 Markdown 编辑器 4 | 5 | [Arya](https://markdown.lovejade.cn/?utm_source=markdown.lovejade.cn&pid=about-arya),是一款基于 [Vue2](https://site.lovejade.cn/post/5b1a221c0526c920d6dfaada)、[Vditor](https://github.com/Vanessa219/vditor),为未来而构建的在线 [Markdown](https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/markdown-tutorial.md) 编辑器;轻量且强大:内置粘贴 HTML 自动转换为 Markdown,支持流程图、甘特图、时序图、任务列表,可导出携带样式的图片、PDF、微信公众号特制的 HTML 等等。 6 | 7 | >**微注**:[Arya](https://markdown.lovejade.cn/?utm_source=markdown.lovejade.cn&pid=about-arya) 另一大优点在于:编辑内容只会在您本地进行保存,不会上传您的数据至服务器,**绝不窥测用户个人隐私,可放心使用**;Github 源码:[markdown-online-editor](https://github.com/nicejade/markdown-online-editor),部分功能仍在开发🚧,敬请期待。 8 | 9 | **背景初衷**:早期就有关注到由"黑客派"所出品的 [Vditor](https://github.com/Vanessa219/vditor):一款为未来而构建的下一代 [Markdown](https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/markdown-tutorial.md) 编辑器。然,现而今市面上所存在的 \`Markdown\` 编辑器,或多或少都存在一些问题(或功能不全,或高级功能收费...),因此基于自身所需,加之 [Vditor](https://github.com/Vanessa219/vditor) 的强大,就诞生了做一款[在线 Markdown 编辑器](https://markdown.lovejade.cn/?utm_source=github.com) 的念头;取其名曰 [\`Arya\`(二丫)](https://quickapp.lovejade.cn/talking-game-of-thrones/?utm_source=markdown.lovejade.cn)。 10 | 11 |
12 | 13 | node version 14 | 15 | 16 | LICENSE 17 | 18 | 19 | Prettier 20 | 21 | 22 | Prettier 23 | 24 | 25 | Chat On My Blog 26 | 27 | 28 | Author nicejade 29 | 30 |
31 | 32 | ------ 33 | 34 | ## 什么是 Markdown 35 | 36 | \`Markdown\` 是一种方便记忆、书写的纯文本标记语言,用户可以使用这些标记符号,以最小的输入代价,生成极富表现力的文档:譬如您正在阅读的这份文档。它使用简单的符号标记不同的标题,分割不同的段落,**粗体**、*斜体* 或者[超文本链接](https://vue-cli3.lovejade.cn/explore/),更棒的是,它还可以: 37 | 38 | ### 1. 制作待办事宜 \`Todo\` 列表 39 | 40 | - [x] 🎉 通常 \`Markdown\` 解析器自带的基本功能; 41 | - [x] 🍀 支持**流程图**、**甘特图**、**时序图**、**任务列表**; 42 | - [x] 🏁 支持粘贴 HTML 自动转换为 Markdown; 43 | - [x] 💃🏻 支持插入原生 Emoji、设置常用表情列表; 44 | - [x] 🚑 支持编辑内容保存**本地存储**,防止意外丢失; 45 | - [x] 📝 支持**实时预览**,主窗口大小拖拽,字符计数; 46 | - [x] 🛠 支持常用快捷键(**Tab**),及代码块添加复制 47 | - [x] ✨ 支持**导出**携带样式的 PDF、PNG、JPEG 等; 48 | - [x] ✨ 升级 Vditor,新增对 \`echarts\` 图表的支持; 49 | - [x] ✨ 注入 [RevealJs](https://revealjs.com/#/),增设对 \`PPT\` 预览支持; 50 | - [x] 👏 支持检查并格式化 Markdown 语法,使其专业; 51 | - [x] 🦑 支持五线谱、及[部分站点、视频、音频解析](https://github.com/b3log/vditor/issues/117?utm_source=hacpai.com#issuecomment-526986052); 52 | - [x] 🌟 增加对**所见即所得**编辑模式的支持(\`⌘-⇧-M\`); 53 | 54 | ### 2. 书写一个质能守恒公式[^LaTeX] 55 | 56 | $$ 57 | E=mc^2 58 | $$ 59 | 60 | ### 3. 高亮一段代码[^code] 61 | 62 | \`\`\`js 63 | // 给页面里所有的 DOM 元素添加一个 1px 的描边(outline); 64 | [].forEach.call($$("*"),function(a){ 65 | a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16); 66 | }) 67 | \`\`\` 68 | 69 | ### 4. 高效绘制[流程图](https://github.com/knsv/mermaid#flowchart) 70 | 71 | \`\`\`mermaid 72 | graph TD 73 | A[用户请求] --> B[语义解析] 74 | B --> C[RAG检索] 75 | 76 | C -->|✅ 知识库匹配| D[上下文增强] 77 | C -->|❌ 无匹配| E[任务分解] 78 | 79 | D --> E 80 | 81 | E --> F{工具选择} 82 | 83 | F -->|🛠️ 核心工具| G{基础操作} 84 | F -->|🔌 MCP扩展服务| H{MCP操作} 85 | 86 | G -->|✏️ 文件操作| I[读写/替换] 87 | G -->|🖥️ 系统命令执行| J[执行命令] 88 | G -->|🔍 代码分析| K[代码分析] 89 | 90 | H -->|⚙️ 使用MCP工具| L[使用MCP工具] 91 | H -->|📦 访问MCP资源| M[访问MCP资源] 92 | 93 | I --> N[结果验证] 94 | J --> N 95 | K --> N 96 | L --> N 97 | M --> N 98 | 99 | N --> O{完成判断} 100 | 101 | O -->|✅| P[提交最终结果] 102 | O -->|❌| E 103 | \`\`\` 104 | 105 | ### 5. 高效绘制[序列图](https://github.com/knsv/mermaid#sequence-diagram) 106 | 107 | \`\`\` 108 | sequenceDiagram 109 | participant Alice 110 | participant Bob 111 | Alice->John: Hello John, how are you? 112 | loop Healthcheck 113 | John->John: Fight against hypochondria 114 | end 115 | Note right of John: Rational thoughts
prevail... 116 | John-->Alice: Great! 117 | John->Bob: How about you? 118 | Bob-->John: Jolly good! 119 | \`\`\` 120 | 121 | \`\`\`mermaid 122 | sequenceDiagram 123 | participant Alice 124 | participant Bob 125 | Alice->John: Hello John, how are you? 126 | loop Healthcheck 127 | John->John: Fight against hypochondria 128 | end 129 | Note right of John: Rational thoughts
prevail... 130 | John-->Alice: Great! 131 | John->Bob: How about you? 132 | Bob-->John: Jolly good! 133 | \`\`\` 134 | 135 | ### 6. 高效绘制[甘特图](https://github.com/knsv/mermaid#gantt-diagram) 136 | 137 | >**甘特图**内在思想简单。基本是一条线条图,横轴表示时间,纵轴表示活动(项目),线条表示在整个期间上计划和实际的活动完成情况。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。 138 | 139 | \`\`\` 140 | gantt 141 | title 项目开发流程 142 | section 项目确定 143 | 需求分析 :a1, 2019-06-22, 3d 144 | 可行性报告 :after a1, 5d 145 | 概念验证 : 5d 146 | section 项目实施 147 | 概要设计 :2019-07-05 , 5d 148 | 详细设计 :2019-07-08, 10d 149 | 编码 :2019-07-15, 10d 150 | 测试 :2019-07-22, 5d 151 | section 发布验收 152 | 发布: 2d 153 | 验收: 3d 154 | \`\`\` 155 | 156 | \`\`\`mermaid 157 | gantt 158 | title 项目开发流程 159 | section 项目确定 160 | 需求分析 :a1, 2019-06-22, 3d 161 | 可行性报告 :after a1, 5d 162 | 概念验证 : 5d 163 | section 项目实施 164 | 概要设计 :2019-07-05 , 5d 165 | 详细设计 :2019-07-08, 10d 166 | 编码 :2019-07-15, 10d 167 | 测试 :2019-07-22, 5d 168 | section 发布验收 169 | 发布: 2d 170 | 验收: 3d 171 | \`\`\` 172 | 173 | ### 7. 绘制表格 174 | 175 | | 作品名称 | 在线地址 | 上线日期 | 176 | | :-------- | :----- | :----: | 177 | | 逍遥自在轩 | [https://niceshare.site](https://niceshare.site/?ref=markdown.lovejade.cn) |2024-04-26| 178 | | 玉桃文飨轩 | [https://share.lovejade.cn](https://share.lovejade.cn/?ref=markdown.lovejade.cn) |2022-08-26| 179 | | 缘知随心庭 | [https://fine.niceshare.site](https://fine.niceshare.site/?ref=markdown.lovejade.cn) |2022-02-26| 180 | | 静轩之别苑 | [http://quickapp.lovejade.cn](http://quickapp.lovejade.cn/?ref=markdown.lovejade.cn) |2019-01-12| 181 | | 晚晴幽草轩 | [https://www.jeffjade.com](https://www.jeffjade.com/?ref=markdown.lovejade.cn) |2014-09-20| 182 | 183 | ### 8. 支持部分站点、视频、音频解析 184 | 185 | [飞雪连天射白鹿,笑书神侠倚碧鸳](https://img.hacpai.com/file/2018/11/-97d5cec5.mp4) 186 | 187 | ### 9. 更详细语法说明 188 | 189 | 想要查看更详细的语法说明,可以参考这份 [Markdown 资源列表](https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/markdown-tutorial.md),涵盖入门至进阶教程,以及资源、平台等信息,能让您对她有更深的认知。 190 | 191 | 总而言之,不同于其它*所见即所得*的编辑器:你只需使用键盘专注于书写文本内容,就可以生成印刷级的排版格式,省却在键盘和工具栏之间来回切换,调整内容和格式的麻烦。**Markdown 在流畅的书写和印刷级的阅读体验之间找到了平衡。** 目前它已经成为世界上最大的技术分享网站 \`GitHub\` 和 技术问答网站 \`StackOverFlow\` 的御用书写格式,而且越发流行,正在在向各行业渗透。 192 | 193 | --- 194 | 195 | ## 相关链接 196 | 197 | - [清风明月轩](https://www.thebettersites.com/?ref=github.com) 198 | - [逍遥自在轩](https://www.niceshare.site/?ref=github.com) 199 | - [晚晴幽草轩](https://www.jeffjade.com/nicelinks?ref=github.com) 200 | - [缘知随心庭](https://fine.niceshare.site/?ref=github.com) 201 | - [静轩之别苑](https://quickapp.lovejade.cn/?ref=github.com) 202 | - [悠然宜想亭](https://forum.lovejade.cn/?ref=github.com) 203 | - [天意人间舫](https://blog.lovejade.cn/?utm_source=github.com) 204 | - [SegmentFault](https://segmentfault.com/u/jeffjade) 205 | - [X(Twitter)](https://x.com/MarshalXuan) 206 | ` 207 | -------------------------------------------------------------------------------- /src/config/constant.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export const appTitle = 'Arya - 在线 Markdown 编辑器' 4 | 5 | export const exportTextMap = { 6 | '/export/png': '导出 PNG', 7 | '/export/pdf': '导出 PDF', 8 | '/export/ppt': 'PPT 预览', 9 | } 10 | -------------------------------------------------------------------------------- /src/config/default.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const echartsConf = { 4 | backgroundColor: '#212121', 5 | title: { 6 | text: '「晚晴幽草轩」访问来源', 7 | subtext: '2019 年 6 月份', 8 | x: 'center', 9 | textStyle: { 10 | color: '#f2f2f2', 11 | }, 12 | }, 13 | tooltip: { 14 | trigger: 'item', 15 | formatter: '{a}
{b} : {c} ({d}%)', 16 | }, 17 | legend: { 18 | orient: 'vertical', 19 | left: 'left', 20 | data: ['搜索引擎', '直接访问', '推荐', '其他', '社交平台'], 21 | textStyle: { 22 | color: '#f2f2f2', 23 | }, 24 | }, 25 | series: [ 26 | { 27 | name: '访问来源', 28 | type: 'pie', 29 | radius: '55%', 30 | center: ['50%', '60%'], 31 | data: [ 32 | { value: 10440, name: '搜索引擎', itemStyle: { color: '#ef4136' } }, 33 | { value: 4770, name: '直接访问' }, 34 | { value: 2430, name: '推荐' }, 35 | { value: 342, name: '其他' }, 36 | { value: 18, name: '社交平台' }, 37 | ], 38 | itemStyle: { 39 | emphasis: { 40 | shadowBlur: 10, 41 | shadowOffsetX: 0, 42 | shadowColor: 'rgba(0, 0, 0, 0.5)', 43 | }, 44 | }, 45 | }, 46 | ], 47 | } 48 | 49 | const echartsDemoJsonStr = JSON.stringify(echartsConf, null, 2) 50 | 51 | export default `# 欢迎使用 \`Arya\` 在线 Markdown 编辑器 52 | 53 | [Arya](https://markdown.lovejade.cn/?ref=markdown.lovejade.cn),是一款基于 \`Vue\`、\`Vditor\`,为未来而构建的在线 Markdown 编辑器;轻量且强大:内置粘贴 HTML 自动转换为 Markdown,支持流程图、甘特图、时序图、任务列表,可导出携带样式的图片、PDF、微信公众号特制的 HTML 等等。 54 | 55 | --- 56 | 57 | ## 如何使用 58 | 59 | **微注**:清空目前这份默认文档,即处于可使用态。[Arya](https://markdown.lovejade.cn/?ref=markdown.lovejade.cn) 另一大优点在于:编辑内容只会在您本地进行保存,不会上传您的数据至服务器,**绝不窥测用户个人隐私,可放心使用**;Github 源码:[markdown-online-editor](https://github.com/nicejade/markdown-online-editor),部分功能仍在开发🚧,敬请期待。 60 | 61 | 默认为[所见即所得](https://hacpai.com/article/1577370404903?ref=github.com)模式,可通过 \`⌘-⇧-M\`(\`Ctrl-⇧-M\`)进行切换;或通过以下方式: 62 | - 所见即所得:\`⌘-⌥-7\`(\`Ctrl-alt-7\`); 63 | - 即时渲染:\`⌘-⌥-8\`(\`Ctrl-alt-8\`); 64 | - 分屏渲染:\`⌘-⌥-9\`(\`Ctrl-alt-9\`); 65 | 66 | ### PPT 预览 67 | 68 | 如果您用作 \`PPT\` 预览(入口在\`设置\`中),需要注意,这里暂还不能支持各种图表的渲染;您可以使用 \`---\` 来定义水平方向上幻灯片,用 \`--\` 来定义垂直幻灯片;更多设定可以参见 [RevealJs 文档](https://github.com/hakimel/reveal.js#table-of-contents)。 69 | 70 | 71 | --- 72 | 73 | ## 什么是 Markdown 74 | 75 | \`Markdown\` 是一种方便记忆、书写的纯文本标记语言,用户可以使用这些标记符号,以最小的输入代价,生成极富表现力的文档:譬如您正在阅读的这份文档。它使用简单的符号标记不同的标题,分割不同的段落,**粗体**、*斜体* 或者[超文本链接](https://vue-cli3.lovejade.cn/explore/),更棒的是,它还可以: 76 | 77 | --- 78 | 79 | ### 1. 制作待办事宜 \`Todo\` 列表 80 | 81 | - [x] 🎉 通常 \`Markdown\` 解析器自带的基本功能; 82 | - [x] 🍀 支持**流程图**、**甘特图**、**时序图**、**任务列表**; 83 | - [x] 🏁 支持粘贴 HTML 自动转换为 Markdown; 84 | - [x] 💃🏻 支持插入原生 Emoji、设置常用表情列表; 85 | - [x] 🚑 支持编辑内容保存**本地存储**,防止意外丢失; 86 | - [x] 📝 支持**实时预览**,主窗口大小拖拽,字符计数; 87 | - [x] 🛠 支持常用快捷键(**Tab**),及代码块添加复制 88 | - [x] ✨ 支持**导出**携带样式的 PDF、PNG、JPEG 等; 89 | - [x] ✨ 升级 Vditor,新增对 \`echarts\` 图表的支持; 90 | - [x] 👏 支持检查并格式化 Markdown 语法,使其专业; 91 | - [x] 🦑 支持五线谱、及[部分站点、视频、音频解析](https://github.com/b3log/vditor/issues/117?ref=hacpai.com#issuecomment-526986052); 92 | - [x] 🌟 增加对**所见即所得**编辑模式的支持(\`⌘-⇧-M\`); 93 | 94 | --- 95 | 96 | ### 2. 书写一个质能守恒公式[^LaTeX] 97 | 98 | $$ 99 | E=mc^2 100 | $$ 101 | 102 | --- 103 | 104 | ### 3. 高亮一段代码[^code] 105 | 106 | \`\`\`js 107 | // 给页面里所有的 DOM 元素添加一个 1px 的描边(outline); 108 | [].forEach.call($$("*"),function(a){ 109 | a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16); 110 | }) 111 | \`\`\` 112 | 113 | --- 114 | 115 | ### 4. 高效绘制[流程图](https://github.com/knsv/mermaid#flowchart) 116 | 117 | \`\`\`mermaid 118 | graph TD 119 | A[用户请求] --> B[语义解析] 120 | B --> C[RAG检索] 121 | 122 | C -->|✅ 知识库匹配| D[上下文增强] 123 | C -->|❌ 无匹配| E[任务分解] 124 | 125 | D --> E 126 | 127 | E --> F{工具选择} 128 | 129 | F -->|🛠️ 核心工具| G{基础操作} 130 | F -->|🔌 MCP扩展服务| H{MCP操作} 131 | 132 | G -->|✏️ 文件操作| I[读写/替换] 133 | G -->|🖥️ 系统命令执行| J[执行命令] 134 | G -->|🔍 代码分析| K[代码分析] 135 | 136 | H -->|⚙️ 使用MCP工具| L[使用MCP工具] 137 | H -->|📦 访问MCP资源| M[访问MCP资源] 138 | 139 | I --> N[结果验证] 140 | J --> N 141 | K --> N 142 | L --> N 143 | M --> N 144 | 145 | N --> O{完成判断} 146 | 147 | O -->|✅| P[提交最终结果] 148 | O -->|❌| E 149 | \`\`\` 150 | 151 | --- 152 | 153 | ### 5. 高效绘制[序列图](https://github.com/knsv/mermaid#sequence-diagram) 154 | 155 | \`\`\`mermaid 156 | sequenceDiagram 157 | participant Alice 158 | participant Bob 159 | Alice->John: Hello John, how are you? 160 | loop Healthcheck 161 | John->John: Fight against hypochondria 162 | end 163 | Note right of John: Rational thoughts
prevail... 164 | John-->Alice: Great! 165 | John->Bob: How about you? 166 | Bob-->John: Jolly good! 167 | \`\`\` 168 | 169 | --- 170 | 171 | ### 6. 高效绘制[甘特图](https://github.com/knsv/mermaid#gantt-diagram) 172 | 173 | >**甘特图**内在思想简单。基本是一条线条图,横轴表示时间,纵轴表示活动(项目),线条表示在整个期间上计划和实际的活动完成情况。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。 174 | 175 | \`\`\`mermaid 176 | gantt 177 | title 项目开发流程 178 | section 项目确定 179 | 需求分析 :a1, 2019-06-22, 3d 180 | 可行性报告 :after a1, 5d 181 | 概念验证 : 5d 182 | section 项目实施 183 | 概要设计 :2019-07-05 , 5d 184 | 详细设计 :2019-07-08, 10d 185 | 编码 :2019-07-15, 10d 186 | 测试 :2019-07-22, 5d 187 | section 发布验收 188 | 发布: 2d 189 | 验收: 3d 190 | \`\`\` 191 | 192 | ### 7. 支持图表 193 | 194 | \`\`\`echarts 195 | ${echartsDemoJsonStr} 196 | \`\`\` 197 | 198 | >**备注**:上述 echarts 图表📈,其数据,须使用严格的 **JSON** 格式;您可使用 JSON.stringify(data),将对象传换从而得标准数据,即可正常使用。 199 | 200 | --- 201 | 202 | ### 8. 绘制表格 203 | 204 | | 作品名称 | 在线地址 | 上线日期 | 205 | | :-------- | :----- | :----: | 206 | | 逍遥自在轩 | [https://niceshare.site](https://niceshare.site/?ref=markdown.lovejade.cn) |2024-04-26| 207 | | 玉桃文飨轩 | [https://share.lovejade.cn](https://share.lovejade.cn/?ref=markdown.lovejade.cn) |2022-08-26| 208 | | 缘知随心庭 | [https://fine.niceshare.site](https://fine.niceshare.site/?ref=markdown.lovejade.cn) |2022-02-26| 209 | | 静轩之别苑 | [http://quickapp.lovejade.cn](http://quickapp.lovejade.cn/?ref=markdown.lovejade.cn) |2019-01-12| 210 | | 晚晴幽草轩 | [https://www.jeffjade.com](https://www.jeffjade.com/?ref=markdown.lovejade.cn) |2014-09-20| 211 | 212 | --- 213 | 214 | ### 9. 更详细语法说明 215 | 216 | 想要查看更详细的语法说明,可以参考这份 [Markdown 资源列表](https://github.com/nicejade/nice-front-end-tutorial/blob/master/tutorial/markdown-tutorial.md),涵盖入门至进阶教程,以及资源、平台等信息,能让您对她有更深的认知。 217 | 218 | 总而言之,不同于其它**所见即所得**的编辑器:你只需使用键盘专注于书写文本内容,就可以生成印刷级的排版格式,省却在键盘和工具栏之间来回切换,调整内容和格式的麻烦。**Markdown 在流畅的书写和印刷级的阅读体验之间找到了平衡。** 目前它已经成为世界上最大的技术分享网站 \`GitHub\` 和 技术问答网站 \`StackOverFlow\` 的御用书写格式,而且越发流行,正在在向各行业渗透。 219 | 220 | 最新更新于 2025.04.16 221 | ` 222 | -------------------------------------------------------------------------------- /src/global.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from 'vue' 4 | 5 | /* ------------------------Vue Global Config------------------------------ */ 6 | Vue.config.productionTip = false 7 | 8 | /* ------------------------- Global Variable ------------------------------- */ 9 | import { appTitle } from './config/constant' 10 | window.$appTitle = appTitle 11 | 12 | /* ------------------------Vue Global Variable------------------------------ */ 13 | import { $utils, $document, $lodash } from '@helper' 14 | import { Message } from 'element-ui' 15 | Vue.prototype.$_ = $lodash 16 | Vue.prototype.$utils = $utils 17 | Vue.prototype.$document = $document 18 | Vue.prototype.$message = options => Message(options) 19 | 20 | /* ------------------------Vue Global Components------------------------------ */ 21 | import { Button, Dropdown, DropdownMenu, DropdownItem, Loading } from 'element-ui' 22 | Vue.use(Button) 23 | Vue.use(Dropdown) 24 | Vue.use(DropdownMenu) 25 | Vue.use(DropdownItem) 26 | Vue.use(Loading) 27 | 28 | import VueMeta from 'vue-meta' 29 | Vue.use(VueMeta) 30 | 31 | import Icon from '@components/Icon' 32 | Vue.component('icon', Icon) 33 | -------------------------------------------------------------------------------- /src/helper/document.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export default { 4 | toggleClass(el, className) { 5 | if (el.classList) { 6 | el.classList.toggle(className) 7 | } else { 8 | var classes = el.className.split(' ') 9 | var existingIndex = classes.indexOf(className) 10 | 11 | if (existingIndex >= 0) { 12 | classes.splice(existingIndex, 1) 13 | } else { 14 | classes.push(className) 15 | } 16 | el.className = classes.join(' ') 17 | } 18 | }, 19 | 20 | addClass(el, className) { 21 | if (el.classList) { 22 | el.classList.add(className) 23 | } else { 24 | el.className += ` ${className}` 25 | } 26 | }, 27 | 28 | removeClass(el, className) { 29 | if (el.classList) { 30 | el.classList.remove(className) 31 | } else { 32 | el.className = el.className.replace( 33 | new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), 34 | ' ' 35 | ) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/helper/export.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | import html2canvas from 'html2canvas' 3 | import canvg from 'canvg' 4 | 5 | let TARGET_WIDTH = 960 6 | let TARGET_HEIGHT = 800 7 | 8 | const MARGIN_WIDTH = 50 9 | const MARGIN_HEIGHT = 80 10 | const RADIUS = 6 11 | 12 | const SHADOW_X = 0 13 | const SHADOW_Y = 40 14 | const SHADOW_BLUR = 50 15 | const SHADOW_COLOR = 'rgba(0, 0, 0, 0.21)' 16 | 17 | function drawRoundedRec(origCanvas, scale) { 18 | const roundCanvas = document.createElement('canvas') 19 | roundCanvas.width = TARGET_WIDTH * scale 20 | roundCanvas.height = TARGET_HEIGHT * scale 21 | 22 | const roundCtx = roundCanvas.getContext('2d') 23 | const roundRadius = RADIUS * scale 24 | const x1 = roundRadius 25 | const y1 = 0 26 | const x2 = x1 + roundCanvas.width - 2 * roundRadius 27 | const y2 = y1 28 | const x3 = x2 + roundRadius 29 | const y3 = roundRadius 30 | const x4 = x3 31 | const y4 = y3 + roundCanvas.height - 2 * roundRadius 32 | const x5 = x2 33 | const y5 = y4 + roundRadius 34 | const x6 = x1 35 | const y6 = y5 36 | const x7 = x6 - roundRadius 37 | const y7 = y4 38 | const x8 = x7 39 | const y8 = y3 40 | roundCtx.beginPath() 41 | roundCtx.moveTo(x1, y1) 42 | roundCtx.lineTo(x2, y2) 43 | roundCtx.quadraticCurveTo(x3, y2, x3, y3) 44 | roundCtx.lineTo(x4, y4) 45 | roundCtx.quadraticCurveTo(x4, y5, x5, y5) 46 | roundCtx.lineTo(x6, y6) 47 | roundCtx.quadraticCurveTo(x7, y6, x7, y7) 48 | roundCtx.lineTo(x8, y8) 49 | roundCtx.quadraticCurveTo(x8, y1, x1, y1) 50 | roundCtx.clip() 51 | roundCtx.drawImage(origCanvas, 0, 0) 52 | return roundCanvas 53 | } 54 | 55 | function drawShadow(origCanvas) { 56 | const bgdCanvas = document.createElement('canvas') 57 | bgdCanvas.width = origCanvas.width + MARGIN_WIDTH 58 | bgdCanvas.height = origCanvas.height + MARGIN_HEIGHT 59 | const ctx = bgdCanvas.getContext('2d') 60 | 61 | ctx.shadowOffsetX = SHADOW_X 62 | ctx.shadowOffsetY = SHADOW_Y 63 | ctx.shadowBlur = SHADOW_BLUR 64 | ctx.shadowColor = SHADOW_COLOR 65 | ctx.drawImage(origCanvas, MARGIN_WIDTH / 2, 0) 66 | return bgdCanvas 67 | } 68 | 69 | /** 70 | * @desc 兼容使用 html2canvas 库不能完整捕获 SVG 问题; 71 | * @param {Dom} targetElem - 所要导出目标 DOM 72 | */ 73 | const handleCaptureSvg = (targetElem) => { 74 | const nodesToRecover = [] 75 | const nodesToRemove = [] 76 | const svgElem = targetElem.querySelectorAll('svg') 77 | 78 | for (let key = 0, len = svgElem.length; key < len; key++) { 79 | const node = svgElem[key] 80 | const parentNode = node.parentNode 81 | 82 | try { 83 | // 获取SVG的XML字符串 84 | const svgXml = new XMLSerializer().serializeToString(node) 85 | // 创建一个新的canvas元素 86 | const canvas = document.createElement('canvas') 87 | // 设置canvas尺寸与SVG相同 88 | const svgRect = node.getBoundingClientRect() 89 | canvas.width = svgRect.width 90 | canvas.height = svgRect.height 91 | 92 | // 使用canvg渲染SVG到canvas 93 | const ctx = canvas.getContext('2d') 94 | canvg(canvas, svgXml, { 95 | ignoreMouse: true, 96 | ignoreAnimation: true, 97 | ignoreDimensions: false, 98 | ignoreClear: true, 99 | }) 100 | 101 | // 保存原始节点信息以便恢复 102 | nodesToRecover.push({ 103 | parent: parentNode, 104 | child: node, 105 | }) 106 | 107 | // 临时移除SVG节点 108 | parentNode.removeChild(node) 109 | 110 | // 添加canvas替代SVG 111 | nodesToRemove.push({ 112 | parent: parentNode, 113 | child: canvas, 114 | }) 115 | parentNode.appendChild(canvas) 116 | } catch (error) { 117 | console.error('处理SVG时出错:', error) 118 | } 119 | } 120 | 121 | // 返回节点信息以便后续恢复 122 | return { nodesToRecover, nodesToRemove } 123 | } 124 | 125 | export const generateScreenshot = async (targetDom) => { 126 | handleCaptureSvg(targetDom) 127 | const domStyleObj = getComputedStyle(targetDom) 128 | TARGET_WIDTH = +domStyleObj.width.replace(`px`, '') 129 | TARGET_HEIGHT = +domStyleObj.height.replace(`px`, '') 130 | 131 | const scale = window.devicePixelRatio 132 | const options = { 133 | scale, 134 | allowTaint: true, 135 | useCORS: true, // 启用CORS支持 136 | backgroundColor: '#fefefe', 137 | imageTimeout: 0, // 禁用图像超时 138 | logging: false, 139 | } 140 | const origCanvas = await html2canvas(targetDom, options) 141 | const roundCanvas = drawRoundedRec(origCanvas, scale) 142 | return drawShadow(roundCanvas) 143 | } 144 | -------------------------------------------------------------------------------- /src/helper/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export const $utils = require('./utils').default 4 | export const $lodash = require('./lodash').default 5 | export const $document = require('./document').default 6 | -------------------------------------------------------------------------------- /src/helper/lodash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * /* 3 | * https://www.npmjs.com/package/lodash-webpack-plugin 4 | * https://www.npmjs.com/package/babel-plugin-lodash 5 | * 6 | * @format 7 | * @date: 2017-08-01 8 | * @lastModify: 2018-05-16 9 | * @desc: Modular Lodash builds without the hassle. You can freely add the method you need 10 | * @detail: 11 | */ 12 | 13 | import _ from 'lodash' 14 | 15 | export default { 16 | clone: _.clone, 17 | cloneDeep: _.cloneDeep, 18 | endsWith: _.endsWith, 19 | debounce: _.debounce, 20 | throttle: _.throttle, 21 | find: _.find, 22 | isEmpty: _.isEmpty, 23 | flatten: _.flatten, 24 | flattenDepth: _.flattenDepth 25 | } 26 | -------------------------------------------------------------------------------- /src/helper/utils.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from 'vue' 4 | const $lodash = require('./lodash').default 5 | 6 | if (typeof String.prototype.startsWith !== 'function') { 7 | Window.String.prototype.startsWith = function (prefix) { 8 | return this.slice(0, prefix.length) === prefix 9 | } 10 | } 11 | 12 | export default { 13 | resMsg(res) { 14 | let key = { 15 | zh: 'Chinese', 16 | en: 'English', 17 | }[Vue.config.lang] 18 | try { 19 | let obj = JSON.parse(res.Message) 20 | return obj[key] || obj.Chinese 21 | } catch (e) { 22 | return res && res.Message 23 | } 24 | }, 25 | 26 | serverUrl(apiName) { 27 | return `app/${apiName}` 28 | }, 29 | 30 | titleLang(zhStr, enStr) { 31 | return { 32 | zh: zhStr, 33 | en: enStr, 34 | } 35 | }, 36 | 37 | query(search) { 38 | let str = search || window.location.search 39 | let objURL = {} 40 | 41 | str.replace(new RegExp('([^?=&]+)(=([^&]*))?', 'g'), ($0, $1, $2, $3) => { 42 | objURL[$1] = $3 43 | }) 44 | return objURL 45 | }, 46 | 47 | queryString(url, query) { 48 | let str = [] 49 | for (let key in query) { 50 | str.push(key + '=' + query[key]) 51 | } 52 | let paramStr = str.join('&') 53 | return paramStr ? `${url}?${paramStr}` : url 54 | }, 55 | 56 | assembleExternalLink(url) { 57 | const separator = $lodash.endsWith(url, '/') ? '' : '/' 58 | return `${url}${separator}?utm_source=markdown.lovejade.cn` 59 | }, 60 | 61 | openAuthorSite(p) { 62 | window.open(`${this.assembleExternalLink('https://aboutme.lovejade.cn/')}&position=${p}`) 63 | }, 64 | 65 | makeUpZero(num = 0) { 66 | return num < 10 ? `0${num}` : num 67 | }, 68 | 69 | getTimestamp() { 70 | const date = new Date() 71 | const y = `${date.getFullYear()}`.replace('20', '') 72 | let mo = this.makeUpZero(date.getMonth() + 1) 73 | const d = this.makeUpZero(date.getDate()) 74 | const h = this.makeUpZero(date.getHours()) 75 | const m = this.makeUpZero(date.getMinutes()) 76 | const s = this.makeUpZero(date.getSeconds()) 77 | return `${y}-${mo}-${d}-${h}${m}${s}` 78 | }, 79 | 80 | getExportFileName() { 81 | const type = location.pathname.split('/')[0] 82 | const timestamp = this.getTimestamp() 83 | const filename = `arya-${timestamp}.${type}` 84 | return filename 85 | }, 86 | 87 | updateHtmlStyle() { 88 | const htmlNode = document.querySelector('html') 89 | htmlNode.style.overflow = 'auto' 90 | htmlNode.style.height = 'auto' 91 | }, 92 | 93 | hideVditorTextarea() { 94 | const exportVditorNode = document.getElementById('khaleesi') 95 | const option = { 96 | childList: true, // 子节点的变动(新增、删除或者更改) 97 | attributes: true, // 属性的变动 98 | characterData: true, // 节点内容或节点文本的变动 99 | 100 | subtree: true, // 是否将观察器应用于该节点的所有后代节点 101 | attributeFilter: ['class', 'style'], // 观察特定属性 102 | attributeOldValue: true, // 观察 attributes 变动时,是否需要记录变动前的属性值 103 | characterDataOldValue: true, // 观察 characterData 变动,是否需要记录变动前的值 104 | } 105 | const mutationObserver = new MutationObserver(() => { 106 | const vditorTextarea = document.getElementsByClassName('vditor-textarea') 107 | if (vditorTextarea && vditorTextarea[0]) { 108 | vditorTextarea[0].style.display = 'none' 109 | mutationObserver.disconnect() 110 | } 111 | }) 112 | mutationObserver.observe(exportVditorNode, option) 113 | }, 114 | 115 | /* -----------------------------localStorage------------------------------------ */ 116 | /* 117 | * set localStorage 118 | */ 119 | setStorage(name, content) { 120 | if (!name) return 121 | if (typeof content !== 'string') { 122 | content = JSON.stringify(content) 123 | } 124 | window.localStorage.setItem(name, content) 125 | }, 126 | 127 | /** 128 | * get localStorage 129 | */ 130 | getStorage(name) { 131 | if (!name) return 132 | let content = window.localStorage.getItem(name) 133 | return JSON.parse(content) 134 | }, 135 | 136 | /** 137 | * delete localStorage 138 | */ 139 | removeStorage(name) { 140 | if (!name) return 141 | window.localStorage.removeItem(name) 142 | }, 143 | } 144 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from 'vue' 4 | import './global.js' 5 | import App from './App.vue' 6 | import router from './router' 7 | import './registerServiceWorker' 8 | 9 | const updateCanonical = (url) => { 10 | let link = document.querySelector("link[rel='canonical']") 11 | if (!link) { 12 | link = document.createElement('link') 13 | link.setAttribute('rel', 'canonical') 14 | document.head.appendChild(link) 15 | } 16 | link.setAttribute('href', url) 17 | } 18 | 19 | router.beforeEach((to, _, next) => { 20 | const canonicalUrl = `${to.path}` 21 | updateCanonical(canonicalUrl) 22 | next() 23 | }) 24 | 25 | new Vue({ 26 | router, 27 | render: (h) => h(App), 28 | }).$mount('#app') 29 | -------------------------------------------------------------------------------- /src/mixins/metaMixin.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export default { 4 | data() { 5 | const vm = this 6 | return { 7 | title: '首页', 8 | siteTitle: '在线 Markdown 编辑器', 9 | titleTemplate: '%s - ' + vm.siteTitle, 10 | keywords: 11 | 'vue,vue-cli3,webpack,vuex,vue-router,element-ui,TypeScript,ESLint,Prettier,Dayjs,Markdown,Jest,PWA,开箱即用,脚手架,模板', 12 | description: 13 | '此为基于 Vue-Cli3 搭建的开箱即用 Vue 脚手架模版,致力于探究更高效地构建优质 Web 应用。' 14 | } 15 | }, 16 | 17 | created() {}, 18 | 19 | metaInfo() { 20 | const titleContent = this.title ? `${this.title} - ${this.siteTitle}` : `${this.siteTitle}` 21 | return { 22 | title: this.title, 23 | titleTemplate: titleChunk => { 24 | return titleChunk ? `${titleChunk} - ${this.siteTitle}` : `${this.siteTitle}` 25 | }, 26 | meta: [ 27 | { vmid: 'title', name: 'title', content: titleContent }, 28 | { vmid: 'keywords', name: 'keywords', content: this.keywords }, 29 | { vmid: 'description', name: 'description', content: this.description }, 30 | { vmid: 'og:type', property: 'og:type', content: 'website' }, 31 | { vmid: 'og:title', property: 'og:title', content: titleContent }, 32 | { 33 | vmid: 'og:image', 34 | property: 'og:image', 35 | content: 'https://nice.lovejade.cn/logo.png' 36 | }, 37 | { 38 | vmid: 'og:keywords', 39 | property: 'og:keywords', 40 | content: this.keywords 41 | }, 42 | { 43 | vmid: 'og:description', 44 | property: 'og:description', 45 | content: this.description 46 | } 47 | ] 48 | } 49 | }, 50 | 51 | mounted() {}, 52 | 53 | methods: {} 54 | } 55 | -------------------------------------------------------------------------------- /src/pages/About.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 36 | -------------------------------------------------------------------------------- /src/pages/ExportImage.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 73 | -------------------------------------------------------------------------------- /src/pages/ExportPPT.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 78 | 79 | 94 | -------------------------------------------------------------------------------- /src/pages/ExportPdf.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 92 | -------------------------------------------------------------------------------- /src/pages/Main.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 122 | 123 | 174 | -------------------------------------------------------------------------------- /src/pages/partials/Frame.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 28 | 29 | 43 | -------------------------------------------------------------------------------- /src/pages/partials/NotFound.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 34 | 35 | 156 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * /* eslint-disable no-console 3 | * 4 | * @format 5 | */ 6 | 7 | import { register } from 'register-service-worker' 8 | 9 | if (process.env.NODE_ENV === 'production') { 10 | register(`${process.env.BASE_URL}service-worker.js`, { 11 | ready() { 12 | console.log( 13 | 'App is being served from cache by a service worker.\n' + 14 | 'For more details, visit https://goo.gl/AFskqB' 15 | ) 16 | }, 17 | cached() { 18 | console.log('Content has been cached for offline use.') 19 | }, 20 | updated() { 21 | console.log('New content is available; please refresh.') 22 | }, 23 | offline() { 24 | console.log('No internet connection found. App is running in offline mode.') 25 | }, 26 | error(error) { 27 | console.error('Error during service worker registration:', error) 28 | } 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/router/beforeEachHooks.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export default { 4 | updatePageMeta(to, from, next) { 5 | if (to.meta.title) { 6 | document.title = to.meta.title 7 | } 8 | next() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/router/commonRoutes.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Frame from '@pages/partials/Frame' 4 | import NotFound from '@pages/partials/NotFound' 5 | 6 | export default [ 7 | { 8 | path: '/', 9 | meta: { 10 | title: window.$appTitle 11 | }, 12 | component: resolve => require(['@pages/Main'], resolve) 13 | }, 14 | { 15 | path: '/about-arya', 16 | meta: { 17 | title: `关于 | ${window.$appTitle}` 18 | }, 19 | component: Frame, 20 | children: [ 21 | { 22 | path: '/', 23 | meta: { 24 | title: `关于 | ${window.$appTitle}` 25 | }, 26 | component: resolve => require(['@pages/About'], resolve) 27 | } 28 | ] 29 | }, 30 | { 31 | path: '/index', 32 | redirect: '/' 33 | }, 34 | { 35 | path: '*', 36 | meta: { 37 | title: 'Page Not Found' 38 | }, 39 | component: NotFound 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from 'vue' 4 | import Router from 'vue-router' 5 | import beforeEachHooks from './beforeEachHooks' 6 | import RoutesMapConfig from './routes' 7 | import commonRoutesMap from './commonRoutes' 8 | 9 | Vue.use(Router) 10 | 11 | const routerInstance = new Router({ 12 | mode: 'history', 13 | /* ~~~~~~~~~~~~~~~~~~~~~~~~@CHANGE@~~~~~~~~~~~~~~~~~~~~~~~~ */ 14 | /* 15 | @desc: base,应用的基路径;如整个单页应用服务在 /app/ 下,base 就应该设为 "/app/"; 16 | @reference: https://router.vuejs.org/zh-cn/api/options.html#base 17 | */ 18 | base: '/', 19 | linkActiveClass: 'active', 20 | scrollBehavior: () => ({ y: 0 }), 21 | routes: RoutesMapConfig.concat(commonRoutesMap) 22 | }) 23 | 24 | Object.values(beforeEachHooks).forEach(hook => { 25 | routerInstance.beforeEach(hook) 26 | }) 27 | 28 | export default routerInstance 29 | -------------------------------------------------------------------------------- /src/router/routes/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const files = require.context('.', true, /\.js$/) 4 | 5 | var configArray = [] 6 | 7 | files.keys().forEach(key => { 8 | if (key === './index.js') return 9 | configArray = configArray.concat(files(key).default) 10 | }) 11 | export default configArray 12 | -------------------------------------------------------------------------------- /src/router/routes/main.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Frame from '@pages/partials/Frame' 4 | 5 | export default [ 6 | { 7 | path: '/export', 8 | component: Frame, 9 | meta: { 10 | title: window.$appTitle, 11 | }, 12 | children: [ 13 | { 14 | path: 'png', 15 | meta: { 16 | title: `导出 PNG | ${window.$appTitle}`, 17 | }, 18 | component: (resolve) => require(['@pages/ExportImage'], resolve), 19 | }, 20 | { 21 | path: 'pdf', 22 | meta: { 23 | title: `导出 PDF | ${window.$appTitle}`, 24 | }, 25 | component: (resolve) => require(['@pages/ExportPdf'], resolve), 26 | }, 27 | { 28 | path: 'ppt', 29 | meta: { 30 | title: `PPT 预览 | ${window.$appTitle}`, 31 | }, 32 | component: (resolve) => require(['@pages/ExportPPT'], resolve), 33 | }, 34 | ], 35 | }, 36 | ] 37 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | }, 5 | rules: { 6 | 'import/no-extraneous-dependencies': 'off' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/unit/Arrow.spec.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import { shallowMount } from '@vue/test-utils' 4 | import Arrow from '@/components/icons/Arrow.vue' 5 | 6 | describe('Arrow.vue', () => { 7 | // ------------------------Test [props.direction]------------------------Start 8 | it('renders props.direction when passed', () => { 9 | const parpDirection = 'right' 10 | const wrapper = shallowMount(Arrow, { 11 | propsData: { direction: parpDirection } 12 | }) 13 | expect(wrapper.classes()).toContain(parpDirection) 14 | }) 15 | 16 | it('renders props.direction when NOT passed', () => { 17 | // direction default Value is [left] 18 | const parpDirection = '~~left' 19 | const defaultDirection = 'left' 20 | const wrapper = shallowMount(Arrow, { 21 | propsData: { direction: parpDirection } 22 | }) 23 | expect(wrapper.classes()).toContain(defaultDirection) 24 | /* const compClassListStr = wrapper 25 | .find('.arrow-component') 26 | .element.classList.toString() 27 | expect(compClassListStr).toBe(`arrow-component left`) */ 28 | }) 29 | 30 | // ------------------------Test [props.color]------------------------Start 31 | it('renders props.color when passed', () => { 32 | // color default Vaule is [#479537] 33 | const propColor = '#666666' 34 | const wrapper = shallowMount(Arrow, { 35 | propsData: { color: propColor } 36 | }) 37 | const compBorderColor = wrapper.find('.arrow-component').element.style 38 | .borderColor 39 | expect(compBorderColor).toBe(propColor) 40 | }) 41 | 42 | it('renders props.color when Not passed', () => { 43 | // color default Vaule is [#479537] 44 | const propColor = '#666 666#' 45 | const defaultColor = '#479537' 46 | const wrapper = shallowMount(Arrow, { 47 | propsData: { color: propColor } 48 | }) 49 | const compBorderColor = wrapper.find('.arrow-component').element.style 50 | .borderColor 51 | expect(compBorderColor).toBe(defaultColor) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const path = require('path') 4 | const SizePlugin = require('size-plugin') 5 | 6 | const isProductionEnvFlag = process.env.NODE_ENV === 'production' 7 | 8 | function resolveRealPath(dir) { 9 | return path.join(__dirname, dir) 10 | } 11 | 12 | // https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/config.md 13 | module.exports = { 14 | // Project deployment base 15 | // By default we assume your app will be deployed at the root of a domain, 16 | // e.g. https://www.my-app.com/ 17 | // If your app is deployed at a sub-path, you will need to specify that 18 | // sub-path here. For example, if your app is deployed at 19 | // https://www.foobar.com/my-app/ 20 | // then change this to '/my-app/' 21 | publicPath: '/', 22 | 23 | // where to output built files 24 | outputDir: 'dist', 25 | 26 | // whether to use eslint-loader for lint on save. 27 | // valid values: true | false | 'error' 28 | // when set to 'error', lint errors will cause compilation to fail. 29 | lintOnSave: true, 30 | 31 | // https://cli.vuejs.org/config/#runtimecompiler 32 | runtimeCompiler: false, 33 | 34 | // babel-loader skips `node_modules` deps by default. 35 | // explicitly transpile a dependency with this option. 36 | transpileDependencies: [ 37 | /* string or regex */ 38 | ], 39 | 40 | // generate sourceMap for production build? 41 | productionSourceMap: process.env.NODE_ENV !== 'production', 42 | 43 | // tweak internal webpack configuration. 44 | // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md 45 | chainWebpack: (config) => { 46 | config.resolve.alias 47 | .set('vue$', 'vue/dist/vue.esm.js') 48 | .set('@helper', resolveRealPath('src/helper')) 49 | .set('@config', resolveRealPath('src/config')) 50 | .set('@pages', resolveRealPath('src/pages')) 51 | .set('@assets', resolveRealPath('src/assets')) 52 | .set('@router', resolveRealPath('src/router')) 53 | .set('@mixins', resolveRealPath('src/mixins')) 54 | .set('@components', resolveRealPath('src/components')) 55 | 56 | // remove the old loader & add new one 57 | config.module.rules.delete('svg') 58 | config.module 59 | .rule('svg') 60 | .test(/\.svg$/) 61 | .use('svg-sprite-loader') 62 | .loader('svg-sprite-loader') 63 | .options({ 64 | name: '[name]-[hash:7]', 65 | prefixize: true, 66 | }) 67 | 68 | const splitOptions = config.optimization.get('splitChunks') 69 | config.optimization.splitChunks( 70 | Object.assign({}, splitOptions, { 71 | // (缺省值5)按需加载时的最大并行请求数 72 | maxAsyncRequests: 16, 73 | // (默认值3)入口点上的最大并行请求数 74 | maxInitialRequests: 16, 75 | // (默认值:1)分割前共享模块的最小块数 76 | minChunks: 1, 77 | // (默认值:30000)块的最小大小 78 | minSize: 30000, 79 | // webpack 将使用块的起源和名称来生成名称: `vendors~main.js`,如项目与"~"冲突,则可通过此值修改,Eg: '-' 80 | automaticNameDelimiter: '~', 81 | // cacheGroups is an object where keys are the cache group names. 82 | name: true, 83 | cacheGroups: { 84 | default: false, 85 | common: { 86 | name: `chunk-common`, 87 | minChunks: 2, 88 | priority: -20, 89 | chunks: 'initial', 90 | reuseExistingChunk: true, 91 | }, 92 | element: { 93 | name: 'element', 94 | test: /[\\/]node_modules[\\/]element-ui[\\/]/, 95 | chunks: 'initial', 96 | // 默认组的优先级为负数,以允许任何自定义缓存组具有更高的优先级(默认值为0) 97 | priority: -30, 98 | }, 99 | }, 100 | }) 101 | ) 102 | 103 | // https://github.com/webpack-contrib/webpack-bundle-analyzer 104 | if (process.env.npm_config_report) { 105 | config 106 | .plugin('webpack-bundle-analyzer') 107 | .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin) 108 | } 109 | }, 110 | 111 | configureWebpack: { 112 | plugins: [isProductionEnvFlag ? new SizePlugin() : () => {}], 113 | }, 114 | 115 | // use thread-loader for babel & TS in production build 116 | // enabled by default if the machine has more than 1 cores 117 | parallel: require('os').cpus().length > 1, 118 | 119 | // options for the PWA plugin. 120 | // see => https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa 121 | // https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin 122 | pwa: { 123 | name: 'Arya - 在线 Markdown 编辑器', 124 | themeColor: '#4DBA87', 125 | msTileColor: '#000000', 126 | appleMobileWebAppCapable: 'yes', 127 | appleMobileWebAppStatusBarStyle: 'black', 128 | iconPaths: { 129 | favicon32: 'img/icons/favicon-32x32.png', 130 | favicon16: 'img/icons/favicon-16x16.png', 131 | appleTouchIcon: 'img/icons/apple-touch-icon.png', 132 | maskIcon: 'img/icons/safari-pinned-tab.svg', 133 | msTileImage: 'img/icons/mstile-150x150.png', 134 | }, 135 | // configure the workbox plugin (GenerateSW or InjectManifest) 136 | workboxPluginMode: 'InjectManifest', 137 | workboxOptions: { 138 | // swSrc is required in InjectManifest mode. 139 | swSrc: 'public/service-worker.js', 140 | // ...other Workbox options... 141 | }, 142 | }, 143 | 144 | // configure webpack-dev-server behavior 145 | devServer: { 146 | open: process.platform === 'darwin', 147 | host: '0.0.0.0', 148 | port: 8080, 149 | https: false, 150 | hotOnly: false, 151 | // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#configuring-proxy 152 | proxy: null, // string | Object 153 | before: () => {}, 154 | }, 155 | 156 | // options for 3rd party plugins 157 | pluginOptions: {}, 158 | } 159 | --------------------------------------------------------------------------------