diff --git a/.gitignore b/.gitignore index e043bca..47fea0e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ .env.development.local .env.test.local .env.production.local -.vscode npm-debug.log* yarn-debug.log* diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..20d0d06 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run lint diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eb3d18b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll": true + } +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..adc4845 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "exclude": ["node_modules", "dist"] +} diff --git a/mm/mm.config.js b/mm/mm.config.js index 9a15065..57c5e17 100644 --- a/mm/mm.config.js +++ b/mm/mm.config.js @@ -10,11 +10,11 @@ const spaceInfo = { } /** - * 配置说明请参考文档: + * 配置说明请参考文档: * https://hongqiye.com/doc/mockm/config/option.html * @type {import('mockm/@types/config').Config} */ -module.exports = util => { +module.exports = (util) => { const port = 9000 return { port, @@ -29,26 +29,34 @@ module.exports = util => { }) form.parse(req, async (err, fields = [], files) => { 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 { - 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) { // console.log(err) } - res.json({url}) + res.json({ url }) }) }, }, static: [ - { // 测试 netlify 部署 + { + // 测试 netlify 部署 fileDir: `../dist`, path: `/`, }, - { // 测试 gitee/github 部署 + { + // 测试 gitee/github 部署 fileDir: `../dist`, path: `/md`, }, - { // 访问公共目录 + { + // 访问公共目录 fileDir: `../public`, path: `/public`, }, diff --git a/mm/util.js b/mm/util.js index 476211b..6b9608b 100644 --- a/mm/util.js +++ b/mm/util.js @@ -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`) function dcloud(spaceInfo) { - if(Boolean(spaceInfo.spaceId && spaceInfo.clientSecret) === false) { + if (Boolean(spaceInfo.spaceId && spaceInfo.clientSecret) === false) { throw new Error(`请填写 spaceInfo`) } - + function sign(data, secret) { const hmac = require(`crypto`).createHmac(`md5`, secret) // 排序 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) return hmac.digest(`hex`) } - + async function anonymousAuthorize() { const data = { method: `serverless.auth.user.anonymousAuthorize`, @@ -29,7 +33,7 @@ function dcloud(spaceInfo) { method: `POST`, }).then((res) => res.json()) } - + async function report({ id, token }) { const reportReq = { method: `serverless.file.resource.report`, @@ -47,7 +51,7 @@ function dcloud(spaceInfo) { method: `POST`, }).then((res) => res.json()) } - + async function generateProximalSign({ name, token }) { const data = { method: `serverless.file.resource.generateProximalSign`, @@ -66,7 +70,7 @@ function dcloud(spaceInfo) { }).then((res) => res.json()) return res } - + async function upload({ data, file }) { const formdata = new FormData() Object.entries({ @@ -81,7 +85,7 @@ function dcloud(spaceInfo) { success_action_status: 200, file, }).forEach(([key, val]) => formdata.append(key, val)) - + return await fetch(`https://${data.host}`, { headers: { 'X-OSS-server-side-encrpytion': `AES256`, @@ -90,7 +94,7 @@ function dcloud(spaceInfo) { method: `POST`, }) } - + async function uploadFile({ name = `unnamed.file`, file }) { const token = (await anonymousAuthorize()).data.accessToken const res = await generateProximalSign({ name, token }) @@ -99,9 +103,8 @@ function dcloud(spaceInfo) { const fileUrl = `https://${res.data.cdnDomain}/${res.data.ossPath}` return fileUrl } - + return uploadFile - } module.exports = { diff --git a/package.json b/package.json index 1ff6527..995ec32 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,8 @@ "version": "1.5.7", "private": false, "scripts": { + "prepare": "husky install", + "lint": "vue-cli-service lint src && vue-cli-service lint mm", "start": "run-p serve mm", "serve": "vue-cli-service serve", "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", "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": { "@vue/shared": "^3.2.26", "ali-oss": "^6.16.0", @@ -37,6 +76,15 @@ "vuex": "^3.6.2" }, "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-service": "~4.5.15", "async-validator": "^4.0.7", diff --git a/src/App.vue b/src/App.vue index 58c415c..eb28b05 100644 --- a/src/App.vue +++ b/src/App.vue @@ -18,8 +18,8 @@ body, diff --git a/src/main.js b/src/main.js index 2e610f6..dd9f989 100644 --- a/src/main.js +++ b/src/main.js @@ -1,29 +1,29 @@ -import Vue from "vue"; -import App from "./App"; -import store from "./store"; -import ElementUI from "element-ui"; -import "element-ui/lib/theme-chalk/index.css"; -import "./plugins/element"; -import "codemirror/lib/codemirror.css"; -import "codemirror/theme/xq-light.css"; -import "codemirror/mode/css/css"; -import "codemirror/mode/markdown/markdown"; -import "codemirror/addon/edit/closebrackets"; -import "codemirror/addon/edit/matchbrackets"; -import "codemirror/addon/selection/active-line"; -import "codemirror/addon/hint/show-hint.js"; -import "codemirror/addon/hint/css-hint.js"; -import router from './router'; +import Vue from 'vue' +import App from './App' +import store from './store' +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' +import './plugins/element' +import 'codemirror/lib/codemirror.css' +import 'codemirror/theme/xq-light.css' +import 'codemirror/mode/css/css' +import 'codemirror/mode/markdown/markdown' +import 'codemirror/addon/edit/closebrackets' +import 'codemirror/addon/edit/matchbrackets' +import 'codemirror/addon/selection/active-line' +import 'codemirror/addon/hint/show-hint.js' +import 'codemirror/addon/hint/css-hint.js' +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({ router, store, ...App, -}); -app.$mount("#app"); +}) +app.$mount(`#app`) diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 4553748..f34e309 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -6,10 +6,10 @@ diff --git a/src/plugins/element/index.js b/src/plugins/element/index.js index 5ebbd87..26e4cda 100644 --- a/src/plugins/element/index.js +++ b/src/plugins/element/index.js @@ -1,10 +1,7 @@ -import Vue from "vue"; -import { - Loading, - Message, -} from "element-ui"; +import Vue from 'vue' +import { Loading, Message } from 'element-ui' -Vue.component(Message.name, Message); +Vue.component(Message.name, Message) -Vue.prototype.$loading = Loading.service; -Vue.prototype.$message = Message; +Vue.prototype.$loading = Loading.service +Vue.prototype.$message = Message diff --git a/src/router/index.js b/src/router/index.js index b850506..0545ef9 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -9,7 +9,7 @@ const routes = [ path: `/`, name: `index`, component: index, - } + }, ] const router = new VueRouter({ diff --git a/src/store/index.js b/src/store/index.js index d38456b..4fb6e18 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,162 +1,162 @@ -import Vue from "vue"; -import Vuex from "vuex"; -import config from "../assets/scripts/config"; -import WxRenderer from "../assets/scripts/renderers/wx-renderer"; -import { marked } from "marked"; -import CodeMirror from "codemirror/lib/codemirror"; -import DEFAULT_CONTENT from "@/assets/example/markdown.md"; -import DEFAULT_CSS_CONTENT from "@/assets/example/theme-css.txt"; -import { setColor, formatDoc, formatCss } from "../assets/scripts/util"; +import Vue from 'vue' +import Vuex from 'vuex' +import config from '../assets/scripts/config' +import WxRenderer from '../assets/scripts/renderers/wx-renderer' +import { marked } from 'marked' +import CodeMirror from 'codemirror/lib/codemirror' +import DEFAULT_CONTENT from '@/assets/example/markdown.md' +import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt' +import { setColor, formatDoc, formatCss } from '../assets/scripts/util' -Vue.use(Vuex); +Vue.use(Vuex) const state = { wxRenderer: null, - output: "", - html: "", + output: ``, + html: ``, editor: null, cssEditor: null, - currentFont: "", - currentSize: "", - currentColor: "", + currentFont: ``, + currentSize: ``, + currentColor: ``, citeStatus: 0, nightMode: false, codeTheme: config.codeThemeOption[0].value, rightClickMenuVisible: false, -}; +} const mutations = { setEditorValue(state, data) { - state.editor.setValue(data); + state.editor.setValue(data) }, setCssEditorValue(state, data) { - state.cssEditor.setValue(data); + state.cssEditor.setValue(data) }, setWxRendererOptions(state, data) { - state.wxRenderer.setOptions(data); + state.wxRenderer.setOptions(data) }, setCiteStatus(state, data) { - state.citeStatus = data; - localStorage.setItem("citeStatus", data); + state.citeStatus = data + localStorage.setItem(`citeStatus`, data) }, setCurrentFont(state, data) { - state.currentFont = data; - localStorage.setItem("fonts", data); + state.currentFont = data + localStorage.setItem(`fonts`, data) }, setCurrentSize(state, data) { - state.currentSize = data; - localStorage.setItem("size", data); + state.currentSize = data + localStorage.setItem(`size`, data) }, setCurrentColor(state, data) { - state.currentColor = data; - localStorage.setItem("color", data); + state.currentColor = data + localStorage.setItem(`color`, data) }, setCurrentCodeTheme(state, data) { - state.codeTheme = data; - localStorage.setItem("codeTheme", data); + state.codeTheme = data + localStorage.setItem(`codeTheme`, data) }, setRightClickMenuVisible(state, data) { - state.rightClickMenuVisible = data; + state.rightClickMenuVisible = data }, themeChanged(state) { - state.nightMode = !state.nightMode; - localStorage.setItem("nightMode", state.nightMode); + state.nightMode = !state.nightMode + localStorage.setItem(`nightMode`, state.nightMode) }, initEditorState(state) { state.currentFont = - localStorage.getItem("fonts") || config.builtinFonts[0].value; + localStorage.getItem(`fonts`) || config.builtinFonts[0].value state.currentColor = - localStorage.getItem("color") || config.colorOption[0].value; + localStorage.getItem(`color`) || config.colorOption[0].value state.currentSize = - localStorage.getItem("size") || config.sizeOption[2].value; + localStorage.getItem(`size`) || config.sizeOption[2].value state.codeTheme = - localStorage.getItem("codeTheme") || config.codeThemeOption[0].value; - state.citeStatus = localStorage.getItem("citeStatus") === "true"; - state.nightMode = localStorage.getItem("nightMode") === "true"; + localStorage.getItem(`codeTheme`) || config.codeThemeOption[0].value + state.citeStatus = localStorage.getItem(`citeStatus`) === `true` + state.nightMode = localStorage.getItem(`nightMode`) === `true` state.wxRenderer = new WxRenderer({ theme: setColor(state.currentColor), fonts: state.currentFont, size: state.currentSize, - }); + }) }, initEditorEntity(state) { - const editorDom = document.getElementById("editor"); + const editorDom = document.getElementById(`editor`) if (!editorDom.value) { editorDom.value = - localStorage.getItem("__editor_content") || formatDoc(DEFAULT_CONTENT); + localStorage.getItem(`__editor_content`) || formatDoc(DEFAULT_CONTENT) } state.editor = CodeMirror.fromTextArea(editorDom, { - mode: "text/x-markdown", - theme: "xq-light", + mode: `text/x-markdown`, + theme: `xq-light`, lineNumbers: false, lineWrapping: true, styleActiveLine: true, autoCloseBrackets: true, extraKeys: { - "Ctrl-F": function autoFormat(editor) { - const doc = formatDoc(editor.getValue(0)); - localStorage.setItem("__editor_content", doc); - editor.setValue(doc); + 'Ctrl-F': function autoFormat(editor) { + const doc = formatDoc(editor.getValue(0)) + localStorage.setItem(`__editor_content`, doc) + editor.setValue(doc) }, - "Ctrl-S": function save(editor) {}, - "Ctrl-B": function bold(editor) { - const selected = editor.getSelection(); - editor.replaceSelection(`**${selected}**`); + 'Ctrl-S': function save(editor) {}, + 'Ctrl-B': function bold(editor) { + const selected = editor.getSelection() + editor.replaceSelection(`**${selected}**`) }, - "Ctrl-D": function del(editor) { - const selected = editor.getSelection(); - editor.replaceSelection(`~~${selected}~~`); + 'Ctrl-D': function del(editor) { + const selected = editor.getSelection() + editor.replaceSelection(`~~${selected}~~`) }, - "Ctrl-I": function italic(editor) { - const selected = editor.getSelection(); - editor.replaceSelection(`*${selected}*`); + 'Ctrl-I': function italic(editor) { + const selected = editor.getSelection() + editor.replaceSelection(`*${selected}*`) }, }, - }); + }) }, initCssEditorEntity(state) { - const cssEditorDom = document.getElementById("cssEditor"); + const cssEditorDom = document.getElementById(`cssEditor`) if (!cssEditorDom.value) { cssEditorDom.value = - localStorage.getItem("__css_content") || DEFAULT_CSS_CONTENT; + localStorage.getItem(`__css_content`) || DEFAULT_CSS_CONTENT } state.cssEditor = CodeMirror.fromTextArea(cssEditorDom, { - mode: "css", - theme: "style-mirror", + mode: `css`, + theme: `style-mirror`, lineNumbers: false, lineWrapping: true, matchBrackets: true, autofocus: true, extraKeys: { - "Ctrl-F": function autoFormat(editor) { - const doc = formatCss(editor.getValue(0)); - localStorage.setItem("__css_content", doc); - editor.setValue(doc); + 'Ctrl-F': function autoFormat(editor) { + const doc = formatCss(editor.getValue(0)) + localStorage.setItem(`__css_content`, doc) + editor.setValue(doc) }, - "Ctrl-S": function save(editor) {}, + 'Ctrl-S': function save(editor) {}, }, - }); + }) }, editorRefresh(state) { - let renderer = state.wxRenderer.getRenderer(state.citeStatus); - marked.setOptions({ renderer }); - let output = marked.parse(state.editor.getValue(0)); + let renderer = state.wxRenderer.getRenderer(state.citeStatus) + marked.setOptions({ renderer }) + let output = marked.parse(state.editor.getValue(0)) // 去除第一行的 margin-top - output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"'); + output = output.replace(/(style=".*?)"/, `$1;margin-top: 0"`) if (state.citeStatus) { // 引用脚注 - output += state.wxRenderer.buildFootnotes(); + output += state.wxRenderer.buildFootnotes() // 附加的一些 style - output += state.wxRenderer.buildAddition(); + output += state.wxRenderer.buildAddition() } - state.output = output; + state.output = output }, -}; +} export default new Vuex.Store({ state, mutations, actions: {}, -}); +}) diff --git a/vue.config.js b/vue.config.js index 0335098..6c8db8c 100644 --- a/vue.config.js +++ b/vue.config.js @@ -5,9 +5,11 @@ module.exports = { configureWebpack: (config) => { config.module.rules.push({ test: /\.(txt|md)$/i, - use: [{ - loader: 'raw-loader', - }] + use: [ + { + loader: `raw-loader`, + }, + ], }) }, productionSourceMap: !isProd,