diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..4a5a337 --- /dev/null +++ b/.github/workflows/preview.yml @@ -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 diff --git a/README.md b/README.md index 87febe7..65b0229 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ npm run build:h5-netlify - [x] 支持 Ctrl + F 快速格式化文档 - [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) | | 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) | +| - | 自定义上传逻辑 | 是 | 参考[自定义上传逻辑参数详情](#自定义上传逻辑) | ![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) +## 自定义上传逻辑 + +在工具上没有提供预定义图床的情况下,你只需要自定义上传逻辑即可,这对于例如你不方便使用公共图床,而是使用自己的上传服务时非常有用。 + +你只需要在给定的函数中更改上传代码即可,为了方便,这个函数提供了可能使用的一些参数: + +示例代码: + +```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)。 diff --git a/mm.config.js b/mm.config.js index 416e41d..92ee749 100644 --- a/mm.config.js +++ b/mm.config.js @@ -3,15 +3,25 @@ * https://hongqiye.com/doc/mockm/config/option.html * @type {import('mockm/@types/config').Config} */ -module.exports = { - static: [ - { // 测试 netlify 部署 - fileDir: `./dist`, - path: `/`, +module.exports = (util) => { + return { + api: { + '/upload'(req, res) { + res.json({ + msg: `上传成功`, + url: util.libObj.mockjs.mock(`@image`), + }) + }, }, - { // 测试 gitee/github 部署 - fileDir: `./dist`, - path: `/md`, - }, - ], + static: [ + { // 测试 netlify 部署 + fileDir: `./dist`, + path: `/`, + }, + { // 测试 gitee/github 部署 + fileDir: `./dist`, + path: `/md`, + }, + ], + } } diff --git a/src/api/file.js b/src/api/file.js index ad18679..342aff5 100644 --- a/src/api/file.js +++ b/src/api/file.js @@ -7,6 +7,7 @@ import Buffer from "buffer-from"; import { v4 as uuidv4 } from "uuid"; import * as qiniu from "qiniu-js"; import { utf16to8, base64encode, safe64 } from "../assets/scripts/tokenTools"; +import * as tokenTools from "../assets/scripts/tokenTools"; function getConfig(useDefault, platform) { if (useDefault) { @@ -42,6 +43,10 @@ function getConfig(useDefault, platform) { }; } +/** + * 获取 `年/月/日` 形式的目录 + * @returns string + */ function getDir() { const date = new Date(); const year = date.getFullYear(); @@ -50,6 +55,11 @@ function getDir() { return `${year}/${month}/${day}`; } +/** + * 根据文件名获取它以 `时间戳+uuid` 的形式 + * @param {string} filename 文件名 + * @returns + */ function getDateFilename(filename) { const currentTimestamp = new Date().getTime(); 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) { const imgHost = localStorage.getItem("imgHost"); !imgHost && localStorage.setItem("imgHost", "default"); @@ -230,6 +276,8 @@ function fileUpload(content, file) { return giteeUpload(content, file.name); case "github": return ghFileUpload(content, file.name); + case "formCustom": + return formCustomUpload(content, file); default: // return file.size / 1024 < 1024 // ? giteeUpload(content, file.name) diff --git a/src/assets/scripts/util.js b/src/assets/scripts/util.js index 0f7e9d6..7d5340d 100644 --- a/src/assets/scripts/util.js +++ b/src/assets/scripts/util.js @@ -374,3 +374,21 @@ function getElementStyles(element, excludes = ["width", "height"]) { .map(([key, value]) => `${key}:${value};`) .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 +} \ No newline at end of file diff --git a/src/components/CodemirrorEditor/uploadImgDialog.vue b/src/components/CodemirrorEditor/uploadImgDialog.vue index ebe7be5..6284c52 100644 --- a/src/components/CodemirrorEditor/uploadImgDialog.vue +++ b/src/components/CodemirrorEditor/uploadImgDialog.vue @@ -5,7 +5,7 @@ :visible="value" @close="$emit('close')" > - + + + + + + + 参数详情? + + + 保存配置 + + + @@ -518,6 +593,20 @@ export default { .github-panel { display: flex; 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 {