富文本编辑器 ​
AsamEditor 使用结构化文档作为 v-model,提供编辑工具栏、只读显示和有限的实例方法。不要将 HTML 字符串传给模型。
编辑与实例方法 ​
下载示例将初始文档内联为完整单文件,并使用可编辑的公开 HTTPS 图片地址。编辑后可观察 change 次数;按钮分别调用聚焦、失焦、清空、插入图片和读取 HTML,页面同时展示当前结构化模型。
编辑与实例方法
<template><StandaloneDemo /></template>
<script lang="ts" setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external: any = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules: any = {
"Demo.vue": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-root-layout" };
const _hoisted_2: any = { class: "demo-controls" };
const _hoisted_3: any = { class: "example-paragraph" };
const _hoisted_4: any = { class: "demo-output" };
const _hoisted_5: any = { class: "demo-output" };
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
const document_js_1: any = require("./document.js");
// 默认图片来自公开 HTTPS 地址;本项目站内图片须由宿主托管,不支持 data/blob。
exports.default = (0, vue_1.defineComponent)({
...{ name: 'AsamEditorBasicExample' },
__name: 'Demo',
setup(__props?: any) {
const imageUrl: any = (0, vue_3.ref)('https://asam-ui.github.io/assets/favicon.svg');
const editor: any = (0, vue_3.ref)();
const document: any = (0, vue_3.ref)((0, document_js_1.createDocument)());
const changes: any = (0, vue_3.ref)(0);
const imageResult: any = (0, vue_3.ref)('尚未插入');
const html: any = (0, vue_3.ref)('');
function insertImage() {
imageResult.value = String(editor.value?.insertImage({
src: imageUrl.value,
alt: 'ASAM 示例图片',
title: 'ASAM 示例图片'
}) ?? false);
}
function readHTML() {
html.value = editor.value?.getHTML() ?? '';
}
function reset() {
document.value = (0, document_js_1.createDocument)();
html.value = '';
imageResult.value = '尚未插入';
}
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("div", _hoisted_1, [
(0, vue_2.createElementVNode)("div", _hoisted_2, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamInput), {
modelValue: imageUrl.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event?: any) => ((imageUrl).value = $event)),
class: "image-url",
"aria-label": "示例图片地址",
placeholder: "HTTP(S) 或站内根路径"
}, null, 8 /* PROPS */, ["modelValue"]),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[1] || (_cache[1] = ($event?: any) => (editor.value?.focus()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[6] || (_cache[6] = [
(0, vue_2.createTextVNode)("聚焦", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[2] || (_cache[2] = ($event?: any) => (editor.value?.blur()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[7] || (_cache[7] = [
(0, vue_2.createTextVNode)("失焦", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[3] || (_cache[3] = ($event?: any) => (editor.value?.clear()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[8] || (_cache[8] = [
(0, vue_2.createTextVNode)("清空", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: insertImage
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[9] || (_cache[9] = [
(0, vue_2.createTextVNode)("插入示例图片", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: readHTML
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[10] || (_cache[10] = [
(0, vue_2.createTextVNode)("读取 HTML", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: reset
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[11] || (_cache[11] = [
(0, vue_2.createTextVNode)("恢复初始文档", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
})
]),
_cache[13] || (_cache[13] = (0, vue_2.createElementVNode)("p", { class: "example-paragraph" }, "图片按所填地址加载;默认示例需要网络。可替换为本项目托管的图片根路径或 HTTP(S) 地址。", -1 /* CACHED */)),
(0, vue_2.createCommentVNode)(" prettier-ignore "),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamEditor), {
ref_key: "editor",
ref: editor,
modelValue: document.value,
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event?: any) => ((document).value = $event)),
placeholder: "填写排产说明",
onChange: _cache[5] || (_cache[5] = ($event?: any) => (changes.value++))
}, null, 8 /* PROPS */, ["modelValue"]),
(0, vue_2.createElementVNode)("p", _hoisted_3, [
(0, vue_2.createElementVNode)("output", null, "change 次数:" + (0, vue_2.toDisplayString)(changes.value) + ";插图结果:" + (0, vue_2.toDisplayString)(imageResult.value), 1 /* TEXT */)
]),
(0, vue_2.createElementVNode)("pre", _hoisted_4, (0, vue_2.toDisplayString)(html.value || '点击读取 HTML'), 1 /* TEXT */),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapse), null, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_2.withCtx)(() => [...(_cache[12] || (_cache[12] = [
(0, vue_2.createElementVNode)("span", null, "当前结构化文档", -1 /* CACHED */)
]))]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createElementVNode)("pre", _hoisted_5, (0, vue_2.toDisplayString)(JSON.stringify(document.value, null, 2)), 1 /* TEXT */)
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
})
]));
};
}
});
exports.default.__scopeId = "data-v-0793e15e";
},
"document.ts": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDocument = createDocument;
function createDocument() {
return {
type: 'doc',
content: [
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: '排产说明' }] },
{
type: 'paragraph',
content: [{ type: 'text', text: '优先核对任务参数。', marks: [{ type: 'bold' }] }]
}
]
};
}
},
};
const __dependencies: any = {"Demo.vue":{"./document.js":"document.ts"},"document.ts":{}};
const __cache: any = Object.create(null);
function __require(id: any): any {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module: any = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency: any) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.image-url[data-v-0793e15e] {
width: min(420px, 100%);
}
.demo-controls[data-v-0793e15e] {
display: flex;
flex-wrap: wrap;
gap: var(--asam-space-sm);
margin-bottom: var(--asam-space-lg);
}
.demo-output[data-v-0793e15e] {
overflow: auto;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.demo-root-layout[data-v-0793e15e] {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout[data-v-0793e15e] > :is(p, pre, output) {
margin-block: 0;
}
p[data-v-0793e15e]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template><StandaloneDemo /></template>
<script setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules = {
"Demo.vue": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-root-layout" };
const _hoisted_2 = { class: "demo-controls" };
const _hoisted_3 = { class: "example-paragraph" };
const _hoisted_4 = { class: "demo-output" };
const _hoisted_5 = { class: "demo-output" };
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
const document_js_1 = require("./document.js");
// 默认图片来自公开 HTTPS 地址;本项目站内图片须由宿主托管,不支持 data/blob。
exports.default = Object.assign({ name: 'AsamEditorBasicExample' }, {
__name: 'Demo',
setup(__props) {
const imageUrl = (0, vue_2.ref)('https://asam-ui.github.io/assets/favicon.svg');
const editor = (0, vue_2.ref)();
const document = (0, vue_2.ref)((0, document_js_1.createDocument)());
const changes = (0, vue_2.ref)(0);
const imageResult = (0, vue_2.ref)('尚未插入');
const html = (0, vue_2.ref)('');
function insertImage() {
imageResult.value = String(editor.value?.insertImage({
src: imageUrl.value,
alt: 'ASAM 示例图片',
title: 'ASAM 示例图片'
}) ?? false);
}
function readHTML() {
html.value = editor.value?.getHTML() ?? '';
}
function reset() {
document.value = (0, document_js_1.createDocument)();
html.value = '';
imageResult.value = '尚未插入';
}
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("div", _hoisted_1, [
(0, vue_1.createElementVNode)("div", _hoisted_2, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamInput), {
modelValue: imageUrl.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((imageUrl).value = $event)),
class: "image-url",
"aria-label": "示例图片地址",
placeholder: "HTTP(S) 或站内根路径"
}, null, 8 /* PROPS */, ["modelValue"]),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[1] || (_cache[1] = $event => (editor.value?.focus()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[6] || (_cache[6] = [
(0, vue_1.createTextVNode)("聚焦", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[2] || (_cache[2] = $event => (editor.value?.blur()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[7] || (_cache[7] = [
(0, vue_1.createTextVNode)("失焦", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[3] || (_cache[3] = $event => (editor.value?.clear()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[8] || (_cache[8] = [
(0, vue_1.createTextVNode)("清空", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: insertImage
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[9] || (_cache[9] = [
(0, vue_1.createTextVNode)("插入示例图片", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: readHTML
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[10] || (_cache[10] = [
(0, vue_1.createTextVNode)("读取 HTML", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: reset
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[11] || (_cache[11] = [
(0, vue_1.createTextVNode)("恢复初始文档", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
})
]),
_cache[13] || (_cache[13] = (0, vue_1.createElementVNode)("p", { class: "example-paragraph" }, "图片按所填地址加载;默认示例需要网络。可替换为本项目托管的图片根路径或 HTTP(S) 地址。", -1 /* CACHED */)),
(0, vue_1.createCommentVNode)(" prettier-ignore "),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamEditor), {
ref_key: "editor",
ref: editor,
modelValue: document.value,
"onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((document).value = $event)),
placeholder: "填写排产说明",
onChange: _cache[5] || (_cache[5] = $event => (changes.value++))
}, null, 8 /* PROPS */, ["modelValue"]),
(0, vue_1.createElementVNode)("p", _hoisted_3, [
(0, vue_1.createElementVNode)("output", null, "change 次数:" + (0, vue_1.toDisplayString)(changes.value) + ";插图结果:" + (0, vue_1.toDisplayString)(imageResult.value), 1 /* TEXT */)
]),
(0, vue_1.createElementVNode)("pre", _hoisted_4, (0, vue_1.toDisplayString)(html.value || '点击读取 HTML'), 1 /* TEXT */),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapse), null, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_1.withCtx)(() => [...(_cache[12] || (_cache[12] = [
(0, vue_1.createElementVNode)("span", null, "当前结构化文档", -1 /* CACHED */)
]))]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createElementVNode)("pre", _hoisted_5, (0, vue_1.toDisplayString)(JSON.stringify(document.value, null, 2)), 1 /* TEXT */)
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
})
]));
};
}
});
exports.default.__scopeId = "data-v-e6aa5d09";
},
"document.js": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDocument = createDocument;
function createDocument() {
return {
type: 'doc',
content: [
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: '排产说明' }] },
{
type: 'paragraph',
content: [{ type: 'text', text: '优先核对任务参数。', marks: [{ type: 'bold' }] }]
}
]
};
}
},
};
const __dependencies = {"Demo.vue":{"./document.js":"document.js"},"document.js":{}};
const __cache = Object.create(null);
function __require(id) {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.image-url[data-v-e6aa5d09] {
width: min(420px, 100%);
}
.demo-controls[data-v-e6aa5d09] {
display: flex;
flex-wrap: wrap;
gap: var(--asam-space-sm);
margin-bottom: var(--asam-space-lg);
}
.demo-output[data-v-e6aa5d09] {
overflow: auto;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.demo-root-layout[data-v-e6aa5d09] {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout[data-v-e6aa5d09] > :is(p, pre, output) {
margin-block: 0;
}
p[data-v-e6aa5d09]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
modelValue 的根节点为 { type: 'doc', content: [...] },内容数组非空;空文档可使用一个空 paragraph。 完整说明。
只读与禁用 ​
editable 默认开启,disabled 默认关闭。任一条件使编辑不可用时隐藏工具栏,clear 不修改内容,insertImage 返回 false;getHTML 仍可读取展示内容。示例在只读与禁用之间切换,并展示拒绝插图的返回值。
只读与禁用
<template><StandaloneDemo /></template>
<script lang="ts" setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external: any = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules: any = {
"Demo.vue": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-root-layout" };
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
const document_js_1: any = require("./document.js");
exports.default = (0, vue_1.defineComponent)({
...{ name: 'AsamEditorReadonlyExample' },
__name: 'Demo',
setup(__props?: any) {
const document: any = (0, vue_3.ref)((0, document_js_1.createDocument)());
const editor: any = (0, vue_3.ref)();
const disabled: any = (0, vue_3.ref)(false);
const result: any = (0, vue_3.ref)('尚未尝试');
function tryInsert() {
result.value = String(editor.value?.insertImage({
src: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22%3E%3Crect width=%2220%22 height=%2220%22 fill=%22%232d67cf%22/%3E%3C/svg%3E'
}) ?? false);
}
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("div", _hoisted_1, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[0] || (_cache[0] = ($event?: any) => (disabled.value = !disabled.value))
}, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createTextVNode)((0, vue_2.toDisplayString)(disabled.value ? '切换为只读' : '切换为禁用'), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
onClick: tryInsert
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[2] || (_cache[2] = [
(0, vue_2.createTextVNode)("尝试插图", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createCommentVNode)(" prettier-ignore "),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamEditor), {
ref_key: "editor",
ref: editor,
modelValue: document.value,
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event?: any) => ((document).value = $event)),
editable: false,
disabled: disabled.value
}, null, 8 /* PROPS */, ["modelValue", "disabled"]),
(0, vue_2.createElementVNode)("output", null, "模式:" + (0, vue_2.toDisplayString)(disabled.value ? '禁用' : '只读') + ";插图结果:" + (0, vue_2.toDisplayString)(result.value), 1 /* TEXT */)
]));
};
}
});
exports.default.__scopeId = "data-v-90547504";
},
"document.ts": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDocument = createDocument;
function createDocument() {
return {
type: 'doc',
content: [
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: '排产说明' }] },
{
type: 'paragraph',
content: [{ type: 'text', text: '优先核对任务参数。', marks: [{ type: 'bold' }] }]
}
]
};
}
},
};
const __dependencies: any = {"Demo.vue":{"./document.js":"document.ts"},"document.ts":{}};
const __cache: any = Object.create(null);
function __require(id: any): any {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module: any = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency: any) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.demo-root-layout[data-v-90547504] {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout[data-v-90547504] > :is(p, pre, output) {
margin-block: 0;
}
</style>
<template><StandaloneDemo /></template>
<script setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules = {
"Demo.vue": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-root-layout" };
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
const document_js_1 = require("./document.js");
exports.default = Object.assign({ name: 'AsamEditorReadonlyExample' }, {
__name: 'Demo',
setup(__props) {
const document = (0, vue_2.ref)((0, document_js_1.createDocument)());
const editor = (0, vue_2.ref)();
const disabled = (0, vue_2.ref)(false);
const result = (0, vue_2.ref)('尚未尝试');
function tryInsert() {
result.value = String(editor.value?.insertImage({
src: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22%3E%3Crect width=%2220%22 height=%2220%22 fill=%22%232d67cf%22/%3E%3C/svg%3E'
}) ?? false);
}
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("div", _hoisted_1, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: _cache[0] || (_cache[0] = $event => (disabled.value = !disabled.value))
}, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createTextVNode)((0, vue_1.toDisplayString)(disabled.value ? '切换为只读' : '切换为禁用'), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
onClick: tryInsert
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[2] || (_cache[2] = [
(0, vue_1.createTextVNode)("尝试插图", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createCommentVNode)(" prettier-ignore "),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamEditor), {
ref_key: "editor",
ref: editor,
modelValue: document.value,
"onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((document).value = $event)),
editable: false,
disabled: disabled.value
}, null, 8 /* PROPS */, ["modelValue", "disabled"]),
(0, vue_1.createElementVNode)("output", null, "模式:" + (0, vue_1.toDisplayString)(disabled.value ? '禁用' : '只读') + ";插图结果:" + (0, vue_1.toDisplayString)(result.value), 1 /* TEXT */)
]));
};
}
});
exports.default.__scopeId = "data-v-f1127584";
},
"document.js": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDocument = createDocument;
function createDocument() {
return {
type: 'doc',
content: [
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: '排产说明' }] },
{
type: 'paragraph',
content: [{ type: 'text', text: '优先核对任务参数。', marks: [{ type: 'bold' }] }]
}
]
};
}
},
};
const __dependencies = {"Demo.vue":{"./document.js":"document.js"},"document.js":{}};
const __cache = Object.create(null);
function __require(id) {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.demo-root-layout[data-v-f1127584] {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout[data-v-f1127584] > :is(p, pre, output) {
margin-block: 0;
}
</style>
placeholder 默认是“请输入内容”,在空文档中显示提示。工具栏支持段落、1–3 级标题、引用、列表、代码块、分隔线、常用行内标记、撤销重做和链接。
占位提示响应后续 placeholder 变化,不需要重新聚焦;该更新不改写文档、不触发模型或 change 回声,也不清除撤销历史。
安全链接、资源插入与外部替换 ​
下面同时展示 HTTP(S)/mailto 链接、非法资源被拒绝、只读/禁用切换和外部 JSON 替换。链接工具栏支持选区添加和移除,不把任意字符串作为 HTML 注入。外部替换不发 change 回声,并重建历史;新文档不能通过 undo 恢复旧文档。所有编辑操作仍受 editable/disabled 限制。
链接、图片安全与外部 JSON 替换
<template>
<div class="example">
<AsamEditor
ref="editor"
v-model="document"
:editable="editable"
:disabled="disabled"
@change="changes++"
/>
<div class="controls">
<AsamInput
v-model="imageUrl"
class="image-url"
aria-label="安全示例图片地址"
placeholder="HTTP(S) 或站内根路径"
/>
<AsamCheckboxItem v-model="editable"> 可编辑</AsamCheckboxItem>
<AsamCheckboxItem v-model="disabled"> 禁用</AsamCheckboxItem>
<AsamButton type="default" @click="insertExample">插入示例图片</AsamButton>
<AsamButton type="default" @click="insert('javascript:alert(1)')"
>尝试非法图片</AsamButton
>
<AsamButton type="default" @click="replace">外部替换 JSON</AsamButton>
<AsamButton type="default" @click="editor?.clear()">清空文档</AsamButton>
<AsamButton type="default" @click="html = editor?.getHTML() ?? ''"
>读取序列化</AsamButton
>
</div>
<p class="example-paragraph">图片按所填地址加载;默认示例需要网络。可替换为本项目托管的图片根路径或 HTTP(S) 地址。</p>
<output>插图:{{ inserted }};change:{{ changes }}</output>
<pre>{{ html }}</pre>
<pre>{{ JSON.stringify(document, null, 2) }}</pre>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamEditorSecurityExample' });
import { ref } from 'vue';
import { AsamCheckboxItem, AsamEditor, AsamButton, AsamInput } from '@asam-ui/ui';
import type { AsamEditorDocument, AsamEditorExpose } from '@asam-ui/ui';
// Editor 图片只接收根路径或 HTTP(S) URL,内联 data/blob 会按公开契约被拒绝。
const imageUrl = ref('https://asam-ui.github.io/assets/favicon.svg');
const editor = ref<AsamEditorExpose>();
const editable = ref(true);
const disabled = ref(false);
const inserted = ref('尚未插图');
const changes = ref(0);
const html = ref('');
const document = ref<AsamEditorDocument>({
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: '安全链接',
marks: [{ type: 'link', attrs: { href: 'https://example.com' } }]
},
{ type: 'hardBreak' },
{
type: 'text',
text: '邮件',
marks: [{ type: 'link', attrs: { href: 'mailto:demo@example.com' } }]
}
]
}
]
});
function insert(src: string): void {
inserted.value = String(editor.value?.insertImage({ src, alt: '示例图片' }));
}
function insertExample(): void {
insert(imageUrl.value);
}
function replace(): void {
document.value = {
type: 'doc',
content: [
{
type: 'heading',
attrs: { level: 2 },
content: [{ type: 'text', text: '外部新文档' }]
},
{ type: 'paragraph' }
]
};
}
</script>
<style scoped>
.image-url {
width: min(420px, 100%);
}
.example {
display: grid;
gap: var(--asam-space-md);
}
.controls {
display: flex;
flex-wrap: wrap;
gap: var(--asam-space-sm);
}
pre {
white-space: pre-wrap;
overflow-wrap: anywhere;
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template>
<div class="example">
<AsamEditor
ref="editor"
v-model="document"
:editable="editable"
:disabled="disabled"
@change="changes++"
/>
<div class="controls">
<AsamInput
v-model="imageUrl"
class="image-url"
aria-label="安全示例图片地址"
placeholder="HTTP(S) 或站内根路径"
/>
<AsamCheckboxItem v-model="editable"> 可编辑</AsamCheckboxItem>
<AsamCheckboxItem v-model="disabled"> 禁用</AsamCheckboxItem>
<AsamButton type="default" @click="insertExample">插入示例图片</AsamButton>
<AsamButton type="default" @click="insert('javascript:alert(1)')"
>尝试非法图片</AsamButton
>
<AsamButton type="default" @click="replace">外部替换 JSON</AsamButton>
<AsamButton type="default" @click="editor?.clear()">清空文档</AsamButton>
<AsamButton type="default" @click="html = editor?.getHTML() ?? ''"
>读取序列化</AsamButton
>
</div>
<p class="example-paragraph">图片按所填地址加载;默认示例需要网络。可替换为本项目托管的图片根路径或 HTTP(S) 地址。</p>
<output>插图:{{ inserted }};change:{{ changes }}</output>
<pre>{{ html }}</pre>
<pre>{{ JSON.stringify(document, null, 2) }}</pre>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamEditorSecurityExample' });
import { ref } from 'vue';
import { AsamCheckboxItem, AsamEditor, AsamButton, AsamInput } from '@asam-ui/ui';
// Editor 图片只接收根路径或 HTTP(S) URL,内联 data/blob 会按公开契约被拒绝。
const imageUrl = ref('https://asam-ui.github.io/assets/favicon.svg');
const editor = ref();
const editable = ref(true);
const disabled = ref(false);
const inserted = ref('尚未插图');
const changes = ref(0);
const html = ref('');
const document = ref({
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: '安全链接',
marks: [{ type: 'link', attrs: { href: 'https://example.com' } }]
},
{ type: 'hardBreak' },
{
type: 'text',
text: '邮件',
marks: [{ type: 'link', attrs: { href: 'mailto:demo@example.com' } }]
}
]
}
]
});
function insert(src) {
inserted.value = String(editor.value?.insertImage({ src, alt: '示例图片' }));
}
function insertExample() {
insert(imageUrl.value);
}
function replace() {
document.value = {
type: 'doc',
content: [
{
type: 'heading',
attrs: { level: 2 },
content: [{ type: 'text', text: '外部新文档' }]
},
{ type: 'paragraph' }
]
};
}
</script>
<style scoped>
.image-url {
width: min(420px, 100%);
}
.example {
display: grid;
gap: var(--asam-space-md);
}
.controls {
display: flex;
flex-wrap: wrap;
gap: var(--asam-space-sm);
}
pre {
white-space: pre-wrap;
overflow-wrap: anywhere;
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
初始化与外部替换会验证结构化文档。非法根、字段、节点、标记、嵌套、heading level 或资源地址被拒绝;这不是只检查 JSON 是否可解析。粘贴内容的图片会剥离;Tiptap 序列化输出会去除内部默认属性后再验证。服务器持久化结构化模型,getHTML 用于当前显示的序列化,不扩大为任意 HTML 编辑接口。
文档与资源边界 ​
支持文本、标题、引用、列表、代码块与图片等节点,以及常用文本标记。 完整说明。
图片资源由调用方提供 src 和可选 alt、title;允许 HTTP(S) 或以单个 / 开头的站内绝对路径,拒绝 data、blob、协议相对地址、凭据和控制字符。insertImage 在资源非法或不可编辑时返回 false。粘贴 HTML 中的图片被移除,不负责上传、文件选择或后端存储。
链接只接受 HTTP(S) 或有效 mailto 地址。getHTML 返回当前序列化内容,持久化建议保留结构化模型;宿主对其他来源 HTML 的处理仍由宿主负责。公开方法仅为 focus、blur、clear、insertImage、getHTML,不暴露底层编辑器实例,也没有公开内容插槽。
可调用的公开辅助出口 ​
EMPTY_EDITOR_DOCUMENT 是共享的 AsamEditorDocument 对象,形状为 { type: 'doc', content: [{ type: 'paragraph' }] }。类型字段只读;创建独立模型时可用 structuredClone,不直接改写共享常量。
isSafeImageUrl(value: string): boolean 判定 HTTP(S) 无凭据地址或单个 / 开头的站内路径。 完整说明。
validateEditorDocument(value: unknown): asserts value is AsamEditorDocument 验证上述完整文档语法。 完整说明。
公开辅助函数与动态占位提示
<template>
<div class="example">
<div class="controls">
<label for="image-url">图片地址判定</label>
<AsamInput id="image-url" v-model="imageUrl" />
<label for="link-url">链接地址判定</label>
<AsamInput id="link-url" v-model="linkUrl" />
</div>
<output data-url-results>
图片允许:{{ isSafeImageUrl(imageUrl) }};链接允许:{{ isSafeLinkUrl(linkUrl) }}
</output>
<label for="json-source">待验证 JSON</label>
<AsamTextarea id="json-source" v-model="jsonSource" :rows="5"></AsamTextarea>
<AsamButton type="default" @click="load">验证并加载 JSON</AsamButton>
<output data-parse-result>{{ result }}</output>
<label for="placeholder-value">空文档占位提示</label>
<AsamInput id="placeholder-value" v-model="placeholder" />
<AsamEditor
:model-value="document"
:placeholder="placeholder"
@update:model-value="
document = $event;
updates++;
"
@change="changes++"
/>
<output data-editor-events>模型更新 {{ updates }};change {{ changes }}</output>
<pre data-editor-document>{{ JSON.stringify(document) }}</pre>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamEditorHelpersExample' });
import { ref } from 'vue';
import {
AsamInput,
AsamTextarea,
AsamButton,
AsamEditor,
EMPTY_EDITOR_DOCUMENT,
isSafeImageUrl,
isSafeLinkUrl,
validateEditorDocument
} from '@asam-ui/ui';
import type { AsamEditorDocument } from '@asam-ui/ui';
const document = ref<AsamEditorDocument>(structuredClone(EMPTY_EDITOR_DOCUMENT));
const jsonSource = ref(JSON.stringify(EMPTY_EDITOR_DOCUMENT, null, 2));
const imageUrl = ref('/images/avatar.jpg');
const linkUrl = ref('mailto:demo@example.com');
const placeholder = ref('初始占位提示');
const changes = ref(0);
const updates = ref(0);
const result = ref('常量已复制为独立文档;辅助函数不发送请求。');
function load(): void {
try {
const value: unknown = JSON.parse(jsonSource.value);
validateEditorDocument(value);
document.value = value;
result.value = '有效文档已加载;验证不改写输入。';
} catch (error) {
result.value = `验证失败:${error instanceof Error ? error.message : String(error)}`;
}
}
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-sm);
}
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
pre {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
</style>
<template>
<div class="example">
<div class="controls">
<label for="image-url">图片地址判定</label>
<AsamInput id="image-url" v-model="imageUrl" />
<label for="link-url">链接地址判定</label>
<AsamInput id="link-url" v-model="linkUrl" />
</div>
<output data-url-results>
图片允许:{{ isSafeImageUrl(imageUrl) }};链接允许:{{ isSafeLinkUrl(linkUrl) }}
</output>
<label for="json-source">待验证 JSON</label>
<AsamTextarea id="json-source" v-model="jsonSource" :rows="5"></AsamTextarea>
<AsamButton type="default" @click="load">验证并加载 JSON</AsamButton>
<output data-parse-result>{{ result }}</output>
<label for="placeholder-value">空文档占位提示</label>
<AsamInput id="placeholder-value" v-model="placeholder" />
<AsamEditor
:model-value="document"
:placeholder="placeholder"
@update:model-value="
document = $event;
updates++;
"
@change="changes++"
/>
<output data-editor-events>模型更新 {{ updates }};change {{ changes }}</output>
<pre data-editor-document>{{ JSON.stringify(document) }}</pre>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamEditorHelpersExample' });
import { ref } from 'vue';
import {
AsamInput,
AsamTextarea,
AsamButton,
AsamEditor,
EMPTY_EDITOR_DOCUMENT,
isSafeImageUrl,
isSafeLinkUrl,
validateEditorDocument
} from '@asam-ui/ui';
const document = ref(structuredClone(EMPTY_EDITOR_DOCUMENT));
const jsonSource = ref(JSON.stringify(EMPTY_EDITOR_DOCUMENT, null, 2));
const imageUrl = ref('/images/avatar.jpg');
const linkUrl = ref('mailto:demo@example.com');
const placeholder = ref('初始占位提示');
const changes = ref(0);
const updates = ref(0);
const result = ref('常量已复制为独立文档;辅助函数不发送请求。');
function load() {
try {
const value = JSON.parse(jsonSource.value);
validateEditorDocument(value);
document.value = value;
result.value = '有效文档已加载;验证不改写输入。';
} catch (error) {
result.value = `验证失败:${error instanceof Error ? error.message : String(error)}`;
}
}
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-sm);
}
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
pre {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
</style>
示例按需导入全部辅助出口,展示地址判定、未知 JSON 的验证和加载、独立空文档,以及动态占位提示与事件计数。
下载示例的图片来源 ​
编辑器的图片模型只接收可访问的 HTTP(S) 或站内根路径,不接受内联 data/blob。 完整说明。
API
属性 AsamEditorProps
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
modelValue | — | AsamEditorDocument | — |
editable | — | boolean | undefined | true |
disabled | — | boolean | undefined | false |
placeholder | — | string | undefined | '请输入内容' |
事件 AsamEditorEmits
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
update:modelValue | — | [document: AsamEditorDocument] | — |
change | — | [document: AsamEditorDocument] | — |
实例方法与状态 AsamEditorExpose
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
focus | — | () => void | — |
blur | — | () => void | — |
clear | — | () => void | — |
insertImage | — | (resource: AsamEditorImageResource) => boolean | — |
getHTML | — | () => string | — |
类型 AsamEditorAttribute
类型 AsamEditorMark
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
type | — | string | — |
attrs | — | Readonly<Record<string, AsamEditorAttribute>> | undefined | — |
类型 AsamEditorNode
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
type | — | string | — |
attrs | — | Readonly<Record<string, AsamEditorAttribute>> | undefined | — |
content | — | readonly AsamEditorNode[] | undefined | — |
marks | — | readonly AsamEditorMark[] | undefined | — |
text | — | string | undefined | — |
类型 AsamEditorDocument
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
type | — | enum"doc"完整类型:"doc" | — |
content | — | readonly AsamEditorNode[] | — |
类型 AsamEditorImageResource
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
src | — | string | — |
alt | — | string | undefined | — |
title | — | string | undefined | — |
类型 EMPTY_EDITOR_DOCUMENT
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
type | — | enum"doc"完整类型:"doc" | — |
content | — | readonly AsamEditorNode[] | — |
服务 isSafeImageUrl
(value: string): boolean
服务 isSafeLinkUrl
(value: string): boolean
服务 validateEditorDocument
(value: unknown): asserts value is AsamEditorDocument
用法与契约补充说明 ​
编辑与实例方法 ​
modelValue 的根节点为 { type: 'doc', content: [...] },内容数组非空;空文档可使用一个空 paragraph。编辑产生 update:modelValue 和 change,两者传入结构化文档。外部替换模型时组件更新内容并重建撤销历史,不应依赖旧文档的 undo 恢复已替换数据。
可调用的公开辅助出口 ​
isSafeImageUrl(value: string): boolean 判定 HTTP(S) 无凭据地址或单个 / 开头的站内路径。isSafeLinkUrl(value: string): boolean 判定 HTTP(S) 无凭据地址或 mailto 路径包含 @ 的地址;它不保证邮箱有效或可送达。两者拒绝首尾空白、控制字符、反斜杠及不允许的协议,返回布尔值,不创建资源、不发送请求、不替调用方改写地址。
可调用的公开辅助出口 ​
validateEditorDocument(value: unknown): asserts value is AsamEditorDocument 验证上述完整文档语法。合法输入正常返回,并使 TypeScript 在后续代码中收窄类型;非法输入抛 TypeError,消息以 AsamEditor 开头。它只验证,不改写输入,不返回修复后的文档。JSON.parse 的语法错误应另行处理。
下载示例的图片来源 ​
编辑器的图片模型只接收可访问的 HTTP(S) 或站内根路径,不接受内联 data/blob。两个插图示例默认使用公开的 HTTPS 图片,按钮明确标为“插入示例图片”,地址可改为消费项目已托管的资源。默认图片需要网络;单个 Demo.vue 不提供文件上传、资源托管或新的根路径接口。其他允许 data URL 的图片显示组件仍可使用内联 SVG。此说明不改变编辑器的公开安全规则。
更多参数与事件说明 ​
文档与资源边界 ​
节点支持 paragraph、heading、blockquote、bulletList、orderedList、listItem、codeBlock、horizontalRule、image,以及文本块中的 text、hardBreak。heading 的 level 为 1、2 或 3;列表包含以 paragraph 开始的 listItem;引用和列表不能为空。标记支持 bold、italic、strike、code、underline、link,link 的 attrs 只包含 href。运行时会拒绝未知字段、非法嵌套或资源地址;类型中的通用字符串字段不代表任意节点名都受支持。
关联组件
参数与事件补充说明
参数与事件说明
modelValue
必填:是;声明来源:ASAM 声明。
modelValue 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
modelValue.type:"doc"— ASAM 声明枚举:
"doc"modelValue.content:readonly AsamEditorNode[]— ASAM 声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
editable
必填:否;声明来源:ASAM 声明。
editable 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
true(源码表达式;ASAM 声明)disabled
必填:否;声明来源:ASAM 声明。
disabled 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(源码表达式;ASAM 声明)placeholder
必填:否;声明来源:ASAM 声明。
默认值与来源
'请输入内容'(源码表达式;ASAM 声明)AsamEditorProps 公开类型视图
import type { AsamEditorProps, AsamEditorDocument, AsamEditorNode, AsamEditorImageResource } from '@asam-ui/ui';
// AsamEditorProps
type PublicContract = {
readonly "modelValue": AsamEditorDocument;
readonly "editable"?: boolean | undefined;
readonly "disabled"?: boolean | undefined;
readonly "placeholder"?: string | undefined;
};
参数与事件说明
update:modelValue
必填:是;声明来源:ASAM 声明。
change
必填:是;声明来源:ASAM 声明。
AsamEditorEmits 公开类型视图
import type { AsamEditorEmits, AsamEditorDocument } from '@asam-ui/ui';
// AsamEditorEmits
type PublicContract = {
"update:modelValue": [document: AsamEditorDocument];
"change": [document: AsamEditorDocument];
};
参数与事件说明
focus
必填:是;声明来源:ASAM 声明。
focus 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
blur
必填:是;声明来源:ASAM 声明。
blur 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
clear
必填:是;声明来源:ASAM 声明。
clear 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
insertImage
必填:是;声明来源:ASAM 声明。
insertImage 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(resource: AsamEditorImageResource): boolean
getHTML
必填:是;声明来源:ASAM 声明。
getHTML 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): string
AsamEditorExpose 公开类型视图
import type { AsamEditorExpose, AsamEditorImageResource } from '@asam-ui/ui';
// AsamEditorExpose
type PublicContract = {
"focus": () => void;
"blur": () => void;
"clear": () => void;
"insertImage": (resource: AsamEditorImageResource) => boolean;
"getHTML": () => string;
};
组合契约按分支生效;请同时查看公开类型。
AsamEditorAttribute 公开类型视图
import type { AsamEditorAttribute } from '@asam-ui/ui';
// AsamEditorAttribute
type PublicContract = AsamEditorAttribute;
参数与事件说明
type
必填:是;声明来源:ASAM 声明。
attrs
必填:否;声明来源:ASAM 声明。
AsamEditorMark 公开类型视图
import type { AsamEditorMark, AsamEditorImageResource, AsamEditorAttribute } from '@asam-ui/ui';
// AsamEditorMark
type PublicContract = {
readonly "type": string;
readonly "attrs"?: Readonly<Record<string, AsamEditorAttribute>> | undefined;
};
参数与事件说明
type
必填:是;声明来源:ASAM 声明。
attrs
必填:否;声明来源:ASAM 声明。
content
必填:否;声明来源:ASAM 声明。
marks
必填:否;声明来源:ASAM 声明。
text
必填:否;声明来源:ASAM 声明。
AsamEditorNode 公开类型视图
import type { AsamEditorNode, AsamEditorImageResource, AsamEditorAttribute, AsamEditorMark } from '@asam-ui/ui';
// AsamEditorNode
type PublicContract = {
readonly "type": string;
readonly "attrs"?: Readonly<Record<string, AsamEditorAttribute>> | undefined;
readonly "content"?: readonly AsamEditorNode[] | undefined;
readonly "marks"?: readonly AsamEditorMark[] | undefined;
readonly "text"?: string | undefined;
};
参数与事件说明
type
必填:是;声明来源:ASAM 声明。
type 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"doc"
content
必填:是;声明来源:ASAM 声明。
content 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
AsamEditorDocument 公开类型视图
import type { AsamEditorDocument, AsamEditorNode } from '@asam-ui/ui';
// AsamEditorDocument
type PublicContract = {
readonly "type": "doc";
readonly "content": readonly AsamEditorNode[];
};
参数与事件说明
src
必填:是;声明来源:ASAM 声明。
alt
必填:否;声明来源:ASAM 声明。
title
必填:否;声明来源:ASAM 声明。
AsamEditorImageResource 公开类型视图
import type { AsamEditorImageResource } from '@asam-ui/ui';
// AsamEditorImageResource
type PublicContract = {
readonly "src": string;
readonly "alt"?: string | undefined;
readonly "title"?: string | undefined;
};
参数与事件说明
type
必填:是;声明来源:ASAM 声明。
type 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"doc"
content
必填:是;声明来源:ASAM 声明。
content 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
EMPTY_EDITOR_DOCUMENT 公开类型视图
export type EMPTY_EDITOR_DOCUMENT = AsamEditorDocument;
isSafeImageUrl 公开类型视图
import type { AsamEditorImageResource } from '@asam-ui/ui';
export declare function isSafeImageUrl(value: string): boolean;
isSafeLinkUrl 公开类型视图
import type { AsamEditorImageResource } from '@asam-ui/ui';
export declare function isSafeLinkUrl(value: string): boolean;
validateEditorDocument 公开类型视图
import type { AsamEditorProps, AsamEditorDocument } from '@asam-ui/ui';
export declare function validateEditorDocument(value: unknown): asserts value is AsamEditorDocument;