mirror of
https://github.com/doocs/md.git
synced 2024-11-24 11:00:33 +08:00
chore: convert js to ts (#405)
This commit is contained in:
parent
d3a7d08f9d
commit
b99ddef0ca
32
package-lock.json
generated
32
package-lock.json
generated
@ -41,9 +41,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "2.26.0",
|
||||
"@types/buffer-from": "^1.1.3",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/marked": "^4.0.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^22.4.1",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@unocss/eslint-plugin": "^0.62.2",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
@ -913,7 +915,6 @@
|
||||
},
|
||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||
"version": "1.3.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -2229,6 +2230,15 @@
|
||||
"vue": "^2.7.0 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/buffer-from": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/buffer-from/-/buffer-from-1.1.3.tgz",
|
||||
"integrity": "sha512-2lq4YC9uLUMGHkl2IDtX4tCXSo2+hwMpOJcY1qiIk1kybc31rIlPyM1HCVJhkPFIo75a/pOVxqyvwuf5TpCG/w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "5.60.15",
|
||||
"resolved": "https://registry.npmmirror.com/@types/codemirror/-/codemirror-5.60.15.tgz",
|
||||
@ -2238,6 +2248,12 @@
|
||||
"@types/tern": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/crypto-js": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz",
|
||||
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
@ -2273,12 +2289,6 @@
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/marked": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/marked/-/marked-4.3.2.tgz",
|
||||
"integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "3.0.15",
|
||||
"resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-3.0.15.tgz",
|
||||
@ -2318,6 +2328,12 @@
|
||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||
|
@ -50,9 +50,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "2.26.0",
|
||||
"@types/buffer-from": "^1.1.3",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/marked": "^4.0.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^22.4.1",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@unocss/eslint-plugin": "^0.62.2",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toMerged } from 'es-toolkit'
|
||||
|
||||
import type { Theme } from '@/types'
|
||||
import type { IConfigOption, Theme } from '@/types'
|
||||
|
||||
const defaultTheme: Theme = {
|
||||
base: {
|
||||
@ -342,7 +342,12 @@ const graceTheme = toMerged(defaultTheme, {
|
||||
},
|
||||
})
|
||||
|
||||
export const themeOptions = [
|
||||
export const themeMap = {
|
||||
default: defaultTheme,
|
||||
grace: graceTheme,
|
||||
}
|
||||
|
||||
export const themeOptions: IConfigOption<keyof typeof themeMap>[] = [
|
||||
{
|
||||
label: `经典`,
|
||||
value: `default`,
|
||||
@ -354,8 +359,3 @@ export const themeOptions = [
|
||||
desc: ``,
|
||||
},
|
||||
]
|
||||
|
||||
export const themeMap = {
|
||||
default: defaultTheme,
|
||||
grace: graceTheme,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
const output = ref(``)
|
||||
|
||||
// 文本字体
|
||||
const theme = useStorage(addPrefix(`theme`), themeOptions[0].value)
|
||||
const theme = useStorage<keyof typeof themeMap>(addPrefix(`theme`), themeOptions[0].value)
|
||||
// 文本字体
|
||||
const fontFamily = useStorage(`fonts`, fontFamilyOptions[0].value)
|
||||
// 文本大小
|
||||
@ -46,15 +46,15 @@ export const useStore = defineStore(`store`, () => {
|
||||
const fontSizeNumber = computed(() => fontSize.value.replace(`px`, ``))
|
||||
|
||||
// 内容编辑器编辑器
|
||||
const editor = ref(null)
|
||||
const editor = ref<CodeMirror.EditorFromTextArea | null>(null)
|
||||
// 编辑区域内容
|
||||
const editorContent = useStorage(`__editor_content`, DEFAULT_CONTENT)
|
||||
|
||||
// 格式化文档
|
||||
const formatContent = () => {
|
||||
formatDoc(editor.value.getValue()).then((doc) => {
|
||||
editorContent.value = doc
|
||||
editor.value.setValue(doc)
|
||||
formatDoc((editor.value!).getValue()).then((doc) => {
|
||||
editorContent.value = doc;
|
||||
(editor.value!).setValue(doc)
|
||||
})
|
||||
}
|
||||
|
||||
@ -76,9 +76,9 @@ export const useStore = defineStore(`store`, () => {
|
||||
}
|
||||
|
||||
// 自义定 CSS 编辑器
|
||||
const cssEditor = ref(null)
|
||||
const setCssEditorValue = (content) => {
|
||||
cssEditor.value.setValue(content)
|
||||
const cssEditor = ref<CodeMirror.EditorFromTextArea | null>(null)
|
||||
const setCssEditorValue = (content: string) => {
|
||||
(cssEditor.value!).setValue(content)
|
||||
}
|
||||
// 自定义 CSS 内容
|
||||
const cssContent = useStorage(`__css_content`, DEFAULT_CSS_CONTENT)
|
||||
@ -99,24 +99,24 @@ export const useStore = defineStore(`store`, () => {
|
||||
})
|
||||
const getCurrentTab = () => cssContentConfig.value.tabs.find((tab) => {
|
||||
return tab.name === cssContentConfig.value.active
|
||||
})
|
||||
const tabChanged = (name) => {
|
||||
})!
|
||||
const tabChanged = (name: string) => {
|
||||
cssContentConfig.value.active = name
|
||||
const content = cssContentConfig.value.tabs.find((tab) => {
|
||||
return tab.name === name
|
||||
}).content
|
||||
})!.content
|
||||
setCssEditorValue(content)
|
||||
}
|
||||
|
||||
// 重命名 css 方案
|
||||
const renameTab = (name) => {
|
||||
const tab = getCurrentTab()
|
||||
const renameTab = (name: string) => {
|
||||
const tab = getCurrentTab()!
|
||||
tab.title = name
|
||||
tab.name = name
|
||||
cssContentConfig.value.active = name
|
||||
}
|
||||
|
||||
const addCssContentTab = (name) => {
|
||||
const addCssContentTab = (name: string) => {
|
||||
cssContentConfig.value.tabs.push({
|
||||
name,
|
||||
title: name,
|
||||
@ -125,20 +125,21 @@ export const useStore = defineStore(`store`, () => {
|
||||
cssContentConfig.value.active = name
|
||||
setCssEditorValue(DEFAULT_CSS_CONTENT)
|
||||
}
|
||||
const validatorTabName = (val) => {
|
||||
const validatorTabName = (val: string) => {
|
||||
return cssContentConfig.value.tabs.every(({ name }) => name !== val)
|
||||
}
|
||||
|
||||
const renderer = initRenderer({
|
||||
theme: customCssWithTemplate(css2json(getCurrentTab().content), primaryColor.value, customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber.value, color: primaryColor.value })),
|
||||
fonts: fontFamily.value,
|
||||
size: fontSizeNumber.value,
|
||||
})
|
||||
|
||||
// 更新编辑器
|
||||
const editorRefresh = () => {
|
||||
codeThemeChange()
|
||||
renderer.reset({ status: isCiteStatus.value, legend: legend.value })
|
||||
let outputTemp = marked.parse(editor.value.getValue(0))
|
||||
let outputTemp = marked.parse(editor.value!.getValue()) as string
|
||||
|
||||
// 去除第一行的 margin-top
|
||||
outputTemp = outputTemp.replace(/(style=".*?)"/, `$1;margin-top: 0"`)
|
||||
@ -173,7 +174,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
|
||||
// 更新 CSS
|
||||
const updateCss = () => {
|
||||
const json = css2json(cssEditor.value.getValue())
|
||||
const json = css2json(cssEditor.value!.getValue())
|
||||
const newTheme = customCssWithTemplate(json, primaryColor.value, customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber.value, color: primaryColor.value }))
|
||||
renderer.setOptions({
|
||||
theme: newTheme,
|
||||
@ -182,7 +183,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
}
|
||||
// 初始化 CSS 编辑器
|
||||
onMounted(() => {
|
||||
const cssEditorDom = document.querySelector(`#cssEditor`)
|
||||
const cssEditorDom = document.querySelector<HTMLTextAreaElement>(`#cssEditor`)!
|
||||
cssEditorDom.value = getCurrentTab().content
|
||||
const theme = isDark.value ? `darcula` : `xq-light`
|
||||
cssEditor.value = markRaw(
|
||||
@ -190,32 +191,32 @@ export const useStore = defineStore(`store`, () => {
|
||||
mode: `css`,
|
||||
theme,
|
||||
lineNumbers: false,
|
||||
styleActiveLine: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
autofocus: true,
|
||||
extraKeys: {
|
||||
[`${shiftKey}-${altKey}-F`]: function autoFormat(editor) {
|
||||
[`${shiftKey}-${altKey}-F`]: function autoFormat(editor: CodeMirror.Editor) {
|
||||
formatDoc(editor.getValue(), `css`).then((doc) => {
|
||||
getCurrentTab().content = doc
|
||||
editor.setValue(doc)
|
||||
})
|
||||
},
|
||||
},
|
||||
}),
|
||||
} as never),
|
||||
)
|
||||
|
||||
// 自动提示
|
||||
cssEditor.value.on(`keyup`, (cm, e) => {
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
|
||||
cm.showHint(e)
|
||||
(cm as any).showHint(e)
|
||||
}
|
||||
})
|
||||
|
||||
// 实时保存
|
||||
cssEditor.value.on(`update`, () => {
|
||||
updateCss()
|
||||
getCurrentTab().content = cssEditor.value.getValue()
|
||||
getCurrentTab().content = cssEditor.value!.getValue()
|
||||
})
|
||||
})
|
||||
|
||||
@ -249,25 +250,25 @@ export const useStore = defineStore(`store`, () => {
|
||||
],
|
||||
}
|
||||
|
||||
cssEditor.value.setValue(DEFAULT_CSS_CONTENT)
|
||||
cssEditor.value!.setValue(DEFAULT_CSS_CONTENT)
|
||||
|
||||
updateCss()
|
||||
editorRefresh()
|
||||
}
|
||||
|
||||
// 为函数添加刷新编辑器的功能
|
||||
const withAfterRefresh = fn => (...rest) => {
|
||||
const withAfterRefresh = (fn: (...rest: any[]) => void) => (...rest: any[]) => {
|
||||
fn(...rest)
|
||||
editorRefresh()
|
||||
}
|
||||
|
||||
const getTheme = (size, color) => {
|
||||
const getTheme = (size: string, color: string) => {
|
||||
const newTheme = themeMap[theme.value]
|
||||
const fontSize = size.replace(`px`, ``)
|
||||
return customCssWithTemplate(css2json(getCurrentTab().content), color, customizeTheme(newTheme, { fontSize, color }))
|
||||
}
|
||||
|
||||
const themeChanged = withAfterRefresh((newTheme) => {
|
||||
const themeChanged = withAfterRefresh((newTheme: keyof typeof themeMap) => {
|
||||
renderer.setOptions({
|
||||
theme: customCssWithTemplate(css2json(getCurrentTab().content), primaryColor.value, customizeTheme(themeMap[newTheme], { fontSize: fontSizeNumber.value })),
|
||||
})
|
||||
@ -320,12 +321,12 @@ export const useStore = defineStore(`store`, () => {
|
||||
// 导出编辑器内容为 HTML,并且下载到本地
|
||||
const exportEditorContent2HTML = () => {
|
||||
exportHTML()
|
||||
document.querySelector(`#output`).innerHTML = output.value
|
||||
document.querySelector(`#output`)!.innerHTML = output.value
|
||||
}
|
||||
|
||||
// 导出编辑器内容到本地
|
||||
const exportEditorContent2MD = () => {
|
||||
downloadMD(editor.value.getValue())
|
||||
downloadMD(editor.value!.getValue())
|
||||
}
|
||||
|
||||
// 导入 Markdown 文档
|
||||
@ -336,7 +337,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
input.name = `filename`
|
||||
input.accept = `.md`
|
||||
input.onchange = () => {
|
||||
const file = input.files[0]
|
||||
const file = input.files![0]
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
@ -344,7 +345,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
const reader = new FileReader()
|
||||
reader.readAsText(file)
|
||||
reader.onload = (event) => {
|
||||
editor.value.setValue(event.target.result)
|
||||
(editor.value!).setValue((event.target !).result as string)
|
||||
ElMessage.success(`文档导入成功`)
|
||||
}
|
||||
}
|
||||
@ -374,7 +375,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
editor.value.focus()
|
||||
(editor.value!).focus()
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { PropertiesHyphen } from 'csstype'
|
||||
|
||||
type Block = `h1` | `h2` | `h3` | `h4` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
|
||||
type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
|
||||
export type Block = `h1` | `h2` | `h3` | `h4` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
|
||||
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
|
||||
|
||||
interface CustomCSSProperties {
|
||||
[`--md-primary-color`]?: string
|
||||
@ -20,14 +20,14 @@ export interface IOpts {
|
||||
theme: Theme
|
||||
fonts: string
|
||||
size: string
|
||||
legend: string
|
||||
status: boolean
|
||||
legend?: string
|
||||
status?: boolean
|
||||
}
|
||||
|
||||
export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
|
||||
|
||||
export interface IConfigOption {
|
||||
export interface IConfigOption<VT = string> {
|
||||
label: string
|
||||
value: string
|
||||
value: VT
|
||||
desc: string
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ const service = axios.create({
|
||||
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
if (/^(?:post|put|delete)$/i.test(config.method)) {
|
||||
if (/^(?:post|put|delete)$/i.test(`${config.method}`)) {
|
||||
if (config.data && config.data.upload) {
|
||||
config.headers[`Content-Type`] = `multipart/form-data`
|
||||
}
|
@ -11,7 +11,7 @@ import { base64encode, safe64, utf16to8 } from '@/utils/tokenTools'
|
||||
import * as tokenTools from '@/utils/tokenTools'
|
||||
import { giteeConfig, githubConfig } from '@/config'
|
||||
|
||||
function getConfig(useDefault, platform) {
|
||||
function getConfig(useDefault: boolean, platform: string) {
|
||||
if (useDefault) {
|
||||
// load default config file
|
||||
const config = platform === `github` ? githubConfig : giteeConfig
|
||||
@ -29,7 +29,7 @@ function getConfig(useDefault, platform) {
|
||||
}
|
||||
|
||||
// load configuration from localStorage
|
||||
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`))
|
||||
const customConfig = JSON.parse(localStorage.getItem(`${platform}Config`)!)
|
||||
|
||||
// split username/repo
|
||||
const repoUrl = customConfig.repo
|
||||
@ -62,7 +62,7 @@ function getDir() {
|
||||
* @param {string} filename 文件名
|
||||
* @returns {string} `时间戳+uuid`
|
||||
*/
|
||||
function getDateFilename(filename) {
|
||||
function getDateFilename(filename: string) {
|
||||
const currentTimestamp = new Date().getTime()
|
||||
const fileSuffix = filename.split(`.`)[1]
|
||||
return `${currentTimestamp}-${uuidv4()}.${fileSuffix}`
|
||||
@ -72,7 +72,7 @@ function getDateFilename(filename) {
|
||||
// GitHub File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function ghFileUpload(content, filename) {
|
||||
async function ghFileUpload(content: string, filename: string) {
|
||||
const useDefault = localStorage.getItem(`imgHost`) === `default`
|
||||
const { username, repo, branch, accessToken } = getConfig(
|
||||
useDefault,
|
||||
@ -81,7 +81,18 @@ async function ghFileUpload(content, filename) {
|
||||
const dir = getDir()
|
||||
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`
|
||||
const dateFilename = getDateFilename(filename)
|
||||
const res = await fetch({
|
||||
const res = await fetch<{ content: {
|
||||
download_url: string
|
||||
} }, {
|
||||
content: {
|
||||
download_url: string
|
||||
}
|
||||
data: {
|
||||
content: {
|
||||
download_url: string
|
||||
}
|
||||
}
|
||||
}>({
|
||||
url: url + dateFilename,
|
||||
method: `put`,
|
||||
headers: {
|
||||
@ -95,7 +106,7 @@ async function ghFileUpload(content, filename) {
|
||||
})
|
||||
const githubResourceUrl = `raw.githubusercontent.com/${username}/${repo}/${branch}/`
|
||||
const cdnResourceUrl = `fastly.jsdelivr.net/gh/${username}/${repo}@${branch}/`
|
||||
res.content = res.data?.content || res.content
|
||||
res.content = res.data.content || res.content
|
||||
return useDefault
|
||||
? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl)
|
||||
: res.content.download_url
|
||||
@ -105,13 +116,24 @@ async function ghFileUpload(content, filename) {
|
||||
// Gitee File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function giteeUpload(content, filename) {
|
||||
async function giteeUpload(content: any, filename: string) {
|
||||
const useDefault = localStorage.getItem(`imgHost`) === `default`
|
||||
const { username, repo, branch, accessToken } = getConfig(useDefault, `gitee`)
|
||||
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<{ content: {
|
||||
download_url: string
|
||||
} }, {
|
||||
content: {
|
||||
download_url: string
|
||||
}
|
||||
data: {
|
||||
content: {
|
||||
download_url: string
|
||||
}
|
||||
}
|
||||
}>({
|
||||
url,
|
||||
method: `POST`,
|
||||
data: {
|
||||
@ -129,7 +151,10 @@ async function giteeUpload(content, filename) {
|
||||
// Qiniu File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
function getQiniuToken(accessKey, secretKey, putPolicy) {
|
||||
function getQiniuToken(accessKey: string, secretKey: string, putPolicy: {
|
||||
scope: string
|
||||
deadline: number
|
||||
}) {
|
||||
const policy = JSON.stringify(putPolicy)
|
||||
const encoded = base64encode(utf16to8(policy))
|
||||
const hash = CryptoJS.HmacSHA1(encoded, secretKey)
|
||||
@ -137,9 +162,9 @@ function getQiniuToken(accessKey, secretKey, putPolicy) {
|
||||
return `${accessKey}:${safe64(encodedSigned)}:${encoded}`
|
||||
}
|
||||
|
||||
async function qiniuUpload(file) {
|
||||
async function qiniuUpload(file: File) {
|
||||
const { accessKey, secretKey, bucket, region, path, domain } = JSON.parse(
|
||||
localStorage.getItem(`qiniuConfig`),
|
||||
localStorage.getItem(`qiniuConfig`)!,
|
||||
)
|
||||
const token = getQiniuToken(accessKey, secretKey, {
|
||||
scope: bucket,
|
||||
@ -167,10 +192,10 @@ async function qiniuUpload(file) {
|
||||
// AliOSS File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function aliOSSFileUpload(file) {
|
||||
async function aliOSSFileUpload(file: File) {
|
||||
const dateFilename = getDateFilename(file.name)
|
||||
const { region, bucket, accessKeyId, accessKeySecret, useSSL, cdnHost, path }
|
||||
= JSON.parse(localStorage.getItem(`aliOSSConfig`))
|
||||
= JSON.parse(localStorage.getItem(`aliOSSConfig`)!)
|
||||
const dir = path ? `${path}/${dateFilename}` : dateFilename
|
||||
const secure = useSSL === undefined || useSSL
|
||||
const protocol = secure ? `https` : `http`
|
||||
@ -195,10 +220,10 @@ async function aliOSSFileUpload(file) {
|
||||
// TxCOS File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function txCOSFileUpload(file) {
|
||||
async function txCOSFileUpload(file: File) {
|
||||
const dateFilename = getDateFilename(file.name)
|
||||
const { secretId, secretKey, bucket, region, path, cdnHost } = JSON.parse(
|
||||
localStorage.getItem(`txCOSConfig`),
|
||||
localStorage.getItem(`txCOSConfig`)!,
|
||||
)
|
||||
const cos = new COS({
|
||||
SecretId: secretId,
|
||||
@ -235,13 +260,13 @@ async function txCOSFileUpload(file) {
|
||||
// Minio File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function minioFileUpload(content, filename) {
|
||||
async function minioFileUpload(content: string, filename: string) {
|
||||
const dateFilename = getDateFilename(filename)
|
||||
const { endpoint, port, useSSL, bucket, accessKey, secretKey } = JSON.parse(
|
||||
localStorage.getItem(`minioConfig`),
|
||||
localStorage.getItem(`minioConfig`)!,
|
||||
)
|
||||
const buffer = Buffer(content, `base64`)
|
||||
const conf = {
|
||||
const conf: Minio.ClientOptions = {
|
||||
endPoint: endpoint,
|
||||
useSSL,
|
||||
accessKey,
|
||||
@ -278,7 +303,7 @@ async function minioFileUpload(content, filename) {
|
||||
// formCustom File Upload
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
async function formCustomUpload(content, file) {
|
||||
async function formCustomUpload(content: string, file: File) {
|
||||
const str = `
|
||||
async (CUSTOM_ARG) => {
|
||||
${localStorage.getItem(`formCustomConfig`)}
|
||||
@ -304,16 +329,18 @@ async function formCustomUpload(content, file) {
|
||||
errCb: reject, // 上传失败调用的函数
|
||||
}
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(str)(exportObj).catch((err) => {
|
||||
eval(str)(exportObj).catch((err: any) => {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function fileUpload(content, file) {
|
||||
function fileUpload(content: string, file: File) {
|
||||
const imgHost = localStorage.getItem(`imgHost`)
|
||||
!imgHost && localStorage.setItem(`imgHost`, `default`)
|
||||
if (!imgHost) {
|
||||
localStorage.setItem(`imgHost`, `default`)
|
||||
}
|
||||
switch (imgHost) {
|
||||
case `aliOSS`:
|
||||
return aliOSSFileUpload(file)
|
@ -5,31 +5,36 @@ import * as prettierPluginMarkdown from 'prettier/plugins/markdown'
|
||||
import * as prettierPluginBabel from 'prettier/plugins/babel'
|
||||
import * as prettierPluginEstree from 'prettier/plugins/estree'
|
||||
import * as prettierPluginCss from 'prettier/plugins/postcss'
|
||||
import type { PropertiesHyphen } from 'csstype'
|
||||
import { prefix } from '@/config'
|
||||
import type { Block, Inline, Theme } from '@/types'
|
||||
|
||||
export function addPrefix(str) {
|
||||
export function addPrefix(str: string) {
|
||||
return `${prefix}__${str}`
|
||||
}
|
||||
|
||||
export function customizeTheme(theme, options) {
|
||||
export function customizeTheme(theme: Theme, options: {
|
||||
fontSize?: string
|
||||
color?: string
|
||||
}) {
|
||||
const newTheme = JSON.parse(JSON.stringify(theme))
|
||||
const { fontSize, color } = options
|
||||
if (fontSize) {
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const v = newTheme.block[`h${i}`][`font-size`]
|
||||
newTheme.block[`h${i}`][`font-size`] = `${fontSize * Number.parseFloat(v)}px`
|
||||
newTheme.block[`h${i}`][`font-size`] = `${Number(fontSize) * Number.parseFloat(v)}px`
|
||||
}
|
||||
}
|
||||
if (color) {
|
||||
newTheme.base[`--md-primary-color`] = color
|
||||
}
|
||||
return newTheme
|
||||
return newTheme as Theme
|
||||
}
|
||||
|
||||
export function customCssWithTemplate(jsonString, color, theme) {
|
||||
export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline, PropertiesHyphen>>, color: string, theme: Theme) {
|
||||
const newTheme = customizeTheme(theme, { color })
|
||||
|
||||
const mergeProperties = (target, source, keys) => {
|
||||
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline, PropertiesHyphen>>, keys: T[]) => {
|
||||
keys.forEach((key) => {
|
||||
if (source[key]) {
|
||||
target[key] = Object.assign(target[key] || {}, source[key])
|
||||
@ -37,7 +42,7 @@ export function customCssWithTemplate(jsonString, color, theme) {
|
||||
})
|
||||
}
|
||||
|
||||
const blockKeys = [
|
||||
const blockKeys: Block[] = [
|
||||
`h1`,
|
||||
`h2`,
|
||||
`h3`,
|
||||
@ -52,7 +57,7 @@ export function customCssWithTemplate(jsonString, color, theme) {
|
||||
`ul`,
|
||||
`ol`,
|
||||
]
|
||||
const inlineKeys = [`strong`, `codespan`, `link`, `wx_link`, `listitem`]
|
||||
const inlineKeys: Inline[] = [`strong`, `codespan`, `link`, `wx_link`, `listitem`]
|
||||
|
||||
mergeProperties(newTheme.block, jsonString, blockKeys)
|
||||
mergeProperties(newTheme.inline, jsonString, inlineKeys)
|
||||
@ -65,16 +70,16 @@ export function customCssWithTemplate(jsonString, color, theme) {
|
||||
* @param {string} css - CSS 字符串
|
||||
* @returns {object} - JSON 格式的 CSS
|
||||
*/
|
||||
export function css2json(css) {
|
||||
export function css2json(css: string): Partial<Record<Block | Inline, PropertiesHyphen>> {
|
||||
// 去除所有 CSS 注释
|
||||
css = css.replace(/\/\*[\s\S]*?\*\//g, ``)
|
||||
|
||||
const json = {}
|
||||
const json: Partial<Record<Block | Inline, PropertiesHyphen>> = {}
|
||||
|
||||
// 辅助函数:将声明数组转换为对象
|
||||
const toObject = array =>
|
||||
array.reduce((obj, item) => {
|
||||
const [property, value] = item.split(`:`).map(part => part.trim())
|
||||
const toObject = (array: any[]) =>
|
||||
array.reduce<{ [k: string]: string }>((obj, item) => {
|
||||
const [property, value] = item.split(`:`).map((part: string) => part.trim())
|
||||
if (property)
|
||||
obj[property] = value
|
||||
return obj
|
||||
@ -93,7 +98,7 @@ export function css2json(css) {
|
||||
// 获取选择器并去除空格
|
||||
const selectors = css.substring(0, lbracket)
|
||||
.split(`,`)
|
||||
.map(selector => selector.trim())
|
||||
.map(selector => selector.trim()) as (Block | Inline)[]
|
||||
|
||||
const declarationObj = toObject(declarations)
|
||||
|
||||
@ -106,6 +111,8 @@ export function css2json(css) {
|
||||
css = css.slice(rbracket + 1).trim()
|
||||
}
|
||||
|
||||
console.log(`json`, json)
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
@ -115,7 +122,7 @@ export function css2json(css) {
|
||||
* @param {'markdown' | 'css'} [type] - 内容类型,决定使用的解析器,默认为'markdown'
|
||||
* @returns {Promise<string>} - 格式化后的内容
|
||||
*/
|
||||
export async function formatDoc(content, type = `markdown`) {
|
||||
export async function formatDoc(content: string, type: `markdown` | `css` = `markdown`) {
|
||||
const plugins = {
|
||||
markdown: [prettierPluginMarkdown, prettierPluginBabel, prettierPluginEstree],
|
||||
css: [prettierPluginCss],
|
||||
@ -132,7 +139,7 @@ export async function formatDoc(content, type = `markdown`) {
|
||||
* 导出原始 Markdown 文档
|
||||
* @param {string} doc - 文档内容
|
||||
*/
|
||||
export function downloadMD(doc) {
|
||||
export function downloadMD(doc: string) {
|
||||
const downLink = document.createElement(`a`)
|
||||
|
||||
downLink.download = `content.md`
|
||||
@ -149,7 +156,7 @@ export function downloadMD(doc) {
|
||||
* 导出 HTML 生成内容
|
||||
*/
|
||||
export function exportHTML() {
|
||||
const element = document.querySelector(`#output`)
|
||||
const element = document.querySelector(`#output`)!
|
||||
setStyles(element)
|
||||
const htmlStr = element.innerHTML
|
||||
|
||||
@ -166,14 +173,14 @@ export function exportHTML() {
|
||||
downLink.click()
|
||||
document.body.removeChild(downLink)
|
||||
|
||||
function setStyles(element) {
|
||||
function setStyles(element: Element) {
|
||||
/**
|
||||
* 获取一个 DOM 元素的所有样式,
|
||||
* @param {DOM 元素} element DOM 元素
|
||||
* @param {排除的属性} excludes 如果某些属性对结果有不良影响,可以使用这个参数来排除
|
||||
* @returns 行内样式拼接结果
|
||||
*/
|
||||
function getElementStyles(element, excludes = [`width`, `height`]) {
|
||||
function getElementStyles(element: Element, excludes = [`width`, `height`]) {
|
||||
const styles = getComputedStyle(element, null)
|
||||
return Object.entries(styles)
|
||||
.filter(
|
||||
@ -188,15 +195,13 @@ export function exportHTML() {
|
||||
case isCode(element):
|
||||
case isSpan(element):
|
||||
element.setAttribute(`style`, getElementStyles(element))
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
default:
|
||||
}
|
||||
if (element.children.length) {
|
||||
Array.from(element.children).forEach(child => setStyles(child))
|
||||
}
|
||||
|
||||
// 判断是否是包裹代码块的 pre 元素
|
||||
function isPre(element) {
|
||||
function isPre(element: Element) {
|
||||
return (
|
||||
element.tagName === `PRE`
|
||||
&& Array.from(element.classList).includes(`code__pre`)
|
||||
@ -204,16 +209,19 @@ export function exportHTML() {
|
||||
}
|
||||
|
||||
// 判断是否是包裹代码块的 code 元素
|
||||
function isCode(element) {
|
||||
function isCode(element: Element | null) {
|
||||
if (element == null) {
|
||||
return false
|
||||
}
|
||||
return element.tagName === `CODE`
|
||||
}
|
||||
|
||||
// 判断是否是包裹代码字符的 span 元素
|
||||
function isSpan(element) {
|
||||
function isSpan(element: Element) {
|
||||
return (
|
||||
element.tagName === `SPAN`
|
||||
&& (isCode(element.parentElement)
|
||||
|| isCode(element.parentElement.parentElement))
|
||||
|| isCode((element.parentElement!).parentElement))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -228,7 +236,7 @@ export function exportHTML() {
|
||||
* @param {number} options.cols - 列数
|
||||
* @returns {string} 生成的 Markdown 表格
|
||||
*/
|
||||
export function createTable({ data, rows, cols }) {
|
||||
export function createTable({ data, rows, cols }: { data: { [k: string]: string }, rows: number, cols: number }) {
|
||||
let table = ``
|
||||
for (let i = 0; i < rows + 2; ++i) {
|
||||
table += `| `
|
||||
@ -244,16 +252,16 @@ export function createTable({ data, rows, cols }) {
|
||||
return table
|
||||
}
|
||||
|
||||
export function toBase64(file) {
|
||||
export function toBase64(file: Blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => resolve(reader.result.split(`,`).pop())
|
||||
reader.onload = () => resolve((reader.result as string).split(`,`).pop())
|
||||
reader.onerror = error => reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
export function checkImage(file) {
|
||||
export function checkImage(file: File) {
|
||||
// 检查文件名后缀
|
||||
const isValidSuffix = /\.(?:gif|jpe?g|png)$/i.test(file.name)
|
||||
if (!isValidSuffix) {
|
||||
@ -277,27 +285,27 @@ export function checkImage(file) {
|
||||
|
||||
/**
|
||||
* 移除左边多余空格
|
||||
* @param {*} str
|
||||
* @param {string} str
|
||||
* @returns string
|
||||
*/
|
||||
export function removeLeft(str) {
|
||||
export function removeLeft(str: string) {
|
||||
const lines = str.split(`\n`)
|
||||
// 获取应该删除的空白符数量
|
||||
const minSpaceNum = lines
|
||||
.filter(item => item.trim())
|
||||
.map(item => item.match(/(^\s+)?/)[0].length)
|
||||
.map(item => (item.match(/(^\s+)?/)!)[0].length)
|
||||
.sort((a, b) => a - b)[0]
|
||||
// 删除空白符
|
||||
return lines.map(item => item.slice(minSpaceNum)).join(`\n`)
|
||||
}
|
||||
|
||||
export function solveWeChatImage() {
|
||||
const clipboardDiv = document.getElementById(`output`)
|
||||
const clipboardDiv = document.getElementById(`output`)!
|
||||
const images = clipboardDiv.getElementsByTagName(`img`)
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
const image = images[i]
|
||||
const width = image.getAttribute(`width`)
|
||||
const height = image.getAttribute(`height`)
|
||||
const width = image.getAttribute(`width`)!
|
||||
const height = image.getAttribute(`height`)!
|
||||
image.removeAttribute(`width`)
|
||||
image.removeAttribute(`height`)
|
||||
image.style.width = width
|
||||
@ -305,7 +313,7 @@ export function solveWeChatImage() {
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeCss(html) {
|
||||
export function mergeCss(html: string) {
|
||||
return juice(html, {
|
||||
inlinePseudoElements: true,
|
||||
preserveImportant: true,
|
@ -109,13 +109,13 @@ export function initRenderer(opts: IOpts) {
|
||||
return footnoteIndex
|
||||
}
|
||||
|
||||
function reset(newOpts: IOpts): void {
|
||||
function reset(newOpts: Partial<IOpts>): void {
|
||||
footnotes.length = 0
|
||||
footnoteIndex = 0
|
||||
setOptions(newOpts)
|
||||
}
|
||||
|
||||
function setOptions(newOpts: IOpts): void {
|
||||
function setOptions(newOpts: Partial<IOpts>): void {
|
||||
opts = { ...opts, ...newOpts }
|
||||
styleMapping = buildTheme(opts)
|
||||
}
|
||||
@ -197,7 +197,7 @@ export function initRenderer(opts: IOpts) {
|
||||
},
|
||||
|
||||
image({ href, title, text }: Tokens.Image): string {
|
||||
const subText = styledContent(`figcaption`, transform(opts.legend, text, title))
|
||||
const subText = styledContent(`figcaption`, transform(opts.legend!, text, title))
|
||||
const figureStyles = styles(`figure`)
|
||||
const imgStyles = styles(`image`)
|
||||
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`
|
||||
|
@ -1,4 +1,4 @@
|
||||
export function utf16to8(str) {
|
||||
export function utf16to8(str: string) {
|
||||
let out = ``
|
||||
const len = str.length
|
||||
|
||||
@ -22,7 +22,7 @@ export function utf16to8(str) {
|
||||
return out
|
||||
}
|
||||
|
||||
export function utf8to16(str) {
|
||||
export function utf8to16(str: string) {
|
||||
let out = ``
|
||||
let i = 0
|
||||
const len = str.length
|
||||
@ -196,7 +196,7 @@ const base64DecodeChars = [
|
||||
-1,
|
||||
]
|
||||
|
||||
export function base64encode(str) {
|
||||
export function base64encode(str: string) {
|
||||
let out = ``
|
||||
let i = 0
|
||||
const len = str.length
|
||||
@ -232,7 +232,7 @@ export function base64encode(str) {
|
||||
return out
|
||||
}
|
||||
|
||||
export function base64decode(str) {
|
||||
export function base64decode(str: string) {
|
||||
let c1, c2, c3, c4
|
||||
let i = 0
|
||||
const len = str.length
|
||||
@ -283,7 +283,7 @@ export function base64decode(str) {
|
||||
return out
|
||||
}
|
||||
|
||||
export function safe64(base64) {
|
||||
export function safe64(base64: string) {
|
||||
base64 = base64.replace(/\+/g, `-`)
|
||||
base64 = base64.replace(/\//g, `_`)
|
||||
return base64
|
Loading…
Reference in New Issue
Block a user