mirror of
https://github.com/doocs/md.git
synced 2024-11-24 19:10:34 +08:00
refactor: use uni-app framework (#55)
* update framework to uni-app * fix: bug fix * fix: bug fix * 修改输出路径 * feat: publicPath * feat: manifest update * fix: cssEditor theme * fix: style * style: format code with prettier * fix: table style & copy style on the night mode * fix: upload image * fix: style Co-authored-by: yanglbme <szuyanglb@outlook.com>
This commit is contained in:
parent
432db15576
commit
b50ae32834
0
.automator/h5/.automator.json
Normal file
0
.automator/h5/.automator.json
Normal file
@ -1,2 +0,0 @@
|
|||||||
> 1%
|
|
||||||
last 2 versions
|
|
@ -1,5 +1,18 @@
|
|||||||
[*.{js,jsx,ts,tsx,vue}]
|
# https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
max_line_length = 80
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = 0
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[COMMIT_EDITMSG]
|
||||||
|
max_line_length = 0
|
||||||
|
48
.eslintrc.js
48
.eslintrc.js
@ -1,27 +1,27 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
|
},
|
||||||
|
extends: ["plugin:vue/essential", "@vue/standard"],
|
||||||
|
rules: {
|
||||||
|
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||||
|
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||||
|
camelcase: "off",
|
||||||
|
eqeqeq: "off",
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
parser: "babel-eslint",
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/__tests__/*.{j,t}s?(x)",
|
||||||
|
"**/tests/unit/**/*.spec.{j,t}s?(x)",
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extends: ["plugin:vue/essential", "@vue/standard"],
|
],
|
||||||
rules: {
|
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
|
||||||
camelcase: "off",
|
|
||||||
eqeqeq: "off",
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
parser: "babel-eslint",
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
"**/__tests__/*.{j,t}s?(x)",
|
|
||||||
"**/tests/unit/**/*.spec.{j,t}s?(x)",
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,60 @@
|
|||||||
|
const plugins = [];
|
||||||
|
|
||||||
|
if (process.env.UNI_OPT_TREESHAKINGNG) {
|
||||||
|
plugins.push(
|
||||||
|
require("@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(process.env.UNI_PLATFORM === "app-plus" && process.env.UNI_USING_V8) ||
|
||||||
|
(process.env.UNI_PLATFORM === "h5" &&
|
||||||
|
process.env.UNI_H5_BROWSER === "builtin")
|
||||||
|
) {
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const isWin = /^win/.test(process.platform);
|
||||||
|
|
||||||
|
const normalizePath = (path) => (isWin ? path.replace(/\\/g, "/") : path);
|
||||||
|
|
||||||
|
const input = normalizePath(process.env.UNI_INPUT_DIR);
|
||||||
|
try {
|
||||||
|
plugins.push([
|
||||||
|
require("@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console"),
|
||||||
|
{
|
||||||
|
file(file) {
|
||||||
|
file = normalizePath(file);
|
||||||
|
if (file.indexOf(input) === 0) {
|
||||||
|
return path.relative(input, file);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.UNI_LIBRARIES = process.UNI_LIBRARIES || ["@dcloudio/uni-ui"];
|
||||||
|
process.UNI_LIBRARIES.forEach((libraryName) => {
|
||||||
|
plugins.push([
|
||||||
|
"import",
|
||||||
|
{
|
||||||
|
libraryName: libraryName,
|
||||||
|
customName: (name) => {
|
||||||
|
return `${libraryName}/lib/${name}/${name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ["@vue/cli-plugin-babel/preset"],
|
presets: [
|
||||||
|
[
|
||||||
|
"@vue/app",
|
||||||
|
{
|
||||||
|
modules: "commonjs",
|
||||||
|
useBuiltIns: process.env.UNI_PLATFORM === "h5" ? "usage" : "entry",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
plugins,
|
||||||
};
|
};
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: "@vue/cli-plugin-unit-jest",
|
|
||||||
};
|
|
27468
package-lock.json
generated
27468
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
115
package.json
115
package.json
@ -1,53 +1,112 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-md",
|
"name": "md",
|
||||||
"version": "1.4.7",
|
"version": "1.5.0",
|
||||||
"homepage": ".",
|
"private": true,
|
||||||
"description": "An open-source wechat markdown editor.",
|
|
||||||
"author": "doocs",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "npm run dev:h5",
|
||||||
"build": "vue-cli-service build",
|
"build": "npm run build:h5",
|
||||||
"lint": "vue-cli-service lint",
|
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
|
||||||
"test:unit": "vue-cli-service test:unit"
|
"build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
|
||||||
|
"build:h5": "cross-env NODE_ENV=production UNI_OUTPUT_DIR=dist UNI_PLATFORM=h5 vue-cli-service uni-build",
|
||||||
|
"build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
|
||||||
|
"build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
|
||||||
|
"build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
|
||||||
|
"build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
|
||||||
|
"build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
|
||||||
|
"build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
|
||||||
|
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
|
||||||
|
"build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
|
||||||
|
"build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
|
||||||
|
"build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
|
||||||
|
"build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
|
||||||
|
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
|
||||||
|
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
|
||||||
|
"dev:h5": "cross-env NODE_ENV=development UNI_OUTPUT_DIR=dist UNI_PLATFORM=h5 vue-cli-service uni-serve",
|
||||||
|
"dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
|
||||||
|
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
|
||||||
|
"dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
|
||||||
|
"dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
|
||||||
|
"dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
|
||||||
|
"dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
|
||||||
|
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
|
||||||
|
"serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
|
||||||
|
"test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
|
||||||
|
"test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
|
||||||
|
"test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
|
||||||
|
"test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
|
||||||
|
"test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app-plus": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-h5": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-helper-json": "*",
|
||||||
|
"@dcloudio/uni-mp-360": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-alipay": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-baidu": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-qq": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-vue": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-mp-weixin": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-quickapp-native": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "^2.0.0-30720210122002",
|
||||||
|
"@dcloudio/uni-stat": "^2.0.0-30720210122002",
|
||||||
|
"@vue/shared": "^3.0.0",
|
||||||
"ali-oss": "^6.11.2",
|
"ali-oss": "^6.11.2",
|
||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
"buffer-from": "^1.1.1",
|
"buffer-from": "^1.1.1",
|
||||||
"codemirror": "^5.58.3",
|
"codemirror": "^5.58.3",
|
||||||
"core-js": "^3.7.0",
|
"core-js": "^3.8.3",
|
||||||
"cos-js-sdk-v5": "^1.1.0",
|
"cos-js-sdk-v5": "^1.1.0",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"element-ui": "^2.14.1",
|
"element-ui": "^2.14.1",
|
||||||
|
"flyio": "^0.6.2",
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
"juice": "^7.0.0",
|
"juice": "^7.0.0",
|
||||||
"less": "^3.12.2",
|
|
||||||
"marked": "^2.0.0",
|
"marked": "^2.0.0",
|
||||||
"prettier": "^2.2.0",
|
"prettier": "^2.2.0",
|
||||||
"prettify": "^0.1.7",
|
"prettify": "^0.1.7",
|
||||||
"qiniu-js": "^3.1.2",
|
"qiniu-js": "^3.1.2",
|
||||||
|
"regenerator-runtime": "^0.12.1",
|
||||||
"uuid": "^8.3.1",
|
"uuid": "^8.3.1",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
"vue-router": "^3.4.9",
|
"vue-router": "^3.4.9",
|
||||||
"vuex": "^3.5.1"
|
"vuex": "^3.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.5.9",
|
"@dcloudio/types": "*",
|
||||||
"@vue/cli-plugin-eslint": "^4.5.9",
|
"@dcloudio/uni-automator": "^2.0.0-30720210122002",
|
||||||
"@vue/cli-plugin-unit-jest": "^4.5.9",
|
"@dcloudio/uni-cli-shared": "^2.0.0-30720210122002",
|
||||||
"@vue/cli-service": "^4.5.9",
|
"@dcloudio/uni-migration": "^2.0.0-30720210122002",
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@dcloudio/uni-template-compiler": "^2.0.0-30720210122002",
|
||||||
"@vue/test-utils": "1.1.1",
|
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-30720210122002",
|
||||||
"babel-eslint": "^10.1.0",
|
"@dcloudio/vue-cli-plugin-uni": "^2.0.0-30720210122002",
|
||||||
"eslint": "^7.14.0",
|
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-30720210122002",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"@dcloudio/webpack-uni-mp-loader": "^2.0.0-30720210122002",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"@dcloudio/webpack-uni-pages-loader": "^2.0.0-30720210122002",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"eslint-plugin-vue": "^7.1.0",
|
"async-validator": "^1.11.5",
|
||||||
"less-loader": "^7.1.0",
|
"babel-plugin-import": "^1.11.0",
|
||||||
"node-sass": "^5.0.0",
|
"cross-env": "^7.0.2",
|
||||||
"sass-loader": "^10.1.0",
|
"jest": "^25.4.0",
|
||||||
"vue-template-compiler": "^2.6.12"
|
"less": "^3.12.2",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"mini-types": "*",
|
||||||
|
"miniprogram-api-typings": "*",
|
||||||
|
"postcss-comment": "^2.0.0",
|
||||||
|
"sass-loader": "^11.0.1",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"Android >= 4",
|
||||||
|
"ios >= 8"
|
||||||
|
],
|
||||||
|
"uni-app": {
|
||||||
|
"scripts": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
postcss.config.js
Normal file
22
postcss.config.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const path = require("path");
|
||||||
|
module.exports = {
|
||||||
|
parser: require("postcss-comment"),
|
||||||
|
plugins: [
|
||||||
|
require("postcss-import")({
|
||||||
|
resolve(id, basedir, importOptions) {
|
||||||
|
if (id.startsWith("~@/")) {
|
||||||
|
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3));
|
||||||
|
} else if (id.startsWith("@/")) {
|
||||||
|
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2));
|
||||||
|
} else if (id.startsWith("/") && !id.startsWith("//")) {
|
||||||
|
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1));
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
require("autoprefixer")({
|
||||||
|
remove: process.env.UNI_PLATFORM !== "h5",
|
||||||
|
}),
|
||||||
|
require("@dcloudio/vue-cli-plugin-uni/packages/postcss"),
|
||||||
|
],
|
||||||
|
};
|
@ -1,33 +1,29 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
|
||||||
<meta
|
|
||||||
name="keywords"
|
|
||||||
content="md,markdown,markdown-editor,wechat,official-account,yanglbme,doocs"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Wechat Markdown Editor | 一款高度简洁的微信 Markdown 编辑器"
|
|
||||||
/>
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
|
||||||
/>
|
|
||||||
<title>微信 Markdown 编辑器</title>
|
|
||||||
<link
|
|
||||||
rel="shortcut icon"
|
|
||||||
href="https://gitee.com/yanglbme/resource/raw/master/doocs-md/favicon.png"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="apple-touch-icon-precomposed"
|
|
||||||
href="https://gitee.com/yanglbme/resource/raw/master/doocs-md/qrcode.png"
|
|
||||||
/>
|
|
||||||
<script src="https://cdn.bootcdn.net/ajax/libs/prettify/r224/prettify.min.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<div id="app"></div>
|
<meta charset="utf-8">
|
||||||
</body>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
</html>
|
<meta name="keywords" content="md,markdown,markdown-editor,wechat,official-account,yanglbme,doocs" />
|
||||||
|
<meta name="description" content="Wechat Markdown Editor | 一款高度简洁的微信 Markdown 编辑器" />
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
|
||||||
|
<!-- <title>
|
||||||
|
<%= htmlWebpackPlugin.options.title %>
|
||||||
|
</title> -->
|
||||||
|
<title>微信 Markdown 编辑器</title>
|
||||||
|
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
|
||||||
|
<link rel="shortcut icon" href="https://gitee.com/yanglbme/resource/raw/master/doocs-md/favicon.png" />
|
||||||
|
<link rel="apple-touch-icon-precomposed" href="https://gitee.com/yanglbme/resource/raw/master/doocs-md/qrcode.png" />
|
||||||
|
<script src="https://cdn.bootcdn.net/ajax/libs/prettify/r224/prettify.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>Please enable JavaScript to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
101
src/App.vue
101
src/App.vue
@ -1,43 +1,76 @@
|
|||||||
<template>
|
|
||||||
<transition name="fade" v-if="loading">
|
|
||||||
<loading />
|
|
||||||
</transition>
|
|
||||||
<codemirror-editor v-else />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Loading from "./components/Loading";
|
|
||||||
import CodemirrorEditor from "./view/CodemirrorEditor";
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
onLaunch: function () {
|
||||||
components: {
|
console.log("App Launch");
|
||||||
Loading,
|
},
|
||||||
CodemirrorEditor,
|
onShow: function () {
|
||||||
},
|
console.log("App Show");
|
||||||
data() {
|
},
|
||||||
return {
|
onHide: function () {
|
||||||
loading: true,
|
console.log("App Hide");
|
||||||
};
|
},
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.loading = false;
|
|
||||||
}, 100);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="less">
|
||||||
.fade-enter,
|
/* 每个页面公共css */
|
||||||
.fade-leave-to {
|
@import url("./assets/less/style-mirror.css");
|
||||||
opacity: 0;
|
@import url("./assets/less/theme.less");
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
.fade-enter-to,
|
|
||||||
.fade-leave {
|
::-webkit-scrollbar-track {
|
||||||
opacity: 1;
|
border-radius: 6px;
|
||||||
|
background-color: rgba(200, 200, 200, 0.3);
|
||||||
}
|
}
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
::-webkit-scrollbar-thumb {
|
||||||
transition: all 1s;
|
border-radius: 6px;
|
||||||
|
background-color: rgba(144, 146, 152, 0.5);
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgba(144, 146, 152, 0.5);
|
||||||
|
}
|
||||||
|
/* CSS-hints */
|
||||||
|
.CodeMirror-hints {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
overflow: hidden;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
min-width: 200px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
|
max-height: 20em;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-hint {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: pre;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.CodeMirror-hint:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.CodeMirror-hint:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
const githubConfig = {
|
const githubConfig = {
|
||||||
username: "filess",
|
username: "filess",
|
||||||
repoList: Array.from(
|
repoList: Array.from(
|
||||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
||||||
(e) => `img${e}`
|
(e) => `img${e}`
|
||||||
),
|
),
|
||||||
branch: "main",
|
branch: "main",
|
||||||
accessTokenList: [
|
accessTokenList: [
|
||||||
"7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af",
|
"7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af",
|
||||||
"c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe",
|
"c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe",
|
||||||
"2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b",
|
"2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b",
|
||||||
"445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c",
|
"445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c",
|
||||||
"cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46",
|
"cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46",
|
||||||
"b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff",
|
"b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff",
|
||||||
"618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3",
|
"618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3",
|
||||||
"a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55",
|
"a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55",
|
||||||
"77904db41aee57ad79bdoocsmd760f848201dac9c96fd5e",
|
"77904db41aee57ad79bdoocsmd760f848201dac9c96fd5e",
|
||||||
"02f251cb14ac62ab100doocsmdddbfc8527d773f1f04ce1",
|
"02f251cb14ac62ab100doocsmdddbfc8527d773f1f04ce1",
|
||||||
"eb321079a95ba7028d9doocsmde2e84c502dac70de7cf08",
|
"eb321079a95ba7028d9doocsmde2e84c502dac70de7cf08",
|
||||||
"22f74fcfb071a961fa2doocsmde28dabc746f0503a15e5d",
|
"22f74fcfb071a961fa2doocsmde28dabc746f0503a15e5d",
|
||||||
"85124c2bfe7abba0938doocsmd0af7f67918b99d085a5fd",
|
"85124c2bfe7abba0938doocsmd0af7f67918b99d085a5fd",
|
||||||
"0a561b4d4bbecb2de7edoocsmdd9ba3833d11dbc5e430f5",
|
"0a561b4d4bbecb2de7edoocsmdd9ba3833d11dbc5e430f5",
|
||||||
"e8a01491188d8d5a097doocsmd03ede0aad1fe9e3af24e9",
|
"e8a01491188d8d5a097doocsmd03ede0aad1fe9e3af24e9",
|
||||||
"36e1f420d7e5bdebd67doocsmd65463562f5f25b20b8377",
|
"36e1f420d7e5bdebd67doocsmd65463562f5f25b20b8377",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const giteeConfig = {
|
const giteeConfig = {
|
||||||
username: "filesss",
|
username: "filesss",
|
||||||
repoList: Array.from(
|
repoList: Array.from(
|
||||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
||||||
(e) => `img${e}`
|
(e) => `img${e}`
|
||||||
),
|
),
|
||||||
branch: "main",
|
branch: "main",
|
||||||
accessTokenList: [
|
accessTokenList: [
|
||||||
"ed5fc9866bd6c2fdoocsmddd433f806fd2f399c",
|
"ed5fc9866bd6c2fdoocsmddd433f806fd2f399c",
|
||||||
"5448ffebbbf1151doocsmdc4e337cf814fc8a62",
|
"5448ffebbbf1151doocsmdc4e337cf814fc8a62",
|
||||||
"25b05efd2557ca2doocsmd75b5c0835e3395911",
|
"25b05efd2557ca2doocsmd75b5c0835e3395911",
|
||||||
"11628c7a5aef015doocsmd2eeff9fb9566f0458",
|
"11628c7a5aef015doocsmd2eeff9fb9566f0458",
|
||||||
"cb2f5145ed938dedoocsmdbd063b4ed244eecf8",
|
"cb2f5145ed938dedoocsmdbd063b4ed244eecf8",
|
||||||
"d8c0b57500672c1doocsmd55f48b866b5ebcd98",
|
"d8c0b57500672c1doocsmd55f48b866b5ebcd98",
|
||||||
"78c56eadb88e453doocsmd43ddd95753351771a",
|
"78c56eadb88e453doocsmd43ddd95753351771a",
|
||||||
"03e1a688003948fdoocsmda16fcf41e6f03f1f0",
|
"03e1a688003948fdoocsmda16fcf41e6f03f1f0",
|
||||||
"c49121cf4d191fbdoocsmdd6a7877ed537e474a",
|
"c49121cf4d191fbdoocsmdd6a7877ed537e474a",
|
||||||
"adfeb2fadcdc4aadoocsmdfe1ee869ac9c968ff",
|
"adfeb2fadcdc4aadoocsmdfe1ee869ac9c968ff",
|
||||||
"116c94549ca4a0ddoocsmd192653af5c0694616",
|
"116c94549ca4a0ddoocsmd192653af5c0694616",
|
||||||
"ecf30ed7f2eb184doocsmd51ea4ec8300371d9e",
|
"ecf30ed7f2eb184doocsmd51ea4ec8300371d9e",
|
||||||
"5837cf2bd5afd93doocsmd73904bed31934949e",
|
"5837cf2bd5afd93doocsmd73904bed31934949e",
|
||||||
"b5b7e1c7d57e01fdoocsmd5266f552574297d78",
|
"b5b7e1c7d57e01fdoocsmd5266f552574297d78",
|
||||||
"684d55564ffbd0bdoocsmd7d747e5cc23aed6d6",
|
"684d55564ffbd0bdoocsmd7d747e5cc23aed6d6",
|
||||||
"3fc04a9d272ab71doocsmd010c56cb57d88d2ba",
|
"3fc04a9d272ab71doocsmd010c56cb57d88d2ba",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export { githubConfig, giteeConfig };
|
export { githubConfig, giteeConfig };
|
||||||
|
@ -2,29 +2,29 @@ import axios from "axios";
|
|||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: "",
|
baseURL: "",
|
||||||
timeout: 10 * 1000, // 请求超时时间
|
timeout: 10 * 1000, // 请求超时时间
|
||||||
});
|
});
|
||||||
|
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
if (/^(post)|(put)|(delete)$/i.test(config.method)) {
|
if (/^(post)|(put)|(delete)$/i.test(config.method)) {
|
||||||
if (config.data && config.data.upload) {
|
if (config.data && config.data.upload) {
|
||||||
config.headers["Content-Type"] = "multipart/form-data";
|
config.headers["Content-Type"] = "multipart/form-data";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
Promise.reject(error);
|
|
||||||
}
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
Promise.reject(error);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
(res) => {
|
(res) => {
|
||||||
return res.data ? res.data : Promise.reject(res);
|
return res.data ? res.data : Promise.reject(res);
|
||||||
},
|
},
|
||||||
(error) => Promise.reject(error)
|
(error) => Promise.reject(error)
|
||||||
);
|
);
|
||||||
|
|
||||||
export default service;
|
export default service;
|
||||||
|
358
src/api/file.js
358
src/api/file.js
@ -9,51 +9,51 @@ import * as qiniu from "qiniu-js";
|
|||||||
import { utf16to8, base64encode, safe64 } from "../assets/scripts/tokenTools";
|
import { utf16to8, base64encode, safe64 } from "../assets/scripts/tokenTools";
|
||||||
|
|
||||||
function getConfig(useDefault, platform) {
|
function getConfig(useDefault, platform) {
|
||||||
if (useDefault) {
|
if (useDefault) {
|
||||||
// load default config file
|
// load default config file
|
||||||
const config = platform === "github" ? githubConfig : giteeConfig;
|
const config = platform === "github" ? githubConfig : giteeConfig;
|
||||||
const { username, repoList, branch, accessTokenList } = config;
|
const { username, repoList, branch, accessTokenList } = config;
|
||||||
|
|
||||||
// choose random token from access_token list
|
// choose random token from access_token list
|
||||||
const tokenIndex = Math.floor(Math.random() * accessTokenList.length);
|
const tokenIndex = Math.floor(Math.random() * accessTokenList.length);
|
||||||
const accessToken = accessTokenList[tokenIndex].replace("doocsmd", "");
|
const accessToken = accessTokenList[tokenIndex].replace("doocsmd", "");
|
||||||
|
|
||||||
// choose random repo from repo list
|
// choose random repo from repo list
|
||||||
const repoIndex = Math.floor(Math.random() * repoList.length);
|
const repoIndex = Math.floor(Math.random() * repoList.length);
|
||||||
const repo = repoList[repoIndex];
|
const repo = repoList[repoIndex];
|
||||||
|
|
||||||
return { username, repo, branch, accessToken };
|
return { username, repo, branch, accessToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
// load configuration from localStorage
|
// load configuration from localStorage
|
||||||
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`));
|
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`));
|
||||||
|
|
||||||
// split username/repo
|
// split username/repo
|
||||||
const repoUrl = customConfig.repo
|
const repoUrl = customConfig.repo
|
||||||
.replace(`https://${platform}.com/`, "")
|
.replace(`https://${platform}.com/`, "")
|
||||||
.replace(`http://${platform}.com/`, "")
|
.replace(`http://${platform}.com/`, "")
|
||||||
.replace(`${platform}.com/`, "")
|
.replace(`${platform}.com/`, "")
|
||||||
.split("/");
|
.split("/");
|
||||||
return {
|
return {
|
||||||
username: repoUrl[0],
|
username: repoUrl[0],
|
||||||
repo: repoUrl[1],
|
repo: repoUrl[1],
|
||||||
branch: customConfig.branch || "master",
|
branch: customConfig.branch || "master",
|
||||||
accessToken: customConfig.accessToken,
|
accessToken: customConfig.accessToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDir() {
|
function getDir() {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
const day = date.getDate().toString().padStart(2, "0");
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
return `${year}/${month}/${day}`;
|
return `${year}/${month}/${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDateFilename(filename) {
|
function getDateFilename(filename) {
|
||||||
const currentTimestamp = new Date().getTime();
|
const currentTimestamp = new Date().getTime();
|
||||||
const fileSuffix = filename.split(".")[1];
|
const fileSuffix = filename.split(".")[1];
|
||||||
return `${currentTimestamp}-${uuidv4()}.${fileSuffix}`;
|
return `${currentTimestamp}-${uuidv4()}.${fileSuffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -61,32 +61,32 @@ function getDateFilename(filename) {
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
async function ghFileUpload(content, filename) {
|
async function ghFileUpload(content, filename) {
|
||||||
const useDefault = localStorage.getItem("imgHost") === "default";
|
const useDefault = localStorage.getItem("imgHost") === "default";
|
||||||
const { username, repo, branch, accessToken } = getConfig(
|
const { username, repo, branch, accessToken } = getConfig(
|
||||||
useDefault,
|
useDefault,
|
||||||
"github"
|
"github"
|
||||||
);
|
);
|
||||||
const dir = getDir();
|
const dir = getDir();
|
||||||
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`;
|
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`;
|
||||||
const dateFilename = getDateFilename(filename);
|
const dateFilename = getDateFilename(filename);
|
||||||
const res = await fetch({
|
const res = await fetch({
|
||||||
url: url + dateFilename,
|
url: url + dateFilename,
|
||||||
method: "put",
|
method: "put",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `token ${accessToken}`,
|
Authorization: `token ${accessToken}`,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
content,
|
content,
|
||||||
branch,
|
branch,
|
||||||
message: `Upload by ${window.location.href}`,
|
message: `Upload by ${window.location.href}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/`;
|
const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/`;
|
||||||
const cdnResourceUrl = `cdn.jsdelivr.net/gh/${username}/${repo}@${branch}/`;
|
const cdnResourceUrl = `cdn.jsdelivr.net/gh/${username}/${repo}@${branch}/`;
|
||||||
return useDefault
|
return useDefault
|
||||||
? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl)
|
? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl)
|
||||||
: res.content.download_url;
|
: res.content.download_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -94,25 +94,25 @@ async function ghFileUpload(content, filename) {
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
async function giteeUpload(content, filename) {
|
async function giteeUpload(content, filename) {
|
||||||
const useDefault = localStorage.getItem("imgHost") === "default";
|
const useDefault = localStorage.getItem("imgHost") === "default";
|
||||||
const { username, repo, branch, accessToken } = getConfig(
|
const { username, repo, branch, accessToken } = getConfig(
|
||||||
useDefault,
|
useDefault,
|
||||||
"gitee"
|
"gitee"
|
||||||
);
|
);
|
||||||
const dir = getDir();
|
const dir = getDir();
|
||||||
const dateFilename = getDateFilename(filename);
|
const dateFilename = getDateFilename(filename);
|
||||||
const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${dir}/${dateFilename}`;
|
const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${dir}/${dateFilename}`;
|
||||||
const res = await fetch({
|
const res = await fetch({
|
||||||
url,
|
url,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
content,
|
content,
|
||||||
branch,
|
branch,
|
||||||
access_token: accessToken,
|
access_token: accessToken,
|
||||||
message: `Upload by ${window.location.href}`,
|
message: `Upload by ${window.location.href}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return encodeURI(res.content.download_url);
|
return encodeURI(res.content.download_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -120,37 +120,37 @@ async function giteeUpload(content, filename) {
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
function getQiniuToken(accessKey, secretKey, putPolicy) {
|
function getQiniuToken(accessKey, secretKey, putPolicy) {
|
||||||
const policy = JSON.stringify(putPolicy);
|
const policy = JSON.stringify(putPolicy);
|
||||||
const encoded = base64encode(utf16to8(policy));
|
const encoded = base64encode(utf16to8(policy));
|
||||||
const hash = CryptoJS.HmacSHA1(encoded, secretKey);
|
const hash = CryptoJS.HmacSHA1(encoded, secretKey);
|
||||||
const encodedSigned = hash.toString(CryptoJS.enc.Base64);
|
const encodedSigned = hash.toString(CryptoJS.enc.Base64);
|
||||||
return `${accessKey}:${safe64(encodedSigned)}:${encoded}`;
|
return `${accessKey}:${safe64(encodedSigned)}:${encoded}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function qiniuUpload(file) {
|
async function qiniuUpload(file) {
|
||||||
const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse(
|
const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse(
|
||||||
localStorage.getItem("qiniuConfig")
|
localStorage.getItem("qiniuConfig")
|
||||||
);
|
);
|
||||||
const token = getQiniuToken(accessKey, secretKey, {
|
const token = getQiniuToken(accessKey, secretKey, {
|
||||||
scope: bucket,
|
scope: bucket,
|
||||||
deadline: Math.trunc(new Date().getTime() / 1000) + 3600,
|
deadline: Math.trunc(new Date().getTime() / 1000) + 3600,
|
||||||
});
|
});
|
||||||
const dir = path ? `${path}/` : "";
|
const dir = path ? `${path}/` : "";
|
||||||
const dateFilename = dir + getDateFilename(file.name);
|
const dateFilename = dir + getDateFilename(file.name);
|
||||||
const observable = qiniu.upload(file, dateFilename, token, {}, { region });
|
const observable = qiniu.upload(file, dateFilename, token, {}, { region });
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
observable.subscribe({
|
observable.subscribe({
|
||||||
next: (result) => {
|
next: (result) => {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
reject(err.message);
|
reject(err.message);
|
||||||
},
|
},
|
||||||
complete: (result) => {
|
complete: (result) => {
|
||||||
resolve(`${domain}/${result.key}`);
|
resolve(`${domain}/${result.key}`);
|
||||||
},
|
},
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -158,30 +158,30 @@ async function qiniuUpload(file) {
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
async function aliOSSFileUpload(content, filename) {
|
async function aliOSSFileUpload(content, filename) {
|
||||||
const dateFilename = getDateFilename(filename);
|
const dateFilename = getDateFilename(filename);
|
||||||
const {
|
const {
|
||||||
region,
|
region,
|
||||||
bucket,
|
bucket,
|
||||||
accessKeyId,
|
accessKeyId,
|
||||||
accessKeySecret,
|
accessKeySecret,
|
||||||
cdnHost,
|
cdnHost,
|
||||||
path,
|
path,
|
||||||
} = JSON.parse(localStorage.getItem("aliOSSConfig"));
|
} = JSON.parse(localStorage.getItem("aliOSSConfig"));
|
||||||
const buffer = Buffer(content, "base64");
|
const buffer = Buffer(content, "base64");
|
||||||
const dir = `${path}/${dateFilename}`;
|
const dir = `${path}/${dateFilename}`;
|
||||||
const client = new OSS({
|
const client = new OSS({
|
||||||
region,
|
region,
|
||||||
bucket,
|
bucket,
|
||||||
accessKeyId,
|
accessKeyId,
|
||||||
accessKeySecret,
|
accessKeySecret,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const res = await client.put(dir, buffer);
|
const res = await client.put(dir, buffer);
|
||||||
if (cdnHost == "") return res.url;
|
if (cdnHost == "") return res.url;
|
||||||
return `${cdnHost}/${path == "" ? dateFilename : dir}`;
|
return `${cdnHost}/${path == "" ? dateFilename : dir}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -189,61 +189,61 @@ async function aliOSSFileUpload(content, filename) {
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
async function txCOSFileUpload(file) {
|
async function txCOSFileUpload(file) {
|
||||||
const dateFilename = getDateFilename(file.name);
|
const dateFilename = getDateFilename(file.name);
|
||||||
const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse(
|
const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse(
|
||||||
localStorage.getItem("txCOSConfig")
|
localStorage.getItem("txCOSConfig")
|
||||||
|
);
|
||||||
|
const cos = new COS({
|
||||||
|
SecretId: secretId,
|
||||||
|
SecretKey: secretKey,
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
cos.putObject(
|
||||||
|
{
|
||||||
|
Bucket: bucket,
|
||||||
|
Region: region,
|
||||||
|
Key: `${path}/${dateFilename}`,
|
||||||
|
Body: file,
|
||||||
|
},
|
||||||
|
function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else if (cdnHost) {
|
||||||
|
resolve(
|
||||||
|
path == ""
|
||||||
|
? `${cdnHost}/${dateFilename}`
|
||||||
|
: `${cdnHost}/${path}/${dateFilename}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve(`https://${data.Location}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const cos = new COS({
|
});
|
||||||
SecretId: secretId,
|
|
||||||
SecretKey: secretKey,
|
|
||||||
});
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
cos.putObject(
|
|
||||||
{
|
|
||||||
Bucket: bucket,
|
|
||||||
Region: region,
|
|
||||||
Key: `${path}/${dateFilename}`,
|
|
||||||
Body: file,
|
|
||||||
},
|
|
||||||
function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else if (cdnHost) {
|
|
||||||
resolve(
|
|
||||||
path == ""
|
|
||||||
? `${cdnHost}/${dateFilename}`
|
|
||||||
: `${cdnHost}/${path}/${dateFilename}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resolve(`https://${data.Location}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileUpload(content, file) {
|
function fileUpload(content, file) {
|
||||||
const imgHost = localStorage.getItem("imgHost");
|
const imgHost = localStorage.getItem("imgHost");
|
||||||
!imgHost && localStorage.setItem("imgHost", "default");
|
!imgHost && localStorage.setItem("imgHost", "default");
|
||||||
switch (imgHost) {
|
switch (imgHost) {
|
||||||
case "aliOSS":
|
case "aliOSS":
|
||||||
return aliOSSFileUpload(content, file.name);
|
return aliOSSFileUpload(content, file.name);
|
||||||
case "txCOS":
|
case "txCOS":
|
||||||
return txCOSFileUpload(file);
|
return txCOSFileUpload(file);
|
||||||
case "qiniu":
|
case "qiniu":
|
||||||
return qiniuUpload(file);
|
return qiniuUpload(file);
|
||||||
case "gitee":
|
case "gitee":
|
||||||
return giteeUpload(content, file.name);
|
return giteeUpload(content, file.name);
|
||||||
case "github":
|
case "github":
|
||||||
return ghFileUpload(content, file.name);
|
return ghFileUpload(content, file.name);
|
||||||
default:
|
default:
|
||||||
// return file.size / 1024 < 1024
|
// return file.size / 1024 < 1024
|
||||||
// ? giteeUpload(content, file.name)
|
// ? giteeUpload(content, file.name)
|
||||||
// : ghFileUpload(content, file.name);
|
// : ghFileUpload(content, file.name);
|
||||||
return ghFileUpload(content, file.name);
|
return ghFileUpload(content, file.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fileUpload,
|
fileUpload,
|
||||||
};
|
};
|
||||||
|
@ -41,14 +41,6 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top {
|
|
||||||
height: 60px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.web-title {
|
.web-title {
|
||||||
margin: 0 15px 0 5px;
|
margin: 0 15px 0 5px;
|
||||||
}
|
}
|
||||||
@ -75,7 +67,6 @@ section {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctrl {
|
.ctrl {
|
||||||
@ -92,8 +83,8 @@ section {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: scroll;
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-section {
|
.main-section {
|
||||||
@ -107,6 +98,7 @@ section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
|
position: relative;
|
||||||
margin: 0 -20px;
|
margin: 0 -20px;
|
||||||
width: 375px;
|
width: 375px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@ -123,31 +115,6 @@ section {
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
.preview table tr:nth-child(even){
|
|
||||||
background: rgb(250, 250, 250);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
.select-item-left {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-item-right {
|
|
||||||
float: right;
|
|
||||||
color: #8492a6;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
height: 100% !important;
|
|
||||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 20px;
|
|
||||||
width: 100% !important;
|
|
||||||
font-family: "PingFang SC", BlinkMacSystemFont, Roboto, "Helvetica Neue",
|
|
||||||
sans-serif !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ele ui */
|
/* ele ui */
|
||||||
.el-form-item {
|
.el-form-item {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
@ -157,33 +124,10 @@ section {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
uni-page-body,
|
||||||
width: 6px;
|
uni-page-refresh {
|
||||||
height: 6px;
|
display: block;
|
||||||
background-color: #fff;
|
box-sizing: border-box;
|
||||||
}
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: rgba(200, 200, 200, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: rgba(144, 146, 152, 0.5);
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: rgba(144, 146, 152, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-vscrollbar:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-scroll,
|
|
||||||
.preview-wrapper {
|
|
||||||
overflow: unset;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.cm-s-style-mirror.CodeMirror {
|
.cm-s-style-mirror.CodeMirror {
|
||||||
background: #f5f5f5;
|
|
||||||
color: #444;
|
color: #444;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 20px;
|
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-s-style-mirror .CodeMirror-scroll {
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden!important;
|
||||||
|
overflow-y: scroll!important;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-s-style-mirror div.CodeMirror-selected {
|
.cm-s-style-mirror div.CodeMirror-selected {
|
||||||
background: #e0e0e0;
|
background: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
@ -129,3 +129,32 @@
|
|||||||
background-color: @nightCodeMirrorColor;
|
background-color: @nightCodeMirrorColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
padding-bottom: 0;
|
||||||
|
height: 100% !important;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "PingFang SC", BlinkMacSystemFont, Roboto, "Helvetica Neue",
|
||||||
|
sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-vscrollbar:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
padding: 20px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-vscrollbar {
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeMirror-wrapper {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
@ -1,74 +1,74 @@
|
|||||||
export default {
|
export default {
|
||||||
builtinFonts: [
|
builtinFonts: [
|
||||||
{
|
{
|
||||||
label: "无衬线",
|
label: "无衬线",
|
||||||
value:
|
value:
|
||||||
"-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif",
|
"-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif",
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "衬线",
|
|
||||||
value:
|
|
||||||
"Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sizeOption: [
|
|
||||||
{
|
|
||||||
label: "12px",
|
|
||||||
value: "12px",
|
|
||||||
desc: "更小",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "13px",
|
|
||||||
value: "13px",
|
|
||||||
desc: "稍小",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "14px",
|
|
||||||
value: "14px",
|
|
||||||
desc: "推荐",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "15px",
|
|
||||||
value: "15px",
|
|
||||||
desc: "稍大",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "16px",
|
|
||||||
value: "16px",
|
|
||||||
desc: "更大",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
colorOption: [
|
|
||||||
{
|
|
||||||
label: "经典蓝",
|
|
||||||
value: "rgba(15, 76, 129, 1)",
|
|
||||||
desc: "最新流行",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "翡翠绿",
|
|
||||||
value: "rgba(0, 152, 116, 1)",
|
|
||||||
desc: "优雅清新",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "活力橘",
|
|
||||||
value: "rgba(250, 81, 81, 1)",
|
|
||||||
desc: "热情活泼",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
codeThemeOption: [
|
|
||||||
{
|
|
||||||
label: "微信",
|
|
||||||
value: "wechat",
|
|
||||||
desc: "默认样式",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "GitHub",
|
|
||||||
value: "github",
|
|
||||||
desc: "精简风格",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
form: {
|
|
||||||
rows: 1,
|
|
||||||
cols: 1,
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "衬线",
|
||||||
|
value:
|
||||||
|
"Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sizeOption: [
|
||||||
|
{
|
||||||
|
label: "12px",
|
||||||
|
value: "12px",
|
||||||
|
desc: "更小",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "13px",
|
||||||
|
value: "13px",
|
||||||
|
desc: "稍小",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "14px",
|
||||||
|
value: "14px",
|
||||||
|
desc: "推荐",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "15px",
|
||||||
|
value: "15px",
|
||||||
|
desc: "稍大",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "16px",
|
||||||
|
value: "16px",
|
||||||
|
desc: "更大",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
colorOption: [
|
||||||
|
{
|
||||||
|
label: "经典蓝",
|
||||||
|
value: "rgba(15, 76, 129, 1)",
|
||||||
|
desc: "最新流行",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "翡翠绿",
|
||||||
|
value: "rgba(0, 152, 116, 1)",
|
||||||
|
desc: "优雅清新",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "活力橘",
|
||||||
|
value: "rgba(250, 81, 81, 1)",
|
||||||
|
desc: "热情活泼",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
codeThemeOption: [
|
||||||
|
{
|
||||||
|
label: "微信",
|
||||||
|
value: "wechat",
|
||||||
|
desc: "默认样式",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "GitHub",
|
||||||
|
value: "github",
|
||||||
|
desc: "精简风格",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
form: {
|
||||||
|
rows: 1,
|
||||||
|
cols: 1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import juice from "juice";
|
import juice from "juice";
|
||||||
|
|
||||||
export function solveWeChatImage() {
|
export function solveWeChatImage() {
|
||||||
const clipboardDiv = document.getElementById("output");
|
const clipboardDiv = document.getElementById("output");
|
||||||
const images = clipboardDiv.getElementsByTagName("img");
|
const images = clipboardDiv.getElementsByTagName("img");
|
||||||
for (let i = 0; i < images.length; i++) {
|
for (let i = 0; i < images.length; i++) {
|
||||||
const image = images[i];
|
const image = images[i];
|
||||||
const width = image.getAttribute("width");
|
const width = image.getAttribute("width");
|
||||||
const height = image.getAttribute("height");
|
const height = image.getAttribute("height");
|
||||||
image.removeAttribute("width");
|
image.removeAttribute("width");
|
||||||
image.removeAttribute("height");
|
image.removeAttribute("height");
|
||||||
image.style.width = width;
|
image.style.width = width;
|
||||||
image.style.height = height;
|
image.style.height = height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function solveHtml() {
|
export function solveHtml() {
|
||||||
const element = document.getElementById("output-wrapper");
|
const element = document.getElementById("output-wrapper");
|
||||||
let html = element.innerHTML;
|
let html = element.innerHTML;
|
||||||
let res = "";
|
let res = "";
|
||||||
res = juice.inlineContent(html, {
|
res = juice.inlineContent(html, {
|
||||||
inlinePseudoElements: true,
|
inlinePseudoElements: true,
|
||||||
preserveImportant: true,
|
preserveImportant: true,
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
import marked from "marked";
|
import marked from "marked";
|
||||||
class WxRenderer {
|
class WxRenderer {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
let ENV_STRETCH_IMAGE = true;
|
let ENV_STRETCH_IMAGE = true;
|
||||||
|
|
||||||
let footnotes = [];
|
let footnotes = [];
|
||||||
let footnoteIndex = 0;
|
let footnoteIndex = 0;
|
||||||
let styleMapping = null;
|
let styleMapping = null;
|
||||||
|
|
||||||
const CODE_FONT_FAMILY =
|
const CODE_FONT_FAMILY =
|
||||||
"Menlo, Operator Mono, Consolas, Monaco, monospace";
|
"Menlo, Operator Mono, Consolas, Monaco, monospace";
|
||||||
|
|
||||||
let merge = (base, extend) => Object.assign({}, base, extend);
|
let merge = (base, extend) => Object.assign({}, base, extend);
|
||||||
|
|
||||||
this.buildTheme = (themeTpl) => {
|
this.buildTheme = (themeTpl) => {
|
||||||
let mapping = {};
|
let mapping = {};
|
||||||
let base = merge(themeTpl.BASE, {
|
let base = merge(themeTpl.BASE, {
|
||||||
"font-family": this.opts.fonts,
|
"font-family": this.opts.fonts,
|
||||||
"font-size": this.opts.size,
|
"font-size": this.opts.size,
|
||||||
});
|
});
|
||||||
let base_block = merge(base, {});
|
let base_block = merge(base, {});
|
||||||
for (let ele in themeTpl.inline) {
|
for (let ele in themeTpl.inline) {
|
||||||
if (themeTpl.inline.hasOwnProperty(ele)) {
|
if (themeTpl.inline.hasOwnProperty(ele)) {
|
||||||
let style = themeTpl.inline[ele];
|
let style = themeTpl.inline[ele];
|
||||||
mapping[ele] = merge(base, style);
|
mapping[ele] = merge(base, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let ele in themeTpl.block) {
|
for (let ele in themeTpl.block) {
|
||||||
if (themeTpl.block.hasOwnProperty(ele)) {
|
if (themeTpl.block.hasOwnProperty(ele)) {
|
||||||
let style = themeTpl.block[ele];
|
let style = themeTpl.block[ele];
|
||||||
if (ele === "code") {
|
if (ele === "code") {
|
||||||
style["font-family"] = CODE_FONT_FAMILY;
|
style["font-family"] = CODE_FONT_FAMILY;
|
||||||
}
|
}
|
||||||
mapping[ele] = merge(base_block, style);
|
mapping[ele] = merge(base_block, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mapping;
|
return mapping;
|
||||||
};
|
};
|
||||||
|
|
||||||
let getStyles = (tokenName, addition) => {
|
let getStyles = (tokenName, addition) => {
|
||||||
let arr = [];
|
let arr = [];
|
||||||
let dict = styleMapping[tokenName];
|
let dict = styleMapping[tokenName];
|
||||||
if (!dict) return "";
|
if (!dict) return "";
|
||||||
for (const key in dict) {
|
for (const key in dict) {
|
||||||
arr.push(key + ":" + dict[key]);
|
arr.push(key + ":" + dict[key]);
|
||||||
}
|
}
|
||||||
return `style="${arr.join(";") + (addition || "")}"`;
|
return `style="${arr.join(";") + (addition || "")}"`;
|
||||||
};
|
};
|
||||||
|
|
||||||
let addFootnote = (title, link) => {
|
let addFootnote = (title, link) => {
|
||||||
footnotes.push([++footnoteIndex, title, link]);
|
footnotes.push([++footnoteIndex, title, link]);
|
||||||
return footnoteIndex;
|
return footnoteIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.buildFootnotes = () => {
|
this.buildFootnotes = () => {
|
||||||
let footnoteArray = footnotes.map((x) => {
|
let footnoteArray = footnotes.map((x) => {
|
||||||
if (x[1] === x[2]) {
|
if (x[1] === x[2]) {
|
||||||
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code>: <i>${x[1]}</i><br/>`;
|
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code>: <i>${x[1]}</i><br/>`;
|
||||||
}
|
}
|
||||||
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code> ${x[1]}: <i>${x[2]}</i><br/>`;
|
return `<code style="font-size: 90%; opacity: 0.6;">[${x[0]}]</code> ${x[1]}: <i>${x[2]}</i><br/>`;
|
||||||
});
|
});
|
||||||
return `<h4 ${getStyles("h4")}>引用链接</h4><p ${getStyles(
|
return `<h4 ${getStyles("h4")}>引用链接</h4><p ${getStyles(
|
||||||
"footnotes"
|
"footnotes"
|
||||||
)}>${footnoteArray.join("\n")}</p>`;
|
)}>${footnoteArray.join("\n")}</p>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.buildAddition = () => {
|
this.buildAddition = () => {
|
||||||
return `
|
return `
|
||||||
<style>
|
<style>
|
||||||
.preview-wrapper pre::before {
|
.preview-wrapper pre::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -83,134 +83,122 @@ class WxRenderer {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setOptions = (newOpts) => {
|
this.setOptions = (newOpts) => {
|
||||||
this.opts = merge(this.opts, newOpts);
|
this.opts = merge(this.opts, newOpts);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hasFootnotes = () => footnotes.length !== 0;
|
this.hasFootnotes = () => footnotes.length !== 0;
|
||||||
|
|
||||||
this.getRenderer = (status) => {
|
this.getRenderer = (status) => {
|
||||||
footnotes = [];
|
footnotes = [];
|
||||||
footnoteIndex = 0;
|
footnoteIndex = 0;
|
||||||
|
|
||||||
styleMapping = this.buildTheme(this.opts.theme);
|
styleMapping = this.buildTheme(this.opts.theme);
|
||||||
let renderer = new marked.Renderer();
|
let renderer = new marked.Renderer();
|
||||||
|
|
||||||
renderer.heading = (text, level) => {
|
renderer.heading = (text, level) => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 1:
|
case 1:
|
||||||
return `<h1 ${getStyles("h1")}>${text}</h1>`;
|
return `<h1 ${getStyles("h1")}>${text}</h1>`;
|
||||||
case 2:
|
case 2:
|
||||||
return `<h2 ${getStyles("h2")}>${text}</h2>`;
|
return `<h2 ${getStyles("h2")}>${text}</h2>`;
|
||||||
case 3:
|
case 3:
|
||||||
return `<h3 ${getStyles("h3")}>${text}</h3>`;
|
return `<h3 ${getStyles("h3")}>${text}</h3>`;
|
||||||
default:
|
default:
|
||||||
return `<h4 ${getStyles("h4")}>${text}</h4>`;
|
return `<h4 ${getStyles("h4")}>${text}</h4>`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
renderer.paragraph = (text) => {
|
renderer.paragraph = (text) => {
|
||||||
if (
|
if (text.indexOf("<figure") != -1 && text.indexOf("<img") != -1) {
|
||||||
text.indexOf("<figure") != -1 &&
|
return text;
|
||||||
text.indexOf("<img") != -1
|
}
|
||||||
) {
|
return text.replace(/ /g, "") === ""
|
||||||
return text;
|
? ""
|
||||||
}
|
: `<p ${getStyles("p")}>${text}</p>`;
|
||||||
return text.replace(/ /g, "") === ""
|
};
|
||||||
? ""
|
|
||||||
: `<p ${getStyles("p")}>${text}</p>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderer.blockquote = (text) => {
|
renderer.blockquote = (text) => {
|
||||||
text = text.replace(
|
text = text.replace(/<p.*?>/g, `<p ${getStyles("blockquote_p")}>`);
|
||||||
/<p.*?>/g,
|
return `<blockquote ${getStyles("blockquote")}>${text}</blockquote>`;
|
||||||
`<p ${getStyles("blockquote_p")}>`
|
};
|
||||||
);
|
renderer.code = (text, lang) => {
|
||||||
return `<blockquote ${getStyles(
|
text = text.replace(/</g, "<").replace(/>/g, ">");
|
||||||
"blockquote"
|
const codeLines = text
|
||||||
)}>${text}</blockquote>`;
|
.split("\n")
|
||||||
};
|
.map(
|
||||||
renderer.code = (text, lang) => {
|
(line) =>
|
||||||
text = text.replace(/</g, "<").replace(/>/g, ">");
|
`<code class="prettyprint"><span class="code-snippet_outer">${
|
||||||
const codeLines = text
|
line || "<br>"
|
||||||
.split("\n")
|
}</span></code>`
|
||||||
.map(
|
);
|
||||||
(line) =>
|
const codeTheme = "github";
|
||||||
`<code class="prettyprint"><span class="code-snippet_outer">${
|
return `
|
||||||
line || "<br>"
|
|
||||||
}</span></code>`
|
|
||||||
);
|
|
||||||
const codeTheme = "github";
|
|
||||||
return `
|
|
||||||
<section class="code-snippet__${codeTheme}">
|
<section class="code-snippet__${codeTheme}">
|
||||||
<pre class="code__pre" data-lang="${lang}">
|
<pre class="code__pre" data-lang="${lang}">
|
||||||
${codeLines.join("")}
|
${codeLines.join("")}
|
||||||
</pre>
|
</pre>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
renderer.codespan = (text, lang) =>
|
renderer.codespan = (text, lang) =>
|
||||||
`<code ${getStyles("codespan")}>${text}</code>`;
|
`<code ${getStyles("codespan")}>${text}</code>`;
|
||||||
renderer.listitem = (text) =>
|
renderer.listitem = (text) =>
|
||||||
`<span ${getStyles(
|
`<span ${getStyles(
|
||||||
"listitem"
|
"listitem"
|
||||||
)}><span style="margin-right: 10px;"><%s/></span>${text}</span>`;
|
)}><span style="margin-right: 10px;"><%s/></span>${text}</span>`;
|
||||||
|
|
||||||
renderer.list = (text, ordered, start) => {
|
renderer.list = (text, ordered, start) => {
|
||||||
text = text.replace(/<\/*p.*?>/g, "");
|
text = text.replace(/<\/*p.*?>/g, "");
|
||||||
let segments = text.split(`<%s/>`);
|
let segments = text.split(`<%s/>`);
|
||||||
if (!ordered) {
|
if (!ordered) {
|
||||||
text = segments.join("•");
|
text = segments.join("•");
|
||||||
return `<p ${getStyles("ul")}>${text}</p>`;
|
return `<p ${getStyles("ul")}>${text}</p>`;
|
||||||
}
|
}
|
||||||
text = segments[0];
|
text = segments[0];
|
||||||
for (let i = 1; i < segments.length; i++) {
|
for (let i = 1; i < segments.length; i++) {
|
||||||
text = text + i + "." + segments[i];
|
text = text + i + "." + segments[i];
|
||||||
}
|
}
|
||||||
return `<p ${getStyles("ol")}>${text}</p>`;
|
return `<p ${getStyles("ol")}>${text}</p>`;
|
||||||
};
|
};
|
||||||
renderer.image = (href, title, text) => {
|
renderer.image = (href, title, text) => {
|
||||||
let subText = "";
|
let subText = "";
|
||||||
if (text) {
|
if (text) {
|
||||||
subText = `<figcaption ${getStyles(
|
subText = `<figcaption ${getStyles(
|
||||||
"figcaption"
|
"figcaption"
|
||||||
)}>${text}</figcaption>`;
|
)}>${text}</figcaption>`;
|
||||||
}
|
}
|
||||||
let figureStyles = getStyles("figure");
|
let figureStyles = getStyles("figure");
|
||||||
let imgStyles = getStyles(
|
let imgStyles = getStyles(ENV_STRETCH_IMAGE ? "image" : "image_org");
|
||||||
ENV_STRETCH_IMAGE ? "image" : "image_org"
|
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`;
|
||||||
);
|
};
|
||||||
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`;
|
renderer.link = (href, title, text) => {
|
||||||
};
|
if (href.startsWith("https://mp.weixin.qq.com")) {
|
||||||
renderer.link = (href, title, text) => {
|
return `<a href="${href}" title="${title || text}" ${getStyles(
|
||||||
if (href.startsWith("https://mp.weixin.qq.com")) {
|
"wx_link"
|
||||||
return `<a href="${href}" title="${
|
)}>${text}</a>`;
|
||||||
title || text
|
}
|
||||||
}" ${getStyles("wx_link")}>${text}</a>`;
|
if (href === text || !status) {
|
||||||
}
|
return text;
|
||||||
if (href === text || !status) {
|
}
|
||||||
return text;
|
let ref = addFootnote(title || text, href);
|
||||||
}
|
return `<span ${getStyles("link")}>${text}<sup>[${ref}]</sup></span>`;
|
||||||
let ref = addFootnote(title || text, href);
|
};
|
||||||
return `<span ${getStyles(
|
renderer.strong = (text) =>
|
||||||
"link"
|
`<strong ${getStyles("strong")}>${text}</strong>`;
|
||||||
)}>${text}<sup>[${ref}]</sup></span>`;
|
renderer.em = (text) =>
|
||||||
};
|
`<span style="font-style: italic;">${text}</span>`;
|
||||||
renderer.strong = (text) =>
|
renderer.table = (header, body) =>
|
||||||
`<strong ${getStyles("strong")}>${text}</strong>`;
|
`<section style="padding:0 8px;"><table class="preview-table"><thead ${getStyles(
|
||||||
renderer.em = (text) =>
|
"thead"
|
||||||
`<span style="font-style: italic;">${text}</span>`;
|
)}>${header}</thead><tbody>${body}</tbody></table></section>`;
|
||||||
renderer.table = (header, body) =>
|
renderer.tablecell = (text, flags) =>
|
||||||
`<section style="padding:0 8px;"><table class="preview-table"><thead ${getStyles(
|
`<td ${getStyles("td")}>${text}</td>`;
|
||||||
"thead"
|
renderer.hr = () =>
|
||||||
)}>${header}</thead><tbody>${body}</tbody></table></section>`;
|
`<hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);">`;
|
||||||
renderer.tablecell = (text, flags) =>
|
return renderer;
|
||||||
`<td ${getStyles("td")}>${text}</td>`;
|
};
|
||||||
renderer.hr = () =>
|
}
|
||||||
`<hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);">`;
|
|
||||||
return renderer;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export default WxRenderer;
|
export default WxRenderer;
|
||||||
|
@ -1,178 +1,178 @@
|
|||||||
export default {
|
export default {
|
||||||
BASE: {
|
BASE: {
|
||||||
"text-align": "left",
|
"text-align": "left",
|
||||||
color: "#3f3f3f",
|
color: "#3f3f3f",
|
||||||
"line-height": "1.75",
|
"line-height": "1.75",
|
||||||
|
},
|
||||||
|
BASE_BLOCK: {
|
||||||
|
margin: "1em 8px",
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
// 一级标题样式
|
||||||
|
h1: {
|
||||||
|
"font-size": "1.2em",
|
||||||
|
"text-align": "center",
|
||||||
|
"font-weight": "bold",
|
||||||
|
display: "table",
|
||||||
|
margin: "2em auto 1em",
|
||||||
|
padding: "0 1em",
|
||||||
|
"border-bottom": "2px solid rgba(0, 152, 116, 0.9)",
|
||||||
},
|
},
|
||||||
BASE_BLOCK: {
|
|
||||||
margin: "1em 8px",
|
// 二级标题样式
|
||||||
|
h2: {
|
||||||
|
"font-size": "1.2em",
|
||||||
|
"text-align": "center",
|
||||||
|
"font-weight": "bold",
|
||||||
|
display: "table",
|
||||||
|
margin: "4em auto 2em",
|
||||||
|
padding: "0 0.2em",
|
||||||
|
background: "rgba(0, 152, 116, 0.9)",
|
||||||
|
color: "#fff",
|
||||||
},
|
},
|
||||||
block: {
|
|
||||||
// 一级标题样式
|
|
||||||
h1: {
|
|
||||||
"font-size": "1.2em",
|
|
||||||
"text-align": "center",
|
|
||||||
"font-weight": "bold",
|
|
||||||
display: "table",
|
|
||||||
margin: "2em auto 1em",
|
|
||||||
padding: "0 1em",
|
|
||||||
"border-bottom": "2px solid rgba(0, 152, 116, 0.9)",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 二级标题样式
|
// 三级标题样式
|
||||||
h2: {
|
h3: {
|
||||||
"font-size": "1.2em",
|
"font-weight": "bold",
|
||||||
"text-align": "center",
|
"font-size": "1.1em",
|
||||||
"font-weight": "bold",
|
margin: "2em 8px 0.75em 0",
|
||||||
display: "table",
|
"line-height": "1.2",
|
||||||
margin: "4em auto 2em",
|
"padding-left": "8px",
|
||||||
padding: "0 0.2em",
|
"border-left": "3px solid rgba(0, 152, 116, 0.9)",
|
||||||
background: "rgba(0, 152, 116, 0.9)",
|
|
||||||
color: "#fff",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 三级标题样式
|
|
||||||
h3: {
|
|
||||||
"font-weight": "bold",
|
|
||||||
"font-size": "1.1em",
|
|
||||||
margin: "2em 8px 0.75em 0",
|
|
||||||
"line-height": "1.2",
|
|
||||||
"padding-left": "8px",
|
|
||||||
"border-left": "3px solid rgba(0, 152, 116, 0.9)",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 四级标题样式
|
|
||||||
h4: {
|
|
||||||
"font-weight": "bold",
|
|
||||||
"font-size": "1em",
|
|
||||||
margin: "2em 8px 0.5em",
|
|
||||||
color: "rgba(66, 185, 131, 0.9)",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 段落样式
|
|
||||||
p: {
|
|
||||||
margin: "1.5em 8px",
|
|
||||||
"letter-spacing": "0.1em",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 引用样式
|
|
||||||
blockquote: {
|
|
||||||
"font-style": "normal",
|
|
||||||
"border-left": "none",
|
|
||||||
padding: "1em",
|
|
||||||
"border-radius": "4px",
|
|
||||||
color: "#FEEEED",
|
|
||||||
background: "rgba(27,31,35,.05)",
|
|
||||||
margin: "2em 8px",
|
|
||||||
},
|
|
||||||
|
|
||||||
blockquote_p: {
|
|
||||||
"letter-spacing": "0.1em",
|
|
||||||
color: "rgb(80, 80, 80)",
|
|
||||||
"font-size": "1em",
|
|
||||||
display: "block",
|
|
||||||
},
|
|
||||||
|
|
||||||
code: {
|
|
||||||
"font-size": "80%",
|
|
||||||
overflow: "auto",
|
|
||||||
color: "#333",
|
|
||||||
"white-space": "pre",
|
|
||||||
background: "rgb(247, 247, 247)",
|
|
||||||
"border-radius": "2px",
|
|
||||||
padding: "10px",
|
|
||||||
"line-height": "1.5",
|
|
||||||
border: "1px solid rgb(236,236,236)",
|
|
||||||
margin: "20px 0",
|
|
||||||
},
|
|
||||||
|
|
||||||
image: {
|
|
||||||
"border-radius": "4px",
|
|
||||||
display: "block",
|
|
||||||
margin: "0.1em auto 0.5em",
|
|
||||||
width: "100% !important",
|
|
||||||
},
|
|
||||||
|
|
||||||
image_org: {
|
|
||||||
"border-radius": "4px",
|
|
||||||
display: "block",
|
|
||||||
},
|
|
||||||
|
|
||||||
ol: {
|
|
||||||
"margin-left": "0",
|
|
||||||
"padding-left": "1em",
|
|
||||||
},
|
|
||||||
|
|
||||||
ul: {
|
|
||||||
"margin-left": "0",
|
|
||||||
"padding-left": "1em",
|
|
||||||
"list-style": "circle",
|
|
||||||
},
|
|
||||||
|
|
||||||
footnotes: {
|
|
||||||
margin: "0.5em 8px",
|
|
||||||
"font-size": "80%",
|
|
||||||
},
|
|
||||||
|
|
||||||
figure: {
|
|
||||||
margin: "1.5em 8px",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
inline: {
|
|
||||||
listitem: {
|
|
||||||
"text-indent": "-1em",
|
|
||||||
display: "block",
|
|
||||||
margin: "0.2em 8px",
|
|
||||||
},
|
|
||||||
|
|
||||||
codespan: {
|
// 四级标题样式
|
||||||
"font-size": "90%",
|
h4: {
|
||||||
"white-space": "pre",
|
"font-weight": "bold",
|
||||||
color: "#d14",
|
"font-size": "1em",
|
||||||
background: "rgba(27,31,35,.05)",
|
margin: "2em 8px 0.5em",
|
||||||
padding: "3px 5px",
|
color: "rgba(66, 185, 131, 0.9)",
|
||||||
"border-radius": "4px",
|
|
||||||
},
|
|
||||||
|
|
||||||
link: {
|
|
||||||
color: "#576b95",
|
|
||||||
},
|
|
||||||
|
|
||||||
wx_link: {
|
|
||||||
color: "#576b95",
|
|
||||||
"text-decoration": "none",
|
|
||||||
},
|
|
||||||
|
|
||||||
// 字体加粗样式
|
|
||||||
strong: {
|
|
||||||
color: "rgba(15, 76, 129, 0.9)",
|
|
||||||
"font-weight": "bold",
|
|
||||||
},
|
|
||||||
|
|
||||||
table: {
|
|
||||||
"border-collapse": "collapse",
|
|
||||||
"text-align": "center",
|
|
||||||
margin: "1em 8px",
|
|
||||||
},
|
|
||||||
|
|
||||||
thead: {
|
|
||||||
background: "rgba(0, 0, 0, 0.05)",
|
|
||||||
"font-weight": "bold",
|
|
||||||
},
|
|
||||||
|
|
||||||
td: {
|
|
||||||
border: "1px solid #dfdfdf",
|
|
||||||
padding: "0.25em 0.5em",
|
|
||||||
},
|
|
||||||
|
|
||||||
footnote: {
|
|
||||||
"font-size": "12px",
|
|
||||||
},
|
|
||||||
|
|
||||||
figcaption: {
|
|
||||||
"text-align": "center",
|
|
||||||
color: "#888",
|
|
||||||
"font-size": "0.8em",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 段落样式
|
||||||
|
p: {
|
||||||
|
margin: "1.5em 8px",
|
||||||
|
"letter-spacing": "0.1em",
|
||||||
|
},
|
||||||
|
|
||||||
|
// 引用样式
|
||||||
|
blockquote: {
|
||||||
|
"font-style": "normal",
|
||||||
|
"border-left": "none",
|
||||||
|
padding: "1em",
|
||||||
|
"border-radius": "4px",
|
||||||
|
color: "#FEEEED",
|
||||||
|
background: "rgba(27,31,35,.05)",
|
||||||
|
margin: "2em 8px",
|
||||||
|
},
|
||||||
|
|
||||||
|
blockquote_p: {
|
||||||
|
"letter-spacing": "0.1em",
|
||||||
|
color: "rgb(80, 80, 80)",
|
||||||
|
"font-size": "1em",
|
||||||
|
display: "block",
|
||||||
|
},
|
||||||
|
|
||||||
|
code: {
|
||||||
|
"font-size": "80%",
|
||||||
|
overflow: "auto",
|
||||||
|
color: "#333",
|
||||||
|
"white-space": "pre",
|
||||||
|
background: "rgb(247, 247, 247)",
|
||||||
|
"border-radius": "2px",
|
||||||
|
padding: "10px",
|
||||||
|
"line-height": "1.5",
|
||||||
|
border: "1px solid rgb(236,236,236)",
|
||||||
|
margin: "20px 0",
|
||||||
|
},
|
||||||
|
|
||||||
|
image: {
|
||||||
|
"border-radius": "4px",
|
||||||
|
display: "block",
|
||||||
|
margin: "0.1em auto 0.5em",
|
||||||
|
width: "100% !important",
|
||||||
|
},
|
||||||
|
|
||||||
|
image_org: {
|
||||||
|
"border-radius": "4px",
|
||||||
|
display: "block",
|
||||||
|
},
|
||||||
|
|
||||||
|
ol: {
|
||||||
|
"margin-left": "0",
|
||||||
|
"padding-left": "1em",
|
||||||
|
},
|
||||||
|
|
||||||
|
ul: {
|
||||||
|
"margin-left": "0",
|
||||||
|
"padding-left": "1em",
|
||||||
|
"list-style": "circle",
|
||||||
|
},
|
||||||
|
|
||||||
|
footnotes: {
|
||||||
|
margin: "0.5em 8px",
|
||||||
|
"font-size": "80%",
|
||||||
|
},
|
||||||
|
|
||||||
|
figure: {
|
||||||
|
margin: "1.5em 8px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inline: {
|
||||||
|
listitem: {
|
||||||
|
"text-indent": "-1em",
|
||||||
|
display: "block",
|
||||||
|
margin: "0.2em 8px",
|
||||||
|
},
|
||||||
|
|
||||||
|
codespan: {
|
||||||
|
"font-size": "90%",
|
||||||
|
"white-space": "pre",
|
||||||
|
color: "#d14",
|
||||||
|
background: "rgba(27,31,35,.05)",
|
||||||
|
padding: "3px 5px",
|
||||||
|
"border-radius": "4px",
|
||||||
|
},
|
||||||
|
|
||||||
|
link: {
|
||||||
|
color: "#576b95",
|
||||||
|
},
|
||||||
|
|
||||||
|
wx_link: {
|
||||||
|
color: "#576b95",
|
||||||
|
"text-decoration": "none",
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字体加粗样式
|
||||||
|
strong: {
|
||||||
|
color: "rgba(15, 76, 129, 0.9)",
|
||||||
|
"font-weight": "bold",
|
||||||
|
},
|
||||||
|
|
||||||
|
table: {
|
||||||
|
"border-collapse": "collapse",
|
||||||
|
"text-align": "center",
|
||||||
|
margin: "1em 8px",
|
||||||
|
},
|
||||||
|
|
||||||
|
thead: {
|
||||||
|
background: "rgba(0, 0, 0, 0.05)",
|
||||||
|
"font-weight": "bold",
|
||||||
|
},
|
||||||
|
|
||||||
|
td: {
|
||||||
|
border: "1px solid #dfdfdf",
|
||||||
|
padding: "0.25em 0.5em",
|
||||||
|
},
|
||||||
|
|
||||||
|
footnote: {
|
||||||
|
"font-size": "12px",
|
||||||
|
},
|
||||||
|
|
||||||
|
figcaption: {
|
||||||
|
"text-align": "center",
|
||||||
|
color: "#888",
|
||||||
|
"font-size": "0.8em",
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,269 +1,265 @@
|
|||||||
export function utf16to8(str) {
|
export function utf16to8(str) {
|
||||||
var out, i, len, c;
|
var out, i, len, c;
|
||||||
out = "";
|
out = "";
|
||||||
len = str.length;
|
len = str.length;
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
c = str.charCodeAt(i);
|
c = str.charCodeAt(i);
|
||||||
if (c >= 0x0001 && c <= 0x007f) {
|
if (c >= 0x0001 && c <= 0x007f) {
|
||||||
out += str.charAt(i);
|
out += str.charAt(i);
|
||||||
} else if (c > 0x07ff) {
|
} else if (c > 0x07ff) {
|
||||||
out += String.fromCharCode(0xe0 | ((c >> 12) & 0x0f));
|
out += String.fromCharCode(0xe0 | ((c >> 12) & 0x0f));
|
||||||
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3f));
|
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3f));
|
||||||
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f));
|
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f));
|
||||||
} else {
|
} else {
|
||||||
out += String.fromCharCode(0xc0 | ((c >> 6) & 0x1f));
|
out += String.fromCharCode(0xc0 | ((c >> 6) & 0x1f));
|
||||||
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f));
|
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return out;
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function utf8to16(str) {
|
export function utf8to16(str) {
|
||||||
var out, i, len, c;
|
var out, i, len, c;
|
||||||
var char2, char3;
|
var char2, char3;
|
||||||
out = "";
|
out = "";
|
||||||
len = str.length;
|
len = str.length;
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
c = str.charCodeAt(i++);
|
c = str.charCodeAt(i++);
|
||||||
switch (c >> 4) {
|
switch (c >> 4) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
case 7:
|
case 7:
|
||||||
// 0xxxxxxx
|
// 0xxxxxxx
|
||||||
out += str.charAt(i - 1);
|
out += str.charAt(i - 1);
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
case 13:
|
case 13:
|
||||||
// 110x xxxx 10xx xxxx
|
// 110x xxxx 10xx xxxx
|
||||||
char2 = str.charCodeAt(i++);
|
char2 = str.charCodeAt(i++);
|
||||||
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
|
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||||
char2 = str.charCodeAt(i++);
|
char2 = str.charCodeAt(i++);
|
||||||
char3 = str.charCodeAt(i++);
|
char3 = str.charCodeAt(i++);
|
||||||
out += String.fromCharCode(
|
out += String.fromCharCode(
|
||||||
((c & 0x0f) << 12) |
|
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
|
||||||
((char2 & 0x3f) << 6) |
|
);
|
||||||
((char3 & 0x3f) << 0)
|
break;
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return out;
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
const base64EncodeChars =
|
const base64EncodeChars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
const base64DecodeChars = new Array(
|
const base64DecodeChars = new Array(
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
62,
|
62,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
63,
|
63,
|
||||||
52,
|
52,
|
||||||
53,
|
53,
|
||||||
54,
|
54,
|
||||||
55,
|
55,
|
||||||
56,
|
56,
|
||||||
57,
|
57,
|
||||||
58,
|
58,
|
||||||
59,
|
59,
|
||||||
60,
|
60,
|
||||||
61,
|
61,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
3,
|
3,
|
||||||
4,
|
4,
|
||||||
5,
|
5,
|
||||||
6,
|
6,
|
||||||
7,
|
7,
|
||||||
8,
|
8,
|
||||||
9,
|
9,
|
||||||
10,
|
10,
|
||||||
11,
|
11,
|
||||||
12,
|
12,
|
||||||
13,
|
13,
|
||||||
14,
|
14,
|
||||||
15,
|
15,
|
||||||
16,
|
16,
|
||||||
17,
|
17,
|
||||||
18,
|
18,
|
||||||
19,
|
19,
|
||||||
20,
|
20,
|
||||||
21,
|
21,
|
||||||
22,
|
22,
|
||||||
23,
|
23,
|
||||||
24,
|
24,
|
||||||
25,
|
25,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
26,
|
26,
|
||||||
27,
|
27,
|
||||||
28,
|
28,
|
||||||
29,
|
29,
|
||||||
30,
|
30,
|
||||||
31,
|
31,
|
||||||
32,
|
32,
|
||||||
33,
|
33,
|
||||||
34,
|
34,
|
||||||
35,
|
35,
|
||||||
36,
|
36,
|
||||||
37,
|
37,
|
||||||
38,
|
38,
|
||||||
39,
|
39,
|
||||||
40,
|
40,
|
||||||
41,
|
41,
|
||||||
42,
|
42,
|
||||||
43,
|
43,
|
||||||
44,
|
44,
|
||||||
45,
|
45,
|
||||||
46,
|
46,
|
||||||
47,
|
47,
|
||||||
48,
|
48,
|
||||||
49,
|
49,
|
||||||
50,
|
50,
|
||||||
51,
|
51,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
-1
|
-1
|
||||||
);
|
);
|
||||||
export function base64encode(str) {
|
export function base64encode(str) {
|
||||||
var out, i, len;
|
var out, i, len;
|
||||||
var c1, c2, c3;
|
var c1, c2, c3;
|
||||||
len = str.length;
|
len = str.length;
|
||||||
i = 0;
|
i = 0;
|
||||||
out = "";
|
out = "";
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
c1 = str.charCodeAt(i++) & 0xff;
|
c1 = str.charCodeAt(i++) & 0xff;
|
||||||
if (i == len) {
|
if (i == len) {
|
||||||
out += base64EncodeChars.charAt(c1 >> 2);
|
out += base64EncodeChars.charAt(c1 >> 2);
|
||||||
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
|
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
|
||||||
out += "==";
|
out += "==";
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
c2 = str.charCodeAt(i++);
|
|
||||||
if (i == len) {
|
|
||||||
out += base64EncodeChars.charAt(c1 >> 2);
|
|
||||||
out += base64EncodeChars.charAt(
|
|
||||||
((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)
|
|
||||||
);
|
|
||||||
out += base64EncodeChars.charAt((c2 & 0xf) << 2);
|
|
||||||
out += "=";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c3 = str.charCodeAt(i++);
|
|
||||||
out += base64EncodeChars.charAt(c1 >> 2);
|
|
||||||
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
|
|
||||||
out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6));
|
|
||||||
out += base64EncodeChars.charAt(c3 & 0x3f);
|
|
||||||
}
|
}
|
||||||
return out;
|
c2 = str.charCodeAt(i++);
|
||||||
|
if (i == len) {
|
||||||
|
out += base64EncodeChars.charAt(c1 >> 2);
|
||||||
|
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
|
||||||
|
out += base64EncodeChars.charAt((c2 & 0xf) << 2);
|
||||||
|
out += "=";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c3 = str.charCodeAt(i++);
|
||||||
|
out += base64EncodeChars.charAt(c1 >> 2);
|
||||||
|
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
|
||||||
|
out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6));
|
||||||
|
out += base64EncodeChars.charAt(c3 & 0x3f);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64decode(str) {
|
export function base64decode(str) {
|
||||||
var c1, c2, c3, c4;
|
var c1, c2, c3, c4;
|
||||||
var i, len, out;
|
var i, len, out;
|
||||||
len = str.length;
|
len = str.length;
|
||||||
i = 0;
|
i = 0;
|
||||||
out = "";
|
out = "";
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
/* c1 */
|
/* c1 */
|
||||||
do {
|
do {
|
||||||
c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
||||||
} while (i < len && c1 == -1);
|
} while (i < len && c1 == -1);
|
||||||
if (c1 == -1) break;
|
if (c1 == -1) break;
|
||||||
/* c2 */
|
/* c2 */
|
||||||
do {
|
do {
|
||||||
c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
||||||
} while (i < len && c2 == -1);
|
} while (i < len && c2 == -1);
|
||||||
if (c2 == -1) break;
|
if (c2 == -1) break;
|
||||||
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
|
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
|
||||||
/* c3 */
|
/* c3 */
|
||||||
do {
|
do {
|
||||||
c3 = str.charCodeAt(i++) & 0xff;
|
c3 = str.charCodeAt(i++) & 0xff;
|
||||||
if (c3 == 61) return out;
|
if (c3 == 61) return out;
|
||||||
c3 = base64DecodeChars[c3];
|
c3 = base64DecodeChars[c3];
|
||||||
} while (i < len && c3 == -1);
|
} while (i < len && c3 == -1);
|
||||||
if (c3 == -1) break;
|
if (c3 == -1) break;
|
||||||
out += String.fromCharCode(((c2 & 0xf) << 4) | ((c3 & 0x3c) >> 2));
|
out += String.fromCharCode(((c2 & 0xf) << 4) | ((c3 & 0x3c) >> 2));
|
||||||
/* c4 */
|
/* c4 */
|
||||||
do {
|
do {
|
||||||
c4 = str.charCodeAt(i++) & 0xff;
|
c4 = str.charCodeAt(i++) & 0xff;
|
||||||
if (c4 == 61) return out;
|
if (c4 == 61) return out;
|
||||||
c4 = base64DecodeChars[c4];
|
c4 = base64DecodeChars[c4];
|
||||||
} while (i < len && c4 == -1);
|
} while (i < len && c4 == -1);
|
||||||
if (c4 == -1) break;
|
if (c4 == -1) break;
|
||||||
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
|
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function safe64(base64) {
|
export function safe64(base64) {
|
||||||
base64 = base64.replace(/\+/g, "-");
|
base64 = base64.replace(/\+/g, "-");
|
||||||
base64 = base64.replace(/\//g, "_");
|
base64 = base64.replace(/\//g, "_");
|
||||||
return base64;
|
return base64;
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,7 @@ import prettierCss from "prettier/parser-postcss";
|
|||||||
|
|
||||||
// 设置自定义颜色
|
// 设置自定义颜色
|
||||||
export function setColorWithTemplate(template) {
|
export function setColorWithTemplate(template) {
|
||||||
return function (color) {
|
return function (color) {
|
||||||
let custom_theme = JSON.parse(JSON.stringify(template));
|
|
||||||
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
|
||||||
custom_theme.block.h2["background"] = color;
|
|
||||||
custom_theme.block.h3["border-left"] = `3px solid ${color}`;
|
|
||||||
custom_theme.block.h4["color"] = color;
|
|
||||||
custom_theme.inline.strong["color"] = color;
|
|
||||||
return custom_theme;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setColorWithCustomTemplate = function setColorWithCustomTemplate(
|
|
||||||
template,
|
|
||||||
color
|
|
||||||
) {
|
|
||||||
let custom_theme = JSON.parse(JSON.stringify(template));
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
||||||
custom_theme.block.h2["background"] = color;
|
custom_theme.block.h2["background"] = color;
|
||||||
@ -27,69 +13,83 @@ export const setColorWithCustomTemplate = function setColorWithCustomTemplate(
|
|||||||
custom_theme.block.h4["color"] = color;
|
custom_theme.block.h4["color"] = color;
|
||||||
custom_theme.inline.strong["color"] = color;
|
custom_theme.inline.strong["color"] = color;
|
||||||
return custom_theme;
|
return custom_theme;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setColorWithCustomTemplate = function setColorWithCustomTemplate(
|
||||||
|
template,
|
||||||
|
color
|
||||||
|
) {
|
||||||
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
|
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
||||||
|
custom_theme.block.h2["background"] = color;
|
||||||
|
custom_theme.block.h3["border-left"] = `3px solid ${color}`;
|
||||||
|
custom_theme.block.h4["color"] = color;
|
||||||
|
custom_theme.inline.strong["color"] = color;
|
||||||
|
return custom_theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 设置自定义字体大小
|
// 设置自定义字体大小
|
||||||
export function setFontSizeWithTemplate(template) {
|
export function setFontSizeWithTemplate(template) {
|
||||||
return function (fontSize) {
|
return function (fontSize) {
|
||||||
let custom_theme = JSON.parse(JSON.stringify(template));
|
let custom_theme = JSON.parse(JSON.stringify(template));
|
||||||
custom_theme.block.h1["font-size"] = `${fontSize * 1.14}px`;
|
custom_theme.block.h1["font-size"] = `${fontSize * 1.14}px`;
|
||||||
custom_theme.block.h2["font-size"] = `${fontSize * 1.1}px`;
|
custom_theme.block.h2["font-size"] = `${fontSize * 1.1}px`;
|
||||||
custom_theme.block.h3["font-size"] = `${fontSize}px`;
|
custom_theme.block.h3["font-size"] = `${fontSize}px`;
|
||||||
custom_theme.block.h4["font-size"] = `${fontSize}px`;
|
custom_theme.block.h4["font-size"] = `${fontSize}px`;
|
||||||
return custom_theme;
|
return custom_theme;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setColor = setColorWithTemplate(default_theme);
|
export const setColor = setColorWithTemplate(default_theme);
|
||||||
export const setFontSize = setFontSizeWithTemplate(default_theme);
|
export const setFontSize = setFontSizeWithTemplate(default_theme);
|
||||||
|
|
||||||
export function customCssWithTemplate(jsonString, color, theme) {
|
export function customCssWithTemplate(jsonString, color, theme) {
|
||||||
let custom_theme = JSON.parse(JSON.stringify(theme));
|
let custom_theme = JSON.parse(JSON.stringify(theme));
|
||||||
// block
|
// block
|
||||||
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
custom_theme.block.h1["border-bottom"] = `2px solid ${color}`;
|
||||||
custom_theme.block.h2["background"] = color;
|
custom_theme.block.h2["background"] = color;
|
||||||
custom_theme.block.h3["border-left"] = `3px solid ${color}`;
|
custom_theme.block.h3["border-left"] = `3px solid ${color}`;
|
||||||
custom_theme.block.h4["color"] = color;
|
custom_theme.block.h4["color"] = color;
|
||||||
custom_theme.inline.strong["color"] = color;
|
custom_theme.inline.strong["color"] = color;
|
||||||
|
|
||||||
custom_theme.block.h1 = Object.assign(custom_theme.block.h1, jsonString.h1);
|
custom_theme.block.h1 = Object.assign(custom_theme.block.h1, jsonString.h1);
|
||||||
custom_theme.block.h2 = Object.assign(custom_theme.block.h2, jsonString.h2);
|
custom_theme.block.h2 = Object.assign(custom_theme.block.h2, jsonString.h2);
|
||||||
custom_theme.block.h3 = Object.assign(custom_theme.block.h3, jsonString.h3);
|
custom_theme.block.h3 = Object.assign(custom_theme.block.h3, jsonString.h3);
|
||||||
custom_theme.block.h4 = Object.assign(custom_theme.block.h4, jsonString.h4);
|
custom_theme.block.h4 = Object.assign(custom_theme.block.h4, jsonString.h4);
|
||||||
custom_theme.block.p = Object.assign(custom_theme.block.p, jsonString.p);
|
custom_theme.block.p = Object.assign(custom_theme.block.p, jsonString.p);
|
||||||
custom_theme.block.blockquote = Object.assign(
|
custom_theme.block.blockquote = Object.assign(
|
||||||
custom_theme.block.blockquote,
|
custom_theme.block.blockquote,
|
||||||
jsonString.blockquote
|
jsonString.blockquote
|
||||||
);
|
);
|
||||||
custom_theme.block.blockquote_p = Object.assign(
|
custom_theme.block.blockquote_p = Object.assign(
|
||||||
custom_theme.block.blockquote_p,
|
custom_theme.block.blockquote_p,
|
||||||
jsonString.blockquote_p
|
jsonString.blockquote_p
|
||||||
);
|
);
|
||||||
custom_theme.block.image = Object.assign(
|
custom_theme.block.image = Object.assign(
|
||||||
custom_theme.block.image,
|
custom_theme.block.image,
|
||||||
jsonString.image
|
jsonString.image
|
||||||
);
|
);
|
||||||
|
|
||||||
// inline
|
// inline
|
||||||
custom_theme.inline.strong = Object.assign(
|
custom_theme.inline.strong = Object.assign(
|
||||||
custom_theme.inline.strong,
|
custom_theme.inline.strong,
|
||||||
jsonString.strong
|
jsonString.strong
|
||||||
);
|
);
|
||||||
custom_theme.inline.codespan = Object.assign(
|
custom_theme.inline.codespan = Object.assign(
|
||||||
custom_theme.inline.codespan,
|
custom_theme.inline.codespan,
|
||||||
jsonString.codespan
|
jsonString.codespan
|
||||||
);
|
);
|
||||||
custom_theme.inline.link = Object.assign(
|
custom_theme.inline.link = Object.assign(
|
||||||
custom_theme.inline.link,
|
custom_theme.inline.link,
|
||||||
jsonString.link
|
jsonString.link
|
||||||
);
|
);
|
||||||
custom_theme.inline.wx_link = Object.assign(
|
custom_theme.inline.wx_link = Object.assign(
|
||||||
custom_theme.inline.wx_link,
|
custom_theme.inline.wx_link,
|
||||||
jsonString.wx_link
|
jsonString.wx_link
|
||||||
);
|
);
|
||||||
|
|
||||||
return custom_theme;
|
return custom_theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,81 +98,77 @@ export function customCssWithTemplate(jsonString, color, theme) {
|
|||||||
* @param {css字符串} css
|
* @param {css字符串} css
|
||||||
*/
|
*/
|
||||||
export function css2json(css) {
|
export function css2json(css) {
|
||||||
// 移除CSS所有注释
|
// 移除CSS所有注释
|
||||||
let open, close;
|
let open, close;
|
||||||
while (
|
while (
|
||||||
(open = css.indexOf("/*")) !== -1 &&
|
(open = css.indexOf("/*")) !== -1 &&
|
||||||
(close = css.indexOf("*/")) !== -1
|
(close = css.indexOf("*/")) !== -1
|
||||||
) {
|
) {
|
||||||
css = css.substring(0, open) + css.substring(close + 2);
|
css = css.substring(0, open) + css.substring(close + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化返回值
|
||||||
|
let json = {};
|
||||||
|
|
||||||
|
while (css.length > 0 && css.indexOf("{") !== -1 && css.indexOf("}") !== -1) {
|
||||||
|
// 存储第一个左/右花括号的下标
|
||||||
|
const lbracket = css.indexOf("{");
|
||||||
|
const rbracket = css.indexOf("}");
|
||||||
|
|
||||||
|
// 第一步:将声明转换为Object,如:
|
||||||
|
// `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;`
|
||||||
|
// ==>
|
||||||
|
// `{"font": "'Times New Roman' 1em", "color": "#ff0000", "margin-top": "1em"}`
|
||||||
|
|
||||||
|
// 辅助方法:将array转为object
|
||||||
|
function toObject(array) {
|
||||||
|
let ret = {};
|
||||||
|
array.forEach((e) => {
|
||||||
|
const index = e.indexOf(":");
|
||||||
|
const property = e.substring(0, index).trim();
|
||||||
|
const value = e.substring(index + 1).trim();
|
||||||
|
ret[property] = value;
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化返回值
|
// 切割声明块并移除空白符,然后放入数组中
|
||||||
let json = {};
|
let declarations = css
|
||||||
|
.substring(lbracket + 1, rbracket)
|
||||||
|
.split(";")
|
||||||
|
.map((e) => e.trim())
|
||||||
|
.filter((e) => e.length > 0); // 移除所有""空值
|
||||||
|
|
||||||
while (
|
// 转为Object对象
|
||||||
css.length > 0 &&
|
declarations = toObject(declarations);
|
||||||
css.indexOf("{") !== -1 &&
|
|
||||||
css.indexOf("}") !== -1
|
|
||||||
) {
|
|
||||||
// 存储第一个左/右花括号的下标
|
|
||||||
const lbracket = css.indexOf("{");
|
|
||||||
const rbracket = css.indexOf("}");
|
|
||||||
|
|
||||||
// 第一步:将声明转换为Object,如:
|
// 第二步:选择器处理,每个选择器会与它对应的声明相关联,如:
|
||||||
// `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;`
|
// `h1, p#bar {color: red}`
|
||||||
// ==>
|
// ==>
|
||||||
// `{"font": "'Times New Roman' 1em", "color": "#ff0000", "margin-top": "1em"}`
|
// {"h1": {color: red}, "p#bar": {color: red}}
|
||||||
|
|
||||||
// 辅助方法:将array转为object
|
let selectors = css
|
||||||
function toObject(array) {
|
.substring(0, lbracket)
|
||||||
let ret = {};
|
// 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"]
|
||||||
array.forEach((e) => {
|
.split(",")
|
||||||
const index = e.indexOf(":");
|
.map((selector) => selector.trim());
|
||||||
const property = e.substring(0, index).trim();
|
|
||||||
const value = e.substring(index + 1).trim();
|
|
||||||
ret[property] = value;
|
|
||||||
});
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切割声明块并移除空白符,然后放入数组中
|
// 迭代赋值
|
||||||
let declarations = css
|
selectors.forEach((selector) => {
|
||||||
.substring(lbracket + 1, rbracket)
|
// 若不存在,则先初始化
|
||||||
.split(";")
|
if (!json[selector]) json[selector] = {};
|
||||||
.map((e) => e.trim())
|
// 赋值到JSON
|
||||||
.filter((e) => e.length > 0); // 移除所有""空值
|
Object.keys(declarations).forEach((key) => {
|
||||||
|
json[selector][key] = declarations[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 转为Object对象
|
// 继续下个声明块
|
||||||
declarations = toObject(declarations);
|
css = css.slice(rbracket + 1).trim();
|
||||||
|
}
|
||||||
|
|
||||||
// 第二步:选择器处理,每个选择器会与它对应的声明相关联,如:
|
// 返回JSON形式的结果串
|
||||||
// `h1, p#bar {color: red}`
|
return json;
|
||||||
// ==>
|
|
||||||
// {"h1": {color: red}, "p#bar": {color: red}}
|
|
||||||
|
|
||||||
let selectors = css
|
|
||||||
.substring(0, lbracket)
|
|
||||||
// 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"]
|
|
||||||
.split(",")
|
|
||||||
.map((selector) => selector.trim());
|
|
||||||
|
|
||||||
// 迭代赋值
|
|
||||||
selectors.forEach((selector) => {
|
|
||||||
// 若不存在,则先初始化
|
|
||||||
if (!json[selector]) json[selector] = {};
|
|
||||||
// 赋值到JSON
|
|
||||||
Object.keys(declarations).forEach((key) => {
|
|
||||||
json[selector][key] = declarations[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 继续下个声明块
|
|
||||||
css = css.slice(rbracket + 1).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回JSON形式的结果串
|
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,12 +177,12 @@ export function css2json(css) {
|
|||||||
* @param {*} name
|
* @param {*} name
|
||||||
*/
|
*/
|
||||||
export function saveEditorContent(editor, name) {
|
export function saveEditorContent(editor, name) {
|
||||||
const content = editor.getValue(0);
|
const content = editor.getValue(0);
|
||||||
if (content) {
|
if (content) {
|
||||||
localStorage.setItem(name, content);
|
localStorage.setItem(name, content);
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(name);
|
localStorage.removeItem(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,11 +190,11 @@ export function saveEditorContent(editor, name) {
|
|||||||
* @param {文档内容} content
|
* @param {文档内容} content
|
||||||
*/
|
*/
|
||||||
export function formatDoc(content) {
|
export function formatDoc(content) {
|
||||||
const doc = prettier.format(content, {
|
const doc = prettier.format(content, {
|
||||||
parser: "markdown",
|
parser: "markdown",
|
||||||
plugins: [prettierMarkdown],
|
plugins: [prettierMarkdown],
|
||||||
});
|
});
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,20 +202,20 @@ export function formatDoc(content) {
|
|||||||
* @param {css内容}} content
|
* @param {css内容}} content
|
||||||
*/
|
*/
|
||||||
export function formatCss(content) {
|
export function formatCss(content) {
|
||||||
const doc = prettier.format(content, {
|
const doc = prettier.format(content, {
|
||||||
parser: "css",
|
parser: "css",
|
||||||
plugins: [prettierCss],
|
plugins: [prettierCss],
|
||||||
});
|
});
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fixCodeWhiteSpace(value = "pre") {
|
export function fixCodeWhiteSpace(value = "pre") {
|
||||||
const preDomList = document.getElementsByClassName("code__pre");
|
const preDomList = document.getElementsByClassName("code__pre");
|
||||||
if (preDomList.length > 0) {
|
if (preDomList.length > 0) {
|
||||||
preDomList.forEach((pre) => {
|
preDomList.forEach((pre) => {
|
||||||
pre.style.whiteSpace = value;
|
pre.style.whiteSpace = value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,16 +223,16 @@ export function fixCodeWhiteSpace(value = "pre") {
|
|||||||
* @param {文档内容} doc
|
* @param {文档内容} doc
|
||||||
*/
|
*/
|
||||||
export function downloadMD(doc) {
|
export function downloadMD(doc) {
|
||||||
let downLink = document.createElement("a");
|
let downLink = document.createElement("a");
|
||||||
|
|
||||||
downLink.download = "content.md";
|
downLink.download = "content.md";
|
||||||
downLink.style.display = "none";
|
downLink.style.display = "none";
|
||||||
let blob = new Blob([doc]);
|
let blob = new Blob([doc]);
|
||||||
|
|
||||||
downLink.href = URL.createObjectURL(blob);
|
downLink.href = URL.createObjectURL(blob);
|
||||||
document.body.appendChild(downLink);
|
document.body.appendChild(downLink);
|
||||||
downLink.click();
|
downLink.click();
|
||||||
document.body.removeChild(downLink);
|
document.body.removeChild(downLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,50 +242,50 @@ export function downloadMD(doc) {
|
|||||||
* @param {*} cols 列
|
* @param {*} cols 列
|
||||||
*/
|
*/
|
||||||
export function createTable({ data, rows, cols }) {
|
export function createTable({ data, rows, cols }) {
|
||||||
let table = "";
|
let table = "";
|
||||||
let currRow = [];
|
let currRow = [];
|
||||||
for (let i = 0; i < rows + 2; ++i) {
|
for (let i = 0; i < rows + 2; ++i) {
|
||||||
table += "|\t";
|
table += "|\t";
|
||||||
currRow = [];
|
currRow = [];
|
||||||
for (let j = 0; j < cols; ++j) {
|
for (let j = 0; j < cols; ++j) {
|
||||||
const rowIdx = i > 1 ? i - 1 : i;
|
const rowIdx = i > 1 ? i - 1 : i;
|
||||||
i === 1
|
i === 1
|
||||||
? currRow.push("---\t")
|
? currRow.push("---\t")
|
||||||
: currRow.push(data[`k_${rowIdx}_${j}`] || "");
|
: currRow.push(data[`k_${rowIdx}_${j}`] || "");
|
||||||
}
|
|
||||||
table += currRow.join("\t|\t");
|
|
||||||
table += "\t|\n";
|
|
||||||
}
|
}
|
||||||
|
table += currRow.join("\t|\t");
|
||||||
|
table += "\t|\n";
|
||||||
|
}
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const toBase64 = (file) =>
|
export const toBase64 = (file) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = () => resolve(reader.result.split(",").pop());
|
reader.onload = () => resolve(reader.result.split(",").pop());
|
||||||
reader.onerror = (error) => reject(error);
|
reader.onerror = (error) => reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
export function checkImage(file) {
|
export function checkImage(file) {
|
||||||
// check filename suffix
|
// check filename suffix
|
||||||
const isValidSuffix = /\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name);
|
const isValidSuffix = /\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name);
|
||||||
if (!isValidSuffix) {
|
if (!isValidSuffix) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: "请上传 JPG/PNG/GIF 格式的图片",
|
msg: "请上传 JPG/PNG/GIF 格式的图片",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// check file size
|
// check file size
|
||||||
const maxSize = 5;
|
const maxSize = 5;
|
||||||
const isLt5M = file.size / 1024 / 1024 <= maxSize;
|
const isLt5M = file.size / 1024 / 1024 <= maxSize;
|
||||||
if (!isLt5M) {
|
if (!isLt5M) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: `由于公众号限制,图片大小不能超过 ${maxSize}M`,
|
msg: `由于公众号限制,图片大小不能超过 ${maxSize}M`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="关于"
|
title="关于"
|
||||||
class="about__dialog"
|
class="about__dialog"
|
||||||
:visible="value"
|
:visible="value"
|
||||||
@close="$emit('input', false)"
|
@close="$emit('input', false)"
|
||||||
width="30%"
|
width="30%"
|
||||||
center
|
center
|
||||||
>
|
>
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<h3>一款高度简洁的微信 Markdown 编辑器</h3>
|
<h3>一款高度简洁的微信 Markdown 编辑器</h3>
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align: center; margin-top: 10px">
|
<div style="text-align: center; margin-top: 10px">
|
||||||
<p>扫码关注我的公众号,原创技术文章第一时间推送!</p>
|
<p>扫码关注我的公众号,原创技术文章第一时间推送!</p>
|
||||||
<img
|
<img
|
||||||
src="https://gitee.com/yanglbme/resource/raw/master/doocs-md/qrcode.png"
|
src="https://gitee.com/yanglbme/resource/raw/master/doocs-md/qrcode.png"
|
||||||
style="width: 40%; display: block; margin: 20px auto 10px"
|
style="width: 40%; display: block; margin: 20px auto 10px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span slot="footer" class="dialog-footer">
|
<span slot="footer" class="dialog-footer">
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="onRedirect('https://github.com/doocs/md')"
|
@click="onRedirect('https://github.com/doocs/md')"
|
||||||
plain
|
plain
|
||||||
>GitHub 仓库</el-button
|
>GitHub 仓库</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="onRedirect('https://gitee.com/doocs/md')"
|
@click="onRedirect('https://gitee.com/doocs/md')"
|
||||||
plain
|
plain
|
||||||
>Gitee 仓库</el-button
|
>Gitee 仓库</el-button
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
},
|
||||||
onRedirect(url) {
|
methods: {
|
||||||
window.open(url);
|
onRedirect(url) {
|
||||||
},
|
window.open(url);
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
/deep/ .el-dialog {
|
/deep/ .el-dialog {
|
||||||
min-width: 420px;
|
min-width: 420px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,194 +1,186 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-container class="top is-dark">
|
<el-container class="top is-dark">
|
||||||
<!-- 图片上传 -->
|
<div class="left-side">
|
||||||
<el-tooltip
|
<!-- 图片上传 -->
|
||||||
:effect="effect"
|
<el-tooltip :effect="effect" content="上传图片" placement="bottom-start">
|
||||||
content="上传图片"
|
<i
|
||||||
placement="bottom-start"
|
class="el-icon-upload"
|
||||||
|
size="medium"
|
||||||
|
@click="$emit('show-dialog-upload-img')"
|
||||||
|
></i>
|
||||||
|
</el-tooltip>
|
||||||
|
<!-- 下载文本文档 -->
|
||||||
|
<el-tooltip
|
||||||
|
class="header__item"
|
||||||
|
:effect="effect"
|
||||||
|
content="下载 Markdown 文档"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="el-icon-download"
|
||||||
|
size="medium"
|
||||||
|
@click="$emit('download')"
|
||||||
|
></i>
|
||||||
|
</el-tooltip>
|
||||||
|
<!-- 页面重置 -->
|
||||||
|
<el-tooltip
|
||||||
|
class="header__item"
|
||||||
|
:effect="effect"
|
||||||
|
content="重置页面"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="el-icon-refresh"
|
||||||
|
size="medium"
|
||||||
|
@click="showResetConfirm = true"
|
||||||
|
></i>
|
||||||
|
</el-tooltip>
|
||||||
|
<!-- 插入表格 -->
|
||||||
|
<el-tooltip
|
||||||
|
class="header__item header__item_last"
|
||||||
|
:effect="effect"
|
||||||
|
content="插入表格"
|
||||||
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="el-icon-s-grid"
|
||||||
|
size="medium"
|
||||||
|
@click="$emit('show-dialog-form')"
|
||||||
|
></i>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-select
|
||||||
|
v-model="selectFont"
|
||||||
|
size="mini"
|
||||||
|
placeholder="选择字体"
|
||||||
|
clearable
|
||||||
|
@change="fontChanged"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="font in config.builtinFonts"
|
||||||
|
:style="{ fontFamily: font.value }"
|
||||||
|
:key="font.value"
|
||||||
|
:label="font.label"
|
||||||
|
:value="font.value"
|
||||||
>
|
>
|
||||||
<i
|
<span class="select-item-left">{{ font.label }}</span>
|
||||||
class="el-icon-upload"
|
<span class="select-item-right">Abc</span>
|
||||||
size="medium"
|
</el-option>
|
||||||
@click="$emit('show-dialog-upload-img')"
|
</el-select>
|
||||||
></i>
|
<el-select
|
||||||
</el-tooltip>
|
v-model="selectSize"
|
||||||
<!-- 下载文本文档 -->
|
size="mini"
|
||||||
<el-tooltip
|
placeholder="选择段落字号"
|
||||||
class="header__item"
|
clearable
|
||||||
:effect="effect"
|
@change="sizeChanged"
|
||||||
content="下载 Markdown 文档"
|
>
|
||||||
placement="bottom-start"
|
<el-option
|
||||||
|
v-for="size in config.sizeOption"
|
||||||
|
:key="size.value"
|
||||||
|
:label="size.label"
|
||||||
|
:value="size.value"
|
||||||
>
|
>
|
||||||
<i
|
<span class="select-item-left">{{ size.label }}</span>
|
||||||
class="el-icon-download"
|
<span class="select-item-right">{{ size.desc }}</span>
|
||||||
size="medium"
|
</el-option>
|
||||||
@click="$emit('download')"
|
</el-select>
|
||||||
></i>
|
<el-select
|
||||||
</el-tooltip>
|
v-model="selectColor"
|
||||||
<!-- 页面重置 -->
|
size="mini"
|
||||||
<el-tooltip
|
placeholder="选择颜色"
|
||||||
class="header__item"
|
clearable
|
||||||
:effect="effect"
|
@change="colorChanged"
|
||||||
content="重置页面"
|
>
|
||||||
placement="bottom-start"
|
<el-option
|
||||||
|
v-for="color in config.colorOption"
|
||||||
|
:key="color.value"
|
||||||
|
:label="color.label"
|
||||||
|
:value="color.value"
|
||||||
>
|
>
|
||||||
<i
|
<span class="select-item-left">{{ color.label }}</span>
|
||||||
class="el-icon-refresh"
|
<span class="select-item-right">{{ color.desc }}</span>
|
||||||
size="medium"
|
</el-option>
|
||||||
@click="showResetConfirm = true"
|
</el-select>
|
||||||
></i>
|
<el-tooltip content="自定义颜色" :effect="effect" placement="top">
|
||||||
</el-tooltip>
|
<el-color-picker
|
||||||
<!-- 插入表格 -->
|
v-model="selectColor"
|
||||||
<el-tooltip
|
size="mini"
|
||||||
class="header__item header__item_last"
|
show-alpha
|
||||||
:effect="effect"
|
@change="colorChanged"
|
||||||
content="插入表格"
|
></el-color-picker>
|
||||||
placement="bottom-start"
|
</el-tooltip>
|
||||||
|
<el-tooltip
|
||||||
|
content="微信外链自动转为文末引用"
|
||||||
|
:effect="effect"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
class="header__switch"
|
||||||
|
v-model="citeStatus"
|
||||||
|
active-color="#67c23a"
|
||||||
|
inactive-color="#dcdfe6"
|
||||||
|
@change="statusChanged"
|
||||||
>
|
>
|
||||||
<i
|
</el-switch>
|
||||||
class="el-icon-s-grid"
|
</el-tooltip>
|
||||||
size="medium"
|
</div>
|
||||||
@click="$emit('show-dialog-form')"
|
<div class="right-side">
|
||||||
></i>
|
<el-tooltip
|
||||||
</el-tooltip>
|
class="item"
|
||||||
<el-form size="mini" class="ctrl" :inline="true">
|
:effect="effect"
|
||||||
<el-form-item>
|
content="自定义CSS样式"
|
||||||
<el-select
|
placement="left"
|
||||||
v-model="selectFont"
|
>
|
||||||
size="mini"
|
|
||||||
placeholder="选择字体"
|
|
||||||
clearable
|
|
||||||
@change="fontChanged"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="font in config.builtinFonts"
|
|
||||||
:style="{ fontFamily: font.value }"
|
|
||||||
:key="font.value"
|
|
||||||
:label="font.label"
|
|
||||||
:value="font.value"
|
|
||||||
>
|
|
||||||
<span class="select-item-left">{{ font.label }}</span>
|
|
||||||
<span class="select-item-right">Abc</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-select
|
|
||||||
v-model="selectSize"
|
|
||||||
size="mini"
|
|
||||||
placeholder="选择段落字号"
|
|
||||||
clearable
|
|
||||||
@change="sizeChanged"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="size in config.sizeOption"
|
|
||||||
:key="size.value"
|
|
||||||
:label="size.label"
|
|
||||||
:value="size.value"
|
|
||||||
>
|
|
||||||
<span class="select-item-left">{{ size.label }}</span>
|
|
||||||
<span class="select-item-right">{{ size.desc }}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-select
|
|
||||||
v-model="selectColor"
|
|
||||||
size="mini"
|
|
||||||
placeholder="选择颜色"
|
|
||||||
clearable
|
|
||||||
@change="colorChanged"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="color in config.colorOption"
|
|
||||||
:key="color.value"
|
|
||||||
:label="color.label"
|
|
||||||
:value="color.value"
|
|
||||||
>
|
|
||||||
<span class="select-item-left">{{ color.label }}</span>
|
|
||||||
<span class="select-item-right">{{ color.desc }}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-tooltip content="自定义颜色" :effect="effect" placement="top">
|
|
||||||
<el-color-picker
|
|
||||||
v-model="selectColor"
|
|
||||||
size="mini"
|
|
||||||
show-alpha
|
|
||||||
@change="colorChanged"
|
|
||||||
></el-color-picker>
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip
|
|
||||||
content="微信外链自动转为文末引用"
|
|
||||||
:effect="effect"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<el-switch
|
|
||||||
class="header__switch"
|
|
||||||
v-model="citeStatus"
|
|
||||||
active-color="#67c23a"
|
|
||||||
inactive-color="#dcdfe6"
|
|
||||||
@change="statusChanged"
|
|
||||||
>
|
|
||||||
</el-switch>
|
|
||||||
</el-tooltip>
|
|
||||||
</el-form>
|
|
||||||
<el-tooltip
|
|
||||||
class="item"
|
|
||||||
:effect="effect"
|
|
||||||
content="自定义CSS样式"
|
|
||||||
placement="left"
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
:type="btnType"
|
|
||||||
plain
|
|
||||||
size="medium"
|
|
||||||
icon="el-icon-setting"
|
|
||||||
@click="customStyle"
|
|
||||||
></el-button>
|
|
||||||
</el-tooltip>
|
|
||||||
<el-button
|
<el-button
|
||||||
:type="btnType"
|
:type="btnType"
|
||||||
plain
|
plain
|
||||||
size="medium"
|
size="medium"
|
||||||
@click="copy"
|
icon="el-icon-setting"
|
||||||
placement="bottom-start"
|
@click="customStyle"
|
||||||
>复制</el-button
|
></el-button>
|
||||||
>
|
</el-tooltip>
|
||||||
<el-button
|
<el-button
|
||||||
:type="btnType"
|
:type="btnType"
|
||||||
plain
|
plain
|
||||||
size="medium"
|
size="medium"
|
||||||
class="about"
|
@click="copy"
|
||||||
@click="$emit('show-about-dialog')"
|
placement="bottom-start"
|
||||||
>关于</el-button
|
>复制</el-button
|
||||||
>
|
>
|
||||||
<el-tooltip
|
<el-button
|
||||||
:content="btnContent"
|
:type="btnType"
|
||||||
:effect="effect"
|
plain
|
||||||
placement="bottom-start"
|
size="medium"
|
||||||
>
|
class="about"
|
||||||
<div
|
@click="$emit('show-about-dialog')"
|
||||||
class="mode__switch mode__switch_black"
|
>关于</el-button
|
||||||
v-if="nightMode"
|
>
|
||||||
@click="themeChanged"
|
<el-tooltip
|
||||||
></div>
|
:content="btnContent"
|
||||||
<div class="mode__switch" v-else @click="themeChanged"></div>
|
:effect="effect"
|
||||||
</el-tooltip>
|
placement="bottom-start"
|
||||||
<resetDialog
|
>
|
||||||
:showResetConfirm="showResetConfirm"
|
<div
|
||||||
@confirm="confirmReset"
|
class="mode__switch mode__switch_black"
|
||||||
@close="cancelReset"
|
v-if="nightMode"
|
||||||
/>
|
@click="themeChanged"
|
||||||
</el-container>
|
></div>
|
||||||
|
<div class="mode__switch" v-else @click="themeChanged"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<resetDialog
|
||||||
|
:showResetConfirm="showResetConfirm"
|
||||||
|
@confirm="confirmReset"
|
||||||
|
@close="cancelReset"
|
||||||
|
/>
|
||||||
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
downloadMD,
|
downloadMD,
|
||||||
setFontSize,
|
setFontSize,
|
||||||
fixCodeWhiteSpace,
|
fixCodeWhiteSpace,
|
||||||
setColorWithCustomTemplate,
|
setColorWithCustomTemplate,
|
||||||
} from "../../assets/scripts/util";
|
} from "../../assets/scripts/util";
|
||||||
import { solveWeChatImage, solveHtml } from "../../assets/scripts/converter";
|
import { solveWeChatImage, solveHtml } from "../../assets/scripts/converter";
|
||||||
import config from "../../assets/scripts/config";
|
import config from "../../assets/scripts/config";
|
||||||
@ -196,196 +188,226 @@ import DEFAULT_CSS_CONTENT from "../../assets/scripts/themes/default-theme-css";
|
|||||||
import resetDialog from "./resetDialog";
|
import resetDialog from "./resetDialog";
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
export default {
|
export default {
|
||||||
name: "editor-header",
|
name: "editor-header",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
config: config,
|
config: config,
|
||||||
citeStatus: false,
|
citeStatus: false,
|
||||||
showResetConfirm: false,
|
showResetConfirm: false,
|
||||||
selectFont: "",
|
selectFont: "",
|
||||||
selectSize: "",
|
selectSize: "",
|
||||||
selectColor: "",
|
selectColor: "",
|
||||||
selectCodeTheme: "github",
|
selectCodeTheme: "github",
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
resetDialog,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
effect() {
|
||||||
|
return this.nightMode ? "dark" : "light";
|
||||||
},
|
},
|
||||||
components: {
|
btnContent() {
|
||||||
resetDialog,
|
return this.nightMode ? "浅色模式" : "暗黑模式";
|
||||||
},
|
},
|
||||||
computed: {
|
btnType() {
|
||||||
effect() {
|
return this.nightMode ? "default" : "primary";
|
||||||
return this.nightMode ? "dark" : "light";
|
|
||||||
},
|
|
||||||
btnContent() {
|
|
||||||
return this.nightMode ? "浅色模式" : "暗黑模式";
|
|
||||||
},
|
|
||||||
btnType() {
|
|
||||||
return this.nightMode ? "default" : "primary";
|
|
||||||
},
|
|
||||||
...mapState({
|
|
||||||
output: (state) => state.output,
|
|
||||||
editor: (state) => state.editor,
|
|
||||||
cssEditor: (state) => state.cssEditor,
|
|
||||||
currentFont: (state) => state.currentFont,
|
|
||||||
currentSize: (state) => state.currentSize,
|
|
||||||
currentColor: (state) => state.currentColor,
|
|
||||||
codeTheme: (state) => state.codeTheme,
|
|
||||||
nightMode: (state) => state.nightMode,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
methods: {
|
...mapState({
|
||||||
fontChanged(fonts) {
|
output: (state) => state.output,
|
||||||
this.setWxRendererOptions({
|
editor: (state) => state.editor,
|
||||||
fonts: fonts,
|
cssEditor: (state) => state.cssEditor,
|
||||||
});
|
currentFont: (state) => state.currentFont,
|
||||||
this.setCurrentFont(fonts);
|
currentSize: (state) => state.currentSize,
|
||||||
this.$emit("refresh");
|
currentColor: (state) => state.currentColor,
|
||||||
},
|
codeTheme: (state) => state.codeTheme,
|
||||||
sizeChanged(size) {
|
nightMode: (state) => state.nightMode,
|
||||||
let theme = setFontSize(size.replace("px", ""));
|
}),
|
||||||
theme = setColorWithCustomTemplate(theme, this.currentColor);
|
},
|
||||||
this.setWxRendererOptions({
|
methods: {
|
||||||
size: size,
|
fontChanged(fonts) {
|
||||||
theme: theme,
|
this.setWxRendererOptions({
|
||||||
});
|
fonts: fonts,
|
||||||
this.setCurrentSize(size);
|
});
|
||||||
this.$emit("refresh");
|
this.setCurrentFont(fonts);
|
||||||
},
|
this.$emit("refresh");
|
||||||
colorChanged(color) {
|
},
|
||||||
let theme = setFontSize(this.currentSize.replace("px", ""));
|
sizeChanged(size) {
|
||||||
|
let theme = setFontSize(size.replace("px", ""));
|
||||||
|
theme = setColorWithCustomTemplate(theme, this.currentColor);
|
||||||
|
this.setWxRendererOptions({
|
||||||
|
size: size,
|
||||||
|
theme: theme,
|
||||||
|
});
|
||||||
|
this.setCurrentSize(size);
|
||||||
|
this.$emit("refresh");
|
||||||
|
},
|
||||||
|
colorChanged(color) {
|
||||||
|
let theme = setFontSize(this.currentSize.replace("px", ""));
|
||||||
|
|
||||||
theme = setColorWithCustomTemplate(theme, color);
|
theme = setColorWithCustomTemplate(theme, color);
|
||||||
this.setWxRendererOptions({
|
this.setWxRendererOptions({
|
||||||
theme: theme,
|
theme: theme,
|
||||||
});
|
});
|
||||||
this.setCurrentColor(color);
|
this.setCurrentColor(color);
|
||||||
this.$emit("refresh");
|
this.$emit("refresh");
|
||||||
},
|
|
||||||
codeThemeChanged(theme) {
|
|
||||||
this.setCurrentCodeTheme(theme);
|
|
||||||
this.$emit("refresh");
|
|
||||||
},
|
|
||||||
statusChanged(val) {
|
|
||||||
this.setCiteStatus(val);
|
|
||||||
this.$emit("refresh");
|
|
||||||
},
|
|
||||||
// 复制到微信公众号
|
|
||||||
copy(e) {
|
|
||||||
this.$emit("startCopy");
|
|
||||||
setTimeout(() => {
|
|
||||||
let clipboardDiv = document.getElementById("output");
|
|
||||||
solveWeChatImage();
|
|
||||||
fixCodeWhiteSpace();
|
|
||||||
solveHtml();
|
|
||||||
clipboardDiv.focus();
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
let range = document.createRange();
|
|
||||||
|
|
||||||
range.setStartBefore(clipboardDiv.firstChild);
|
|
||||||
range.setEndAfter(clipboardDiv.lastChild);
|
|
||||||
window.getSelection().addRange(range);
|
|
||||||
document.execCommand("copy");
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
fixCodeWhiteSpace("normal");
|
|
||||||
|
|
||||||
clipboardDiv.innerHTML = this.output;
|
|
||||||
// 输出提示
|
|
||||||
this.$notify({
|
|
||||||
showClose: true,
|
|
||||||
message:
|
|
||||||
"已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴",
|
|
||||||
offset: 80,
|
|
||||||
duration: 1600,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
this.$emit("refresh");
|
|
||||||
this.$emit("endCopy");
|
|
||||||
}, 350);
|
|
||||||
e.target.blur();
|
|
||||||
},
|
|
||||||
// 自定义CSS样式
|
|
||||||
async customStyle() {
|
|
||||||
this.$emit("showCssEditor");
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (!this.cssEditor) {
|
|
||||||
this.cssEditor.refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
this.cssEditor.refresh();
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
let flag = await localStorage.getItem("__css_content");
|
|
||||||
if (!flag) {
|
|
||||||
this.setCssEditorValue(DEFAULT_CSS_CONTENT);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 重置页面
|
|
||||||
confirmReset() {
|
|
||||||
localStorage.clear();
|
|
||||||
this.clearEditorToDefault();
|
|
||||||
this.editor.focus();
|
|
||||||
this.citeStatus = false;
|
|
||||||
this.statusChanged(false);
|
|
||||||
this.fontChanged(this.config.builtinFonts[0].value);
|
|
||||||
this.colorChanged(this.config.colorOption[0].value);
|
|
||||||
this.sizeChanged(this.config.sizeOption[2].value);
|
|
||||||
this.$emit("cssChanged");
|
|
||||||
this.selectFont = this.currentFont;
|
|
||||||
this.selectSize = this.currentSize;
|
|
||||||
this.selectColor = this.currentColor;
|
|
||||||
this.showResetConfirm = false;
|
|
||||||
},
|
|
||||||
cancelReset() {
|
|
||||||
this.showResetConfirm = false;
|
|
||||||
this.editor.focus();
|
|
||||||
},
|
|
||||||
...mapMutations([
|
|
||||||
"clearEditorToDefault",
|
|
||||||
"setCurrentColor",
|
|
||||||
"setCiteStatus",
|
|
||||||
"themeChanged",
|
|
||||||
"setCurrentFont",
|
|
||||||
"setCurrentSize",
|
|
||||||
"setCssEditorValue",
|
|
||||||
"setCurrentCodeTheme",
|
|
||||||
"setWxRendererOptions",
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
mounted() {
|
codeThemeChanged(theme) {
|
||||||
this.selectFont = this.currentFont;
|
this.setCurrentCodeTheme(theme);
|
||||||
this.selectSize = this.currentSize;
|
this.$emit("refresh");
|
||||||
this.selectColor = this.currentColor;
|
|
||||||
this.selectCodeTheme = this.codeTheme;
|
|
||||||
},
|
},
|
||||||
|
statusChanged(val) {
|
||||||
|
this.setCiteStatus(val);
|
||||||
|
this.$emit("refresh");
|
||||||
|
},
|
||||||
|
// 复制到微信公众号
|
||||||
|
copy(e) {
|
||||||
|
this.$emit("startCopy");
|
||||||
|
setTimeout(() => {
|
||||||
|
let clipboardDiv = document.getElementById("output");
|
||||||
|
solveWeChatImage();
|
||||||
|
fixCodeWhiteSpace();
|
||||||
|
solveHtml();
|
||||||
|
clipboardDiv.focus();
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
let range = document.createRange();
|
||||||
|
|
||||||
|
range.setStartBefore(clipboardDiv.firstChild);
|
||||||
|
range.setEndAfter(clipboardDiv.lastChild);
|
||||||
|
window.getSelection().addRange(range);
|
||||||
|
document.execCommand("copy");
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
fixCodeWhiteSpace("normal");
|
||||||
|
|
||||||
|
clipboardDiv.innerHTML = this.output;
|
||||||
|
// 输出提示
|
||||||
|
this.$notify({
|
||||||
|
showClose: true,
|
||||||
|
message: "已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴",
|
||||||
|
offset: 80,
|
||||||
|
duration: 1600,
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
this.$emit("refresh");
|
||||||
|
this.$emit("endCopy");
|
||||||
|
}, 350);
|
||||||
|
},
|
||||||
|
// 自定义CSS样式
|
||||||
|
async customStyle() {
|
||||||
|
this.$emit("showCssEditor");
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.cssEditor) {
|
||||||
|
this.cssEditor.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.cssEditor.refresh();
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
let flag = await localStorage.getItem("__css_content");
|
||||||
|
if (!flag) {
|
||||||
|
this.setCssEditorValue(DEFAULT_CSS_CONTENT);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 重置页面
|
||||||
|
confirmReset() {
|
||||||
|
localStorage.clear();
|
||||||
|
this.clearEditorToDefault();
|
||||||
|
this.editor.focus();
|
||||||
|
this.citeStatus = false;
|
||||||
|
this.statusChanged(false);
|
||||||
|
this.fontChanged(this.config.builtinFonts[0].value);
|
||||||
|
this.colorChanged(this.config.colorOption[0].value);
|
||||||
|
this.sizeChanged(this.config.sizeOption[2].value);
|
||||||
|
this.$emit("cssChanged");
|
||||||
|
this.selectFont = this.currentFont;
|
||||||
|
this.selectSize = this.currentSize;
|
||||||
|
this.selectColor = this.currentColor;
|
||||||
|
this.showResetConfirm = false;
|
||||||
|
},
|
||||||
|
cancelReset() {
|
||||||
|
this.showResetConfirm = false;
|
||||||
|
this.editor.focus();
|
||||||
|
},
|
||||||
|
...mapMutations([
|
||||||
|
"clearEditorToDefault",
|
||||||
|
"setCurrentColor",
|
||||||
|
"setCiteStatus",
|
||||||
|
"themeChanged",
|
||||||
|
"setCurrentFont",
|
||||||
|
"setCurrentSize",
|
||||||
|
"setCssEditorValue",
|
||||||
|
"setCurrentCodeTheme",
|
||||||
|
"setWxRendererOptions",
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.selectFont = this.currentFont;
|
||||||
|
this.selectSize = this.currentSize;
|
||||||
|
this.selectColor = this.currentColor;
|
||||||
|
this.selectCodeTheme = this.codeTheme;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.editor__header {
|
.editor__header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.header__item {
|
.header__item {
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
}
|
}
|
||||||
.header__item_last {
|
.header__item_last {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
.header__switch {
|
.header__switch {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
.mode__switch {
|
.mode__switch {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
width: 24px;
|
margin-right: 24px;
|
||||||
height: 24px;
|
width: 24px;
|
||||||
background: url("../../assets/images/night.png") no-repeat;
|
height: 24px;
|
||||||
background-size: cover;
|
background: url("../../assets/images/night.png") no-repeat;
|
||||||
transition: all 0.3s;
|
background-size: cover;
|
||||||
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
.mode__switch_black {
|
.mode__switch_black {
|
||||||
background: url("../../assets/images/light.png") no-repeat;
|
background: url("../../assets/images/light.png") no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
.top {
|
.top {
|
||||||
margin-right: 0;
|
height: 60px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.el-select {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
.left-side {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.right-side {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.preview table tr:nth-child(even){
|
||||||
|
background: rgb(250, 250, 250);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.select-item-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-item-right {
|
||||||
|
float: right;
|
||||||
|
color: #8492a6;
|
||||||
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,57 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="插入表格"
|
title="插入表格"
|
||||||
class="insert__dialog"
|
class="insert__dialog"
|
||||||
:visible="value"
|
:visible="value"
|
||||||
@close="$emit('input', false)"
|
@close="$emit('input', false)"
|
||||||
border
|
border
|
||||||
>
|
>
|
||||||
<el-row class="tb-options" type="flex" align="middle" :gutter="10">
|
<el-row class="tb-options" type="flex" align="middle" :gutter="10">
|
||||||
<el-col>
|
<el-col>
|
||||||
行数:
|
行数:
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="rowNum"
|
v-model="rowNum"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="100"
|
:max="100"
|
||||||
size="small"
|
size="small"
|
||||||
></el-input-number>
|
></el-input-number>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
列数:
|
列数:
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="colNum"
|
v-model="colNum"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="100"
|
:max="100"
|
||||||
size="small"
|
size="small"
|
||||||
></el-input-number>
|
></el-input-number>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<table style="border-collapse: collapse" class="input-table">
|
<table style="border-collapse: collapse" class="input-table">
|
||||||
<tr
|
<tr
|
||||||
:class="{ 'head-style': row === 1 }"
|
:class="{ 'head-style': row === 1 }"
|
||||||
v-for="row in rowNum + 1"
|
v-for="row in rowNum + 1"
|
||||||
:key="row"
|
:key="row"
|
||||||
>
|
>
|
||||||
<td v-for="col in colNum" :key="col">
|
<td v-for="col in colNum" :key="col">
|
||||||
<el-input
|
<el-input
|
||||||
align="center"
|
align="center"
|
||||||
v-model="tableData[`k_${row - 1}_${col - 1}`]"
|
v-model="tableData[`k_${row - 1}_${col - 1}`]"
|
||||||
:placeholder="row === 1 ? '表头' : ''"
|
:placeholder="row === 1 ? '表头' : ''"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button :type="btnType" plain @click="$emit('input', false)"
|
<el-button :type="btnType" plain @click="$emit('input', false)"
|
||||||
>取 消</el-button
|
>取 消</el-button
|
||||||
>
|
>
|
||||||
<el-button :type="btnType" @click="insertTable" plain
|
<el-button :type="btnType" @click="insertTable" plain>确 定</el-button>
|
||||||
>确 定</el-button
|
</div>
|
||||||
>
|
</el-dialog>
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -59,66 +57,66 @@ import config from "../../assets/scripts/config";
|
|||||||
import { createTable } from "../../assets/scripts/util";
|
import { createTable } from "../../assets/scripts/util";
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
config: config,
|
return {
|
||||||
rowNum: 3,
|
config: config,
|
||||||
colNum: 3,
|
rowNum: 3,
|
||||||
tableData: {},
|
colNum: 3,
|
||||||
};
|
tableData: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
btnType() {
|
||||||
|
return this.nightMode ? "default" : "primary";
|
||||||
},
|
},
|
||||||
computed: {
|
...mapState({
|
||||||
btnType() {
|
nightMode: (state) => state.nightMode,
|
||||||
return this.nightMode ? "default" : "primary";
|
editor: (state) => state.editor,
|
||||||
},
|
}),
|
||||||
...mapState({
|
},
|
||||||
nightMode: (state) => state.nightMode,
|
methods: {
|
||||||
editor: (state) => state.editor,
|
// 插入表格
|
||||||
}),
|
insertTable() {
|
||||||
},
|
const cursor = this.editor.getCursor();
|
||||||
methods: {
|
const table = createTable({
|
||||||
// 插入表格
|
data: this.tableData,
|
||||||
insertTable() {
|
rows: this.rowNum,
|
||||||
const cursor = this.editor.getCursor();
|
cols: this.colNum,
|
||||||
const table = createTable({
|
});
|
||||||
data: this.tableData,
|
|
||||||
rows: this.rowNum,
|
|
||||||
cols: this.colNum,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tableData = {};
|
this.tableData = {};
|
||||||
this.rowNum = 3;
|
this.rowNum = 3;
|
||||||
this.colNum = 3;
|
this.colNum = 3;
|
||||||
this.editor.replaceSelection(`\n${table}\n`, "end");
|
this.editor.replaceSelection(`\n${table}\n`, "end");
|
||||||
this.$emit("input", false);
|
this.$emit("input", false);
|
||||||
this.editorRefresh();
|
this.editorRefresh();
|
||||||
},
|
|
||||||
...mapMutations(["editorRefresh"]),
|
|
||||||
},
|
},
|
||||||
|
...mapMutations(["editorRefresh"]),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
/deep/ .el-dialog {
|
/deep/ .el-dialog {
|
||||||
width: 55%;
|
width: 55%;
|
||||||
min-height: 375px;
|
min-height: 375px;
|
||||||
min-width: 440px;
|
min-width: 440px;
|
||||||
}
|
}
|
||||||
.tb-options {
|
.tb-options {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-table ::v-deep .el-input__inner {
|
.input-table ::v-deep .el-input__inner {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-style /deep/ .el-input__inner {
|
.head-style /deep/ .el-input__inner {
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,57 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="提示"
|
title="提示"
|
||||||
class="reset__dialog"
|
class="reset__dialog"
|
||||||
:visible="showResetConfirm"
|
:visible="showResetConfirm"
|
||||||
@close="$emit('close')"
|
@close="$emit('close')"
|
||||||
>
|
>
|
||||||
<div class="text">
|
<div class="text">此操作将丢失本地缓存的文本和自定义样式,是否继续?</div>
|
||||||
此操作将丢失本地缓存的文本和自定义样式,是否继续?
|
<div slot="footer" class="dialog-footer">
|
||||||
</div>
|
<el-button :type="btnType" plain @click="$emit('close')">取 消</el-button>
|
||||||
<div slot="footer" class="dialog-footer">
|
<el-button :type="btnType" @click="$emit('confirm')" plain
|
||||||
<el-button :type="btnType" plain @click="$emit('close')"
|
>确 定</el-button
|
||||||
>取 消</el-button
|
>
|
||||||
>
|
</div>
|
||||||
<el-button :type="btnType" @click="$emit('confirm')" plain
|
</el-dialog>
|
||||||
>确 定</el-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
showResetConfirm: {
|
showResetConfirm: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
},
|
||||||
btnType() {
|
computed: {
|
||||||
return this.nightMode ? "default" : "primary";
|
btnType() {
|
||||||
},
|
return this.nightMode ? "default" : "primary";
|
||||||
...mapState({
|
|
||||||
nightMode: (state) => state.nightMode,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
...mapState({
|
||||||
|
nightMode: (state) => state.nightMode,
|
||||||
|
}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
/deep/ .el-dialog {
|
/deep/ .el-dialog {
|
||||||
min-width: 440px;
|
min-width: 440px;
|
||||||
}
|
}
|
||||||
.reset__dialog {
|
.reset__dialog {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,139 +1,139 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul
|
<ul
|
||||||
v-show="value"
|
v-show="value"
|
||||||
id="menu"
|
id="menu"
|
||||||
class="menu"
|
class="menu"
|
||||||
:style="`left: ${left}px;top: ${top}px;`"
|
:style="`left: ${left}px;top: ${top}px;`"
|
||||||
>
|
>
|
||||||
<div class="menu__group" v-for="(menuItem, index) in menu" :key="index">
|
<div class="menu__group" v-for="(menuItem, index) in menu" :key="index">
|
||||||
<li
|
<li
|
||||||
v-for="item of menuItem"
|
v-for="item of menuItem"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="menu_item"
|
class="menu_item"
|
||||||
@mousedown="onMouseDown(item.key)"
|
@mousedown="onMouseDown(item.key)"
|
||||||
>
|
>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
|
||||||
top: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
left: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
top: {
|
||||||
return {
|
type: Number,
|
||||||
menu: [
|
default: 0,
|
||||||
[
|
|
||||||
{
|
|
||||||
text: "上传图片",
|
|
||||||
key: "insertPic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "插入表格",
|
|
||||||
key: "insertTable",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "页面重置",
|
|
||||||
key: "pageReset",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: "下载 Markdown 文档",
|
|
||||||
key: "download",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "格式化 Markdown 文档",
|
|
||||||
key: "formatMarkdown",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
left: {
|
||||||
closeCB() {
|
type: Number,
|
||||||
this.$emit("input", false);
|
default: 0,
|
||||||
},
|
|
||||||
onMouseDown(key) {
|
|
||||||
this.$emit("menuTick", key);
|
|
||||||
this.$emit("closeMenu", false);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menu: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "上传图片",
|
||||||
|
key: "insertPic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "插入表格",
|
||||||
|
key: "insertTable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "页面重置",
|
||||||
|
key: "pageReset",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "下载 Markdown 文档",
|
||||||
|
key: "download",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "格式化 Markdown 文档",
|
||||||
|
key: "formatMarkdown",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeCB() {
|
||||||
|
this.$emit("input", false);
|
||||||
|
},
|
||||||
|
onMouseDown(key) {
|
||||||
|
this.$emit("menuTick", key);
|
||||||
|
this.$emit("closeMenu", false);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.menu {
|
.menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08);
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu_item {
|
.menu_item {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #f0f0f0;
|
background: #f0f0f0;
|
||||||
}
|
}
|
||||||
span,
|
span,
|
||||||
.btn-upload {
|
.btn-upload {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.btn-upload {
|
.btn-upload {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.btn-upload:hover {
|
.btn-upload:hover {
|
||||||
background: #aaaaaa;
|
background: #aaaaaa;
|
||||||
}
|
}
|
||||||
::v-deep .el-upload {
|
::v-deep .el-upload {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu__group {
|
.menu__group {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
border-bottom: 1px solid #eeeeee;
|
border-bottom: 1px solid #eeeeee;
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li:hover {
|
li:hover {
|
||||||
background-color: #1790ff;
|
background-color: #1790ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,47 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loading" id="loading">
|
<div class="loading" id="loading">
|
||||||
<div class="loading-wrapper">
|
<div class="loading-wrapper">
|
||||||
<div class="loading-anim"></div>
|
<div class="loading-anim"></div>
|
||||||
<div class="loading-text">致力于让 Markdown 编辑更简单</div>
|
<div class="loading-text">致力于让 Markdown 编辑更简单</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script></script>
|
<script></script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.loading-wrapper {
|
.loading-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%) translateY(-50%);
|
transform: translateX(-50%) translateY(-50%);
|
||||||
}
|
}
|
||||||
.loading {
|
.loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading_night {
|
.loading_night {
|
||||||
background-color: #303133;
|
background-color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-text {
|
.loading-text {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: 26px;
|
margin-top: 26px;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-anim {
|
.loading-anim {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
background: url("../assets/images/favicon.png") no-repeat;
|
background: url("../assets/images/favicon.png") no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
14
src/main.js
14
src/main.js
@ -1,5 +1,5 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import App from "./App.vue";
|
import App from "./App";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
import ElementUI from "element-ui";
|
import ElementUI from "element-ui";
|
||||||
import "element-ui/lib/theme-chalk/index.css";
|
import "element-ui/lib/theme-chalk/index.css";
|
||||||
@ -13,13 +13,15 @@ import "codemirror/addon/edit/matchbrackets";
|
|||||||
import "codemirror/addon/selection/active-line";
|
import "codemirror/addon/selection/active-line";
|
||||||
import "codemirror/addon/hint/show-hint.js";
|
import "codemirror/addon/hint/show-hint.js";
|
||||||
import "codemirror/addon/hint/css-hint.js";
|
import "codemirror/addon/hint/css-hint.js";
|
||||||
import "./assets/less/theme.less";
|
|
||||||
|
|
||||||
Vue.use(ElementUI);
|
Vue.use(ElementUI);
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
new Vue({
|
App.mpType = "app";
|
||||||
store,
|
|
||||||
render: (h) => h(App),
|
const app = new Vue({
|
||||||
}).$mount("#app");
|
store,
|
||||||
|
...App,
|
||||||
|
});
|
||||||
|
app.$mount();
|
||||||
|
71
src/manifest.json
Normal file
71
src/manifest.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"name": "uni-md",
|
||||||
|
"appid": "",
|
||||||
|
"description": "",
|
||||||
|
"versionName": "1.0.0",
|
||||||
|
"versionCode": "100",
|
||||||
|
"transformPx": false,
|
||||||
|
"h5": {
|
||||||
|
"publicPath": "/md/"
|
||||||
|
},
|
||||||
|
"app-plus": {
|
||||||
|
"usingComponents": true,
|
||||||
|
"splashscreen": {
|
||||||
|
"alwaysShowBeforeRender": true,
|
||||||
|
"waiting": true,
|
||||||
|
"autoclose": true,
|
||||||
|
"delay": 0
|
||||||
|
},
|
||||||
|
"modules": {},
|
||||||
|
"distribute": {
|
||||||
|
"android": {
|
||||||
|
"permissions": [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ios": {},
|
||||||
|
"sdkConfigs": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"quickapp": {},
|
||||||
|
"mp-weixin": {
|
||||||
|
"appid": "",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false
|
||||||
|
},
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-alipay": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-baidu": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-toutiao": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-qq": {
|
||||||
|
"usingComponents": true
|
||||||
|
}
|
||||||
|
}
|
17
src/pages.json
Normal file
17
src/pages.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationBarTitleText": "uni-app",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
|
"backgroundColor": "#F8F8F8"
|
||||||
|
}
|
||||||
|
}
|
43
src/pages/index/index.vue
Normal file
43
src/pages/index/index.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="fade" v-if="loading">
|
||||||
|
<loading />
|
||||||
|
</transition>
|
||||||
|
<codemirror-editor v-else />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Loading from "../../components/Loading";
|
||||||
|
import CodemirrorEditor from "./view/CodemirrorEditor";
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
Loading,
|
||||||
|
CodemirrorEditor,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loading = false;
|
||||||
|
}, 100);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.fade-enter,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.fade-enter-to,
|
||||||
|
.fade-leave {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: all 1s;
|
||||||
|
}
|
||||||
|
</style>
|
486
src/pages/index/view/CodemirrorEditor.vue
Normal file
486
src/pages/index/view/CodemirrorEditor.vue
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container" :class="{ container_night: nightMode }">
|
||||||
|
<el-container>
|
||||||
|
<el-header class="editor__header">
|
||||||
|
<editor-header
|
||||||
|
ref="header"
|
||||||
|
@refresh="onEditorRefresh"
|
||||||
|
@cssChanged="cssChanged"
|
||||||
|
@download="downloadEditorContent"
|
||||||
|
@showCssEditor="showCssEditor = !showCssEditor"
|
||||||
|
@show-about-dialog="aboutDialogVisible = true"
|
||||||
|
@show-dialog-form="dialogFormVisible = true"
|
||||||
|
@show-dialog-upload-img="dialogUploadImgVisible = true"
|
||||||
|
@startCopy="(isCoping = true), (backLight = true)"
|
||||||
|
@endCopy="endCopy"
|
||||||
|
/>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="main-body">
|
||||||
|
<el-row class="main-section">
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
class="codeMirror-wrapper"
|
||||||
|
@contextmenu.prevent.native="openMenu($event)"
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
id="editor"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="Your markdown text here."
|
||||||
|
v-model="source"
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
</el-col>
|
||||||
|
<el-col
|
||||||
|
:span="12"
|
||||||
|
class="preview-wrapper"
|
||||||
|
id="preview"
|
||||||
|
ref="preview"
|
||||||
|
:class="{
|
||||||
|
'preview-wrapper_night': nightMode && isCoping,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<section
|
||||||
|
id="output-wrapper"
|
||||||
|
:class="{ output_night: nightMode && !backLight }"
|
||||||
|
>
|
||||||
|
<div class="preview">
|
||||||
|
<section id="output" v-html="output"></section>
|
||||||
|
<div class="loading-mask" v-if="nightMode && isCoping">
|
||||||
|
<div class="loading__img"></div>
|
||||||
|
<span>正在生成</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</el-col>
|
||||||
|
<transition
|
||||||
|
name="custom-classes-transition"
|
||||||
|
enter-active-class="bounceInRight"
|
||||||
|
>
|
||||||
|
<el-col
|
||||||
|
id="cssBox"
|
||||||
|
v-show="showCssEditor"
|
||||||
|
:span="12"
|
||||||
|
class="cssEditor-wrapper"
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
id="cssEditor"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="Your custom css here."
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
</el-col>
|
||||||
|
</transition>
|
||||||
|
</el-row>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
<upload-img-dialog
|
||||||
|
v-model="dialogUploadImgVisible"
|
||||||
|
@close="dialogUploadImgVisible = false"
|
||||||
|
@beforeUpload="beforeUpload"
|
||||||
|
@uploadImage="uploadImage"
|
||||||
|
@uploaded="uploaded"
|
||||||
|
/>
|
||||||
|
<about-dialog v-model="aboutDialogVisible" />
|
||||||
|
<insert-form-dialog v-model="dialogFormVisible" />
|
||||||
|
<right-click-menu
|
||||||
|
v-model="rightClickMenuVisible"
|
||||||
|
:left="mouseLeft"
|
||||||
|
:top="mouseTop"
|
||||||
|
@menuTick="onMenuEvent"
|
||||||
|
@closeMenu="closeRightClickMenu"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import editorHeader from "../../../components/CodemirrorEditor/header";
|
||||||
|
import aboutDialog from "../../../components/CodemirrorEditor/aboutDialog";
|
||||||
|
import insertFormDialog from "../../../components/CodemirrorEditor/insertForm";
|
||||||
|
import rightClickMenu from "../../../components/CodemirrorEditor/rightClickMenu";
|
||||||
|
import uploadImgDialog from "../../../components/CodemirrorEditor/uploadImgDialog";
|
||||||
|
|
||||||
|
import {
|
||||||
|
css2json,
|
||||||
|
downloadMD,
|
||||||
|
formatDoc,
|
||||||
|
setFontSize,
|
||||||
|
saveEditorContent,
|
||||||
|
customCssWithTemplate,
|
||||||
|
checkImage,
|
||||||
|
} from "../../../assets/scripts/util";
|
||||||
|
|
||||||
|
import { toBase64 } from "../../../assets/scripts/util";
|
||||||
|
import fileApi from "../../../api/file";
|
||||||
|
|
||||||
|
require("codemirror/mode/javascript/javascript");
|
||||||
|
import { mapState, mapMutations } from "vuex";
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showCssEditor: false,
|
||||||
|
aboutDialogVisible: false,
|
||||||
|
dialogUploadImgVisible: false,
|
||||||
|
dialogFormVisible: false,
|
||||||
|
isCoping: false,
|
||||||
|
isImgLoading: false,
|
||||||
|
backLight: false,
|
||||||
|
timeout: null,
|
||||||
|
changeTimer: null,
|
||||||
|
source: "",
|
||||||
|
mouseLeft: 0,
|
||||||
|
mouseTop: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
editorHeader,
|
||||||
|
aboutDialog,
|
||||||
|
insertFormDialog,
|
||||||
|
rightClickMenu,
|
||||||
|
uploadImgDialog,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
wxRenderer: (state) => state.wxRenderer,
|
||||||
|
output: (state) => state.output,
|
||||||
|
editor: (state) => state.editor,
|
||||||
|
cssEditor: (state) => state.cssEditor,
|
||||||
|
currentSize: (state) => state.currentSize,
|
||||||
|
currentColor: (state) => state.currentColor,
|
||||||
|
nightMode: (state) => state.nightMode,
|
||||||
|
rightClickMenuVisible: (state) => state.rightClickMenuVisible,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initEditorState();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.initEditor();
|
||||||
|
this.initCssEditor();
|
||||||
|
this.onEditorRefresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initEditor() {
|
||||||
|
this.initEditorEntity();
|
||||||
|
this.editor.on("change", (cm, e) => {
|
||||||
|
if (this.changeTimer) clearTimeout(this.changeTimer);
|
||||||
|
this.changeTimer = setTimeout(() => {
|
||||||
|
this.onEditorRefresh();
|
||||||
|
saveEditorContent(this.editor, "__editor_content");
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 粘贴上传图片并插入
|
||||||
|
this.editor.on("paste", (cm, e) => {
|
||||||
|
if (!(e.clipboardData && e.clipboardData.items) || this.isImgLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) {
|
||||||
|
let item = e.clipboardData.items[i];
|
||||||
|
if (item.kind === "file") {
|
||||||
|
// 校验图床参数
|
||||||
|
const pasteFile = item.getAsFile();
|
||||||
|
const isValid = this.beforeUpload(pasteFile);
|
||||||
|
if (!isValid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.uploadImage(pasteFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.on("mousedown", () => {
|
||||||
|
this.$store.commit("setRightClickMenuVisible", false);
|
||||||
|
});
|
||||||
|
this.editor.on("blur", () => {
|
||||||
|
//!影响到右键菜单的点击事件,右键菜单的点击事件在组件内通过mousedown触发
|
||||||
|
this.$store.commit("setRightClickMenuVisible", false);
|
||||||
|
});
|
||||||
|
this.editor.on("scroll", () => {
|
||||||
|
this.$store.commit("setRightClickMenuVisible", false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
initCssEditor() {
|
||||||
|
this.initCssEditorEntity();
|
||||||
|
// 自动提示
|
||||||
|
this.cssEditor.on("keyup", (cm, e) => {
|
||||||
|
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
|
||||||
|
cm.showHint(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.cssEditor.on("update", (instance) => {
|
||||||
|
this.cssChanged();
|
||||||
|
saveEditorContent(this.cssEditor, "__css_content");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cssChanged() {
|
||||||
|
let json = css2json(this.cssEditor.getValue(0));
|
||||||
|
let theme = setFontSize(this.currentSize.replace("px", ""));
|
||||||
|
|
||||||
|
theme = customCssWithTemplate(json, this.currentColor, theme);
|
||||||
|
this.setWxRendererOptions({
|
||||||
|
theme: theme,
|
||||||
|
});
|
||||||
|
this.onEditorRefresh();
|
||||||
|
},
|
||||||
|
beforeUpload(file) {
|
||||||
|
// validate image
|
||||||
|
const checkResult = checkImage(file);
|
||||||
|
if (!checkResult.ok) {
|
||||||
|
this.$message.error(checkResult.msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check image host
|
||||||
|
let imgHost = localStorage.getItem("imgHost");
|
||||||
|
imgHost = imgHost ? imgHost : "default";
|
||||||
|
localStorage.setItem("imgHost", imgHost);
|
||||||
|
|
||||||
|
const config = localStorage.getItem(`${imgHost}Config`);
|
||||||
|
const isValidHost = imgHost == "default" || config;
|
||||||
|
if (!isValidHost) {
|
||||||
|
this.$message.error(`请先配置 ${imgHost} 图床参数`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
uploadImage(file) {
|
||||||
|
this.isImgLoading = true;
|
||||||
|
toBase64(file)
|
||||||
|
.then((base64Content) => {
|
||||||
|
fileApi
|
||||||
|
.fileUpload(base64Content, file)
|
||||||
|
.then((url) => {
|
||||||
|
this.uploaded(url);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.$message.error(err.message);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.$message.error(err.message);
|
||||||
|
});
|
||||||
|
this.isImgLoading = false;
|
||||||
|
},
|
||||||
|
// 图片上传结束
|
||||||
|
uploaded(response) {
|
||||||
|
if (!response) {
|
||||||
|
this.$message.error("上传图片未知异常");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.dialogUploadImgVisible = false;
|
||||||
|
// 上传成功,获取光标
|
||||||
|
const cursor = this.editor.getCursor();
|
||||||
|
const imageUrl = response;
|
||||||
|
const markdownImage = `![](${imageUrl})`;
|
||||||
|
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
|
||||||
|
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
|
||||||
|
this.$message.success("图片上传成功");
|
||||||
|
this.onEditorRefresh();
|
||||||
|
},
|
||||||
|
// 左右滚动
|
||||||
|
leftAndRightScroll() {
|
||||||
|
const scrollCB = (text) => {
|
||||||
|
let source, target;
|
||||||
|
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
if (text === "preview") {
|
||||||
|
source = this.$refs.preview.$el;
|
||||||
|
target = document.getElementsByClassName("CodeMirror-scroll")[0];
|
||||||
|
this.editor.off("scroll", editorScrollCB);
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.editor.on("scroll", editorScrollCB);
|
||||||
|
}, 300);
|
||||||
|
} else if (text === "editor") {
|
||||||
|
source = document.getElementsByClassName("CodeMirror-scroll")[0];
|
||||||
|
target = this.$refs.preview.$el;
|
||||||
|
target.removeEventListener("scroll", previewScrollCB, false);
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
target.addEventListener("scroll", previewScrollCB, false);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentage =
|
||||||
|
source.scrollTop / (source.scrollHeight - source.offsetHeight);
|
||||||
|
let height = percentage * (target.scrollHeight - target.offsetHeight);
|
||||||
|
|
||||||
|
target.scrollTo(0, height);
|
||||||
|
};
|
||||||
|
const editorScrollCB = () => {
|
||||||
|
scrollCB("editor");
|
||||||
|
};
|
||||||
|
const previewScrollCB = () => {
|
||||||
|
scrollCB("preview");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$refs.preview.$el.addEventListener("scroll", previewScrollCB, false);
|
||||||
|
this.editor.on("scroll", editorScrollCB);
|
||||||
|
},
|
||||||
|
// 更新编辑器
|
||||||
|
onEditorRefresh() {
|
||||||
|
this.editorRefresh();
|
||||||
|
setTimeout(() => PR.prettyPrint(), 0);
|
||||||
|
},
|
||||||
|
// 复制结束
|
||||||
|
endCopy() {
|
||||||
|
this.backLight = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isCoping = false;
|
||||||
|
}, 800);
|
||||||
|
},
|
||||||
|
// 下载编辑器内容到本地
|
||||||
|
downloadEditorContent() {
|
||||||
|
downloadMD(this.editor.getValue(0));
|
||||||
|
},
|
||||||
|
// 格式化文档
|
||||||
|
formatContent() {
|
||||||
|
const doc = formatDoc(this.editor.getValue(0));
|
||||||
|
localStorage.setItem("__editor_content", doc);
|
||||||
|
this.editor.setValue(doc);
|
||||||
|
},
|
||||||
|
// 右键菜单
|
||||||
|
openMenu(e) {
|
||||||
|
const menuMinWidth = 105;
|
||||||
|
const offsetLeft = this.$el.getBoundingClientRect().left;
|
||||||
|
const offsetWidth = this.$el.offsetWidth;
|
||||||
|
const maxLeft = offsetWidth - menuMinWidth;
|
||||||
|
const left = e.clientX - offsetLeft;
|
||||||
|
this.mouseLeft = Math.min(maxLeft, left);
|
||||||
|
this.mouseTop = e.clientY + 10;
|
||||||
|
this.$store.commit("setRightClickMenuVisible", true);
|
||||||
|
},
|
||||||
|
closeRightClickMenu() {
|
||||||
|
this.$store.commit("setRightClickMenuVisible", false);
|
||||||
|
},
|
||||||
|
onMenuEvent(type, info = {}) {
|
||||||
|
switch (type) {
|
||||||
|
case "pageReset":
|
||||||
|
this.$refs.header.showResetConfirm = true;
|
||||||
|
break;
|
||||||
|
case "insertPic":
|
||||||
|
this.dialogUploadImgVisible = true;
|
||||||
|
break;
|
||||||
|
case "download":
|
||||||
|
this.downloadEditorContent();
|
||||||
|
break;
|
||||||
|
case "insertTable":
|
||||||
|
this.dialogFormVisible = true;
|
||||||
|
break;
|
||||||
|
case "formatMarkdown":
|
||||||
|
this.formatContent();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...mapMutations([
|
||||||
|
"initEditorState",
|
||||||
|
"initEditorEntity",
|
||||||
|
"setWxRendererOptions",
|
||||||
|
"editorRefresh",
|
||||||
|
"initCssEditorEntity",
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.leftAndRightScroll();
|
||||||
|
PR.prettyPrint();
|
||||||
|
}, 300);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.main-body {
|
||||||
|
padding-top: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.el-main {
|
||||||
|
transition: all 0.3s;
|
||||||
|
padding: 0;
|
||||||
|
margin: 20px;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.textarea-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.preview-wrapper_night {
|
||||||
|
overflow-y: inherit;
|
||||||
|
position: relative;
|
||||||
|
left: -3px;
|
||||||
|
.preview {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#output-wrapper {
|
||||||
|
position: relative;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
.loading-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 376px;
|
||||||
|
height: 101%;
|
||||||
|
padding-top: 1px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: gray;
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
.loading__img {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 330px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: url("../../../assets/images/favicon.png") no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 390px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bounceInRight {
|
||||||
|
animation-name: bounceInRight;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
/deep/ .preview-table {
|
||||||
|
border-spacing: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounceInRight {
|
||||||
|
0%,
|
||||||
|
60%,
|
||||||
|
75%,
|
||||||
|
90%,
|
||||||
|
100% {
|
||||||
|
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
}
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(3000px, 0, 0);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(-25px, 0, 0);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translate3d(10px, 0, 0);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
transform: translate3d(-5px, 0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url("../../../assets/less/app.less");
|
||||||
|
@import url("../../../assets/less/github-v2.min.css");
|
||||||
|
</style>
|
@ -1,22 +1,22 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Header,
|
Header,
|
||||||
Upload,
|
Upload,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Form,
|
Form,
|
||||||
FormItem,
|
FormItem,
|
||||||
Select,
|
Select,
|
||||||
Option,
|
Option,
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
Switch,
|
Switch,
|
||||||
Button,
|
Button,
|
||||||
Main,
|
Main,
|
||||||
Col,
|
Col,
|
||||||
Row,
|
Row,
|
||||||
Dialog,
|
Dialog,
|
||||||
Loading,
|
Loading,
|
||||||
Message,
|
Message,
|
||||||
} from "element-ui";
|
} from "element-ui";
|
||||||
|
|
||||||
Vue.use(Container);
|
Vue.use(Container);
|
||||||
|
@ -11,154 +11,147 @@ import { setColor, formatDoc, formatCss } from "../assets/scripts/util";
|
|||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
wxRenderer: null,
|
wxRenderer: null,
|
||||||
output: "",
|
output: "",
|
||||||
html: "",
|
html: "",
|
||||||
editor: null,
|
editor: null,
|
||||||
cssEditor: null,
|
cssEditor: null,
|
||||||
currentFont: "",
|
currentFont: "",
|
||||||
currentSize: "",
|
currentSize: "",
|
||||||
currentColor: "",
|
currentColor: "",
|
||||||
citeStatus: 0,
|
citeStatus: 0,
|
||||||
nightMode: false,
|
nightMode: false,
|
||||||
codeTheme: "github",
|
codeTheme: "github",
|
||||||
rightClickMenuVisible: false,
|
rightClickMenuVisible: false,
|
||||||
};
|
};
|
||||||
const mutations = {
|
const mutations = {
|
||||||
setEditorValue(state, data) {
|
setEditorValue(state, data) {
|
||||||
state.editor.setValue(data);
|
state.editor.setValue(data);
|
||||||
},
|
},
|
||||||
setCssEditorValue(state, data) {
|
setCssEditorValue(state, data) {
|
||||||
state.cssEditor.setValue(data);
|
state.cssEditor.setValue(data);
|
||||||
},
|
},
|
||||||
setWxRendererOptions(state, data) {
|
setWxRendererOptions(state, data) {
|
||||||
state.wxRenderer.setOptions(data);
|
state.wxRenderer.setOptions(data);
|
||||||
},
|
},
|
||||||
setCiteStatus(state, data) {
|
setCiteStatus(state, data) {
|
||||||
state.citeStatus = data;
|
state.citeStatus = data;
|
||||||
localStorage.setItem("citeStatus", data);
|
localStorage.setItem("citeStatus", data);
|
||||||
},
|
},
|
||||||
setCurrentFont(state, data) {
|
setCurrentFont(state, data) {
|
||||||
state.currentFont = data;
|
state.currentFont = data;
|
||||||
localStorage.setItem("fonts", data);
|
localStorage.setItem("fonts", data);
|
||||||
},
|
},
|
||||||
setCurrentSize(state, data) {
|
setCurrentSize(state, data) {
|
||||||
state.currentSize = data;
|
state.currentSize = data;
|
||||||
localStorage.setItem("size", data);
|
localStorage.setItem("size", data);
|
||||||
},
|
},
|
||||||
setCurrentColor(state, data) {
|
setCurrentColor(state, data) {
|
||||||
state.currentColor = data;
|
state.currentColor = data;
|
||||||
localStorage.setItem("color", data);
|
localStorage.setItem("color", data);
|
||||||
},
|
},
|
||||||
setCurrentCodeTheme(state, data) {
|
setCurrentCodeTheme(state, data) {
|
||||||
state.codeTheme = data;
|
state.codeTheme = data;
|
||||||
localStorage.setItem("codeTheme", data);
|
localStorage.setItem("codeTheme", data);
|
||||||
},
|
},
|
||||||
setRightClickMenuVisible(state, data) {
|
setRightClickMenuVisible(state, data) {
|
||||||
state.rightClickMenuVisible = data;
|
state.rightClickMenuVisible = data;
|
||||||
},
|
},
|
||||||
themeChanged(state) {
|
themeChanged(state) {
|
||||||
state.nightMode = !state.nightMode;
|
state.nightMode = !state.nightMode;
|
||||||
localStorage.setItem("nightMode", state.nightMode);
|
localStorage.setItem("nightMode", state.nightMode);
|
||||||
},
|
},
|
||||||
initEditorState(state) {
|
initEditorState(state) {
|
||||||
state.currentFont =
|
state.currentFont =
|
||||||
localStorage.getItem("fonts") || config.builtinFonts[0].value;
|
localStorage.getItem("fonts") || config.builtinFonts[0].value;
|
||||||
state.currentColor =
|
state.currentColor =
|
||||||
localStorage.getItem("color") || config.colorOption[0].value;
|
localStorage.getItem("color") || config.colorOption[0].value;
|
||||||
state.currentSize =
|
state.currentSize =
|
||||||
localStorage.getItem("size") || config.sizeOption[2].value;
|
localStorage.getItem("size") || config.sizeOption[2].value;
|
||||||
state.codeTheme =
|
state.codeTheme =
|
||||||
localStorage.getItem("codeTheme") ||
|
localStorage.getItem("codeTheme") || config.codeThemeOption[0].value;
|
||||||
config.codeThemeOption[0].value;
|
state.citeStatus = localStorage.getItem("citeStatus") === "true";
|
||||||
state.citeStatus = localStorage.getItem("citeStatus") === "true";
|
state.nightMode = localStorage.getItem("nightMode") === "true";
|
||||||
state.nightMode = localStorage.getItem("nightMode") === "true";
|
state.wxRenderer = new WxRenderer({
|
||||||
state.wxRenderer = new WxRenderer({
|
theme: setColor(state.currentColor),
|
||||||
theme: setColor(state.currentColor),
|
fonts: state.currentFont,
|
||||||
fonts: state.currentFont,
|
size: state.currentSize,
|
||||||
size: state.currentSize,
|
status: state.citeStatus,
|
||||||
status: state.citeStatus,
|
});
|
||||||
});
|
},
|
||||||
},
|
initEditorEntity(state) {
|
||||||
initEditorEntity(state) {
|
const editorDom = document.getElementById("editor");
|
||||||
state.editor = CodeMirror.fromTextArea(
|
|
||||||
document.getElementById("editor"),
|
|
||||||
{
|
|
||||||
value: "",
|
|
||||||
mode: "text/x-markdown",
|
|
||||||
theme: "xq-light",
|
|
||||||
lineNumbers: false,
|
|
||||||
lineWrapping: true,
|
|
||||||
styleActiveLine: true,
|
|
||||||
autoCloseBrackets: true,
|
|
||||||
extraKeys: {
|
|
||||||
"Ctrl-F": function autoFormat(editor) {
|
|
||||||
const doc = formatDoc(editor.getValue(0));
|
|
||||||
localStorage.setItem("__editor_content", doc);
|
|
||||||
editor.setValue(doc);
|
|
||||||
},
|
|
||||||
"Ctrl-S": function save(editor) {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 如果有编辑器内容被保存则读取,否则加载默认内容
|
if (!editorDom.value) {
|
||||||
state.editor.setValue(
|
editorDom.value =
|
||||||
localStorage.getItem("__editor_content") ||
|
localStorage.getItem("__editor_content") || formatDoc(DEFAULT_CONTENT);
|
||||||
formatDoc(DEFAULT_CONTENT)
|
}
|
||||||
);
|
state.editor = CodeMirror.fromTextArea(editorDom, {
|
||||||
},
|
mode: "text/x-markdown",
|
||||||
initCssEditorEntity(state) {
|
theme: "xq-light",
|
||||||
state.cssEditor = CodeMirror.fromTextArea(
|
lineNumbers: false,
|
||||||
document.getElementById("cssEditor"),
|
lineWrapping: true,
|
||||||
{
|
styleActiveLine: true,
|
||||||
value: "",
|
autoCloseBrackets: true,
|
||||||
mode: "css",
|
extraKeys: {
|
||||||
theme: "style-mirror",
|
"Ctrl-F": function autoFormat(editor) {
|
||||||
lineNumbers: false,
|
const doc = formatDoc(editor.getValue(0));
|
||||||
lineWrapping: true,
|
localStorage.setItem("__editor_content", doc);
|
||||||
matchBrackets: true,
|
editor.setValue(doc);
|
||||||
autofocus: true,
|
},
|
||||||
extraKeys: {
|
"Ctrl-S": function save(editor) {},
|
||||||
"Ctrl-F": function autoFormat(editor) {
|
},
|
||||||
const doc = formatCss(editor.getValue(0));
|
});
|
||||||
localStorage.setItem("__css_content", doc);
|
},
|
||||||
editor.setValue(doc);
|
initCssEditorEntity(state) {
|
||||||
},
|
const cssEditorDom = document.getElementById("cssEditor");
|
||||||
"Ctrl-S": function save(editor) {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 如果有编辑器内容被保存则读取,否则加载默认内容
|
if (!cssEditorDom.value) {
|
||||||
state.cssEditor.setValue(
|
cssEditorDom.value =
|
||||||
localStorage.getItem("__css_content") || DEFAULT_CSS_CONTENT
|
localStorage.getItem("__css_content") || DEFAULT_CSS_CONTENT;
|
||||||
);
|
}
|
||||||
},
|
state.cssEditor = CodeMirror.fromTextArea(cssEditorDom, {
|
||||||
editorRefresh(state) {
|
mode: "css",
|
||||||
let output = marked(state.editor.getValue(0), {
|
theme: "style-mirror",
|
||||||
renderer: state.wxRenderer.getRenderer(state.citeStatus),
|
lineNumbers: false,
|
||||||
});
|
lineWrapping: true,
|
||||||
|
matchBrackets: true,
|
||||||
|
autofocus: true,
|
||||||
|
extraKeys: {
|
||||||
|
"Ctrl-F": function autoFormat(editor) {
|
||||||
|
const doc = formatCss(editor.getValue(0));
|
||||||
|
localStorage.setItem("__css_content", doc);
|
||||||
|
editor.setValue(doc);
|
||||||
|
},
|
||||||
|
"Ctrl-S": function save(editor) {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
editorRefresh(state) {
|
||||||
|
let output = marked(state.editor.getValue(0), {
|
||||||
|
renderer: state.wxRenderer.getRenderer(state.citeStatus),
|
||||||
|
});
|
||||||
|
|
||||||
// 去除第一行的 margin-top
|
// 去除第一行的 margin-top
|
||||||
output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"');
|
output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"');
|
||||||
if (state.citeStatus) {
|
if (state.citeStatus) {
|
||||||
// 引用脚注
|
// 引用脚注
|
||||||
output += state.wxRenderer.buildFootnotes();
|
output += state.wxRenderer.buildFootnotes();
|
||||||
// 附加的一些 style
|
// 附加的一些 style
|
||||||
output += state.wxRenderer.buildAddition();
|
output += state.wxRenderer.buildAddition();
|
||||||
}
|
}
|
||||||
state.output = output;
|
state.output = output;
|
||||||
},
|
},
|
||||||
clearEditorToDefault(state) {
|
clearEditorToDefault(state) {
|
||||||
const doc = formatDoc(DEFAULT_CONTENT);
|
const doc = formatDoc(DEFAULT_CONTENT);
|
||||||
state.editor.setValue(doc);
|
|
||||||
state.cssEditor.setValue(DEFAULT_CSS_CONTENT);
|
state.editor.setValue(doc);
|
||||||
},
|
state.cssEditor.setValue(DEFAULT_CSS_CONTENT);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state,
|
state,
|
||||||
mutations,
|
mutations,
|
||||||
actions: {},
|
actions: {},
|
||||||
});
|
});
|
||||||
|
@ -1,505 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container" :class="{ container_night: nightMode }">
|
|
||||||
<el-container>
|
|
||||||
<el-header class="editor__header">
|
|
||||||
<editor-header
|
|
||||||
ref="header"
|
|
||||||
@refresh="onEditorRefresh"
|
|
||||||
@cssChanged="cssChanged"
|
|
||||||
@download="downloadEditorContent"
|
|
||||||
@showCssEditor="showCssEditor = !showCssEditor"
|
|
||||||
@show-about-dialog="aboutDialogVisible = true"
|
|
||||||
@show-dialog-form="dialogFormVisible = true"
|
|
||||||
@show-dialog-upload-img="dialogUploadImgVisible = true"
|
|
||||||
@startCopy="(isCoping = true), (backLight = true)"
|
|
||||||
@endCopy="endCopy"
|
|
||||||
/>
|
|
||||||
</el-header>
|
|
||||||
<el-main class="main-body">
|
|
||||||
<el-row class="main-section">
|
|
||||||
<el-col
|
|
||||||
:span="12"
|
|
||||||
@contextmenu.prevent.native="openMenu($event)"
|
|
||||||
>
|
|
||||||
<textarea
|
|
||||||
id="editor"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="Your markdown text here."
|
|
||||||
v-model="source"
|
|
||||||
>
|
|
||||||
</textarea>
|
|
||||||
</el-col>
|
|
||||||
<el-col
|
|
||||||
:span="12"
|
|
||||||
class="preview-wrapper"
|
|
||||||
id="preview"
|
|
||||||
ref="preview"
|
|
||||||
:class="{
|
|
||||||
'preview-wrapper_night': nightMode && isCoping,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<section
|
|
||||||
id="output-wrapper"
|
|
||||||
:class="{ output_night: nightMode && !backLight }"
|
|
||||||
>
|
|
||||||
<div class="preview">
|
|
||||||
<section id="output" v-html="output"></section>
|
|
||||||
<div
|
|
||||||
class="loading-mask"
|
|
||||||
v-if="nightMode && isCoping"
|
|
||||||
>
|
|
||||||
<div class="loading__img"></div>
|
|
||||||
<span>正在生成</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</el-col>
|
|
||||||
<transition
|
|
||||||
name="custom-classes-transition"
|
|
||||||
enter-active-class="bounceInRight"
|
|
||||||
>
|
|
||||||
<el-col id="cssBox" :span="12" v-show="showCssEditor">
|
|
||||||
<textarea
|
|
||||||
id="cssEditor"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="Your custom css here."
|
|
||||||
>
|
|
||||||
</textarea>
|
|
||||||
</el-col>
|
|
||||||
</transition>
|
|
||||||
</el-row>
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
<upload-img-dialog
|
|
||||||
v-model="dialogUploadImgVisible"
|
|
||||||
@close="dialogUploadImgVisible = false"
|
|
||||||
@beforeUpload="beforeUpload"
|
|
||||||
@uploadImage="uploadImage"
|
|
||||||
@uploaded="uploaded"
|
|
||||||
/>
|
|
||||||
<about-dialog v-model="aboutDialogVisible" />
|
|
||||||
<insert-form-dialog v-model="dialogFormVisible" />
|
|
||||||
<right-click-menu
|
|
||||||
v-model="rightClickMenuVisible"
|
|
||||||
:left="mouseLeft"
|
|
||||||
:top="mouseTop"
|
|
||||||
@menuTick="onMenuEvent"
|
|
||||||
@closeMenu="closeRightClickMenu"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import editorHeader from "../components/CodemirrorEditor/header";
|
|
||||||
import aboutDialog from "../components/CodemirrorEditor/aboutDialog";
|
|
||||||
import insertFormDialog from "../components/CodemirrorEditor/insertForm";
|
|
||||||
import rightClickMenu from "../components/CodemirrorEditor/rightClickMenu";
|
|
||||||
import uploadImgDialog from "../components/CodemirrorEditor/uploadImgDialog";
|
|
||||||
|
|
||||||
import {
|
|
||||||
css2json,
|
|
||||||
downloadMD,
|
|
||||||
formatDoc,
|
|
||||||
setFontSize,
|
|
||||||
saveEditorContent,
|
|
||||||
customCssWithTemplate,
|
|
||||||
checkImage,
|
|
||||||
} from "../assets/scripts/util";
|
|
||||||
|
|
||||||
import { toBase64 } from "../assets/scripts/util";
|
|
||||||
import fileApi from "../api/file";
|
|
||||||
|
|
||||||
require("codemirror/mode/javascript/javascript");
|
|
||||||
import { mapState, mapMutations } from "vuex";
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showCssEditor: false,
|
|
||||||
aboutDialogVisible: false,
|
|
||||||
dialogUploadImgVisible: false,
|
|
||||||
dialogFormVisible: false,
|
|
||||||
isCoping: false,
|
|
||||||
isImgLoading: false,
|
|
||||||
backLight: false,
|
|
||||||
timeout: null,
|
|
||||||
changeTimer: null,
|
|
||||||
source: "",
|
|
||||||
mouseLeft: 0,
|
|
||||||
mouseTop: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
editorHeader,
|
|
||||||
aboutDialog,
|
|
||||||
insertFormDialog,
|
|
||||||
rightClickMenu,
|
|
||||||
uploadImgDialog,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
wxRenderer: (state) => state.wxRenderer,
|
|
||||||
output: (state) => state.output,
|
|
||||||
editor: (state) => state.editor,
|
|
||||||
cssEditor: (state) => state.cssEditor,
|
|
||||||
currentSize: (state) => state.currentSize,
|
|
||||||
currentColor: (state) => state.currentColor,
|
|
||||||
nightMode: (state) => state.nightMode,
|
|
||||||
rightClickMenuVisible: (state) => state.rightClickMenuVisible,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.initEditorState();
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initEditor();
|
|
||||||
this.initCssEditor();
|
|
||||||
this.onEditorRefresh();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initEditor() {
|
|
||||||
this.initEditorEntity();
|
|
||||||
this.editor.on("change", (cm, e) => {
|
|
||||||
if (this.changeTimer) clearTimeout(this.changeTimer);
|
|
||||||
this.changeTimer = setTimeout(() => {
|
|
||||||
this.onEditorRefresh();
|
|
||||||
saveEditorContent(this.editor, "__editor_content");
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 粘贴上传图片并插入
|
|
||||||
this.editor.on("paste", (cm, e) => {
|
|
||||||
if (
|
|
||||||
!(e.clipboardData && e.clipboardData.items) ||
|
|
||||||
this.isImgLoading
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (
|
|
||||||
let i = 0, len = e.clipboardData.items.length;
|
|
||||||
i < len;
|
|
||||||
++i
|
|
||||||
) {
|
|
||||||
let item = e.clipboardData.items[i];
|
|
||||||
if (item.kind === "file") {
|
|
||||||
// 校验图床参数
|
|
||||||
const pasteFile = item.getAsFile();
|
|
||||||
const isValid = this.beforeUpload(pasteFile);
|
|
||||||
if (!isValid) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.uploadImage(pasteFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.editor.on("mousedown", () => {
|
|
||||||
this.$store.commit("setRightClickMenuVisible", false);
|
|
||||||
});
|
|
||||||
this.editor.on("blur", () => {
|
|
||||||
//!影响到右键菜单的点击事件,右键菜单的点击事件在组件内通过mousedown触发
|
|
||||||
this.$store.commit("setRightClickMenuVisible", false);
|
|
||||||
});
|
|
||||||
this.editor.on("scroll", () => {
|
|
||||||
this.$store.commit("setRightClickMenuVisible", false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
initCssEditor() {
|
|
||||||
this.initCssEditorEntity();
|
|
||||||
// 自动提示
|
|
||||||
this.cssEditor.on("keyup", (cm, e) => {
|
|
||||||
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
|
|
||||||
cm.showHint(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.cssEditor.on("update", (instance) => {
|
|
||||||
this.cssChanged();
|
|
||||||
saveEditorContent(this.cssEditor, "__css_content");
|
|
||||||
});
|
|
||||||
},
|
|
||||||
cssChanged() {
|
|
||||||
let json = css2json(this.cssEditor.getValue(0));
|
|
||||||
let theme = setFontSize(this.currentSize.replace("px", ""));
|
|
||||||
|
|
||||||
theme = customCssWithTemplate(json, this.currentColor, theme);
|
|
||||||
this.setWxRendererOptions({
|
|
||||||
theme: theme,
|
|
||||||
});
|
|
||||||
this.onEditorRefresh();
|
|
||||||
},
|
|
||||||
beforeUpload(file) {
|
|
||||||
// validate image
|
|
||||||
const checkResult = checkImage(file);
|
|
||||||
if (!checkResult.ok) {
|
|
||||||
this.$message.error(checkResult.msg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check image host
|
|
||||||
let imgHost = localStorage.getItem("imgHost");
|
|
||||||
imgHost = imgHost ? imgHost : "default";
|
|
||||||
localStorage.setItem("imgHost", imgHost);
|
|
||||||
|
|
||||||
const config = localStorage.getItem(`${imgHost}Config`);
|
|
||||||
const isValidHost = imgHost == "default" || config;
|
|
||||||
if (!isValidHost) {
|
|
||||||
this.$message.error(`请先配置 ${imgHost} 图床参数`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
uploadImage(file) {
|
|
||||||
this.isImgLoading = true;
|
|
||||||
toBase64(file)
|
|
||||||
.then((base64Content) => {
|
|
||||||
fileApi
|
|
||||||
.fileUpload(base64Content, file)
|
|
||||||
.then((url) => {
|
|
||||||
this.uploaded(url);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.$message.error(err.message);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.$message.error(err.message);
|
|
||||||
});
|
|
||||||
this.isImgLoading = false;
|
|
||||||
},
|
|
||||||
// 图片上传结束
|
|
||||||
uploaded(response) {
|
|
||||||
if (!response) {
|
|
||||||
this.$message.error("上传图片未知异常");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.dialogUploadImgVisible = false;
|
|
||||||
// 上传成功,获取光标
|
|
||||||
const cursor = this.editor.getCursor();
|
|
||||||
const imageUrl = response;
|
|
||||||
const markdownImage = `![](${imageUrl})`;
|
|
||||||
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
|
|
||||||
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
|
|
||||||
this.$message.success("图片上传成功");
|
|
||||||
this.onEditorRefresh();
|
|
||||||
},
|
|
||||||
// 左右滚动
|
|
||||||
leftAndRightScroll() {
|
|
||||||
const scrollCB = (text) => {
|
|
||||||
let source, target;
|
|
||||||
|
|
||||||
clearTimeout(this.timeout);
|
|
||||||
if (text === "preview") {
|
|
||||||
source = this.$refs.preview.$el;
|
|
||||||
target = document.getElementsByClassName(
|
|
||||||
"CodeMirror-scroll"
|
|
||||||
)[0];
|
|
||||||
this.editor.off("scroll", editorScrollCB);
|
|
||||||
this.timeout = setTimeout(() => {
|
|
||||||
this.editor.on("scroll", editorScrollCB);
|
|
||||||
}, 300);
|
|
||||||
} else if (text === "editor") {
|
|
||||||
source = document.getElementsByClassName(
|
|
||||||
"CodeMirror-scroll"
|
|
||||||
)[0];
|
|
||||||
target = this.$refs.preview.$el;
|
|
||||||
target.removeEventListener(
|
|
||||||
"scroll",
|
|
||||||
previewScrollCB,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
this.timeout = setTimeout(() => {
|
|
||||||
target.addEventListener(
|
|
||||||
"scroll",
|
|
||||||
previewScrollCB,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
let percentage =
|
|
||||||
source.scrollTop /
|
|
||||||
(source.scrollHeight - source.offsetHeight);
|
|
||||||
let height =
|
|
||||||
percentage * (target.scrollHeight - target.offsetHeight);
|
|
||||||
|
|
||||||
target.scrollTo(0, height);
|
|
||||||
};
|
|
||||||
const editorScrollCB = () => {
|
|
||||||
scrollCB("editor");
|
|
||||||
};
|
|
||||||
const previewScrollCB = () => {
|
|
||||||
scrollCB("preview");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$refs.preview.$el.addEventListener(
|
|
||||||
"scroll",
|
|
||||||
previewScrollCB,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
this.editor.on("scroll", editorScrollCB);
|
|
||||||
},
|
|
||||||
// 更新编辑器
|
|
||||||
onEditorRefresh() {
|
|
||||||
this.editorRefresh();
|
|
||||||
setTimeout(() => PR.prettyPrint(), 0);
|
|
||||||
},
|
|
||||||
// 复制结束
|
|
||||||
endCopy() {
|
|
||||||
this.backLight = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.isCoping = false;
|
|
||||||
}, 800);
|
|
||||||
},
|
|
||||||
// 下载编辑器内容到本地
|
|
||||||
downloadEditorContent() {
|
|
||||||
downloadMD(this.editor.getValue(0));
|
|
||||||
},
|
|
||||||
// 格式化文档
|
|
||||||
formatContent() {
|
|
||||||
const doc = formatDoc(this.editor.getValue(0));
|
|
||||||
localStorage.setItem("__editor_content", doc);
|
|
||||||
this.editor.setValue(doc);
|
|
||||||
},
|
|
||||||
// 右键菜单
|
|
||||||
openMenu(e) {
|
|
||||||
const menuMinWidth = 105;
|
|
||||||
const offsetLeft = this.$el.getBoundingClientRect().left;
|
|
||||||
const offsetWidth = this.$el.offsetWidth;
|
|
||||||
const maxLeft = offsetWidth - menuMinWidth;
|
|
||||||
const left = e.clientX - offsetLeft;
|
|
||||||
this.mouseLeft = Math.min(maxLeft, left);
|
|
||||||
this.mouseTop = e.clientY + 10;
|
|
||||||
this.$store.commit("setRightClickMenuVisible", true);
|
|
||||||
},
|
|
||||||
closeRightClickMenu() {
|
|
||||||
this.$store.commit("setRightClickMenuVisible", false);
|
|
||||||
},
|
|
||||||
onMenuEvent(type, info = {}) {
|
|
||||||
switch (type) {
|
|
||||||
case "pageReset":
|
|
||||||
this.$refs.header.showResetConfirm = true;
|
|
||||||
break;
|
|
||||||
case "insertPic":
|
|
||||||
this.dialogUploadImgVisible = true;
|
|
||||||
break;
|
|
||||||
case "download":
|
|
||||||
this.downloadEditorContent();
|
|
||||||
break;
|
|
||||||
case "insertTable":
|
|
||||||
this.dialogFormVisible = true;
|
|
||||||
break;
|
|
||||||
case "formatMarkdown":
|
|
||||||
this.formatContent();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...mapMutations([
|
|
||||||
"initEditorState",
|
|
||||||
"initEditorEntity",
|
|
||||||
"setWxRendererOptions",
|
|
||||||
"editorRefresh",
|
|
||||||
"initCssEditorEntity",
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.leftAndRightScroll();
|
|
||||||
PR.prettyPrint();
|
|
||||||
}, 300);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.main-body {
|
|
||||||
padding-top: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.el-main {
|
|
||||||
transition: all 0.3s;
|
|
||||||
padding: 0;
|
|
||||||
margin: 20px;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
.preview {
|
|
||||||
transition: background 0s;
|
|
||||||
transition-delay: 0.2s;
|
|
||||||
}
|
|
||||||
.preview-wrapper_night {
|
|
||||||
overflow-y: inherit;
|
|
||||||
position: relative;
|
|
||||||
left: -3px;
|
|
||||||
.preview {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#output-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.loading-mask {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 376px;
|
|
||||||
height: 101%;
|
|
||||||
padding-top: 1px;
|
|
||||||
font-size: 15px;
|
|
||||||
color: gray;
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
.loading__img {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 330px;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background: url("../assets/images/favicon.png") no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 390px;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bounceInRight {
|
|
||||||
animation-name: bounceInRight;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
@keyframes bounceInRight {
|
|
||||||
0%,
|
|
||||||
60%,
|
|
||||||
75%,
|
|
||||||
90%,
|
|
||||||
100% {
|
|
||||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
||||||
}
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translate3d(3000px, 0, 0);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate3d(-25px, 0, 0);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: translate3d(10px, 0, 0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
transform: translate3d(-5px, 0, 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style lang="less">
|
|
||||||
@import url("../assets/less/app.less");
|
|
||||||
@import url("../assets/less/style-mirror.css");
|
|
||||||
@import url("../assets/less/github-v2.min.css");
|
|
||||||
</style>
|
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": [
|
||||||
|
"@dcloudio/types",
|
||||||
|
"miniprogram-api-typings",
|
||||||
|
"mini-types"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,20 @@
|
|||||||
module.exports = {
|
const fs = require("fs");
|
||||||
outputDir: "dist",
|
function writeManifestJson() {
|
||||||
publicPath: process.env.NETLIFY ? "/" : "/md/",
|
fs.readFile("./src/manifest.json", function (err, data) {
|
||||||
};
|
if (err) {
|
||||||
|
return console.error(err);
|
||||||
|
}
|
||||||
|
const strData = data.toString();
|
||||||
|
const manifest = JSON.parse(strData);
|
||||||
|
|
||||||
|
manifest.h5.publicPath = process.env.NETLIFY ? "/" : "/md/";
|
||||||
|
const result = JSON.stringify(manifest, null, 4);
|
||||||
|
|
||||||
|
fs.writeFile("./src/manifest.json", result, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
writeManifestJson();
|
||||||
|
Loading…
Reference in New Issue
Block a user