文件
OmniCard/mpuniapp/pages/component/editor/editor-preview.uvue

166 行
5.5 KiB
Plaintext

<template>
<!-- #ifdef APP -->
<view class="preview-page preview-page-app">
<web-view id="editor-preview-webview" :class="appPreviewLoaded ? 'preview-webview preview-webview-visible' : 'preview-webview preview-webview-hidden'" src="about:blank"></web-view>
<view v-if="!appPreviewLoaded" class="preview-loading-mask">
<text class="preview-loading-text">预览加载中...</text>
</view>
</view>
<!-- #endif -->
<!-- #ifndef APP -->
<view class="preview-page preview-page-rich">
<rich-text class="preview-rich-text" :nodes="previewHtml"></rich-text>
</view>
<!-- #endif -->
</template>
<script setup lang="uts">
const DEFAULT_PREVIEW_HTML = '<p style="color:#94a3b8;">暂无内容</p>'
const previewHtml = ref(DEFAULT_PREVIEW_HTML)
let previewStorageKey = 'editor-preview-html'
const appPreviewLoaded = ref(false)
let previewWebviewElement: UniWebViewElement | null = null
const BLOCKQUOTE_STYLE = 'margin:12px 0;padding:12px 16px;border-left:4px solid #cbd5e1;background:#ffffff;color:#475569;'
const PRE_STYLE = 'margin:12px 0;padding:12px;border-radius:10px;background:#0f172a;color:#e2e8f0;overflow:auto;white-space:pre-wrap;word-break:break-word;font-family:Menlo,Monaco,Consolas,monospace;'
const INLINE_CODE_STYLE = 'padding:2px 6px;border-radius:6px;background:#e2e8f0;color:#0f172a;font-family:Menlo,Monaco,Consolas,monospace;'
function appendTagStyle(html: string, tagName: string, style: string) : string {
const tagWithStylePattern = new RegExp(`<${tagName}([^>]*)style="([^"]*)"([^>]*)>`, 'gi')
const tagPattern = new RegExp(`<${tagName}(?![^>]*style=)([^>]*)>`, 'gi')
let nextHtml = html.replace(tagWithStylePattern, `<${tagName}$1style="$2;${style}"$3>`)
nextHtml = nextHtml.replace(tagPattern, `<${tagName}$1 style="${style}">`)
return nextHtml
}
function appendClassStyle(html: string, className: string, style: string) : string {
const classWithStylePattern = new RegExp(`<([a-z0-9-]+)([^>]*)class="([^"]*${className}[^"]*)"([^>]*)style="([^"]*)"([^>]*)>`, 'gi')
const classPattern = new RegExp(`<([a-z0-9-]+)(?![^>]*style=)([^>]*)class="([^"]*${className}[^"]*)"([^>]*)>`, 'gi')
let nextHtml = html.replace(classWithStylePattern, `<$1$2class="$3"$4style="$5;${style}"$6>`)
nextHtml = nextHtml.replace(classPattern, `<$1$2class="$3"$4 style="${style}">`)
return nextHtml
}
function enhancePreviewHtml(html: string) : string {
let nextHtml = html
nextHtml = appendTagStyle(nextHtml, 'blockquote', BLOCKQUOTE_STYLE)
nextHtml = appendTagStyle(nextHtml, 'pre', PRE_STYLE)
nextHtml = appendTagStyle(nextHtml, 'code', INLINE_CODE_STYLE)
nextHtml = appendClassStyle(nextHtml, 'ql-blockquote', BLOCKQUOTE_STYLE)
nextHtml = appendClassStyle(nextHtml, 'ql-code-block', PRE_STYLE)
nextHtml = appendClassStyle(nextHtml, 'code-block', PRE_STYLE)
return nextHtml
}
function normalizePreviewHtml(html: string) : string {
const trimmedHtml = html.trim()
if (trimmedHtml == '') {
return DEFAULT_PREVIEW_HTML
}
return enhancePreviewHtml(trimmedHtml)
}
function buildPreviewDocument(html: string) : string {
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><style>html,body{margin:0;padding:0;background:#f8fafc;color:#1f2937;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;}body{padding:20px 16px 28px;line-height:1.7;word-break:break-word;}img,video{max-width:100%;height:auto;}pre,.ql-code-block,.code-block{${PRE_STYLE}}blockquote,.ql-blockquote{${BLOCKQUOTE_STYLE}}code{${INLINE_CODE_STYLE}}table{width:100%;border-collapse:collapse;}td,th{border:1px solid #dbe3eb;padding:8px;}a{color:#2563eb;text-decoration:none;}</style></head><body>${html}</body></html>`
}
function readPreviewHtml(storageKey: string) : string {
const storageValue = uni.getStorageSync(storageKey)
if (typeof storageValue == 'string') {
return normalizePreviewHtml(storageValue as string)
}
return DEFAULT_PREVIEW_HTML
}
function loadAppPreview() {
// #ifdef APP
if (previewWebviewElement == null) {
previewWebviewElement = uni.getElementById('editor-preview-webview') as UniWebViewElement | null
}
const webviewElement = previewWebviewElement
if (webviewElement == null) {
return
}
webviewElement.loadData({
data: buildPreviewDocument(previewHtml.value)
})
appPreviewLoaded.value = true
// #endif
}
onLoad((options: OnLoadOptions) => {
const storageKeyValue = options['storageKey']
if (storageKeyValue != null) {
const nextStorageKey = storageKeyValue as string
if (nextStorageKey.length > 0) {
previewStorageKey = nextStorageKey
}
}
previewHtml.value = readPreviewHtml(previewStorageKey)
})
onReady(() => {
loadAppPreview()
})
onUnload(() => {
uni.removeStorageSync(previewStorageKey)
previewWebviewElement = null
})
</script>
<style>
.preview-page {
background-color: #f8fafc;
}
.preview-page-app {
height: 100%;
display: flex;
flex-direction: column;
position: relative;
}
.preview-page-rich {
padding-top: 16px;
padding-right: 16px;
padding-bottom: 28px;
padding-left: 16px;
}
.preview-webview {
flex: 1;
}
.preview-webview-hidden {
opacity: 0;
}
.preview-webview-visible {
opacity: 1;
}
.preview-loading-mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f8fafc;
}
.preview-loading-text {
color: #64748b;
font-size: 15px;
}
.preview-rich-text {
width: 100%;
}
</style>