跳至正文

文档对应 SoEditor 1.3.0

Vue 表单示例

SoEditor 1.3.0
为网站 CMS 而生

让内容编辑回归简单

一篇文章、一张产品介绍,或下一次发布。用熟悉的工具,把内容写好。

WYSIWYGHTML SourceCMS

点击后加载 · 内容仅保留在当前页面

安装与运行

/cms 组件从 1.3.0 起正式发布;原有 useSoEditorWorkspace 入口继续兼容。

sh
pnpm add @soeditor/editor@1.3.0 @soeditor/vue@1.3.0 vue

点击示例的“开始体验”运行已发布的软件包。下载完整示例源码,解压后执行 pnpm installpnpm dev,打开 vue.html

体验步骤

  1. 编辑文章文字,查看下面的绑定 HTML 是否同步。
  2. 点击“替换 HTML”,确认外部状态更新到编辑器;切换只读后不能继续编辑。
  3. 点击“提交表单”查看 textarea 的规范 HTML;“重置表单”恢复本次挂载的初始值。
  4. 点击“HTML Source”观察首次加载;“重建示例”用当前绑定值创建新实例。结束体验会移除整个 iframe。

完整示例源码

此演示用 Vue 的 h() 写法保持无需 SFC 编译插件;modelValueonUpdate:modelValue 对应模板的 v-model

ts
import { createApp, defineComponent, h, ref, shallowRef } from 'vue';
import { SoEditor } from '@soeditor/vue/cms';
import type { ClassicEditor } from '@soeditor/editor/cms';
import { createEditor, initial, label, options } from './shared.js';

const ArticleForm = defineComponent({
    setup() {
        const html = ref(initial);
        const readonly = ref(false);
        const version = ref(0);
        const output = ref('');
        const status = ref(label('正在加载', 'Loading'));
        const editor = shallowRef<ClassicEditor>();
        const button = (zh: string, en: string, onClick: () => void) =>
            h('button', { type: 'button', onClick }, label(zh, en));
        return () =>
            h(
                'form',
                {
                    onSubmit: (event: Event) => {
                        event.preventDefault();
                        if (event.currentTarget instanceof HTMLFormElement)
                            output.value = String(
                                new FormData(event.currentTarget).get('body'),
                            );
                    },
                },
                [
                    h(
                        'p',
                        { className: 'notice' },
                        label(
                            'Vue 组件预览:内容仅保存在本页。',
                            'Vue component preview: content stays in this page.',
                        ),
                    ),
                    h('div', { className: 'controls' }, [
                        button('替换 HTML', 'Replace HTML', () => {
                            html.value =
                                '<h2>Updated article</h2><p>External value</p>';
                        }),
                        button('切换只读', 'Toggle readonly', () => {
                            readonly.value = !readonly.value;
                        }),
                        button('读取 HTML', 'Read HTML', () => {
                            output.value = editor.value?.getData() ?? '';
                        }),
                        button('HTML Source', 'HTML Source', () => {
                            void editor.value?.setWorkspaceView('source');
                        }),
                        button('重建示例', 'Recreate demo', () => {
                            editor.value = undefined;
                            version.value++;
                        }),
                        h(
                            'button',
                            { type: 'submit' },
                            label('提交表单', 'Submit form'),
                        ),
                        h(
                            'button',
                            { type: 'reset' },
                            label('重置表单', 'Reset form'),
                        ),
                    ]),
                    h('p', { role: 'status' }, status.value),
                    h(SoEditor, {
                        key: version.value,
                        name: 'body',
                        modelValue: html.value,
                        'onUpdate:modelValue': (value: string) => {
                            html.value = value;
                        },
                        readonly: readonly.value,
                        options,
                        createEditor,
                        onReady: (instance: ClassicEditor) => {
                            editor.value = instance;
                            status.value = label('就绪', 'Ready');
                            document.body.dataset.ready = 'true';
                        },
                        onError: (error: unknown) => {
                            status.value = String(error);
                        },
                    }),
                    h('output', { 'data-bound-value': '' }, html.value),
                    h('pre', null, output.value),
                ],
            );
    },
});
const app = createApp(ArticleForm);
app.mount('#app');
window.addEventListener('pagehide', () => app.unmount(), { once: true });
ts
import '@soeditor/editor/cms/styles.css';
import '../style.css';
import type { CreateClassicEditorOptions } from '@soeditor/editor/cms';

export const locale =
    new URLSearchParams(location.search).get('lang') === 'en' ? 'en' : 'zh-CN';
document.documentElement.lang = locale;
export const label = (zh: string, en: string) => (locale === 'en' ? en : zh);
export const initial = '<h2>CMS article</h2><p>Edit this content.</p>';
export const options: CreateClassicEditorOptions = {
    locale,
    ariaLabel: label('文章正文', 'Article HTML'),
    editingModes: ['wysiwyg', 'source'],
    minHeight: 240,
};
export const createEditor = async (
    host: HTMLElement,
    configuration: CreateClassicEditorOptions,
) => {
    const cms = await import('@soeditor/editor/cms/optional');
    return cms.createClassicEditor(host, configuration);
};
html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="robots" content="noindex, nofollow" />
        <title>SoEditor vue demo</title>
    </head>
    <body>
        <div id="app"></div>
        <script type="module" src="./frameworks/vue.ts"></script>
    </body>
</html>

接入说明

面向网站 CMS 的 HTML 编辑器 · MIT