212 lines
4.5 KiB
Vue
212 lines
4.5 KiB
Vue
<template>
|
|
<div class="demo">
|
|
<div class="tabs">
|
|
<button
|
|
type="button"
|
|
class="tab"
|
|
:class="{ active: tab === 'edit' }"
|
|
@click="tab = 'edit'"
|
|
v-text="'编辑'"
|
|
/>
|
|
<button
|
|
type="button"
|
|
class="tab"
|
|
:class="{ active: tab === 'view' }"
|
|
@click="tab = 'view'"
|
|
v-text="'预览'"
|
|
/>
|
|
</div>
|
|
|
|
<div class="switcher">
|
|
<button
|
|
v-for="item in contentTypeList"
|
|
:key="item.value"
|
|
type="button"
|
|
class="chip"
|
|
:class="{ active: article.contentType === item.value }"
|
|
@click="switchContentType(item.value)"
|
|
v-text="item.label"
|
|
/>
|
|
</div>
|
|
|
|
<section v-show="tab === 'edit'" class="pane">
|
|
<input v-model="article.title" class="field" placeholder="文章标题" />
|
|
<article-editor
|
|
ref="editorRef"
|
|
:model-value="article"
|
|
:upload-image="uploadMedia"
|
|
:upload-video="uploadMedia"
|
|
@change="onChange"
|
|
@update:model-value="onChange"
|
|
/>
|
|
<div class="footer">
|
|
<button type="button" class="btn primary" @click="submit" v-text="'提交'" />
|
|
<span class="status" v-text="status" />
|
|
</div>
|
|
</section>
|
|
|
|
<section v-show="tab === 'view'" class="pane">
|
|
<article-view :article="article" />
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref } from "vue";
|
|
import { ArticleEditor, ArticleView } from "timi-tdesign-mobile";
|
|
import type {
|
|
Article,
|
|
ArticleContentType,
|
|
ArticleEditorExpose,
|
|
UploadResult
|
|
} from "timi-tdesign-mobile";
|
|
|
|
/** 本场景验证「同一份 Article 数据,编辑与预览共用」。 */
|
|
const RICH_CONTENT = `<h2>文章编辑与预览</h2>
|
|
<p>两侧共用同一个 <code>Article</code> 对象,切换标签即可对比。</p>
|
|
<blockquote><p>移动端用标签切换替代左右分栏。</p></blockquote>`;
|
|
|
|
const contentTypeList: Array<{ label: string; value: ArticleContentType }> = [
|
|
{ label: "纯文本", value: "TEXT" },
|
|
{ label: "Markdown", value: "MARKDOWN" },
|
|
{ label: "富文本", value: "TIP_TAP" }
|
|
];
|
|
|
|
const tab = ref("edit");
|
|
const article = ref<Article>({
|
|
title: "编辑器组件演示文章",
|
|
contentType: "TIP_TAP",
|
|
content: RICH_CONTENT,
|
|
attachmentList: []
|
|
});
|
|
|
|
const status = ref("等待提交");
|
|
const editorRef = ref<ArticleEditorExpose>();
|
|
|
|
function switchContentType(contentType: ArticleContentType) {
|
|
const content = contentType === "TEXT"
|
|
? "这是一段纯文本示例。"
|
|
: contentType === "MARKDOWN"
|
|
? "## Markdown 示例\n\n这是一段 Markdown 正文。"
|
|
: RICH_CONTENT;
|
|
article.value = {
|
|
...article.value,
|
|
contentType,
|
|
content
|
|
};
|
|
status.value = `已切换到 ${contentType}`;
|
|
}
|
|
|
|
function onChange(value: Article) {
|
|
article.value = value;
|
|
}
|
|
|
|
async function submit() {
|
|
const value = await editorRef.value?.submit();
|
|
if (value) {
|
|
article.value = value;
|
|
status.value = `已提交,${value.attachmentList?.length || 0} 个附件`;
|
|
}
|
|
}
|
|
|
|
async function uploadMedia(file: File): Promise<UploadResult> {
|
|
return {
|
|
id: `demo-${Date.now()}-${file.name}`,
|
|
name: file.name,
|
|
url: URL.createObjectURL(file)
|
|
};
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.demo {
|
|
.tabs {
|
|
display: flex;
|
|
gap: .25rem;
|
|
padding: .25rem;
|
|
margin-bottom: .75rem;
|
|
border-radius: .5rem;
|
|
background: var(--vp-c-bg-soft);
|
|
}
|
|
|
|
.tab {
|
|
flex: 1;
|
|
padding: .375rem;
|
|
font-size: .75rem;
|
|
cursor: pointer;
|
|
border: none;
|
|
border-radius: .375rem;
|
|
color: var(--vp-c-text-2);
|
|
background: transparent;
|
|
|
|
&.active {
|
|
color: var(--vp-c-brand-1);
|
|
background: var(--vp-c-bg);
|
|
}
|
|
}
|
|
|
|
.switcher {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .375rem;
|
|
margin-bottom: .75rem;
|
|
}
|
|
|
|
.chip {
|
|
padding: .25rem .625rem;
|
|
font-size: .75rem;
|
|
cursor: pointer;
|
|
border: 1px solid var(--vp-c-divider);
|
|
border-radius: 1rem;
|
|
color: var(--vp-c-text-1);
|
|
background: var(--vp-c-bg-soft);
|
|
|
|
&.active {
|
|
color: #fff;
|
|
border-color: var(--vp-c-brand-1);
|
|
background: var(--vp-c-brand-1);
|
|
}
|
|
}
|
|
|
|
.field {
|
|
width: 100%;
|
|
padding: .375rem .5rem;
|
|
margin-bottom: .75rem;
|
|
box-sizing: border-box;
|
|
font-size: .8125rem;
|
|
border: 1px solid var(--vp-c-divider);
|
|
border-radius: .375rem;
|
|
color: var(--vp-c-text-1);
|
|
background: var(--vp-c-bg);
|
|
}
|
|
|
|
.footer {
|
|
display: flex;
|
|
gap: .75rem;
|
|
align-items: center;
|
|
margin-top: .75rem;
|
|
}
|
|
|
|
.btn {
|
|
padding: .25rem .75rem;
|
|
font-size: .75rem;
|
|
cursor: pointer;
|
|
border: 1px solid var(--vp-c-divider);
|
|
border-radius: .375rem;
|
|
color: var(--vp-c-text-1);
|
|
background: var(--vp-c-bg-soft);
|
|
|
|
&.primary {
|
|
color: #fff;
|
|
border-color: var(--vp-c-brand-1);
|
|
background: var(--vp-c-brand-1);
|
|
}
|
|
}
|
|
|
|
.status {
|
|
font-size: .75rem;
|
|
color: var(--vp-c-text-2);
|
|
}
|
|
}
|
|
</style>
|