style: Unified repository code style (#135)

This commit is contained in:
xw 2022-02-28 19:09:39 +08:00 committed by GitHub
parent e23b354006
commit 6202ad2ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1233 additions and 1148 deletions

1
.gitignore vendored
View File

@ -17,7 +17,6 @@
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.vscode
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*

4
.husky/pre-commit Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}

9
jsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

View File

@ -10,11 +10,11 @@ const spaceInfo = {
} }
/** /**
* 配置说明请参考文档: * 配置说明请参考文档:
* https://hongqiye.com/doc/mockm/config/option.html * https://hongqiye.com/doc/mockm/config/option.html
* @type {import('mockm/@types/config').Config} * @type {import('mockm/@types/config').Config}
*/ */
module.exports = util => { module.exports = (util) => {
const port = 9000 const port = 9000
return { return {
port, port,
@ -29,26 +29,34 @@ module.exports = util => {
}) })
form.parse(req, async (err, fields = [], files) => { form.parse(req, async (err, fields = [], files) => {
const file = files.file[0] const file = files.file[0]
let url = `http://127.0.0.1:${port}/public/upload/${path.parse(file.path).base}` let url = `http://127.0.0.1:${port}/public/upload/${
path.parse(file.path).base
}`
try { try {
url = await dcloud(spaceInfo)({name: file.originalFilename, file: fs.createReadStream(file.path)}) url = await dcloud(spaceInfo)({
name: file.originalFilename,
file: fs.createReadStream(file.path),
})
} catch (err) { } catch (err) {
// console.log(err) // console.log(err)
} }
res.json({url}) res.json({ url })
}) })
}, },
}, },
static: [ static: [
{ // 测试 netlify 部署 {
// 测试 netlify 部署
fileDir: `../dist`, fileDir: `../dist`,
path: `/`, path: `/`,
}, },
{ // 测试 gitee/github 部署 {
// 测试 gitee/github 部署
fileDir: `../dist`, fileDir: `../dist`,
path: `/md`, path: `/md`,
}, },
{ // 访问公共目录 {
// 访问公共目录
fileDir: `../public`, fileDir: `../public`,
path: `/public`, path: `/public`,
}, },

View File

@ -1,19 +1,23 @@
const fetch = (...args) => import(`node-fetch`).then(({default: fetch}) => fetch(...args)) const fetch = (...args) =>
import(`node-fetch`).then(({ default: fetch }) => fetch(...args))
const FormData = require(`form-data`) const FormData = require(`form-data`)
function dcloud(spaceInfo) { function dcloud(spaceInfo) {
if(Boolean(spaceInfo.spaceId && spaceInfo.clientSecret) === false) { if (Boolean(spaceInfo.spaceId && spaceInfo.clientSecret) === false) {
throw new Error(`请填写 spaceInfo`) throw new Error(`请填写 spaceInfo`)
} }
function sign(data, secret) { function sign(data, secret) {
const hmac = require(`crypto`).createHmac(`md5`, secret) const hmac = require(`crypto`).createHmac(`md5`, secret)
// 排序 obj 再转换为 key=val&key=val 的格式 // 排序 obj 再转换为 key=val&key=val 的格式
const str = Object.keys(data).sort().reduce((acc, cur) => `${acc}&${cur}=${data[cur]}`, ``).slice(1) const str = Object.keys(data)
.sort()
.reduce((acc, cur) => `${acc}&${cur}=${data[cur]}`, ``)
.slice(1)
hmac.update(str) hmac.update(str)
return hmac.digest(`hex`) return hmac.digest(`hex`)
} }
async function anonymousAuthorize() { async function anonymousAuthorize() {
const data = { const data = {
method: `serverless.auth.user.anonymousAuthorize`, method: `serverless.auth.user.anonymousAuthorize`,
@ -29,7 +33,7 @@ function dcloud(spaceInfo) {
method: `POST`, method: `POST`,
}).then((res) => res.json()) }).then((res) => res.json())
} }
async function report({ id, token }) { async function report({ id, token }) {
const reportReq = { const reportReq = {
method: `serverless.file.resource.report`, method: `serverless.file.resource.report`,
@ -47,7 +51,7 @@ function dcloud(spaceInfo) {
method: `POST`, method: `POST`,
}).then((res) => res.json()) }).then((res) => res.json())
} }
async function generateProximalSign({ name, token }) { async function generateProximalSign({ name, token }) {
const data = { const data = {
method: `serverless.file.resource.generateProximalSign`, method: `serverless.file.resource.generateProximalSign`,
@ -66,7 +70,7 @@ function dcloud(spaceInfo) {
}).then((res) => res.json()) }).then((res) => res.json())
return res return res
} }
async function upload({ data, file }) { async function upload({ data, file }) {
const formdata = new FormData() const formdata = new FormData()
Object.entries({ Object.entries({
@ -81,7 +85,7 @@ function dcloud(spaceInfo) {
success_action_status: 200, success_action_status: 200,
file, file,
}).forEach(([key, val]) => formdata.append(key, val)) }).forEach(([key, val]) => formdata.append(key, val))
return await fetch(`https://${data.host}`, { return await fetch(`https://${data.host}`, {
headers: { headers: {
'X-OSS-server-side-encrpytion': `AES256`, 'X-OSS-server-side-encrpytion': `AES256`,
@ -90,7 +94,7 @@ function dcloud(spaceInfo) {
method: `POST`, method: `POST`,
}) })
} }
async function uploadFile({ name = `unnamed.file`, file }) { async function uploadFile({ name = `unnamed.file`, file }) {
const token = (await anonymousAuthorize()).data.accessToken const token = (await anonymousAuthorize()).data.accessToken
const res = await generateProximalSign({ name, token }) const res = await generateProximalSign({ name, token })
@ -99,9 +103,8 @@ function dcloud(spaceInfo) {
const fileUrl = `https://${res.data.cdnDomain}/${res.data.ossPath}` const fileUrl = `https://${res.data.cdnDomain}/${res.data.ossPath}`
return fileUrl return fileUrl
} }
return uploadFile return uploadFile
} }
module.exports = { module.exports = {

View File

@ -3,6 +3,8 @@
"version": "1.5.7", "version": "1.5.7",
"private": false, "private": false,
"scripts": { "scripts": {
"prepare": "husky install",
"lint": "vue-cli-service lint src && vue-cli-service lint mm",
"start": "run-p serve mm", "start": "run-p serve mm",
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build:h5-netlify": "cross-env SERVER_ENV=NETLIFY vue-cli-service build", "build:h5-netlify": "cross-env SERVER_ENV=NETLIFY vue-cli-service build",
@ -10,6 +12,43 @@
"build-cli": "npm run build && npx shx rm -rf md-cli/dist && npx shx rm -rf dist/**/*.map && npx shx cp -r dist md-cli/ && cd md-cli && npm pack", "build-cli": "npm run build && npx shx rm -rf md-cli/dist && npx shx rm -rf dist/**/*.map && npx shx cp -r dist md-cli/ && cd md-cli && npm pack",
"mm": "npx mockm --cwd=mm" "mm": "npx mockm --cwd=mm"
}, },
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended",
"@vue/prettier"
],
"parserOptions": {
"parser": "babel-eslint"
},
"ignorePatterns": [
"src/assets/scripts/renderers"
],
"rules": {
"prettier/prettier": [
"error",
{
"singleQuote": true,
"semi": false
}
],
"semi": [
"error",
"never"
],
"quotes": [
"error",
"backtick"
],
"no-unused-vars": "off",
"no-console": "off",
"no-debugger": "off"
}
},
"dependencies": { "dependencies": {
"@vue/shared": "^3.2.26", "@vue/shared": "^3.2.26",
"ali-oss": "^6.16.0", "ali-oss": "^6.16.0",
@ -37,6 +76,15 @@
"vuex": "^3.6.2" "vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-eslint": "^4.5.12",
"@vue/cli-plugin-router": "^4.5.12",
"@vue/cli-plugin-vuex": "^4.5.12",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^6.2.2",
"husky": "^7.0.4",
"@vue/cli-plugin-babel": "~4.5.15", "@vue/cli-plugin-babel": "~4.5.15",
"@vue/cli-service": "~4.5.15", "@vue/cli-service": "~4.5.15",
"async-validator": "^4.0.7", "async-validator": "^4.0.7",

View File

@ -18,8 +18,8 @@ body,
<style lang="less"> <style lang="less">
/* 每个页面公共css */ /* 每个页面公共css */
@import url("./assets/less/style-mirror.css"); @import url('./assets/less/style-mirror.css');
@import url("./assets/less/theme.less"); @import url('./assets/less/theme.less');
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; width: 6px;

View File

@ -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 }

View File

@ -1,30 +1,30 @@
import axios from "axios"; import axios from 'axios'
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
baseURL: "", baseURL: ``,
timeout: 30 * 1000, // 请求超时时间 timeout: 30 * 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; return config
}, },
(error) => { (error) => {
Promise.reject(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

View File

@ -1,47 +1,47 @@
import fetch from "./fetch"; import fetch from './fetch'
import { githubConfig, giteeConfig } from "./config"; import { githubConfig, giteeConfig } from './config'
import CryptoJS from "crypto-js"; import CryptoJS from 'crypto-js'
import OSS from "ali-oss"; import OSS from 'ali-oss'
import * as Minio from "minio"; import * as Minio from 'minio'
import COS from "cos-js-sdk-v5"; import COS from 'cos-js-sdk-v5'
import Buffer from "buffer-from"; import Buffer from 'buffer-from'
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from 'uuid'
import * as qiniu from "qiniu-js"; import * as qiniu from 'qiniu-js'
import { utf16to8, base64encode, safe64 } from "../assets/scripts/tokenTools"; import { utf16to8, base64encode, safe64 } from '../assets/scripts/tokenTools'
import * as tokenTools from "../assets/scripts/tokenTools"; import * as tokenTools 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,
}; }
} }
/** /**
@ -49,11 +49,11 @@ function getConfig(useDefault, platform) {
* @returns string * @returns string
*/ */
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}`
} }
/** /**
@ -62,9 +62,9 @@ function getDir() {
* @returns * @returns
*/ */
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}`
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -72,17 +72,17 @@ 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}`,
}, },
@ -91,13 +91,13 @@ async function ghFileUpload(content, filename) {
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}/`
res.content = res.data?.content || res.content; res.content = res.data?.content || res.content
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
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -105,26 +105,23 @@ 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, `gitee`)
useDefault, const dir = getDir()
"gitee" const dateFilename = getDateFilename(filename)
); const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${dir}/${dateFilename}`
const dir = getDir();
const dateFilename = getDateFilename(filename);
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}`,
}, },
}); })
res.content = res.data?.content || res.content; res.content = res.data?.content || res.content
return encodeURI(res.content.download_url); return encodeURI(res.content.download_url)
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -132,37 +129,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}`)
}, },
}); })
}); })
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -170,23 +167,23 @@ async function qiniuUpload(file) {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
async function aliOSSFileUpload(content, filename) { async function aliOSSFileUpload(content, filename) {
const dateFilename = getDateFilename(filename); const dateFilename = getDateFilename(filename)
const { region, bucket, accessKeyId, accessKeySecret, cdnHost, path } = const { region, bucket, accessKeyId, accessKeySecret, cdnHost, 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)
} }
} }
@ -195,14 +192,14 @@ 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({ const cos = new COS({
SecretId: secretId, SecretId: secretId,
SecretKey: secretKey, SecretKey: secretKey,
}); })
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
cos.putObject( cos.putObject(
{ {
@ -213,19 +210,19 @@ async function txCOSFileUpload(file) {
}, },
function (err, data) { function (err, data) {
if (err) { if (err) {
reject(err); reject(err)
} else if (cdnHost) { } else if (cdnHost) {
resolve( resolve(
path == "" path == ``
? `${cdnHost}/${dateFilename}` ? `${cdnHost}/${dateFilename}`
: `${cdnHost}/${path}/${dateFilename}` : `${cdnHost}/${path}/${dateFilename}`
); )
} else { } else {
resolve(`https://${data.Location}`); resolve(`https://${data.Location}`)
} }
} }
); )
}); })
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -233,41 +230,41 @@ async function txCOSFileUpload(file) {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
async function minioFileUpload(content, filename) { async function minioFileUpload(content, filename) {
const dateFilename = getDateFilename(filename); const dateFilename = getDateFilename(filename)
const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse( const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse(
localStorage.getItem("minioConfig") localStorage.getItem(`minioConfig`)
); )
const buffer = Buffer(content, "base64"); const buffer = Buffer(content, `base64`)
const conf = { const conf = {
endPoint: endpoint, endPoint: endpoint,
useSSL: useSSL, useSSL: useSSL,
accessKey: accessKey, accessKey: accessKey,
secretKey: secretKey, secretKey: secretKey,
}; }
const p = Number(port || 0); const p = Number(port || 0)
const isCustomPort = p > 0 && p !== 80 && p !== 443; const isCustomPort = p > 0 && p !== 80 && p !== 443
if (isCustomPort) { if (isCustomPort) {
conf.port = p; conf.port = p
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const minioClient = new Minio.Client(conf); const minioClient = new Minio.Client(conf)
try { try {
minioClient.putObject(bucket, dateFilename, buffer, function (e) { minioClient.putObject(bucket, dateFilename, buffer, function (e) {
if (e) { if (e) {
reject(e); reject(e)
} }
const host = `${useSSL ? "https://" : "http://"}${endpoint}${ const host = `${useSSL ? `https://` : `http://`}${endpoint}${
isCustomPort ? ":" + port : "" isCustomPort ? `:` + port : ``
}`; }`
const url = `${host}/${bucket}/${dateFilename}`; const url = `${host}/${bucket}/${dateFilename}`
// console.log("文件上传成功: ", url) // console.log("文件上传成功: ", url)
resolve(url); resolve(url)
// return `${endpoint}/${bucket}/${dateFilename}`; // return `${endpoint}/${bucket}/${dateFilename}`;
}); })
} catch (e) { } catch (e) {
reject(e); reject(e)
} }
}); })
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -279,7 +276,7 @@ async function formCustomUpload(content, file) {
async (CUSTOM_ARG) => { async (CUSTOM_ARG) => {
${localStorage.getItem(`formCustomConfig`)} ${localStorage.getItem(`formCustomConfig`)}
} }
`; `
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const exportObj = { const exportObj = {
content, // 待上传图片的 base64 content, // 待上传图片的 base64
@ -298,40 +295,40 @@ async function formCustomUpload(content, file) {
}, },
okCb: resolve, // 重要: 上传成功后给此回调传 url 即可 okCb: resolve, // 重要: 上传成功后给此回调传 url 即可
errCb: reject, // 上传失败调用的函数 errCb: reject, // 上传失败调用的函数
}; }
eval(str)(exportObj).catch((err) => { eval(str)(exportObj).catch((err) => {
console.error(err); console.error(err)
reject(err); reject(err)
}); })
}); })
} }
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 "minio": case `minio`:
return minioFileUpload(content, file.name); return minioFileUpload(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)
case "formCustom": case `formCustom`:
return formCustomUpload(content, file); return formCustomUpload(content, file)
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,
}; }

View File

@ -1,89 +1,87 @@
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: "衬线", label: `衬线`,
value: value: `Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif`,
"Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif",
}, },
], ],
sizeOption: [ sizeOption: [
{ {
label: "12px", label: `12px`,
value: "12px", value: `12px`,
desc: "更小", desc: `更小`,
}, },
{ {
label: "13px", label: `13px`,
value: "13px", value: `13px`,
desc: "稍小", desc: `稍小`,
}, },
{ {
label: "14px", label: `14px`,
value: "14px", value: `14px`,
desc: "推荐", desc: `推荐`,
}, },
{ {
label: "15px", label: `15px`,
value: "15px", value: `15px`,
desc: "稍大", desc: `稍大`,
}, },
{ {
label: "16px", label: `16px`,
value: "16px", value: `16px`,
desc: "更大", desc: `更大`,
}, },
], ],
colorOption: [ colorOption: [
{ {
label: "经典蓝", label: `经典蓝`,
value: "rgba(15, 76, 129, 1)", value: `rgba(15, 76, 129, 1)`,
desc: "最新流行", desc: `最新流行`,
}, },
{ {
label: "翡翠绿", label: `翡翠绿`,
value: "rgba(0, 152, 116, 1)", value: `rgba(0, 152, 116, 1)`,
desc: "优雅清新", desc: `优雅清新`,
}, },
{ {
label: "活力橘", label: `活力橘`,
value: "rgba(250, 81, 81, 1)", value: `rgba(250, 81, 81, 1)`,
desc: "热情活泼", desc: `热情活泼`,
}, },
], ],
codeThemeOption: [ codeThemeOption: [
{ {
label: "github", label: `github`,
value: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/github.min.css", value: `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/github.min.css`,
desc: "light", desc: `light`,
}, },
{ {
label: "solarized-light", label: `solarized-light`,
value: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/base16/solarized-light.min.css", value: `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/base16/solarized-light.min.css`,
desc: "light", desc: `light`,
}, },
{ {
label: "atom-one-dark", label: `atom-one-dark`,
value: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/atom-one-dark.min.css", value: `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/atom-one-dark.min.css`,
desc: "dark", desc: `dark`,
}, },
{ {
label: "obsidian", label: `obsidian`,
value: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/obsidian.min.css", value: `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/obsidian.min.css`,
desc: "dark", desc: `dark`,
}, },
{ {
label: "vs2015", label: `vs2015`,
value: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/vs2015.min.css", value: `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/vs2015.min.css`,
desc: "dark", desc: `dark`,
}, },
], ],
form: { form: {
rows: 1, rows: 1,
cols: 1, cols: 1,
}, },
}; }

View File

@ -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
} }

View File

@ -1,192 +1,192 @@
let baseColor = "#3f3f3f" let baseColor = `#3f3f3f`
export default { export default {
BASE: { BASE: {
"text-align": "left", 'text-align': `left`,
"line-height": "1.75" 'line-height': `1.75`,
}, },
block: { block: {
// 一级标题样式 // 一级标题样式
h1: { h1: {
"font-size": "1.2em", 'font-size': `1.2em`,
"text-align": "center", 'text-align': `center`,
"font-weight": "bold", 'font-weight': `bold`,
display: "table", display: `table`,
margin: "2em auto 1em", margin: `2em auto 1em`,
padding: "0 1em", padding: `0 1em`,
"border-bottom": "2px solid rgba(0, 152, 116, 0.9)", 'border-bottom': `2px solid rgba(0, 152, 116, 0.9)`,
color: baseColor, color: baseColor,
}, },
// 二级标题样式 // 二级标题样式
h2: { h2: {
"font-size": "1.2em", 'font-size': `1.2em`,
"text-align": "center", 'text-align': `center`,
"font-weight": "bold", 'font-weight': `bold`,
display: "table", display: `table`,
margin: "4em auto 2em", margin: `4em auto 2em`,
padding: "0 0.2em", padding: `0 0.2em`,
background: "rgba(0, 152, 116, 0.9)", background: `rgba(0, 152, 116, 0.9)`,
color: "#fff", color: `#fff`,
}, },
// 三级标题样式 // 三级标题样式
h3: { h3: {
"font-weight": "bold", 'font-weight': `bold`,
"font-size": "1.1em", 'font-size': `1.1em`,
margin: "2em 8px 0.75em 0", margin: `2em 8px 0.75em 0`,
"line-height": "1.2", 'line-height': `1.2`,
"padding-left": "8px", 'padding-left': `8px`,
"border-left": "3px solid rgba(0, 152, 116, 0.9)", 'border-left': `3px solid rgba(0, 152, 116, 0.9)`,
color: baseColor, color: baseColor,
}, },
// 四级标题样式 // 四级标题样式
h4: { h4: {
"font-weight": "bold", 'font-weight': `bold`,
"font-size": "1em", 'font-size': `1em`,
margin: "2em 8px 0.5em", margin: `2em 8px 0.5em`,
color: "rgba(66, 185, 131, 0.9)", color: `rgba(66, 185, 131, 0.9)`,
}, },
// 段落样式 // 段落样式
p: { p: {
margin: "1.5em 8px", margin: `1.5em 8px`,
"letter-spacing": "0.1em", 'letter-spacing': `0.1em`,
color: baseColor, color: baseColor,
}, },
// 引用样式 // 引用样式
blockquote: { blockquote: {
"font-style": "normal", 'font-style': `normal`,
"border-left": "none", 'border-left': `none`,
padding: "1em", padding: `1em`,
"border-radius": "8px", 'border-radius': `8px`,
color: "rgba(0,0,0,0.5)", color: `rgba(0,0,0,0.5)`,
background: "#f7f7f7", background: `#f7f7f7`,
margin: "2em 8px", margin: `2em 8px`,
}, },
blockquote_p: { blockquote_p: {
"letter-spacing": "0.1em", 'letter-spacing': `0.1em`,
color: "rgb(80, 80, 80)", color: `rgb(80, 80, 80)`,
"font-size": "1em", 'font-size': `1em`,
display: "block", display: `block`,
}, },
code_pre: { code_pre: {
"font-size": "14px", 'font-size': `14px`,
"overflow-x": "auto", 'overflow-x': `auto`,
"border-radius": "8px", 'border-radius': `8px`,
padding: "1em", padding: `1em`,
"line-height": "1.5", 'line-height': `1.5`,
margin: "10px 8px" margin: `10px 8px`,
}, },
code: { code: {
"margin": 0, margin: 0,
"white-space": "nowrap", 'white-space': `nowrap`,
"font-family": "Menlo, Operator Mono, Consolas, Monaco, monospace" 'font-family': `Menlo, Operator Mono, Consolas, Monaco, monospace`,
}, },
image: { image: {
"border-radius": "4px", 'border-radius': `4px`,
display: "block", display: `block`,
margin: "0.1em auto 0.5em", margin: `0.1em auto 0.5em`,
width: "100% !important", width: `100% !important`,
}, },
ol: { ol: {
"margin-left": "0", 'margin-left': `0`,
"padding-left": "1em", 'padding-left': `1em`,
color: baseColor, color: baseColor,
}, },
ul: { ul: {
"margin-left": "0", 'margin-left': `0`,
"padding-left": "1em", 'padding-left': `1em`,
"list-style": "circle", 'list-style': `circle`,
color: baseColor, color: baseColor,
}, },
footnotes: { footnotes: {
margin: "0.5em 8px", margin: `0.5em 8px`,
"font-size": "80%", 'font-size': `80%`,
color: baseColor, color: baseColor,
}, },
figure: { figure: {
margin: "1.5em 8px", margin: `1.5em 8px`,
color: baseColor, color: baseColor,
}, },
hr: { hr: {
"border-style": "solid", 'border-style': `solid`,
"border-width": "1px 0 0", 'border-width': `1px 0 0`,
"border-color": "rgba(0,0,0,0.1)", 'border-color': `rgba(0,0,0,0.1)`,
"-webkit-transform-origin": "0 0", '-webkit-transform-origin': `0 0`,
"-webkit-transform": "scale(1, 0.5)", '-webkit-transform': `scale(1, 0.5)`,
"transform-origin": "0 0", 'transform-origin': `0 0`,
transform: "scale(1, 0.5)", transform: `scale(1, 0.5)`,
}, },
}, },
inline: { inline: {
listitem: { listitem: {
"text-indent": "-1em", 'text-indent': `-1em`,
display: "block", display: `block`,
margin: "0.2em 8px", margin: `0.2em 8px`,
color: baseColor, color: baseColor,
}, },
codespan: { codespan: {
"font-size": "90%", 'font-size': `90%`,
"white-space": "pre", 'white-space': `pre`,
color: "#d14", color: `#d14`,
background: "rgba(27,31,35,.05)", background: `rgba(27,31,35,.05)`,
padding: "3px 5px", padding: `3px 5px`,
"border-radius": "4px", 'border-radius': `4px`,
}, },
link: { link: {
color: "#576b95", color: `#576b95`,
}, },
wx_link: { wx_link: {
color: "#576b95", color: `#576b95`,
"text-decoration": "none", 'text-decoration': `none`,
}, },
// 字体加粗样式 // 字体加粗样式
strong: { strong: {
color: "rgba(15, 76, 129, 0.9)", color: `rgba(15, 76, 129, 0.9)`,
"font-weight": "bold", 'font-weight': `bold`,
}, },
table: { table: {
"border-collapse": "collapse", 'border-collapse': `collapse`,
"text-align": "center", 'text-align': `center`,
margin: "1em 8px", margin: `1em 8px`,
color: baseColor, color: baseColor,
}, },
thead: { thead: {
background: "rgba(0, 0, 0, 0.05)", background: `rgba(0, 0, 0, 0.05)`,
"font-weight": "bold", 'font-weight': `bold`,
color: baseColor, color: baseColor,
}, },
td: { td: {
border: "1px solid #dfdfdf", border: `1px solid #dfdfdf`,
padding: "0.25em 0.5em", padding: `0.25em 0.5em`,
color: baseColor, color: baseColor,
}, },
footnote: { footnote: {
"font-size": "12px", 'font-size': `12px`,
color: baseColor, color: baseColor,
}, },
figcaption: { figcaption: {
"text-align": "center", 'text-align': `center`,
color: "#888", color: `#888`,
"font-size": "0.8em", 'font-size': `0.8em`,
}, },
}, },
}; }

View File

@ -1,31 +1,31 @@
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:
@ -36,29 +36,28 @@ export function utf8to16(str) {
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) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0) ((c & 0x0f) << 12) | ((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,
@ -188,78 +187,78 @@ const base64DecodeChars = new Array(
-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++); c2 = str.charCodeAt(i++)
if (i == len) { if (i == len) {
out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(c1 >> 2)
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4))
out += base64EncodeChars.charAt((c2 & 0xf) << 2); out += base64EncodeChars.charAt((c2 & 0xf) << 2)
out += "="; out += `=`
break; break
} }
c3 = str.charCodeAt(i++); c3 = str.charCodeAt(i++)
out += base64EncodeChars.charAt(c1 >> 2); out += base64EncodeChars.charAt(c1 >> 2)
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4))
out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6)); out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6))
out += base64EncodeChars.charAt(c3 & 0x3f); out += base64EncodeChars.charAt(c3 & 0x3f)
} }
return out; 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
} }

View File

@ -1,102 +1,105 @@
import defaultTheme from "./themes/default-theme"; import defaultTheme from './themes/default-theme'
import prettier from "prettier/standalone"; import prettier from 'prettier/standalone'
import prettierMarkdown from "prettier/parser-markdown"; import prettierMarkdown from 'prettier/parser-markdown'
import prettierCss from "prettier/parser-postcss"; import prettierCss from 'prettier/parser-postcss'
// 设置自定义颜色 // 设置自定义颜色
export function setColorWithTemplate(template) { export function setColorWithTemplate(template) {
return function (color) { return function (color) {
let customTheme = JSON.parse(JSON.stringify(template)); let customTheme = JSON.parse(JSON.stringify(template))
customTheme.block.h1["border-bottom"] = `2px solid ${color}`; customTheme.block.h1[`border-bottom`] = `2px solid ${color}`
customTheme.block.h2["background"] = color; customTheme.block.h2[`background`] = color
customTheme.block.h3["border-left"] = `3px solid ${color}`; customTheme.block.h3[`border-left`] = `3px solid ${color}`
customTheme.block.h4["color"] = color; customTheme.block.h4[`color`] = color
customTheme.inline.strong["color"] = color; customTheme.inline.strong[`color`] = color
return customTheme; return customTheme
}; }
} }
export const setColorWithCustomTemplate = function setColorWithCustomTemplate( export const setColorWithCustomTemplate = function setColorWithCustomTemplate(
template, template,
color color
) { ) {
let customTheme = JSON.parse(JSON.stringify(template)); let customTheme = JSON.parse(JSON.stringify(template))
customTheme.block.h1["border-bottom"] = `2px solid ${color}`; customTheme.block.h1[`border-bottom`] = `2px solid ${color}`
customTheme.block.h2["background"] = color; customTheme.block.h2[`background`] = color
customTheme.block.h3["border-left"] = `3px solid ${color}`; customTheme.block.h3[`border-left`] = `3px solid ${color}`
customTheme.block.h4["color"] = color; customTheme.block.h4[`color`] = color
customTheme.inline.strong["color"] = color; customTheme.inline.strong[`color`] = color
return customTheme; return customTheme
}; }
// 设置自定义字体大小 // 设置自定义字体大小
export function setFontSizeWithTemplate(template) { export function setFontSizeWithTemplate(template) {
return function (fontSize) { return function (fontSize) {
let customTheme = JSON.parse(JSON.stringify(template)); let customTheme = JSON.parse(JSON.stringify(template))
customTheme.block.h1["font-size"] = `${fontSize * 1.14}px`; customTheme.block.h1[`font-size`] = `${fontSize * 1.14}px`
customTheme.block.h2["font-size"] = `${fontSize * 1.1}px`; customTheme.block.h2[`font-size`] = `${fontSize * 1.1}px`
customTheme.block.h3["font-size"] = `${fontSize}px`; customTheme.block.h3[`font-size`] = `${fontSize}px`
customTheme.block.h4["font-size"] = `${fontSize}px`; customTheme.block.h4[`font-size`] = `${fontSize}px`
return customTheme; return customTheme
}; }
} }
export const setColor = setColorWithTemplate(defaultTheme); export const setColor = setColorWithTemplate(defaultTheme)
export const setFontSize = setFontSizeWithTemplate(defaultTheme); export const setFontSize = setFontSizeWithTemplate(defaultTheme)
export function customCssWithTemplate(jsonString, color, theme) { export function customCssWithTemplate(jsonString, color, theme) {
let customTheme = JSON.parse(JSON.stringify(theme)); let customTheme = JSON.parse(JSON.stringify(theme))
// block // block
customTheme.block.h1["border-bottom"] = `2px solid ${color}`; customTheme.block.h1[`border-bottom`] = `2px solid ${color}`
customTheme.block.h2["background"] = color; customTheme.block.h2[`background`] = color
customTheme.block.h3["border-left"] = `3px solid ${color}`; customTheme.block.h3[`border-left`] = `3px solid ${color}`
customTheme.block.h4["color"] = color; customTheme.block.h4[`color`] = color
customTheme.inline.strong["color"] = color; customTheme.inline.strong[`color`] = color
customTheme.block.h1 = Object.assign(customTheme.block.h1, jsonString.h1); customTheme.block.h1 = Object.assign(customTheme.block.h1, jsonString.h1)
customTheme.block.h2 = Object.assign(customTheme.block.h2, jsonString.h2); customTheme.block.h2 = Object.assign(customTheme.block.h2, jsonString.h2)
customTheme.block.h3 = Object.assign(customTheme.block.h3, jsonString.h3); customTheme.block.h3 = Object.assign(customTheme.block.h3, jsonString.h3)
customTheme.block.h4 = Object.assign(customTheme.block.h4, jsonString.h4); customTheme.block.h4 = Object.assign(customTheme.block.h4, jsonString.h4)
customTheme.block.code = Object.assign(customTheme.block.code, jsonString.code); customTheme.block.code = Object.assign(
customTheme.block.p = Object.assign(customTheme.block.p, jsonString.p); customTheme.block.code,
customTheme.block.hr = Object.assign(customTheme.block.hr, jsonString.hr); jsonString.code
)
customTheme.block.p = Object.assign(customTheme.block.p, jsonString.p)
customTheme.block.hr = Object.assign(customTheme.block.hr, jsonString.hr)
customTheme.block.blockquote = Object.assign( customTheme.block.blockquote = Object.assign(
customTheme.block.blockquote, customTheme.block.blockquote,
jsonString.blockquote jsonString.blockquote
); )
customTheme.block.blockquote_p = Object.assign( customTheme.block.blockquote_p = Object.assign(
customTheme.block.blockquote_p, customTheme.block.blockquote_p,
jsonString.blockquote_p jsonString.blockquote_p
); )
customTheme.block.image = Object.assign( customTheme.block.image = Object.assign(
customTheme.block.image, customTheme.block.image,
jsonString.image jsonString.image
); )
// inline // inline
customTheme.inline.strong = Object.assign( customTheme.inline.strong = Object.assign(
customTheme.inline.strong, customTheme.inline.strong,
jsonString.strong jsonString.strong
); )
customTheme.inline.codespan = Object.assign( customTheme.inline.codespan = Object.assign(
customTheme.inline.codespan, customTheme.inline.codespan,
jsonString.codespan jsonString.codespan
); )
customTheme.inline.link = Object.assign( customTheme.inline.link = Object.assign(
customTheme.inline.link, customTheme.inline.link,
jsonString.link jsonString.link
); )
customTheme.inline.wx_link = Object.assign( customTheme.inline.wx_link = Object.assign(
customTheme.inline.wx_link, customTheme.inline.wx_link,
jsonString.wx_link jsonString.wx_link
); )
customTheme.block.ul = Object.assign(customTheme.block.ul, jsonString.ul); customTheme.block.ul = Object.assign(customTheme.block.ul, jsonString.ul)
customTheme.block.ol = Object.assign(customTheme.block.ol, jsonString.ol); customTheme.block.ol = Object.assign(customTheme.block.ol, jsonString.ol)
customTheme.inline.listitem = Object.assign( customTheme.inline.listitem = Object.assign(
customTheme.inline.listitem, customTheme.inline.listitem,
jsonString.li jsonString.li
); )
return customTheme; return customTheme
} }
/** /**
@ -106,21 +109,21 @@ export function customCssWithTemplate(jsonString, color, theme) {
*/ */
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 = {}; let json = {}
while (css.length > 0 && css.indexOf("{") !== -1 && css.indexOf("}") !== -1) { while (css.length > 0 && css.indexOf(`{`) !== -1 && css.indexOf(`}`) !== -1) {
// 存储第一个左/右花括号的下标 // 存储第一个左/右花括号的下标
const lbracket = css.indexOf("{"); const lbracket = css.indexOf(`{`)
const rbracket = css.indexOf("}"); const rbracket = css.indexOf(`}`)
// 第一步将声明转换为Object // 第一步将声明转换为Object
// `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;` // `font: 'Times New Roman' 1em; color: #ff0000; margin-top: 1em;`
@ -128,26 +131,27 @@ export function css2json(css) {
// `{"font": "'Times New Roman' 1em", "color": "#ff0000", "margin-top": "1em"}` // `{"font": "'Times New Roman' 1em", "color": "#ff0000", "margin-top": "1em"}`
// 辅助方法将array转为object // 辅助方法将array转为object
// eslint-disable-next-line no-inner-declarations
function toObject(array) { function toObject(array) {
let ret = {}; let ret = {}
array.forEach((e) => { array.forEach((e) => {
const index = e.indexOf(":"); const index = e.indexOf(`:`)
const property = e.substring(0, index).trim(); const property = e.substring(0, index).trim()
const value = e.substring(index + 1).trim(); const value = e.substring(index + 1).trim()
ret[property] = value; ret[property] = value
}); })
return ret; return ret
} }
// 切割声明块并移除空白符,然后放入数组中 // 切割声明块并移除空白符,然后放入数组中
let declarations = css let declarations = css
.substring(lbracket + 1, rbracket) .substring(lbracket + 1, rbracket)
.split(";") .split(`;`)
.map((e) => e.trim()) .map((e) => e.trim())
.filter((e) => e.length > 0); // 移除所有""空值 .filter((e) => e.length > 0) // 移除所有""空值
// 转为Object对象 // 转为Object对象
declarations = toObject(declarations); declarations = toObject(declarations)
// 第二步:选择器处理,每个选择器会与它对应的声明相关联,如: // 第二步:选择器处理,每个选择器会与它对应的声明相关联,如:
// `h1, p#bar {color: red}` // `h1, p#bar {color: red}`
@ -157,25 +161,25 @@ export function css2json(css) {
let selectors = css let selectors = css
.substring(0, lbracket) .substring(0, lbracket)
// 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"] // 以,切割,并移除空格:`"h1, p#bar, span.foo"` => ["h1", "p#bar", "span.foo"]
.split(",") .split(`,`)
.map((selector) => selector.trim()); .map((selector) => selector.trim())
// 迭代赋值 // 迭代赋值
selectors.forEach((selector) => { selectors.forEach((selector) => {
// 若不存在,则先初始化 // 若不存在,则先初始化
if (!json[selector]) json[selector] = {}; if (!json[selector]) json[selector] = {}
// 赋值到JSON // 赋值到JSON
Object.keys(declarations).forEach((key) => { Object.keys(declarations).forEach((key) => {
json[selector][key] = declarations[key]; json[selector][key] = declarations[key]
}); })
}); })
// 继续下个声明块 // 继续下个声明块
css = css.slice(rbracket + 1).trim(); css = css.slice(rbracket + 1).trim()
} }
// 返回JSON形式的结果串 // 返回JSON形式的结果串
return json; return json
} }
/** /**
@ -184,11 +188,11 @@ 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)
} }
} }
@ -198,10 +202,10 @@ export function saveEditorContent(editor, name) {
*/ */
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
} }
/** /**
@ -210,10 +214,10 @@ export function formatDoc(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
} }
/** /**
@ -221,72 +225,73 @@ export function formatCss(content) {
* @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)
} }
/** /**
* 导出 HTML 生成内容 * 导出 HTML 生成内容
*/ */
export function exportHTML() { export function exportHTML() {
const element = document.querySelector("#output"); const element = document.querySelector(`#output`)
setStyles(element); setStyles(element)
const htmlStr = element.innerHTML; const htmlStr = element.innerHTML
const downLink = document.createElement("a"); const downLink = document.createElement(`a`)
downLink.download = "content.html"; downLink.download = `content.html`
downLink.style.display = "none"; downLink.style.display = `none`
let blob = new Blob([ let blob = new Blob([
`<html><head><meta charset="utf-8" /></head><body><div style="width: 750px; margin: auto;">${htmlStr}</div></body></html>`, `<html><head><meta charset="utf-8" /></head><body><div style="width: 750px; margin: auto;">${htmlStr}</div></body></html>`,
]); ])
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)
function setStyles(element) { function setStyles(element) {
switch (true) { switch (true) {
case isPre(element): case isPre(element):
case isCode(element): case isCode(element):
case isSpan(element): case isSpan(element):
element.setAttribute("style", getElementStyles(element)); element.setAttribute(`style`, getElementStyles(element))
// eslint-disable-next-line no-fallthrough
default: default:
} }
if (element.children.length) { if (element.children.length) {
Array.from(element.children).forEach((child) => setStyles(child)); Array.from(element.children).forEach((child) => setStyles(child))
} }
// 判断是否是包裹代码块的 pre 元素 // 判断是否是包裹代码块的 pre 元素
function isPre(element) { function isPre(element) {
return ( return (
element.tagName === "PRE" && element.tagName === `PRE` &&
Array.from(element.classList).includes("code__pre") Array.from(element.classList).includes(`code__pre`)
); )
} }
// 判断是否是包裹代码块的 code 元素 // 判断是否是包裹代码块的 code 元素
function isCode(element) { function isCode(element) {
return ( return (
element.tagName === "CODE" && element.tagName === `CODE` &&
Array.from(element.classList).includes("prettyprint") Array.from(element.classList).includes(`prettyprint`)
); )
} }
// 判断是否是包裹代码字符的 span 元素 // 判断是否是包裹代码字符的 span 元素
function isSpan(element) { function isSpan(element) {
return ( return (
element.tagName === "SPAN" && element.tagName === `SPAN` &&
(isCode(element.parentElement) || (isCode(element.parentElement) ||
isCode(element.parentElement.parentElement)) isCode(element.parentElement.parentElement))
); )
} }
} }
} }
@ -298,52 +303,52 @@ export function exportHTML() {
* @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 += currRow.join(`\t|\t`)
table += "\t|\n"; 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 = 10; const maxSize = 10
const valid = file.size / 1024 / 1024 <= maxSize; const valid = file.size / 1024 / 1024 <= maxSize
if (!valid) { if (!valid) {
return { return {
ok: false, ok: false,
msg: `由于公众号限制,图片大小不能超过 ${maxSize}M`, msg: `由于公众号限制,图片大小不能超过 ${maxSize}M`,
}; }
} }
return { ok: true }; return { ok: true }
} }
/** /**
@ -352,12 +357,12 @@ export function checkImage(file) {
* @param {排除的属性} excludes 如果某些属性对结果有不良影响可以使用这个参数来排除 * @param {排除的属性} excludes 如果某些属性对结果有不良影响可以使用这个参数来排除
* @returns 行内样式拼接结果 * @returns 行内样式拼接结果
*/ */
function getElementStyles(element, excludes = ["width", "height"]) { function getElementStyles(element, excludes = [`width`, `height`]) {
const styles = getComputedStyle(element, null); const styles = getComputedStyle(element, null)
return Object.entries(styles) return Object.entries(styles)
.filter(([key]) => styles.getPropertyValue(key) && !excludes.includes(key)) .filter(([key]) => styles.getPropertyValue(key) && !excludes.includes(key))
.map(([key, value]) => `${key}:${value};`) .map(([key, value]) => `${key}:${value};`)
.join(""); .join(``)
} }
/** /**
@ -366,14 +371,13 @@ function getElementStyles(element, excludes = ["width", "height"]) {
* @returns * @returns
*/ */
export function removeLeft(str) { export function removeLeft(str) {
const lines = str.split('\n') const lines = str.split(`\n`)
// 获取应该删除的空白符数量 // 获取应该删除的空白符数量
const minSpaceNum = lines.filter(item => item.trim()) const minSpaceNum = lines
.map(item => item.match(/(^\s+)?/)[0].length) .filter((item) => item.trim())
.map((item) => item.match(/(^\s+)?/)[0].length)
.sort((a, b) => a - b)[0] .sort((a, b) => a - b)[0]
// 删除空白符 // 删除空白符
const newStr = lines const newStr = lines.map((item) => item.slice(minSpaceNum)).join(`\n`)
.map(item => item.slice(minSpaceNum))
.join('\n')
return newStr return newStr
} }

View File

@ -44,10 +44,10 @@ export default {
}, },
methods: { methods: {
onRedirect(url) { onRedirect(url) {
window.open(url); window.open(url)
}, },
}, },
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -1,22 +1,32 @@
<template> <template>
<el-container class="top is-dark"> <el-container class="top is-dark">
<el-dialog <el-dialog title="发布" :visible.sync="form.dialogVisible">
title="发布"
:visible.sync="form.dialogVisible"
>
<div class="postInfo"> <div class="postInfo">
<el-form ref="form" :model="form" label-width="80px"> <el-form ref="form" :model="form" label-width="80px">
<el-form-item label="封面"> <el-form-item label="封面">
<el-input v-model="form.thumb" placeholder="自动提取第一张图"></el-input> <el-input
v-model="form.thumb"
placeholder="自动提取第一张图"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="标题"> <el-form-item label="标题">
<el-input v-model="form.title" placeholder="自动提取第一个标题"></el-input> <el-input
v-model="form.title"
placeholder="自动提取第一个标题"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="描述"> <el-form-item label="描述">
<el-input type="textarea" :rows="4" v-model="form.desc" placeholder="自动提取第一个段落"></el-input> <el-input
type="textarea"
:rows="4"
v-model="form.desc"
placeholder="自动提取第一个段落"
></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<div class="info">此功能由第三方浏览器插件支持本平台不保证安全性</div> <div class="info">
此功能由第三方浏览器插件支持本平台不保证安全性
</div>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@ -238,14 +248,14 @@ import {
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'
import DEFAULT_CSS_CONTENT from "@/assets/example/theme-css.txt"; import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt'
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 {
form: { form: {
@ -258,24 +268,24 @@ export default {
config: config, config: config,
citeStatus: false, citeStatus: false,
showResetConfirm: false, showResetConfirm: false,
selectFont: "", selectFont: ``,
selectSize: "", selectSize: ``,
selectColor: "", selectColor: ``,
selectCodeTheme: config.codeThemeOption[0].value selectCodeTheme: config.codeThemeOption[0].value,
}; }
}, },
components: { components: {
resetDialog, resetDialog,
}, },
computed: { computed: {
effect() { effect() {
return this.nightMode ? "dark" : "light"; return this.nightMode ? `dark` : `light`
}, },
btnContent() { btnContent() {
return this.nightMode ? "浅色模式" : "暗黑模式"; return this.nightMode ? `浅色模式` : `暗黑模式`
}, },
btnType() { btnType() {
return this.nightMode ? "default" : "primary"; return this.nightMode ? `default` : `primary`
}, },
...mapState({ ...mapState({
output: (state) => state.output, output: (state) => state.output,
@ -295,7 +305,9 @@ export default {
try { try {
auto = { auto = {
thumb: document.querySelector(`#output img`).src, thumb: document.querySelector(`#output img`).src,
title: [1,2,3,4,5,6].map(h => document.querySelector(`#output h${h}`)).filter(h => h)[0].innerText, title: [1, 2, 3, 4, 5, 6]
.map((h) => document.querySelector(`#output h${h}`))
.filter((h) => h)[0].innerText,
desc: document.querySelector(`#output p`).innerText, desc: document.querySelector(`#output p`).innerText,
content: this.output, content: this.output,
} }
@ -321,124 +333,124 @@ export default {
fontChanged(fonts) { fontChanged(fonts) {
this.setWxRendererOptions({ this.setWxRendererOptions({
fonts: fonts, fonts: fonts,
}); })
this.setCurrentFont(fonts); this.setCurrentFont(fonts)
this.$emit("refresh"); this.$emit(`refresh`)
}, },
sizeChanged(size) { sizeChanged(size) {
let theme = setFontSize(size.replace("px", "")); let theme = setFontSize(size.replace(`px`, ``))
theme = setColorWithCustomTemplate(theme, this.currentColor); theme = setColorWithCustomTemplate(theme, this.currentColor)
this.setWxRendererOptions({ this.setWxRendererOptions({
size: size, size: size,
theme: theme, theme: theme,
}); })
this.setCurrentSize(size); this.setCurrentSize(size)
this.$emit("refresh"); this.$emit(`refresh`)
}, },
colorChanged(color) { colorChanged(color) {
let theme = setFontSize(this.currentSize.replace("px", "")); 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) { codeThemeChanged(theme) {
this.setCurrentCodeTheme(theme); this.setCurrentCodeTheme(theme)
this.$emit("refresh"); this.$emit(`refresh`)
}, },
statusChanged(val) { statusChanged(val) {
this.setCiteStatus(val); this.setCiteStatus(val)
this.$emit("refresh"); this.$emit(`refresh`)
}, },
// //
copy(e) { copy(e) {
this.$emit("startCopy"); this.$emit(`startCopy`)
setTimeout(() => { setTimeout(() => {
let clipboardDiv = document.getElementById("output"); let clipboardDiv = document.getElementById(`output`)
solveWeChatImage(); solveWeChatImage()
solveHtml(); solveHtml()
clipboardDiv.focus(); clipboardDiv.focus()
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges()
let range = document.createRange(); let range = document.createRange()
range.setStartBefore(clipboardDiv.firstChild); range.setStartBefore(clipboardDiv.firstChild)
range.setEndAfter(clipboardDiv.lastChild); range.setEndAfter(clipboardDiv.lastChild)
window.getSelection().addRange(range); window.getSelection().addRange(range)
document.execCommand("copy"); document.execCommand(`copy`)
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges()
clipboardDiv.innerHTML = this.output; clipboardDiv.innerHTML = this.output
// //
this.$notify({ this.$notify({
showClose: true, showClose: true,
message: "已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴", message: `已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴`,
offset: 80, offset: 80,
duration: 1600, duration: 1600,
type: "success", type: `success`,
}); })
this.$emit("refresh"); this.$emit(`refresh`)
this.$emit("endCopy"); this.$emit(`endCopy`)
}, 350); }, 350)
}, },
// CSS // CSS
async customStyle() { async customStyle() {
this.$emit("showCssEditor"); this.$emit(`showCssEditor`)
this.$nextTick(() => { this.$nextTick(() => {
if (!this.cssEditor) { if (!this.cssEditor) {
this.cssEditor.refresh(); this.cssEditor.refresh()
} }
}); })
setTimeout(() => { setTimeout(() => {
this.cssEditor.refresh(); this.cssEditor.refresh()
}, 50); }, 50)
let flag = await localStorage.getItem("__css_content"); let flag = await localStorage.getItem(`__css_content`)
if (!flag) { if (!flag) {
this.setCssEditorValue(DEFAULT_CSS_CONTENT); this.setCssEditorValue(DEFAULT_CSS_CONTENT)
} }
}, },
// //
confirmReset() { confirmReset() {
localStorage.clear(); localStorage.clear()
this.cssEditor.setValue(DEFAULT_CSS_CONTENT); this.cssEditor.setValue(DEFAULT_CSS_CONTENT)
this.citeStatus = false; this.citeStatus = false
this.statusChanged(false); this.statusChanged(false)
this.fontChanged(this.config.builtinFonts[0].value); this.fontChanged(this.config.builtinFonts[0].value)
this.colorChanged(this.config.colorOption[0].value); this.colorChanged(this.config.colorOption[0].value)
this.sizeChanged(this.config.sizeOption[2].value); this.sizeChanged(this.config.sizeOption[2].value)
this.codeThemeChanged(this.config.codeThemeOption[0].value) this.codeThemeChanged(this.config.codeThemeOption[0].value)
this.$emit("cssChanged"); this.$emit(`cssChanged`)
this.selectFont = this.currentFont; this.selectFont = this.currentFont
this.selectSize = this.currentSize; this.selectSize = this.currentSize
this.selectColor = this.currentColor; this.selectColor = this.currentColor
this.showResetConfirm = false; this.showResetConfirm = false
this.selectCodeTheme = this.codeTheme; this.selectCodeTheme = this.codeTheme
}, },
cancelReset() { cancelReset() {
this.showResetConfirm = false; this.showResetConfirm = false
this.editor.focus(); this.editor.focus()
}, },
...mapMutations([ ...mapMutations([
"setCurrentColor", `setCurrentColor`,
"setCiteStatus", `setCiteStatus`,
"themeChanged", `themeChanged`,
"setCurrentFont", `setCurrentFont`,
"setCurrentSize", `setCurrentSize`,
"setCssEditorValue", `setCssEditorValue`,
"setCurrentCodeTheme", `setCurrentCodeTheme`,
"setWxRendererOptions", `setWxRendererOptions`,
]), ]),
}, },
mounted() { mounted() {
this.selectFont = this.currentFont; this.selectFont = this.currentFont
this.selectSize = this.currentSize; this.selectSize = this.currentSize
this.selectColor = this.currentColor; this.selectColor = this.currentColor
this.selectCodeTheme = this.codeTheme; this.selectCodeTheme = this.codeTheme
this.citeStatus = this.currentCiteStatus; this.citeStatus = this.currentCiteStatus
}, },
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -459,12 +471,12 @@ export default {
margin-right: 24px; margin-right: 24px;
width: 24px; width: 24px;
height: 24px; height: 24px;
background: url("../../assets/images/night.png") no-repeat; background: url('../../assets/images/night.png') no-repeat;
background-size: cover; background-size: cover;
transition: all 0.3s; 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 {

View File

@ -53,9 +53,9 @@
</template> </template>
<script> <script>
import config from "../../assets/scripts/config"; 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: {
@ -69,11 +69,11 @@ export default {
rowNum: 3, rowNum: 3,
colNum: 3, colNum: 3,
tableData: {}, tableData: {},
}; }
}, },
computed: { computed: {
btnType() { btnType() {
return this.nightMode ? "default" : "primary"; return this.nightMode ? `default` : `primary`
}, },
...mapState({ ...mapState({
nightMode: (state) => state.nightMode, nightMode: (state) => state.nightMode,
@ -83,23 +83,23 @@ export default {
methods: { methods: {
// //
insertTable() { insertTable() {
const cursor = this.editor.getCursor(); const cursor = this.editor.getCursor()
const table = createTable({ const table = createTable({
data: this.tableData, data: this.tableData,
rows: this.rowNum, rows: this.rowNum,
cols: this.colNum, 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>

View File

@ -16,7 +16,7 @@
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from 'vuex'
export default { export default {
props: { props: {
showResetConfirm: { showResetConfirm: {
@ -26,13 +26,13 @@ export default {
}, },
computed: { computed: {
btnType() { btnType() {
return this.nightMode ? "default" : "primary"; return this.nightMode ? `default` : `primary`
}, },
...mapState({ ...mapState({
nightMode: (state) => state.nightMode, nightMode: (state) => state.nightMode,
}), }),
}, },
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -39,49 +39,49 @@ export default {
menu: [ menu: [
[ [
{ {
text: "上传图片", text: `上传图片`,
key: "insertPic", key: `insertPic`,
}, },
{ {
text: "插入表格", text: `插入表格`,
key: "insertTable", key: `insertTable`,
}, },
{ {
text: "恢复默认样式", text: `恢复默认样式`,
key: "resetStyle", key: `resetStyle`,
}, },
], ],
[ [
{ {
text: '导入 Markdown 文档', text: `导入 Markdown 文档`,
key: 'importMarkdown', key: `importMarkdown`,
}, },
{ {
text: "导出 Markdown 文档", text: `导出 Markdown 文档`,
key: "download", key: `download`,
}, },
{ {
text: "导出 HTML 页面", text: `导出 HTML 页面`,
key: "export", key: `export`,
}, },
{ {
text: "格式化 Markdown 文档", text: `格式化 Markdown 文档`,
key: "formatMarkdown", key: `formatMarkdown`,
}, },
], ],
], ],
}; }
}, },
methods: { methods: {
closeCB() { closeCB() {
this.$emit("input", false); this.$emit(`input`, false)
}, },
onMouseDown(key) { onMouseDown(key) {
this.$emit("menuTick", key); this.$emit(`menuTick`, key)
this.$emit("closeMenu", false); this.$emit(`closeMenu`, false)
}, },
}, },
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -386,8 +386,8 @@
</template> </template>
<script> <script>
import { checkImage, removeLeft } from "../../assets/scripts/util"; import { checkImage, removeLeft } from '../../assets/scripts/util'
import CodeMirror from "codemirror/lib/codemirror"; import CodeMirror from 'codemirror/lib/codemirror'
export default { export default {
props: { props: {
@ -401,45 +401,45 @@ export default {
activeName: `upload`, activeName: `upload`,
formGitHub: { formGitHub: {
repo: "", repo: ``,
branch: "", branch: ``,
accessToken: "", accessToken: ``,
}, },
formGitee: { formGitee: {
repo: "", repo: ``,
branch: "", branch: ``,
accessToken: "", accessToken: ``,
}, },
formAliOSS: { formAliOSS: {
accessKeyId: "", accessKeyId: ``,
accessKeySecret: "", accessKeySecret: ``,
bucket: "", bucket: ``,
region: "", region: ``,
path: "", path: ``,
cdnHost: "", cdnHost: ``,
}, },
minioOSS: { minioOSS: {
endpoint: "", endpoint: ``,
port: "", port: ``,
useSSL: true, useSSL: true,
bucket: "", bucket: ``,
accessKey: "", accessKey: ``,
secretKey: "", secretKey: ``,
}, },
formTxCOS: { formTxCOS: {
secretId: "", secretId: ``,
secretKey: "", secretKey: ``,
bucket: "", bucket: ``,
region: "", region: ``,
path: "", path: ``,
cdnHost: "", cdnHost: ``,
}, },
formQiniu: { formQiniu: {
accessKey: "", accessKey: ``,
secretKey: "", secretKey: ``,
bucket: "", bucket: ``,
domain: "", domain: ``,
region: "", region: ``,
}, },
formCustom: { formCustom: {
code: code:
@ -460,83 +460,83 @@ export default {
}, },
options: [ options: [
{ {
value: "default", value: `default`,
label: "默认", label: `默认`,
}, },
{ {
value: "gitee", value: `gitee`,
label: "Gitee", label: `Gitee`,
}, },
{ {
value: "github", value: `github`,
label: "GitHub", label: `GitHub`,
}, },
{ {
value: "aliOSS", value: `aliOSS`,
label: "阿里云", label: `阿里云`,
}, },
{ {
value: "txCOS", value: `txCOS`,
label: "腾讯云", label: `腾讯云`,
}, },
{ {
value: "qiniu", value: `qiniu`,
label: "七牛云", label: `七牛云`,
}, },
{ {
value: "minio", value: `minio`,
label: "MinIO", label: `MinIO`,
}, },
{ {
value: "formCustom", value: `formCustom`,
label: "自定义代码", label: `自定义代码`,
}, },
], ],
imgHost: "default", imgHost: `default`,
}; }
}, },
created() { created() {
if (localStorage.getItem("githubConfig")) { if (localStorage.getItem(`githubConfig`)) {
this.formGitHub = JSON.parse(localStorage.getItem("githubConfig")); this.formGitHub = JSON.parse(localStorage.getItem(`githubConfig`))
} }
if (localStorage.getItem("giteeConfig")) { if (localStorage.getItem(`giteeConfig`)) {
this.formGitee = JSON.parse(localStorage.getItem("giteeConfig")); this.formGitee = JSON.parse(localStorage.getItem(`giteeConfig`))
} }
if (localStorage.getItem("aliOSSConfig")) { if (localStorage.getItem(`aliOSSConfig`)) {
this.formAliOSS = JSON.parse(localStorage.getItem("aliOSSConfig")); this.formAliOSS = JSON.parse(localStorage.getItem(`aliOSSConfig`))
} }
if (localStorage.getItem("minioConfig")) { if (localStorage.getItem(`minioConfig`)) {
this.minioOSS = JSON.parse(localStorage.getItem("minioConfig")); this.minioOSS = JSON.parse(localStorage.getItem(`minioConfig`))
} }
if (localStorage.getItem("txCOSConfig")) { if (localStorage.getItem(`txCOSConfig`)) {
this.formTxCOS = JSON.parse(localStorage.getItem("txCOSConfig")); this.formTxCOS = JSON.parse(localStorage.getItem(`txCOSConfig`))
} }
if (localStorage.getItem("imgHost")) { if (localStorage.getItem(`imgHost`)) {
this.imgHost = localStorage.getItem("imgHost"); this.imgHost = localStorage.getItem(`imgHost`)
} }
}, },
methods: { methods: {
changeImgHost() { changeImgHost() {
localStorage.setItem("imgHost", this.imgHost); localStorage.setItem(`imgHost`, this.imgHost)
this.$message.success("已成功切换图床"); this.$message.success(`已成功切换图床`)
}, },
saveGitHubConfiguration() { saveGitHubConfiguration() {
if (!(this.formGitHub.repo && this.formGitHub.accessToken)) { if (!(this.formGitHub.repo && this.formGitHub.accessToken)) {
const blankElement = this.formGitHub.repo ? "token" : "GitHub 仓库"; const blankElement = this.formGitHub.repo ? `token` : `GitHub 仓库`
this.$message.error(`参数「${blankElement}」不能为空`); this.$message.error(`参数「${blankElement}」不能为空`)
return; return
} }
localStorage.setItem("githubConfig", JSON.stringify(this.formGitHub)); localStorage.setItem(`githubConfig`, JSON.stringify(this.formGitHub))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
saveGiteeConfiguration() { saveGiteeConfiguration() {
if (!(this.formGitee.repo && this.formGitee.accessToken)) { if (!(this.formGitee.repo && this.formGitee.accessToken)) {
const blankElement = this.formGitee.repo ? "私人令牌" : "Gitee 仓库"; const blankElement = this.formGitee.repo ? `私人令牌` : `Gitee 仓库`
this.$message.error(`参数「${blankElement}」不能为空`); this.$message.error(`参数「${blankElement}」不能为空`)
return; return
} }
localStorage.setItem("giteeConfig", JSON.stringify(this.formGitee)); localStorage.setItem(`giteeConfig`, JSON.stringify(this.formGitee))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
saveAliOSSConfiguration() { saveAliOSSConfiguration() {
if ( if (
@ -547,11 +547,11 @@ export default {
this.formAliOSS.region this.formAliOSS.region
) )
) { ) {
this.$message.error(`阿里云 OSS 参数配置不全`); this.$message.error(`阿里云 OSS 参数配置不全`)
return; return
} }
localStorage.setItem("aliOSSConfig", JSON.stringify(this.formAliOSS)); localStorage.setItem(`aliOSSConfig`, JSON.stringify(this.formAliOSS))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
saveMinioOSSConfiguration() { saveMinioOSSConfiguration() {
if ( if (
@ -562,11 +562,11 @@ export default {
this.minioOSS.secretKey this.minioOSS.secretKey
) )
) { ) {
this.$message.error(`MinIO 参数配置不全`); this.$message.error(`MinIO 参数配置不全`)
return; return
} }
localStorage.setItem("minioConfig", JSON.stringify(this.minioOSS)); localStorage.setItem(`minioConfig`, JSON.stringify(this.minioOSS))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
saveTxCOSConfiguration() { saveTxCOSConfiguration() {
if ( if (
@ -577,11 +577,11 @@ export default {
this.formTxCOS.region this.formTxCOS.region
) )
) { ) {
this.$message.error(`腾讯云 COS 参数配置不全`); this.$message.error(`腾讯云 COS 参数配置不全`)
return; return
} }
localStorage.setItem("txCOSConfig", JSON.stringify(this.formTxCOS)); localStorage.setItem(`txCOSConfig`, JSON.stringify(this.formTxCOS))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
saveQiniuConfiguration() { saveQiniuConfiguration() {
@ -594,40 +594,40 @@ export default {
this.formQiniu.region this.formQiniu.region
) )
) { ) {
this.$message.error(`七牛云 Kodo 参数配置不全`); this.$message.error(`七牛云 Kodo 参数配置不全`)
return; return
} }
localStorage.setItem("qiniuConfig", JSON.stringify(this.formQiniu)); localStorage.setItem(`qiniuConfig`, JSON.stringify(this.formQiniu))
this.$message.success("保存成功"); this.$message.success(`保存成功`)
}, },
formCustomSave() { formCustomSave() {
const str = this.formCustom.editor.getValue(); const str = this.formCustom.editor.getValue()
localStorage.setItem(`formCustomConfig`, str); localStorage.setItem(`formCustomConfig`, str)
this.$message.success(`保存成功`); this.$message.success(`保存成功`)
}, },
beforeImageUpload(file) { beforeImageUpload(file) {
// check image // check image
const checkResult = checkImage(file); const checkResult = checkImage(file)
if (!checkResult.ok) { if (!checkResult.ok) {
this.$message.error(checkResult.msg); this.$message.error(checkResult.msg)
return false; return false
} }
// check image host // check image host
let imgHost = localStorage.getItem("imgHost"); let imgHost = localStorage.getItem(`imgHost`)
imgHost = imgHost ? imgHost : "default"; imgHost = imgHost ? imgHost : `default`
localStorage.setItem("imgHost", imgHost); localStorage.setItem(`imgHost`, imgHost)
const config = localStorage.getItem(`${imgHost}Config`); const config = localStorage.getItem(`${imgHost}Config`)
const isValidHost = imgHost == "default" || config; const isValidHost = imgHost == `default` || config
if (!isValidHost) { if (!isValidHost) {
this.$message.error(`请先配置 ${imgHost} 图床参数`); this.$message.error(`请先配置 ${imgHost} 图床参数`)
return false; return false
} }
return true; return true
}, },
uploadImage(params) { uploadImage(params) {
this.$emit("uploadImage", params.file); this.$emit(`uploadImage`, params.file)
}, },
}, },
watch: { watch: {
@ -637,20 +637,20 @@ export default {
if (val === `formCustom`) { if (val === `formCustom`) {
this.$nextTick(() => { this.$nextTick(() => {
const textarea = const textarea =
this.$refs.formCustomElInput.$el.querySelector(`textarea`); this.$refs.formCustomElInput.$el.querySelector(`textarea`)
this.formCustom.editor = this.formCustom.editor =
this.formCustom.editor || this.formCustom.editor ||
CodeMirror.fromTextArea(textarea, { CodeMirror.fromTextArea(textarea, {
mode: `javascript`, mode: `javascript`,
}); })
this.formCustom.editor.setValue(this.formCustom.code); this.formCustom.editor.setValue(this.formCustom.code)
}); })
} }
}, },
}, },
}, },
mounted() {}, mounted() {},
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -696,8 +696,8 @@ export default {
/deep/ .CodeMirror { /deep/ .CodeMirror {
border: 1px solid #eee; border: 1px solid #eee;
height: 300px !important; height: 300px !important;
font-family: "Fira Mono", "DejaVu Sans Mono", Menlo, Consolas, font-family: 'Fira Mono', 'DejaVu Sans Mono', Menlo, Consolas,
"Liberation Mono", Monaco, "Lucida Console", monospace !important; 'Liberation Mono', Monaco, 'Lucida Console', monospace !important;
line-height: 20px; line-height: 20px;
.CodeMirror-scroll { .CodeMirror-scroll {
padding: 10px; padding: 10px;

View File

@ -41,7 +41,7 @@
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>

View File

@ -1,29 +1,29 @@
import Vue from "vue"; import Vue from 'vue'
import App from "./App"; 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'
import "./plugins/element"; import './plugins/element'
import "codemirror/lib/codemirror.css"; import 'codemirror/lib/codemirror.css'
import "codemirror/theme/xq-light.css"; import 'codemirror/theme/xq-light.css'
import "codemirror/mode/css/css"; import 'codemirror/mode/css/css'
import "codemirror/mode/markdown/markdown"; import 'codemirror/mode/markdown/markdown'
import "codemirror/addon/edit/closebrackets"; import 'codemirror/addon/edit/closebrackets'
import "codemirror/addon/edit/matchbrackets"; 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 router from './router'; import router from './router'
Vue.use(ElementUI); Vue.use(ElementUI)
Vue.config.productionTip = false; Vue.config.productionTip = false
App.mpType = "app"; App.mpType = `app`
const app = new Vue({ const app = new Vue({
router, router,
store, store,
...App, ...App,
}); })
app.$mount("#app"); app.$mount(`#app`)

View File

@ -6,10 +6,10 @@
</template> </template>
<script> <script>
import Loading from "../../components/Loading"; import Loading from '../../components/Loading'
import CodemirrorEditor from "./view/CodemirrorEditor"; import CodemirrorEditor from './view/CodemirrorEditor'
export default { export default {
name: "App", name: `App`,
components: { components: {
Loading, Loading,
CodemirrorEditor, CodemirrorEditor,
@ -17,14 +17,14 @@ export default {
data() { data() {
return { return {
loading: true, loading: true,
}; }
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
this.loading = false; this.loading = false
}, 100); }, 100)
}, },
}; }
</script> </script>
<style scoped> <style scoped>

View File

@ -12,7 +12,7 @@
@show-about-dialog="aboutDialogVisible = true" @show-about-dialog="aboutDialogVisible = true"
@show-dialog-form="dialogFormVisible = true" @show-dialog-form="dialogFormVisible = true"
@show-dialog-upload-img="dialogUploadImgVisible = true" @show-dialog-upload-img="dialogUploadImgVisible = true"
@startCopy="(isCoping = true), (backLight = true)" @startCopy=";(isCoping = true), (backLight = true)"
@endCopy="endCopy" @endCopy="endCopy"
/> />
</el-header> </el-header>
@ -93,11 +93,11 @@
</div> </div>
</template> </template>
<script> <script>
import editorHeader from "../../../components/CodemirrorEditor/header"; import editorHeader from '../../../components/CodemirrorEditor/header'
import aboutDialog from "../../../components/CodemirrorEditor/aboutDialog"; import aboutDialog from '../../../components/CodemirrorEditor/aboutDialog'
import insertFormDialog from "../../../components/CodemirrorEditor/insertForm"; import insertFormDialog from '../../../components/CodemirrorEditor/insertForm'
import rightClickMenu from "../../../components/CodemirrorEditor/rightClickMenu"; import rightClickMenu from '../../../components/CodemirrorEditor/rightClickMenu'
import uploadImgDialog from "../../../components/CodemirrorEditor/uploadImgDialog"; import uploadImgDialog from '../../../components/CodemirrorEditor/uploadImgDialog'
import { import {
css2json, css2json,
@ -108,13 +108,13 @@ import {
saveEditorContent, saveEditorContent,
customCssWithTemplate, customCssWithTemplate,
checkImage, checkImage,
} from "../../../assets/scripts/util"; } from '../../../assets/scripts/util'
import { toBase64 } from "../../../assets/scripts/util"; import { toBase64 } from '../../../assets/scripts/util'
import fileApi from "../../../api/file"; import fileApi from '../../../api/file'
require("codemirror/mode/javascript/javascript"); require(`codemirror/mode/javascript/javascript`)
import { mapState, mapMutations } from "vuex"; import { mapState, mapMutations } from 'vuex'
export default { export default {
data() { data() {
return { return {
@ -127,10 +127,10 @@ export default {
backLight: false, backLight: false,
timeout: null, timeout: null,
changeTimer: null, changeTimer: null,
source: "", source: ``,
mouseLeft: 0, mouseLeft: 0,
mouseTop: 0, mouseTop: 0,
}; }
}, },
components: { components: {
editorHeader, editorHeader,
@ -153,304 +153,304 @@ export default {
}), }),
}, },
created() { created() {
this.initEditorState(); this.initEditorState()
this.$nextTick(() => { this.$nextTick(() => {
this.initEditor(); this.initEditor()
this.initCssEditor(); this.initCssEditor()
this.onEditorRefresh(); this.onEditorRefresh()
}); })
}, },
methods: { methods: {
initEditor() { initEditor() {
this.initEditorEntity(); this.initEditorEntity()
this.editor.on("change", (cm, e) => { this.editor.on(`change`, (cm, e) => {
if (this.changeTimer) clearTimeout(this.changeTimer); if (this.changeTimer) clearTimeout(this.changeTimer)
this.changeTimer = setTimeout(() => { this.changeTimer = setTimeout(() => {
this.onEditorRefresh(); this.onEditorRefresh()
saveEditorContent(this.editor, "__editor_content"); saveEditorContent(this.editor, `__editor_content`)
}, 300); }, 300)
}); })
// //
this.editor.on("paste", (cm, e) => { this.editor.on(`paste`, (cm, e) => {
if (!(e.clipboardData && e.clipboardData.items) || this.isImgLoading) { if (!(e.clipboardData && e.clipboardData.items) || this.isImgLoading) {
return; return
} }
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) { for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) {
let item = e.clipboardData.items[i]; let item = e.clipboardData.items[i]
if (item.kind === "file") { if (item.kind === `file`) {
// //
const pasteFile = item.getAsFile(); const pasteFile = item.getAsFile()
const isValid = this.beforeUpload(pasteFile); const isValid = this.beforeUpload(pasteFile)
if (!isValid) { if (!isValid) {
continue; continue
} }
this.uploadImage(pasteFile); this.uploadImage(pasteFile)
} }
} }
}); })
this.editor.on("mousedown", () => { this.editor.on(`mousedown`, () => {
this.$store.commit("setRightClickMenuVisible", false); this.$store.commit(`setRightClickMenuVisible`, false)
}); })
this.editor.on("blur", () => { this.editor.on(`blur`, () => {
//!mousedown //!mousedown
this.$store.commit("setRightClickMenuVisible", false); this.$store.commit(`setRightClickMenuVisible`, false)
}); })
this.editor.on("scroll", () => { this.editor.on(`scroll`, () => {
this.$store.commit("setRightClickMenuVisible", false); this.$store.commit(`setRightClickMenuVisible`, false)
}); })
}, },
initCssEditor() { initCssEditor() {
this.initCssEditorEntity(); this.initCssEditorEntity()
// //
this.cssEditor.on("keyup", (cm, e) => { this.cssEditor.on(`keyup`, (cm, e) => {
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) { if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
cm.showHint(e); cm.showHint(e)
} }
}); })
this.cssEditor.on("update", (instance) => { this.cssEditor.on(`update`, (instance) => {
this.cssChanged(); this.cssChanged()
saveEditorContent(this.cssEditor, "__css_content"); saveEditorContent(this.cssEditor, `__css_content`)
}); })
}, },
cssChanged() { cssChanged() {
let json = css2json(this.cssEditor.getValue(0)); let json = css2json(this.cssEditor.getValue(0))
let theme = setFontSize(this.currentSize.replace("px", "")); let theme = setFontSize(this.currentSize.replace(`px`, ``))
theme = customCssWithTemplate(json, this.currentColor, theme); theme = customCssWithTemplate(json, this.currentColor, theme)
this.setWxRendererOptions({ this.setWxRendererOptions({
theme: theme, theme: theme,
}); })
this.onEditorRefresh(); this.onEditorRefresh()
}, },
// highlight.js // highlight.js
codeThemeChanged() { codeThemeChanged() {
let cssUrl = this.codeTheme; let cssUrl = this.codeTheme
let el = document.getElementById('hljs') let el = document.getElementById(`hljs`)
if (el != undefined) { if (el != undefined) {
el.setAttribute('href', cssUrl); el.setAttribute(`href`, cssUrl)
} else { } else {
var link = document.createElement('link'); var link = document.createElement(`link`)
link.setAttribute('type','text/css'); link.setAttribute(`type`, `text/css`)
link.setAttribute('rel','stylesheet'); link.setAttribute(`rel`, `stylesheet`)
link.setAttribute('href',cssUrl); link.setAttribute(`href`, cssUrl)
link.setAttribute('id','hljs'); link.setAttribute(`id`, `hljs`)
document.head.appendChild(link); document.head.appendChild(link)
} }
}, },
beforeUpload(file) { beforeUpload(file) {
// validate image // validate image
const checkResult = checkImage(file); const checkResult = checkImage(file)
if (!checkResult.ok) { if (!checkResult.ok) {
this.$message.error(checkResult.msg); this.$message.error(checkResult.msg)
return false; return false
} }
// check image host // check image host
let imgHost = localStorage.getItem("imgHost"); let imgHost = localStorage.getItem(`imgHost`)
imgHost = imgHost ? imgHost : "default"; imgHost = imgHost ? imgHost : `default`
localStorage.setItem("imgHost", imgHost); localStorage.setItem(`imgHost`, imgHost)
const config = localStorage.getItem(`${imgHost}Config`); const config = localStorage.getItem(`${imgHost}Config`)
const isValidHost = imgHost == "default" || config; const isValidHost = imgHost == `default` || config
if (!isValidHost) { if (!isValidHost) {
this.$message.error(`请先配置 ${imgHost} 图床参数`); this.$message.error(`请先配置 ${imgHost} 图床参数`)
return false; return false
} }
return true; return true
}, },
uploadImage(file) { uploadImage(file) {
this.isImgLoading = true; this.isImgLoading = true
toBase64(file) toBase64(file)
.then((base64Content) => { .then((base64Content) => {
fileApi fileApi
.fileUpload(base64Content, file) .fileUpload(base64Content, file)
.then((url) => { .then((url) => {
console.log(url) console.log(url)
this.uploaded(url); this.uploaded(url)
}) })
.catch((err) => { .catch((err) => {
this.$message.error(err.message); this.$message.error(err.message)
}); })
}) })
.catch((err) => { .catch((err) => {
this.$message.error(err.message); this.$message.error(err.message)
}); })
this.isImgLoading = false; this.isImgLoading = false
}, },
// //
uploaded(response) { uploaded(response) {
console.log("图片上传之后: ", response) console.log(`图片上传之后: `, response)
if (!response) { if (!response) {
this.$message.error("上传图片未知异常"); this.$message.error(`上传图片未知异常`)
return; return
} }
this.dialogUploadImgVisible = false; this.dialogUploadImgVisible = false
// //
const cursor = this.editor.getCursor(); const cursor = this.editor.getCursor()
const imageUrl = response; const imageUrl = response
const markdownImage = `![](${imageUrl})`; const markdownImage = `![](${imageUrl})`
// Markdown URL // Markdown URL
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor); this.editor.replaceSelection(`\n${markdownImage}\n`, cursor)
this.$message.success("图片上传成功"); this.$message.success(`图片上传成功`)
this.onEditorRefresh(); this.onEditorRefresh()
}, },
// //
leftAndRightScroll() { leftAndRightScroll() {
const scrollCB = (text) => { const scrollCB = (text) => {
let source, target; let source, target
clearTimeout(this.timeout); clearTimeout(this.timeout)
if (text === "preview") { if (text === `preview`) {
source = this.$refs.preview.$el; source = this.$refs.preview.$el
target = document.getElementsByClassName("CodeMirror-scroll")[0]; target = document.getElementsByClassName(`CodeMirror-scroll`)[0]
this.editor.off("scroll", editorScrollCB); this.editor.off(`scroll`, editorScrollCB)
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.editor.on("scroll", editorScrollCB); this.editor.on(`scroll`, editorScrollCB)
}, 300); }, 300)
} else if (text === "editor") { } else if (text === `editor`) {
source = document.getElementsByClassName("CodeMirror-scroll")[0]; source = document.getElementsByClassName(`CodeMirror-scroll`)[0]
target = this.$refs.preview.$el; target = this.$refs.preview.$el
target.removeEventListener("scroll", previewScrollCB, false); target.removeEventListener(`scroll`, previewScrollCB, false)
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
target.addEventListener("scroll", previewScrollCB, false); target.addEventListener(`scroll`, previewScrollCB, false)
}, 300); }, 300)
} }
let percentage = let percentage =
source.scrollTop / (source.scrollHeight - source.offsetHeight); source.scrollTop / (source.scrollHeight - source.offsetHeight)
let height = percentage * (target.scrollHeight - target.offsetHeight); let height = percentage * (target.scrollHeight - target.offsetHeight)
target.scrollTo(0, height); target.scrollTo(0, height)
}; }
const editorScrollCB = () => { const editorScrollCB = () => {
scrollCB("editor"); scrollCB(`editor`)
}; }
const previewScrollCB = () => { const previewScrollCB = () => {
scrollCB("preview"); scrollCB(`preview`)
}; }
this.$refs.preview.$el.addEventListener("scroll", previewScrollCB, false); this.$refs.preview.$el.addEventListener(`scroll`, previewScrollCB, false)
this.editor.on("scroll", editorScrollCB); this.editor.on(`scroll`, editorScrollCB)
}, },
// //
onEditorRefresh() { onEditorRefresh() {
this.codeThemeChanged(this.codeTheme); this.codeThemeChanged(this.codeTheme)
this.editorRefresh(); this.editorRefresh()
setTimeout(() => PR.prettyPrint(), 0); setTimeout(() => window.PR.prettyPrint(), 0)
}, },
// //
endCopy() { endCopy() {
this.backLight = false; this.backLight = false
setTimeout(() => { setTimeout(() => {
this.isCoping = false; this.isCoping = false
}, 800); }, 800)
}, },
// //
downloadEditorContent() { downloadEditorContent() {
downloadMD(this.editor.getValue(0)); downloadMD(this.editor.getValue(0))
}, },
// HTML // HTML
exportEditorContent() { exportEditorContent() {
this.$nextTick(() => { this.$nextTick(() => {
exportHTML(); exportHTML()
}); })
}, },
// Markdown // Markdown
importMarkdownContent() { importMarkdownContent() {
let menu = document.getElementById("menu"); let menu = document.getElementById(`menu`)
let input = document.createElement("input"); let input = document.createElement(`input`)
input.type = "file"; input.type = `file`
input.name = "filename"; input.name = `filename`
input.accept = ".txt,.md"; input.accept = `.txt,.md`
menu.appendChild(input); menu.appendChild(input)
input.onchange = () => { input.onchange = () => {
if (!input.files) { if (!input.files) {
return; return
} }
const file = input.files[0]; const file = input.files[0]
if (!/\.(txt|TXT|MD|md)$/.test(file.name)) { if (!/\.(txt|TXT|MD|md)$/.test(file.name)) {
this.$message.error("不支持的文档格式"); this.$message.error(`不支持的文档格式`)
return; return
} }
const reader = new FileReader(); const reader = new FileReader()
reader.readAsText(file); reader.readAsText(file)
reader.onload = (event) => { reader.onload = (event) => {
let txt = event.target.result; let txt = event.target.result
txt = formatDoc(txt); txt = formatDoc(txt)
if (txt) { if (txt) {
localStorage.setItem("__editor_content", txt); localStorage.setItem(`__editor_content`, txt)
this.editor.setValue(txt); this.editor.setValue(txt)
this.$message.success("文档导入成功"); this.$message.success(`文档导入成功`)
} }
}; }
}; }
input.click(); input.click()
menu.removeChild(input); menu.removeChild(input)
}, },
// //
formatContent() { formatContent() {
const doc = formatDoc(this.editor.getValue(0)); const doc = formatDoc(this.editor.getValue(0))
localStorage.setItem("__editor_content", doc); localStorage.setItem(`__editor_content`, doc)
this.editor.setValue(doc); this.editor.setValue(doc)
}, },
// //
openMenu(e) { openMenu(e) {
const menuMinWidth = 105; const menuMinWidth = 105
const offsetLeft = this.$el.getBoundingClientRect().left; const offsetLeft = this.$el.getBoundingClientRect().left
const offsetWidth = this.$el.offsetWidth; const offsetWidth = this.$el.offsetWidth
const maxLeft = offsetWidth - menuMinWidth; const maxLeft = offsetWidth - menuMinWidth
const left = e.clientX - offsetLeft; const left = e.clientX - offsetLeft
this.mouseLeft = Math.min(maxLeft, left); this.mouseLeft = Math.min(maxLeft, left)
this.mouseTop = e.clientY + 10; this.mouseTop = e.clientY + 10
this.$store.commit("setRightClickMenuVisible", true); this.$store.commit(`setRightClickMenuVisible`, true)
}, },
closeRightClickMenu() { closeRightClickMenu() {
this.$store.commit("setRightClickMenuVisible", false); this.$store.commit(`setRightClickMenuVisible`, false)
}, },
onMenuEvent(type, info = {}) { onMenuEvent(type, info = {}) {
switch (type) { switch (type) {
case "resetStyle": case `resetStyle`:
this.$refs.header.showResetConfirm = true; this.$refs.header.showResetConfirm = true
break; break
case "insertPic": case `insertPic`:
this.dialogUploadImgVisible = true; this.dialogUploadImgVisible = true
break; break
case "download": case `download`:
this.downloadEditorContent(); this.downloadEditorContent()
break; break
case "export": case `export`:
this.exportEditorContent(); this.exportEditorContent()
break; break
case "insertTable": case `insertTable`:
this.dialogFormVisible = true; this.dialogFormVisible = true
break; break
case "importMarkdown": case `importMarkdown`:
this.importMarkdownContent(); this.importMarkdownContent()
break; break
case "formatMarkdown": case `formatMarkdown`:
this.formatContent(); this.formatContent()
break; break
default: default:
break; break
} }
}, },
...mapMutations([ ...mapMutations([
"initEditorState", `initEditorState`,
"initEditorEntity", `initEditorEntity`,
"setWxRendererOptions", `setWxRendererOptions`,
"editorRefresh", `editorRefresh`,
"initCssEditorEntity", `initCssEditorEntity`,
]), ]),
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
this.leftAndRightScroll(); this.leftAndRightScroll()
PR.prettyPrint(); window.PR.prettyPrint()
}, 300); }, 300)
}, },
}; }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -506,7 +506,7 @@ export default {
width: 50px; width: 50px;
height: 50px; height: 50px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background: url("../../../assets/images/favicon.png") no-repeat; background: url('../../../assets/images/favicon.png') no-repeat;
background-size: cover; background-size: cover;
} }
span { span {
@ -556,5 +556,5 @@ export default {
} }
</style> </style>
<style lang="less" scoped> <style lang="less" scoped>
@import url("../../../assets/less/app.less"); @import url('../../../assets/less/app.less');
</style> </style>

View File

@ -1,10 +1,7 @@
import Vue from "vue"; import Vue from 'vue'
import { import { Loading, Message } from 'element-ui'
Loading,
Message,
} from "element-ui";
Vue.component(Message.name, Message); Vue.component(Message.name, Message)
Vue.prototype.$loading = Loading.service; Vue.prototype.$loading = Loading.service
Vue.prototype.$message = Message; Vue.prototype.$message = Message

View File

@ -9,7 +9,7 @@ const routes = [
path: `/`, path: `/`,
name: `index`, name: `index`,
component: index, component: index,
} },
] ]
const router = new VueRouter({ const router = new VueRouter({

View File

@ -1,162 +1,162 @@
import Vue from "vue"; import Vue from 'vue'
import Vuex from "vuex"; import Vuex from 'vuex'
import config from "../assets/scripts/config"; import config from '../assets/scripts/config'
import WxRenderer from "../assets/scripts/renderers/wx-renderer"; import WxRenderer from '../assets/scripts/renderers/wx-renderer'
import { marked } from "marked"; import { marked } from 'marked'
import CodeMirror from "codemirror/lib/codemirror"; import CodeMirror from 'codemirror/lib/codemirror'
import DEFAULT_CONTENT from "@/assets/example/markdown.md"; import DEFAULT_CONTENT from '@/assets/example/markdown.md'
import DEFAULT_CSS_CONTENT from "@/assets/example/theme-css.txt"; import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt'
import { setColor, formatDoc, formatCss } from "../assets/scripts/util"; 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: config.codeThemeOption[0].value, codeTheme: config.codeThemeOption[0].value,
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") || config.codeThemeOption[0].value; localStorage.getItem(`codeTheme`) || 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,
}); })
}, },
initEditorEntity(state) { initEditorEntity(state) {
const editorDom = document.getElementById("editor"); const editorDom = document.getElementById(`editor`)
if (!editorDom.value) { if (!editorDom.value) {
editorDom.value = editorDom.value =
localStorage.getItem("__editor_content") || formatDoc(DEFAULT_CONTENT); localStorage.getItem(`__editor_content`) || formatDoc(DEFAULT_CONTENT)
} }
state.editor = CodeMirror.fromTextArea(editorDom, { state.editor = CodeMirror.fromTextArea(editorDom, {
mode: "text/x-markdown", mode: `text/x-markdown`,
theme: "xq-light", theme: `xq-light`,
lineNumbers: false, lineNumbers: false,
lineWrapping: true, lineWrapping: true,
styleActiveLine: true, styleActiveLine: true,
autoCloseBrackets: true, autoCloseBrackets: true,
extraKeys: { extraKeys: {
"Ctrl-F": function autoFormat(editor) { 'Ctrl-F': function autoFormat(editor) {
const doc = formatDoc(editor.getValue(0)); const doc = formatDoc(editor.getValue(0))
localStorage.setItem("__editor_content", doc); localStorage.setItem(`__editor_content`, doc)
editor.setValue(doc); editor.setValue(doc)
}, },
"Ctrl-S": function save(editor) {}, 'Ctrl-S': function save(editor) {},
"Ctrl-B": function bold(editor) { 'Ctrl-B': function bold(editor) {
const selected = editor.getSelection(); const selected = editor.getSelection()
editor.replaceSelection(`**${selected}**`); editor.replaceSelection(`**${selected}**`)
}, },
"Ctrl-D": function del(editor) { 'Ctrl-D': function del(editor) {
const selected = editor.getSelection(); const selected = editor.getSelection()
editor.replaceSelection(`~~${selected}~~`); editor.replaceSelection(`~~${selected}~~`)
}, },
"Ctrl-I": function italic(editor) { 'Ctrl-I': function italic(editor) {
const selected = editor.getSelection(); const selected = editor.getSelection()
editor.replaceSelection(`*${selected}*`); editor.replaceSelection(`*${selected}*`)
}, },
}, },
}); })
}, },
initCssEditorEntity(state) { initCssEditorEntity(state) {
const cssEditorDom = document.getElementById("cssEditor"); const cssEditorDom = document.getElementById(`cssEditor`)
if (!cssEditorDom.value) { if (!cssEditorDom.value) {
cssEditorDom.value = cssEditorDom.value =
localStorage.getItem("__css_content") || DEFAULT_CSS_CONTENT; localStorage.getItem(`__css_content`) || DEFAULT_CSS_CONTENT
} }
state.cssEditor = CodeMirror.fromTextArea(cssEditorDom, { state.cssEditor = CodeMirror.fromTextArea(cssEditorDom, {
mode: "css", mode: `css`,
theme: "style-mirror", theme: `style-mirror`,
lineNumbers: false, lineNumbers: false,
lineWrapping: true, lineWrapping: true,
matchBrackets: true, matchBrackets: true,
autofocus: true, autofocus: true,
extraKeys: { extraKeys: {
"Ctrl-F": function autoFormat(editor) { 'Ctrl-F': function autoFormat(editor) {
const doc = formatCss(editor.getValue(0)); const doc = formatCss(editor.getValue(0))
localStorage.setItem("__css_content", doc); localStorage.setItem(`__css_content`, doc)
editor.setValue(doc); editor.setValue(doc)
}, },
"Ctrl-S": function save(editor) {}, 'Ctrl-S': function save(editor) {},
}, },
}); })
}, },
editorRefresh(state) { editorRefresh(state) {
let renderer = state.wxRenderer.getRenderer(state.citeStatus); let renderer = state.wxRenderer.getRenderer(state.citeStatus)
marked.setOptions({ renderer }); marked.setOptions({ renderer })
let output = marked.parse(state.editor.getValue(0)); let output = marked.parse(state.editor.getValue(0))
// 去除第一行的 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
}, },
}; }
export default new Vuex.Store({ export default new Vuex.Store({
state, state,
mutations, mutations,
actions: {}, actions: {},
}); })

View File

@ -5,9 +5,11 @@ module.exports = {
configureWebpack: (config) => { configureWebpack: (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.(txt|md)$/i, test: /\.(txt|md)$/i,
use: [{ use: [
loader: 'raw-loader', {
}] loader: `raw-loader`,
},
],
}) })
}, },
productionSourceMap: !isProd, productionSourceMap: !isProd,