mirror of
https://github.com/doocs/md.git
synced 2024-11-24 19:10:34 +08:00
Merge branch 'main' of https://github.com/doocs/md
This commit is contained in:
commit
69175a72f2
20
.github/workflows/preview.yml
vendored
Normal file
20
.github/workflows/preview.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Surge Preview
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, closed]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
preview:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'doocs/md'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: afc163/surge-preview@main
|
||||||
|
with:
|
||||||
|
surge_token: ${{ secrets.SURGE_TOKEN }}
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
dist: dist
|
||||||
|
build: |
|
||||||
|
npm install
|
||||||
|
npm run build:h5-netlify
|
45
README.md
45
README.md
@ -68,6 +68,7 @@ npm run build:h5-netlify
|
|||||||
- [x] 支持 <kbd>Ctrl</kbd> + <kbd>F</kbd> 快速格式化文档
|
- [x] 支持 <kbd>Ctrl</kbd> + <kbd>F</kbd> 快速格式化文档
|
||||||
- [x] 支持色盘取色,快速替换文章整体色调
|
- [x] 支持色盘取色,快速替换文章整体色调
|
||||||
- [x] 支持多图上传,可自定义配置图床
|
- [x] 支持多图上传,可自定义配置图床
|
||||||
|
- [x] 支持自定义上传逻辑
|
||||||
- [x] 支持在编辑框右键弹出功能选项卡
|
- [x] 支持在编辑框右键弹出功能选项卡
|
||||||
|
|
||||||
## 目前支持哪些图床
|
## 目前支持哪些图床
|
||||||
@ -80,6 +81,7 @@ npm run build:h5-netlify
|
|||||||
| 4 | [阿里云](https://www.aliyun.com/product/oss) | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) |
|
| 4 | [阿里云](https://www.aliyun.com/product/oss) | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) |
|
||||||
| 5 | [腾讯云](https://cloud.tencent.com/act/pro/cos) | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) |
|
| 5 | [腾讯云](https://cloud.tencent.com/act/pro/cos) | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) |
|
||||||
| 6 | [七牛云](https://www.qiniu.com/products/kodo) | 配置 `AccessKey`、`SecretKey`、`Bucket`、`Domain`、`Region` 参数 | [如何使用七牛云 Kodo?](https://developer.qiniu.com/kodo) |
|
| 6 | [七牛云](https://www.qiniu.com/products/kodo) | 配置 `AccessKey`、`SecretKey`、`Bucket`、`Domain`、`Region` 参数 | [如何使用七牛云 Kodo?](https://developer.qiniu.com/kodo) |
|
||||||
|
| - | 自定义上传逻辑 | 是 | 参考[自定义上传逻辑参数详情](#自定义上传逻辑) |
|
||||||
|
|
||||||
![select-and-change-color-theme](https://doocs.oss-cn-shenzhen.aliyuncs.com/img//1606034542281-a8c99fa7-c11e-4e43-98da-e36012f54dc8.gif)
|
![select-and-change-color-theme](https://doocs.oss-cn-shenzhen.aliyuncs.com/img//1606034542281-a8c99fa7-c11e-4e43-98da-e36012f54dc8.gif)
|
||||||
|
|
||||||
@ -89,6 +91,49 @@ npm run build:h5-netlify
|
|||||||
|
|
||||||
![doocs-md-upload-image](https://doocs.oss-cn-shenzhen.aliyuncs.com/img//1606034542512-0769a336-b9eb-4d58-83c1-29db7b54f71b.gif)
|
![doocs-md-upload-image](https://doocs.oss-cn-shenzhen.aliyuncs.com/img//1606034542512-0769a336-b9eb-4d58-83c1-29db7b54f71b.gif)
|
||||||
|
|
||||||
|
## 自定义上传逻辑
|
||||||
|
|
||||||
|
在工具上没有提供预定义图床的情况下,你只需要自定义上传逻辑即可,这对于例如你不方便使用公共图床,而是使用自己的上传服务时非常有用。
|
||||||
|
|
||||||
|
你只需要在给定的函数中更改上传代码即可,为了方便,这个函数提供了可能使用的一些参数:
|
||||||
|
|
||||||
|
示例代码:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const {file, util, okCb, errCb} = CUSTOM_ARG
|
||||||
|
const param = new FormData()
|
||||||
|
param.append('file', file)
|
||||||
|
util.axios.post('http://127.0.0.1:9000/upload', param, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
}).then(res => {
|
||||||
|
okCb(res.url)
|
||||||
|
}).catch(err => {
|
||||||
|
errCb(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 提供的可用参数:
|
||||||
|
// CUSTOM_ARG = {
|
||||||
|
// content, // 待上传图片的 base64
|
||||||
|
// file, // 待上传图片的 file 对象
|
||||||
|
// util: {
|
||||||
|
// axios, // axios 实例
|
||||||
|
// CryptoJS, // 加密库
|
||||||
|
// OSS, // ali-oss
|
||||||
|
// COS, // cos-js-sdk-v5
|
||||||
|
// Buffer, // buffer-from
|
||||||
|
// uuidv4, // uuid
|
||||||
|
// qiniu, // qiniu-js
|
||||||
|
// tokenTools, // 一些编码转换函数
|
||||||
|
// getDir, // 获取 年/月/日 形式的目录
|
||||||
|
// getDateFilename, // 根据文件名获取它以 时间戳+uuid 的形式
|
||||||
|
// },
|
||||||
|
// okCb: resolve, // 重要: 上传成功后给此回调传 url 即可
|
||||||
|
// errCb: reject, // 上传失败调用的函数
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你创建了适用于其他第三方图床的上传代码,我们非常欢迎你分享它。
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
如果你使用了某些浏览器脚本修改了网页背景色,可能导致渲染后的文章出现背景色分块的现象,详见 [#63](https://github.com/doocs/md/issues/63)。
|
如果你使用了某些浏览器脚本修改了网页背景色,可能导致渲染后的文章出现背景色分块的现象,详见 [#63](https://github.com/doocs/md/issues/63)。
|
||||||
|
30
mm.config.js
30
mm.config.js
@ -3,15 +3,25 @@
|
|||||||
* 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 = {
|
module.exports = (util) => {
|
||||||
static: [
|
return {
|
||||||
{ // 测试 netlify 部署
|
api: {
|
||||||
fileDir: `./dist`,
|
'/upload'(req, res) {
|
||||||
path: `/`,
|
res.json({
|
||||||
|
msg: `上传成功`,
|
||||||
|
url: util.libObj.mockjs.mock(`@image`),
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ // 测试 gitee/github 部署
|
static: [
|
||||||
fileDir: `./dist`,
|
{ // 测试 netlify 部署
|
||||||
path: `/md`,
|
fileDir: `./dist`,
|
||||||
},
|
path: `/`,
|
||||||
],
|
},
|
||||||
|
{ // 测试 gitee/github 部署
|
||||||
|
fileDir: `./dist`,
|
||||||
|
path: `/md`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ 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";
|
||||||
|
|
||||||
function getConfig(useDefault, platform) {
|
function getConfig(useDefault, platform) {
|
||||||
if (useDefault) {
|
if (useDefault) {
|
||||||
@ -42,6 +43,10 @@ function getConfig(useDefault, platform) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 `年/月/日` 形式的目录
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
function getDir() {
|
function getDir() {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
@ -50,6 +55,11 @@ function getDir() {
|
|||||||
return `${year}/${month}/${day}`;
|
return `${year}/${month}/${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件名获取它以 `时间戳+uuid` 的形式
|
||||||
|
* @param {string} filename 文件名
|
||||||
|
* @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];
|
||||||
@ -216,6 +226,42 @@ async function txCOSFileUpload(file) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// formCustom File Upload
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function formCustomUpload(content, file) {
|
||||||
|
const str = `
|
||||||
|
async (CUSTOM_ARG) => {
|
||||||
|
${localStorage.getItem(`formCustomConfig`)}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const exportObj = {
|
||||||
|
content, // 待上传图片的 base64
|
||||||
|
file, // 待上传图片的 file 对象
|
||||||
|
util: {
|
||||||
|
axios: fetch, // axios 实例
|
||||||
|
CryptoJS, // 加密库
|
||||||
|
OSS, // ali-oss
|
||||||
|
COS, // cos-js-sdk-v5
|
||||||
|
Buffer, // buffer-from
|
||||||
|
uuidv4, // uuid
|
||||||
|
qiniu, // qiniu-js
|
||||||
|
tokenTools, // 一些编码转换函数
|
||||||
|
getDir, // 获取 年/月/日 形式的目录
|
||||||
|
getDateFilename, // 根据文件名获取它以 时间戳+uuid 的形式
|
||||||
|
},
|
||||||
|
okCb: resolve, // 重要: 上传成功后给此回调传 url 即可
|
||||||
|
errCb: reject, // 上传失败调用的函数
|
||||||
|
}
|
||||||
|
eval(str)(exportObj).catch(err => {
|
||||||
|
console.error(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");
|
||||||
@ -230,6 +276,8 @@ function fileUpload(content, file) {
|
|||||||
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":
|
||||||
|
return formCustomUpload(content, file);
|
||||||
default:
|
default:
|
||||||
// return file.size / 1024 < 1024
|
// return file.size / 1024 < 1024
|
||||||
// ? giteeUpload(content, file.name)
|
// ? giteeUpload(content, file.name)
|
||||||
|
@ -374,3 +374,21 @@ function getElementStyles(element, excludes = ["width", "height"]) {
|
|||||||
.map(([key, value]) => `${key}:${value};`)
|
.map(([key, value]) => `${key}:${value};`)
|
||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除左边多余空格
|
||||||
|
* @param {*} str
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function removeLeft(str) {
|
||||||
|
const lines = str.split('\n')
|
||||||
|
// 获取应该删除的空白符数量
|
||||||
|
const minSpaceNum = lines.filter(item => item.trim())
|
||||||
|
.map(item => item.match(/(^\s+)?/)[0].length)
|
||||||
|
.sort((a, b) => a - b)[0]
|
||||||
|
// 删除空白符
|
||||||
|
const newStr = lines
|
||||||
|
.map(item => item.slice(minSpaceNum))
|
||||||
|
.join('\n')
|
||||||
|
return newStr
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
:visible="value"
|
:visible="value"
|
||||||
@close="$emit('close')"
|
@close="$emit('close')"
|
||||||
>
|
>
|
||||||
<el-tabs type="activeName" :value="'upload'">
|
<el-tabs type="activeName" v-model="activeName">
|
||||||
<el-tab-pane class="upload-panel" label="选择上传" name="upload">
|
<el-tab-pane class="upload-panel" label="选择上传" name="upload">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="imgHost"
|
v-model="imgHost"
|
||||||
@ -288,12 +288,42 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane class="github-panel formCustom" label="自定义代码" name="formCustom">
|
||||||
|
<el-form
|
||||||
|
class="setting-form"
|
||||||
|
:model="formCustom"
|
||||||
|
label-position="right"
|
||||||
|
>
|
||||||
|
<el-form-item label="" :required="true">
|
||||||
|
<el-input
|
||||||
|
class="formCustomElInput"
|
||||||
|
ref="formCustomElInput"
|
||||||
|
type="textarea"
|
||||||
|
resize="none"
|
||||||
|
placeholder="Your custom code here."
|
||||||
|
v-model="formCustom.code">
|
||||||
|
</el-input>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
href="https://github.com/doocs/md#自定义上传逻辑"
|
||||||
|
target="_blank"
|
||||||
|
>参数详情?</el-link
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="formCustomSave"
|
||||||
|
>保存配置</el-button
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { checkImage } from "../../assets/scripts/util";
|
import { checkImage, removeLeft } from "../../assets/scripts/util";
|
||||||
|
import CodeMirror from "codemirror/lib/codemirror";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -304,6 +334,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
activeName: `upload`,
|
||||||
|
|
||||||
formGitHub: {
|
formGitHub: {
|
||||||
repo: "",
|
repo: "",
|
||||||
branch: "",
|
branch: "",
|
||||||
@ -337,6 +369,21 @@ export default {
|
|||||||
domain: "",
|
domain: "",
|
||||||
region: "",
|
region: "",
|
||||||
},
|
},
|
||||||
|
formCustom: {
|
||||||
|
code: localStorage.getItem(`formCustomConfig`) || removeLeft(`
|
||||||
|
const {file, util, okCb, errCb} = CUSTOM_ARG
|
||||||
|
const param = new FormData()
|
||||||
|
param.append('file', file)
|
||||||
|
util.axios.post('http://127.0.0.1:9000/upload', param, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
}).then(res => {
|
||||||
|
okCb(res.url)
|
||||||
|
}).catch(err => {
|
||||||
|
errCb(err)
|
||||||
|
})
|
||||||
|
`).trim(),
|
||||||
|
editor: undefined,
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
value: "default",
|
value: "default",
|
||||||
@ -362,6 +409,10 @@ export default {
|
|||||||
value: "qiniu",
|
value: "qiniu",
|
||||||
label: "七牛云",
|
label: "七牛云",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "formCustom",
|
||||||
|
label: "自定义代码",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
imgHost: "default",
|
imgHost: "default",
|
||||||
};
|
};
|
||||||
@ -454,6 +505,11 @@ export default {
|
|||||||
localStorage.setItem("qiniuConfig", JSON.stringify(this.formQiniu));
|
localStorage.setItem("qiniuConfig", JSON.stringify(this.formQiniu));
|
||||||
this.$message.success("保存成功");
|
this.$message.success("保存成功");
|
||||||
},
|
},
|
||||||
|
formCustomSave() {
|
||||||
|
const str = this.formCustom.editor.getValue()
|
||||||
|
localStorage.setItem(`formCustomConfig`, str)
|
||||||
|
this.$message.success(`保存成功`)
|
||||||
|
},
|
||||||
|
|
||||||
beforeImageUpload(file) {
|
beforeImageUpload(file) {
|
||||||
// check image
|
// check image
|
||||||
@ -479,6 +535,25 @@ export default {
|
|||||||
this.$emit("uploadImage", params.file);
|
this.$emit("uploadImage", params.file);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
activeName: {
|
||||||
|
immediate: true,
|
||||||
|
handler(val) {
|
||||||
|
if(val === `formCustom`) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const textarea = this.$refs.formCustomElInput.$el.querySelector(`textarea`)
|
||||||
|
this.formCustom.editor = this.formCustom.editor || CodeMirror.fromTextArea(textarea, {
|
||||||
|
mode: `javascript`,
|
||||||
|
})
|
||||||
|
this.formCustom.editor.setValue(this.formCustom.code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -518,6 +593,20 @@ export default {
|
|||||||
.github-panel {
|
.github-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
&.formCustom {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.formCustomElInput {
|
||||||
|
/deep/ .CodeMirror {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
height: 300px !important;
|
||||||
|
font-family: "Fira Mono", "DejaVu Sans Mono", Menlo, Consolas, "Liberation Mono", Monaco, "Lucida Console", monospace !important;
|
||||||
|
line-height: 20px;
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-form {
|
.setting-form {
|
||||||
|
Loading…
Reference in New Issue
Block a user