refactor editor
This commit is contained in:
+92
-11
@@ -1,21 +1,102 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<t-cell-group title="测试 cell" theme="card">
|
||||
<t-cell title="title" arrow>测试</t-cell>
|
||||
<t-cell title="title" arrow>测试</t-cell>
|
||||
<t-cell title="title" arrow>测试</t-cell>
|
||||
<t-cell title="title" arrow>测试</t-cell>
|
||||
</t-cell-group>
|
||||
<t-button theme="primary">按钮</t-button>
|
||||
<div class="demo-app">
|
||||
<header class="hero">
|
||||
<div>
|
||||
<p class="eyebrow">timi-tdesign-mobile</p>
|
||||
<h1>移动端组件演示台</h1>
|
||||
<p>每个页面都使用真实公开组件和可重复的本地演示数据。</p>
|
||||
</div>
|
||||
<t-tag theme="primary" variant="light">TDesign Mobile</t-tag>
|
||||
</header>
|
||||
|
||||
<nav class="tabs" aria-label="演示分类">
|
||||
<t-button
|
||||
v-for="item in tabs"
|
||||
:key="item.value"
|
||||
size="small"
|
||||
:theme="activeTab === item.value ? 'primary' : 'default'"
|
||||
variant="outline"
|
||||
@click="activeTab = item.value"
|
||||
v-text="item.label"
|
||||
/>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<editor-showcase v-if="activeTab === 'editor'" />
|
||||
<article-showcase v-else-if="activeTab === 'article'" />
|
||||
<notify-showcase v-else-if="activeTab === 'notify'" />
|
||||
<duration-showcase v-else />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ArticleShowcase from "./components/ArticleShowcase.vue";
|
||||
import DurationShowcase from "./components/DurationShowcase.vue";
|
||||
import EditorShowcase from "./components/EditorShowcase.vue";
|
||||
import NotifyShowcase from "./components/NotifyShowcase.vue";
|
||||
|
||||
type TabValue = "editor" | "article" | "notify" | "duration";
|
||||
|
||||
const tabs: Array<{ label: string; value: TabValue }> = [
|
||||
{ label: "编辑器", value: "editor" },
|
||||
{ label: "文章", value: "article" },
|
||||
{ label: "通知", value: "notify" },
|
||||
{ label: "移动端组件", value: "duration" }
|
||||
];
|
||||
const activeTab = ref<TabValue>("editor");
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.root {
|
||||
width: 100%;
|
||||
background: lightgray;
|
||||
.demo-app {
|
||||
width: min(100% - 2rem, 80rem);
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 0 4rem;
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
.hero {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
h1 {
|
||||
margin: .25rem 0;
|
||||
font-size: clamp(1.75rem, 6vw, 3rem);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: .75rem;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
overflow-x: auto;
|
||||
padding-bottom: .25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
.demo-app {
|
||||
width: min(100% - 1rem, 80rem);
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<section class="demo-section">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="eyebrow">文章场景</p>
|
||||
<h2>文章编辑与预览</h2>
|
||||
<p>切换正文类型,验证统一的 Article 数据结构。</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<t-button
|
||||
v-for="item in contentTypeOptions"
|
||||
:key="item.value"
|
||||
size="small"
|
||||
:theme="articleValue.contentType === item.value ? 'primary' : 'default'"
|
||||
variant="outline"
|
||||
@click="selectContentType(item.value)"
|
||||
v-text="item.label"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-grid">
|
||||
<article class="demo-card">
|
||||
<div class="card-heading">
|
||||
<h3>ArticleEditor</h3>
|
||||
<t-button size="small" theme="primary" @click="submitArticle">提交文章</t-button>
|
||||
</div>
|
||||
<t-input v-model="articleValue.title" class="title-input" placeholder="文章标题" />
|
||||
<article-editor
|
||||
ref="articleEditorRef"
|
||||
:model-value="articleValue"
|
||||
:upload-image="uploadDemoMedia"
|
||||
:upload-video="uploadDemoMedia"
|
||||
@change="onArticleChange"
|
||||
@update:model-value="onArticleChange"
|
||||
/>
|
||||
<p class="status" v-text="articleStatus" />
|
||||
</article>
|
||||
|
||||
<article class="demo-card">
|
||||
<div class="card-heading">
|
||||
<h3>ArticleView</h3>
|
||||
<span v-text="articleValue.contentType" />
|
||||
</div>
|
||||
<article-view :article="articleValue" />
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ArticleEditor,
|
||||
ArticleView
|
||||
} from "timi-tdesign-mobile";
|
||||
import type { ArticleEditorExpose } from "timi-tdesign-mobile";
|
||||
import type { Article, ArticleContentType } from "timi-web/editor";
|
||||
import { DEMO_ARTICLE_CONTENT, cloneArticle, uploadDemoMedia } from "../demo-data";
|
||||
|
||||
const contentTypeOptions: Array<{ label: string; value: ArticleContentType }> = [
|
||||
{ label: "纯文本", value: "TEXT" },
|
||||
{ label: "Markdown", value: "MARKDOWN" },
|
||||
{ label: "Tiptap", value: "TIP_TAP" }
|
||||
];
|
||||
const articleValue = ref<Article>(cloneArticle());
|
||||
const articleEditorRef = ref<ArticleEditorExpose>();
|
||||
const articleStatus = ref("等待提交");
|
||||
|
||||
function selectContentType(contentType: ArticleContentType) {
|
||||
const content = contentType === "TEXT"
|
||||
? "这是一段纯文本示例。"
|
||||
: contentType === "MARKDOWN"
|
||||
? "## Markdown 示例\n\n这是一段 Markdown 正文。"
|
||||
: DEMO_ARTICLE_CONTENT;
|
||||
articleValue.value = {
|
||||
...articleValue.value,
|
||||
contentType,
|
||||
content
|
||||
};
|
||||
}
|
||||
|
||||
function onArticleChange(value: Article) {
|
||||
articleValue.value = value;
|
||||
}
|
||||
|
||||
async function submitArticle() {
|
||||
const value = await articleEditorRef.value?.submit();
|
||||
if (value) {
|
||||
articleValue.value = value;
|
||||
articleStatus.value = `已提交,${value.attachmentList?.length || 0} 个附件`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.demo-section {
|
||||
.section-heading {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
h2 {
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: .75rem;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.actions,
|
||||
.card-heading {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--td-component-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--td-bg-color-container);
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span,
|
||||
.status {
|
||||
font-size: .75rem;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.title-input {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin: 1rem 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.section-heading,
|
||||
.card-heading {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.actions {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<section class="demo-section">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="eyebrow">移动端专属</p>
|
||||
<h2>DurationPicker</h2>
|
||||
<p>验证 Picker 交互和毫秒值双向转换。</p>
|
||||
</div>
|
||||
<span class="value" v-text="`${durationMs} ms`" />
|
||||
</div>
|
||||
<div class="demo-card">
|
||||
<t-cell title="当前毫秒值" arrow @click="visible = true">
|
||||
<span v-text="formatDuration(durationMs)" />
|
||||
</t-cell>
|
||||
</div>
|
||||
<t-popup v-model="visible" placement="bottom" destroy-on-close>
|
||||
<div class="picker-panel">
|
||||
<duration-picker v-model="durationMs" @change="visible = false" />
|
||||
</div>
|
||||
</t-popup>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DurationPicker } from "timi-tdesign-mobile";
|
||||
|
||||
const visible = ref(false);
|
||||
const durationMs = ref(1000);
|
||||
|
||||
function formatDuration(value: number) {
|
||||
if (60 * 60 * 1000 <= value && value % (60 * 60 * 1000) === 0) {
|
||||
return `${value / (60 * 60 * 1000)} 小时`;
|
||||
}
|
||||
if (60 * 1000 <= value && value % (60 * 1000) === 0) {
|
||||
return `${value / (60 * 1000)} 分钟`;
|
||||
}
|
||||
if (1000 <= value && value % 1000 === 0) {
|
||||
return `${value / 1000} 秒`;
|
||||
}
|
||||
return `${value} 毫秒`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.demo-section {
|
||||
.section-heading {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
h2 {
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: .75rem;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
padding: .25rem;
|
||||
border: 1px solid var(--td-component-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--td-bg-color-container);
|
||||
}
|
||||
|
||||
.picker-panel {
|
||||
padding-top: .5rem;
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
background: var(--td-bg-color-container);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<section class="demo-section">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="eyebrow">编辑器基础能力</p>
|
||||
<h2>文本、富文本和代码</h2>
|
||||
<p>通过同一组公共数据规则验证移动端 TDesign 交互。</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<t-button size="small" theme="primary" @click="openSearch">打开搜索</t-button>
|
||||
<t-button size="small" variant="outline" @click="submitEditor">提交富文本</t-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-grid">
|
||||
<article class="demo-card wide">
|
||||
<div class="card-heading">
|
||||
<h3>Tiptap 编辑器</h3>
|
||||
<span v-text="editorStatus" />
|
||||
</div>
|
||||
<div class="editor-box rich-editor">
|
||||
<tiptap-editor
|
||||
ref="tiptapEditorRef"
|
||||
v-model="richContent"
|
||||
:upload-image="uploadDemoMedia"
|
||||
:upload-video="uploadDemoMedia"
|
||||
@change="editorStatus = `内容已更新,${$event.length} 个字符`"
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="demo-card wide">
|
||||
<div class="card-heading">
|
||||
<h3>Tiptap 只读视图</h3>
|
||||
<t-input v-model="maxHeight" class="height-input" placeholder="最大高度,如 20rem" />
|
||||
</div>
|
||||
<tiptap-view :content="richContent" :max-height="maxHeight" />
|
||||
</article>
|
||||
|
||||
<article class="demo-card wide">
|
||||
<div class="card-heading">
|
||||
<h3>代码编辑器</h3>
|
||||
<t-button size="small" variant="text" @click="resetCode">恢复示例</t-button>
|
||||
</div>
|
||||
<div class="editor-box code-editor">
|
||||
<code-editor
|
||||
ref="codeEditorRef"
|
||||
v-model="codeContent"
|
||||
language="typescript"
|
||||
placeholder="请输入代码"
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
CodeEditor,
|
||||
TiptapEditor,
|
||||
TiptapView
|
||||
} from "timi-tdesign-mobile";
|
||||
import type {
|
||||
CodeEditorExpose,
|
||||
TiptapEditorExpose
|
||||
} from "timi-tdesign-mobile";
|
||||
import { DEMO_ARTICLE_CONTENT, DEMO_CODE, uploadDemoMedia } from "../demo-data";
|
||||
|
||||
const richContent = ref(DEMO_ARTICLE_CONTENT);
|
||||
const codeContent = ref(DEMO_CODE);
|
||||
const maxHeight = ref("20rem");
|
||||
const editorStatus = ref("等待编辑");
|
||||
const tiptapEditorRef = ref<TiptapEditorExpose>();
|
||||
const codeEditorRef = ref<CodeEditorExpose>();
|
||||
|
||||
function openSearch() {
|
||||
tiptapEditorRef.value?.openSearch(true);
|
||||
codeEditorRef.value?.openSearch(true);
|
||||
}
|
||||
|
||||
async function submitEditor() {
|
||||
const value = await tiptapEditorRef.value?.submit();
|
||||
if (value !== undefined) {
|
||||
editorStatus.value = `已提交,${value.length} 个字符`;
|
||||
}
|
||||
}
|
||||
|
||||
function resetCode() {
|
||||
codeContent.value = DEMO_CODE;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.demo-section {
|
||||
.section-heading {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
h2 {
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: .75rem;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.actions,
|
||||
.card-heading {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
min-width: 0;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--td-component-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--td-bg-color-container);
|
||||
box-shadow: 0 .25rem 1rem rgba(0, 0, 0, .04);
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: .75rem;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-box {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 24rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.rich-editor {
|
||||
height: 28rem;
|
||||
}
|
||||
|
||||
.height-input {
|
||||
width: 9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
.section-heading {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.card-heading {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.height-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<section class="demo-section">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="eyebrow">通知场景</p>
|
||||
<h2>通知编辑与预览</h2>
|
||||
<p>Notify 组件复用文章编辑器,同时保留通知自己的元数据。</p>
|
||||
</div>
|
||||
<t-button size="small" theme="primary" @click="submitNotify">提交通知</t-button>
|
||||
</div>
|
||||
|
||||
<div class="demo-grid">
|
||||
<article class="demo-card">
|
||||
<div class="card-heading">
|
||||
<h3>NotifyEditor</h3>
|
||||
<span v-text="notifyStatus" />
|
||||
</div>
|
||||
<t-input v-model="notifyValue.subject" class="subject-input" placeholder="通知标题" />
|
||||
<notify-editor
|
||||
ref="notifyEditorRef"
|
||||
:model-value="notifyValue"
|
||||
:upload-image="uploadDemoMedia"
|
||||
:upload-video="uploadDemoMedia"
|
||||
@change="onNotifyChange"
|
||||
@update:model-value="onNotifyChange"
|
||||
/>
|
||||
</article>
|
||||
|
||||
<article class="demo-card">
|
||||
<div class="card-heading">
|
||||
<h3>NotifyView</h3>
|
||||
<span v-text="notifyValue.status" />
|
||||
</div>
|
||||
<notify-view :detail="notifyValue" />
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
NotifyEditor,
|
||||
NotifyView
|
||||
} from "timi-tdesign-mobile";
|
||||
import type { NotifyEditorExpose } from "timi-tdesign-mobile";
|
||||
import type { NotifyDetail } from "timi-web/editor";
|
||||
import { cloneNotify, uploadDemoMedia } from "../demo-data";
|
||||
|
||||
const notifyValue = ref<NotifyDetail>(cloneNotify());
|
||||
const notifyEditorRef = ref<NotifyEditorExpose>();
|
||||
const notifyStatus = ref("等待提交");
|
||||
|
||||
function onNotifyChange(value: NotifyDetail) {
|
||||
notifyValue.value = value;
|
||||
}
|
||||
|
||||
async function submitNotify() {
|
||||
const value = await notifyEditorRef.value?.submit();
|
||||
if (value) {
|
||||
notifyValue.value = value;
|
||||
notifyStatus.value = `已提交,${value.content?.length || 0} 个字符`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.demo-section {
|
||||
.section-heading {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
h2 {
|
||||
margin: .25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: .75rem;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.card-heading {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
justify-content: space-between;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: .75rem;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--td-component-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--td-bg-color-container);
|
||||
}
|
||||
|
||||
.subject-input {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.section-heading,
|
||||
.card-heading {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Article, NotifyDetail, UploadResult } from "timi-web/editor";
|
||||
|
||||
/** 编辑器演示使用的固定内容,避免每个页面各自维护一份 HTML。 */
|
||||
export const DEMO_ARTICLE_CONTENT = `<h2>跨平台编辑器</h2>
|
||||
<p>这段内容同时验证标题、段落、链接、表格和代码块的渲染。</p>
|
||||
<blockquote><p>移动端和 PC 端只替换交互层,共享文档模型。</p></blockquote>
|
||||
<pre><code class="language-typescript">const message: string = "timi editor";</code></pre>`;
|
||||
|
||||
export const DEMO_CODE = `type DemoUser = {
|
||||
\tid: string;
|
||||
\tname: string;
|
||||
};
|
||||
|
||||
const user: DemoUser = {
|
||||
\tid: "demo-001",
|
||||
\tname: "Timi"
|
||||
};
|
||||
|
||||
console.log(user.name);`;
|
||||
|
||||
export const DEMO_ARTICLE: Article = {
|
||||
title: "编辑器组件演示文章",
|
||||
description: "用于验证文章编辑、预览和附件处理。",
|
||||
contentType: "TIP_TAP",
|
||||
content: DEMO_ARTICLE_CONTENT,
|
||||
attachmentList: []
|
||||
};
|
||||
|
||||
export const DEMO_NOTIFY: NotifyDetail = {
|
||||
subject: "编辑器通知演示",
|
||||
msgType: "INTERNAL",
|
||||
contentType: "TIP_TAP",
|
||||
content: DEMO_ARTICLE_CONTENT,
|
||||
status: "WAITING",
|
||||
attachmentList: []
|
||||
};
|
||||
|
||||
export function cloneArticle(): Article {
|
||||
return {
|
||||
...DEMO_ARTICLE,
|
||||
attachmentList: [...(DEMO_ARTICLE.attachmentList || [])]
|
||||
};
|
||||
}
|
||||
|
||||
export function cloneNotify(): NotifyDetail {
|
||||
return {
|
||||
...DEMO_NOTIFY,
|
||||
attachmentList: [...(DEMO_NOTIFY.attachmentList || [])]
|
||||
};
|
||||
}
|
||||
|
||||
/** 返回本地预览地址,让示例无需依赖后端上传接口。 */
|
||||
export async function uploadDemoMedia(file: File): Promise<UploadResult> {
|
||||
return {
|
||||
id: `demo-${Date.now()}-${file.name}`,
|
||||
name: file.name,
|
||||
url: URL.createObjectURL(file)
|
||||
};
|
||||
}
|
||||
+2
-3
@@ -1,15 +1,14 @@
|
||||
import { createApp } from "vue";
|
||||
import Root from "./Root.vue";
|
||||
|
||||
import { axios, VPopup } from "timi-web";
|
||||
import TimiWebUI, { VPopup } from "timi-web";
|
||||
|
||||
import "timi-web/style.css";
|
||||
import "tdesign-mobile-vue/es/style/index.css";
|
||||
import "../src/assets/style/tencent-cloud.less";
|
||||
import "../src/assets/style/tencent-cloud-custom.less";
|
||||
|
||||
axios.defaults.baseURL = "http://localhost:8091";
|
||||
|
||||
const app = createApp(Root);
|
||||
app.use(TimiWebUI);
|
||||
app.directive("popup", VPopup);
|
||||
app.mount("#root");
|
||||
|
||||
Reference in New Issue
Block a user