@vigilkids/section-renderer-vue 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composables/useInlineEdit.d.ts +1 -1
- package/dist/composables/useInlineEdit.mjs +4 -2
- package/dist/composables/useLazyRender.mjs +2 -1
- package/dist/composables/useRegistry.d.ts +1 -1
- package/dist/composables/useRegistry.mjs +6 -3
- package/dist/composables/useSectionSEO.mjs +10 -5
- package/dist/composables/useSectionStyle.d.ts +1 -1
- package/dist/composables/useSectionStyle.mjs +10 -6
- package/dist/editor.d.ts +2 -2
- package/dist/editor.mjs +2 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.mjs +8 -8
- package/dist/preview/createPreviewApp.mjs +12 -4
- package/dist/renderer/FallbackSection.vue +9 -3
- package/dist/renderer/LazySection.vue +25 -22
- package/dist/renderer/SectionErrorBoundary.vue +3 -1
- package/dist/renderer/SectionRenderer.vue +19 -6
- package/dist/renderer/SectionWrapper.vue +4 -5
- package/dist/sections/RichTextSection.vue +12 -12
- package/dist/sections/article/prosemirror.mjs +8 -4
- package/dist/sections/article/shared/ArticleCustomHtml.vue +1 -1
- package/dist/sections/article/shared/ArticleImage.vue +3 -3
- package/dist/sections/article/vigilkids/ArticleBulletList.vue +2 -2
- package/dist/sections/article/vigilkids/ArticleCta.vue +48 -21
- package/dist/sections/article/vigilkids/ArticleFaq.vue +11 -5
- package/dist/sections/article/vigilkids/ArticleFaqItem.vue +6 -2
- package/dist/sections/article/vigilkids/ArticleFeature.vue +16 -8
- package/dist/sections/article/vigilkids/ArticleHeading.vue +3 -4
- package/dist/sections/article/vigilkids/ArticleNotice.vue +33 -9
- package/dist/sections/article/vigilkids/ArticleProsCons.vue +6 -6
- package/dist/sections/article/vigilkids/ArticleQuestion.vue +11 -5
- package/dist/sections/article/vigilkids/ArticleQuote.vue +6 -4
- package/dist/sections/article/vigilkids/ArticleStepList.vue +8 -4
- package/dist/sections/article/vigilkids/ArticleSubheading.vue +18 -6
- package/dist/sections/article/vigilkids/ArticleTable.vue +9 -8
- package/dist/sections/article/vigilkids/ArticleToc.vue +13 -9
- package/dist/sections/article/visiva/ArticleBulletList.vue +2 -2
- package/dist/sections/article/visiva/ArticleCta.vue +127 -30
- package/dist/sections/article/visiva/ArticleFaq.vue +22 -8
- package/dist/sections/article/visiva/ArticleFeature.vue +20 -7
- package/dist/sections/article/visiva/ArticleHeading.vue +2 -2
- package/dist/sections/article/visiva/ArticleNotice.vue +7 -5
- package/dist/sections/article/visiva/ArticleProsCons.vue +37 -23
- package/dist/sections/article/visiva/ArticleQuestion.vue +18 -7
- package/dist/sections/article/visiva/ArticleQuote.vue +9 -5
- package/dist/sections/article/visiva/ArticleStepList.vue +9 -4
- package/dist/sections/article/visiva/ArticleSubheading.vue +32 -27
- package/dist/sections/article/visiva/ArticleTable.vue +79 -60
- package/dist/sections/article/visiva/ArticleToc.vue +42 -12
- package/dist/styles/products/visiva.css +1 -1
- package/package.json +10 -3
|
@@ -28,9 +28,9 @@ const { editableAttrs } = useInlineEdit({
|
|
|
28
28
|
editorMode: () => !!props.editorMode,
|
|
29
29
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
30
30
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
31
|
-
onEditStart:
|
|
31
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
32
32
|
onEditEnd: () => emit('inline-edit-end'),
|
|
33
|
-
onUndoRedo:
|
|
33
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
34
34
|
})
|
|
35
35
|
</script>
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ const { editableAttrs } = useInlineEdit({
|
|
|
38
38
|
<!-- button variant: 单个居中按钮 -->
|
|
39
39
|
<a
|
|
40
40
|
v-if="variant === 'button'"
|
|
41
|
-
:href="safeUrl(String(s.button_url || ''))"
|
|
41
|
+
:href="editorMode ? undefined : safeUrl(String(s.button_url || ''))"
|
|
42
42
|
class="btn btn-primary"
|
|
43
43
|
v-bind="editableAttrs('button_text')"
|
|
44
44
|
>
|
|
@@ -48,7 +48,7 @@ const { editableAttrs } = useInlineEdit({
|
|
|
48
48
|
<!-- button-group variant: 两个按钮并排 -->
|
|
49
49
|
<div v-else-if="variant === 'button-group'" class="btn-group">
|
|
50
50
|
<a
|
|
51
|
-
:href="safeUrl(String(s.button_url || ''))"
|
|
51
|
+
:href="editorMode ? undefined : safeUrl(String(s.button_url || ''))"
|
|
52
52
|
class="btn btn-primary"
|
|
53
53
|
v-bind="editableAttrs('button_text')"
|
|
54
54
|
>
|
|
@@ -56,7 +56,7 @@ const { editableAttrs } = useInlineEdit({
|
|
|
56
56
|
</a>
|
|
57
57
|
<a
|
|
58
58
|
v-if="s.secondary_button_text"
|
|
59
|
-
:href="safeUrl(String(s.secondary_button_url || ''))"
|
|
59
|
+
:href="editorMode ? undefined : safeUrl(String(s.secondary_button_url || ''))"
|
|
60
60
|
class="btn outline-btn"
|
|
61
61
|
v-bind="editableAttrs('secondary_button_text')"
|
|
62
62
|
>
|
|
@@ -67,12 +67,16 @@ const { editableAttrs } = useInlineEdit({
|
|
|
67
67
|
<!-- card variant: 左文字右按钮 -->
|
|
68
68
|
<div v-else-if="variant === 'card'" class="article-try">
|
|
69
69
|
<div class="article-try-content">
|
|
70
|
-
<p v-if="s.title" class="article-try-title" v-bind="editableAttrs('title')">
|
|
71
|
-
|
|
70
|
+
<p v-if="s.title" class="article-try-title" v-bind="editableAttrs('title')">
|
|
71
|
+
{{ s.title }}
|
|
72
|
+
</p>
|
|
73
|
+
<p v-if="s.description" class="article-try-description" v-bind="editableAttrs('description')">
|
|
74
|
+
{{ s.description }}
|
|
75
|
+
</p>
|
|
72
76
|
</div>
|
|
73
77
|
<a
|
|
74
78
|
v-if="s.button_text"
|
|
75
|
-
:href="safeUrl(String(s.button_url || ''))"
|
|
79
|
+
:href="editorMode ? undefined : safeUrl(String(s.button_url || ''))"
|
|
76
80
|
class="btn btn-primary"
|
|
77
81
|
v-bind="editableAttrs('button_text')"
|
|
78
82
|
>
|
|
@@ -83,39 +87,62 @@ const { editableAttrs } = useInlineEdit({
|
|
|
83
87
|
<!-- showcase variant: 左文字+按钮,右图片 -->
|
|
84
88
|
<div v-else-if="variant === 'showcase'" class="article-show">
|
|
85
89
|
<div class="article-show-content">
|
|
86
|
-
<p v-if="s.title" class="article-show-title" v-bind="editableAttrs('title')">
|
|
87
|
-
|
|
90
|
+
<p v-if="s.title" class="article-show-title" v-bind="editableAttrs('title')">
|
|
91
|
+
{{ s.title }}
|
|
92
|
+
</p>
|
|
93
|
+
<p
|
|
94
|
+
v-if="s.description"
|
|
95
|
+
class="article-show-description"
|
|
96
|
+
v-bind="editableAttrs('description')"
|
|
97
|
+
>
|
|
98
|
+
{{ s.description }}
|
|
99
|
+
</p>
|
|
88
100
|
<a
|
|
89
101
|
v-if="s.button_text"
|
|
90
|
-
:href="safeUrl(String(s.button_url || ''))"
|
|
102
|
+
:href="editorMode ? undefined : safeUrl(String(s.button_url || ''))"
|
|
91
103
|
class="btn btn-primary"
|
|
92
104
|
v-bind="editableAttrs('button_text')"
|
|
93
105
|
>
|
|
94
106
|
{{ s.button_text }}
|
|
95
107
|
</a>
|
|
96
108
|
</div>
|
|
97
|
-
<img
|
|
98
|
-
v-if="s.image_src"
|
|
99
|
-
:src="String(s.image_src)"
|
|
100
|
-
alt=""
|
|
101
|
-
loading="lazy"
|
|
102
|
-
>
|
|
109
|
+
<img v-if="s.image_src" :src="String(s.image_src)" alt="" loading="lazy">
|
|
103
110
|
</div>
|
|
104
111
|
|
|
105
112
|
<!-- button-group-dark variant: 黑底容器+白边主按钮+渐变次按钮 (Visiva) -->
|
|
106
113
|
<div v-else-if="variant === 'button-group-dark'" class="btn-group-dark">
|
|
107
114
|
<div class="btn-group-dark__buttons">
|
|
108
115
|
<div v-if="s.button_text" class="btn-group-dark__item">
|
|
109
|
-
<a
|
|
116
|
+
<a
|
|
117
|
+
:href="editorMode ? undefined : safeUrl(String(s.button_url || ''))"
|
|
118
|
+
class="btn btn-outline-white"
|
|
119
|
+
v-bind="editableAttrs('button_text')"
|
|
120
|
+
>
|
|
110
121
|
{{ s.button_text }}
|
|
111
122
|
</a>
|
|
112
|
-
<p
|
|
123
|
+
<p
|
|
124
|
+
v-if="s.button_caption"
|
|
125
|
+
class="btn-group-dark__caption"
|
|
126
|
+
v-bind="editableAttrs('button_caption')"
|
|
127
|
+
>
|
|
128
|
+
{{ s.button_caption }}
|
|
129
|
+
</p>
|
|
113
130
|
</div>
|
|
114
131
|
<div v-if="s.secondary_button_text" class="btn-group-dark__item">
|
|
115
|
-
<a
|
|
132
|
+
<a
|
|
133
|
+
:href="editorMode ? undefined : safeUrl(String(s.secondary_button_url || ''))"
|
|
134
|
+
class="btn btn-gradient"
|
|
135
|
+
v-bind="editableAttrs('secondary_button_text')"
|
|
136
|
+
>
|
|
116
137
|
{{ s.secondary_button_text }}
|
|
117
138
|
</a>
|
|
118
|
-
<p
|
|
139
|
+
<p
|
|
140
|
+
v-if="s.secondary_button_caption"
|
|
141
|
+
class="btn-group-dark__caption"
|
|
142
|
+
v-bind="editableAttrs('secondary_button_caption')"
|
|
143
|
+
>
|
|
144
|
+
{{ s.secondary_button_caption }}
|
|
145
|
+
</p>
|
|
119
146
|
</div>
|
|
120
147
|
</div>
|
|
121
148
|
</div>
|
|
@@ -27,9 +27,9 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
27
27
|
editorMode: () => !!props.editorMode,
|
|
28
28
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
29
29
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
30
|
-
onEditStart:
|
|
30
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
31
31
|
onEditEnd: () => emit('inline-edit-end'),
|
|
32
|
-
onUndoRedo:
|
|
32
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
33
33
|
})
|
|
34
34
|
</script>
|
|
35
35
|
|
|
@@ -37,13 +37,19 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
37
37
|
<!-- Visiva 风格:浅绿卡片 + 绿色标题 + 静态列表 -->
|
|
38
38
|
<div v-if="s.title" class="faq-card">
|
|
39
39
|
<div class="faq-card__header">
|
|
40
|
-
<h2 class="faq-card__title" v-bind="editableAttrs('title')">
|
|
40
|
+
<h2 class="faq-card__title" v-bind="editableAttrs('title')">
|
|
41
|
+
{{ s.title }}
|
|
42
|
+
</h2>
|
|
41
43
|
</div>
|
|
42
44
|
<div class="faq-card__list">
|
|
43
45
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
44
46
|
<div v-if="blocks[blockId]" class="faq-card__item">
|
|
45
|
-
<h3 class="faq-card__question" v-bind="blockEditableAttrs(blockId, 'question')">
|
|
46
|
-
|
|
47
|
+
<h3 class="faq-card__question" v-bind="blockEditableAttrs(blockId, 'question')">
|
|
48
|
+
{{ blocks[blockId]!.settings.question }}
|
|
49
|
+
</h3>
|
|
50
|
+
<p class="faq-card__answer" v-bind="blockEditableAttrs(blockId, 'answer')">
|
|
51
|
+
{{ blocks[blockId]!.settings.answer }}
|
|
52
|
+
</p>
|
|
47
53
|
</div>
|
|
48
54
|
</template>
|
|
49
55
|
</div>
|
|
@@ -14,8 +14,12 @@ function toggle() {
|
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
16
|
<div class="faq-item" :class="{ active }" @click="toggle">
|
|
17
|
-
<p class="faq-item-title">
|
|
18
|
-
|
|
17
|
+
<p class="faq-item-title">
|
|
18
|
+
{{ settings.question }}
|
|
19
|
+
</p>
|
|
20
|
+
<p class="faq-item-answer">
|
|
21
|
+
{{ settings.answer }}
|
|
22
|
+
</p>
|
|
19
23
|
</div>
|
|
20
24
|
</template>
|
|
21
25
|
|
|
@@ -28,9 +28,9 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
28
28
|
editorMode: () => !!props.editorMode,
|
|
29
29
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
30
30
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
31
|
-
onEditStart:
|
|
31
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
32
32
|
onEditEnd: () => emit('inline-edit-end'),
|
|
33
|
-
onUndoRedo:
|
|
33
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
34
34
|
})
|
|
35
35
|
</script>
|
|
36
36
|
|
|
@@ -38,13 +38,17 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
38
38
|
<!-- light / dark 变体:图片+特性列表+CTA (Visiva) -->
|
|
39
39
|
<div
|
|
40
40
|
v-if="variant === 'light' || variant === 'dark'"
|
|
41
|
-
|
|
41
|
+
class="feature-card" :class="[{ 'feature-card--dark': variant === 'dark' }]"
|
|
42
42
|
>
|
|
43
43
|
<div class="feature-card__text">
|
|
44
|
-
<h2 v-if="s.title" class="feature-card__title" v-bind="editableAttrs('title')">
|
|
44
|
+
<h2 v-if="s.title" class="feature-card__title" v-bind="editableAttrs('title')">
|
|
45
|
+
{{ s.title }}
|
|
46
|
+
</h2>
|
|
45
47
|
<ul class="feature-card__list">
|
|
46
48
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
47
|
-
<li v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
49
|
+
<li v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
50
|
+
{{ blocks[blockId]!.settings.text }}
|
|
51
|
+
</li>
|
|
48
52
|
</template>
|
|
49
53
|
</ul>
|
|
50
54
|
<a
|
|
@@ -57,16 +61,20 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
57
61
|
</a>
|
|
58
62
|
</div>
|
|
59
63
|
<div v-if="s.image_src" class="feature-card__image">
|
|
60
|
-
<img :src="String(s.image_src)" alt="" loading="lazy"
|
|
64
|
+
<img :src="String(s.image_src)" alt="" loading="lazy">
|
|
61
65
|
</div>
|
|
62
66
|
</div>
|
|
63
67
|
|
|
64
68
|
<!-- default 变体:原有灰色特性框 (VigilKids,完全不动) -->
|
|
65
69
|
<div v-else class="article-feature">
|
|
66
|
-
<p v-if="s.title" class="article-feature-title" v-bind="editableAttrs('title')">
|
|
70
|
+
<p v-if="s.title" class="article-feature-title" v-bind="editableAttrs('title')">
|
|
71
|
+
{{ s.title }}
|
|
72
|
+
</p>
|
|
67
73
|
<ul class="article-feature-list">
|
|
68
74
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
69
|
-
<li v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
75
|
+
<li v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
76
|
+
{{ blocks[blockId]!.settings.text }}
|
|
77
|
+
</li>
|
|
70
78
|
</template>
|
|
71
79
|
</ul>
|
|
72
80
|
</div>
|
|
@@ -27,17 +27,16 @@ const { editableAttrs } = useInlineEdit({
|
|
|
27
27
|
editorMode: () => !!props.editorMode,
|
|
28
28
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
29
29
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
30
|
-
onEditStart:
|
|
30
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
31
31
|
onEditEnd: () => emit('inline-edit-end'),
|
|
32
|
-
onUndoRedo:
|
|
32
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
33
33
|
})
|
|
34
34
|
</script>
|
|
35
35
|
|
|
36
36
|
<template>
|
|
37
37
|
<h2
|
|
38
38
|
:id="String(s.anchor || '')"
|
|
39
|
-
:class="[
|
|
40
|
-
'article-heading',
|
|
39
|
+
class="article-heading" :class="[
|
|
41
40
|
{
|
|
42
41
|
'article-heading--plain': variant === 'plain',
|
|
43
42
|
'article-heading--accent': variant === 'accent',
|
|
@@ -29,9 +29,9 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
29
29
|
editorMode: () => !!props.editorMode,
|
|
30
30
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
31
31
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
32
|
-
onEditStart:
|
|
32
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
33
33
|
onEditEnd: () => emit('inline-edit-end'),
|
|
34
|
-
onUndoRedo:
|
|
34
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
35
35
|
})
|
|
36
36
|
</script>
|
|
37
37
|
|
|
@@ -39,11 +39,15 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
39
39
|
<!-- tips / note 变体:角标样式 (Visiva) -->
|
|
40
40
|
<div v-if="variant === 'tips' || variant === 'note'" class="notice-badge">
|
|
41
41
|
<div class="notice-badge__tag">
|
|
42
|
-
<span class="notice-badge__label" v-bind="editableAttrs('title')">{{
|
|
42
|
+
<span class="notice-badge__label" v-bind="editableAttrs('title')">{{
|
|
43
|
+
s.title || 'Tips'
|
|
44
|
+
}}</span>
|
|
43
45
|
</div>
|
|
44
46
|
<div class="notice-badge__content">
|
|
45
47
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
46
|
-
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
48
|
+
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
49
|
+
{{ blocks[blockId]!.settings.text }}
|
|
50
|
+
</p>
|
|
47
51
|
</template>
|
|
48
52
|
</div>
|
|
49
53
|
</div>
|
|
@@ -51,12 +55,30 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
51
55
|
<!-- info 变体:圆角卡片+图标标题 (Visiva) -->
|
|
52
56
|
<div v-else-if="variant === 'info'" class="notice-card">
|
|
53
57
|
<div v-if="s.title" class="notice-card__header">
|
|
54
|
-
<svg
|
|
55
|
-
|
|
58
|
+
<svg
|
|
59
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
60
|
+
width="22"
|
|
61
|
+
height="22"
|
|
62
|
+
viewBox="0 0 24 24"
|
|
63
|
+
fill="none"
|
|
64
|
+
stroke="currentColor"
|
|
65
|
+
stroke-width="2"
|
|
66
|
+
stroke-linecap="round"
|
|
67
|
+
stroke-linejoin="round"
|
|
68
|
+
>
|
|
69
|
+
<circle cx="12" cy="12" r="10" />
|
|
70
|
+
<path d="M12 16v-4" />
|
|
71
|
+
<path d="M12 8h.01" />
|
|
72
|
+
</svg>
|
|
73
|
+
<h3 class="notice-card__title" v-bind="editableAttrs('title')">
|
|
74
|
+
{{ s.title }}
|
|
75
|
+
</h3>
|
|
56
76
|
</div>
|
|
57
77
|
<div class="notice-card__content">
|
|
58
78
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
59
|
-
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
79
|
+
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
80
|
+
{{ blocks[blockId]!.settings.text }}
|
|
81
|
+
</p>
|
|
60
82
|
</template>
|
|
61
83
|
</div>
|
|
62
84
|
</div>
|
|
@@ -66,12 +88,14 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
66
88
|
v-else
|
|
67
89
|
class="notice-info"
|
|
68
90
|
:class="{
|
|
69
|
-
warning: isWarning,
|
|
91
|
+
'warning': isWarning,
|
|
70
92
|
'notice-info--row-child': isRow,
|
|
71
93
|
}"
|
|
72
94
|
>
|
|
73
95
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
74
|
-
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
96
|
+
<p v-if="blocks[blockId]" v-bind="blockEditableAttrs(blockId, 'text')">
|
|
97
|
+
{{ blocks[blockId]!.settings.text }}
|
|
98
|
+
</p>
|
|
75
99
|
</template>
|
|
76
100
|
</div>
|
|
77
101
|
</template>
|
|
@@ -24,14 +24,14 @@ const s = computed(() => props.settings)
|
|
|
24
24
|
|
|
25
25
|
const pros = computed(() =>
|
|
26
26
|
props.blockOrder
|
|
27
|
-
.filter(
|
|
28
|
-
.map(
|
|
27
|
+
.filter(id => props.blocks[id]?.type === 'pro')
|
|
28
|
+
.map(id => ({ id, block: props.blocks[id]! })),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
const cons = computed(() =>
|
|
32
32
|
props.blockOrder
|
|
33
|
-
.filter(
|
|
34
|
-
.map(
|
|
33
|
+
.filter(id => props.blocks[id]?.type === 'con')
|
|
34
|
+
.map(id => ({ id, block: props.blocks[id]! })),
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
const isDashed = computed(() => String(s.value.variant) === 'dashed')
|
|
@@ -40,9 +40,9 @@ const { editableAttrs, blockEditableAttrs } = useInlineEdit({
|
|
|
40
40
|
editorMode: () => !!props.editorMode,
|
|
41
41
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
42
42
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
43
|
-
onEditStart:
|
|
43
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
44
44
|
onEditEnd: () => emit('inline-edit-end'),
|
|
45
|
-
onUndoRedo:
|
|
45
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
46
46
|
})
|
|
47
47
|
</script>
|
|
48
48
|
|
|
@@ -29,9 +29,9 @@ const { blockEditableAttrs } = useInlineEdit({
|
|
|
29
29
|
editorMode: () => !!props.editorMode,
|
|
30
30
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
31
31
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
32
|
-
onEditStart:
|
|
32
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
33
33
|
onEditEnd: () => emit('inline-edit-end'),
|
|
34
|
-
onUndoRedo:
|
|
34
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
35
35
|
})
|
|
36
36
|
</script>
|
|
37
37
|
|
|
@@ -40,15 +40,21 @@ const { blockEditableAttrs } = useInlineEdit({
|
|
|
40
40
|
<div v-if="blockOrder.length > 0" class="qa-card">
|
|
41
41
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
42
42
|
<div v-if="blocks[blockId]" class="qa-card__item">
|
|
43
|
-
<h3 class="qa-card__question" v-bind="blockEditableAttrs(blockId, 'question')">
|
|
44
|
-
|
|
43
|
+
<h3 class="qa-card__question" v-bind="blockEditableAttrs(blockId, 'question')">
|
|
44
|
+
{{ blocks[blockId]!.settings.question }}
|
|
45
|
+
</h3>
|
|
46
|
+
<p class="qa-card__answer" v-bind="blockEditableAttrs(blockId, 'answer')">
|
|
47
|
+
{{ blocks[blockId]!.settings.answer }}
|
|
48
|
+
</p>
|
|
45
49
|
</div>
|
|
46
50
|
</template>
|
|
47
51
|
</div>
|
|
48
52
|
|
|
49
53
|
<!-- VigilKids: 单条 richtext 问答 -->
|
|
50
54
|
<div v-else class="article-question">
|
|
51
|
-
<p class="article-question-title">
|
|
55
|
+
<p class="article-question-title">
|
|
56
|
+
{{ title }}
|
|
57
|
+
</p>
|
|
52
58
|
<div class="article-question-content" v-html="renderedContent" />
|
|
53
59
|
</div>
|
|
54
60
|
</template>
|
|
@@ -28,18 +28,20 @@ const { editableAttrs } = useInlineEdit({
|
|
|
28
28
|
editorMode: () => !!props.editorMode,
|
|
29
29
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
30
30
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
31
|
-
onEditStart:
|
|
31
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
32
32
|
onEditEnd: () => emit('inline-edit-end'),
|
|
33
|
-
onUndoRedo:
|
|
33
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
34
34
|
})
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
37
|
<template>
|
|
38
38
|
<div class="article-info">
|
|
39
|
-
<p v-if="s.title" class="article-info-title" v-bind="editableAttrs('title')">
|
|
39
|
+
<p v-if="s.title" class="article-info-title" v-bind="editableAttrs('title')">
|
|
40
|
+
{{ s.title }}
|
|
41
|
+
</p>
|
|
40
42
|
<div class="article-info-content" v-html="renderedContent" />
|
|
41
43
|
<cite v-if="s.attribution" class="article-info-attribution">
|
|
42
|
-
<span class="article-info-divider"
|
|
44
|
+
<span class="article-info-divider" />
|
|
43
45
|
<span v-bind="editableAttrs('attribution')">by {{ s.attribution }}</span>
|
|
44
46
|
</cite>
|
|
45
47
|
</div>
|
|
@@ -27,9 +27,9 @@ const { blockEditableAttrs } = useInlineEdit({
|
|
|
27
27
|
editorMode: () => !!props.editorMode,
|
|
28
28
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
29
29
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
30
|
-
onEditStart:
|
|
30
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
31
31
|
onEditEnd: () => emit('inline-edit-end'),
|
|
32
|
-
onUndoRedo:
|
|
32
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
33
33
|
})
|
|
34
34
|
</script>
|
|
35
35
|
|
|
@@ -37,8 +37,12 @@ const { blockEditableAttrs } = useInlineEdit({
|
|
|
37
37
|
<ul class="step-list">
|
|
38
38
|
<template v-for="blockId in blockOrder" :key="blockId">
|
|
39
39
|
<li v-if="blocks[blockId]">
|
|
40
|
-
<span class="primary-color" v-bind="blockEditableAttrs(blockId, 'label')">{{
|
|
41
|
-
|
|
40
|
+
<span class="primary-color" v-bind="blockEditableAttrs(blockId, 'label')">{{
|
|
41
|
+
blocks[blockId]!.settings.label
|
|
42
|
+
}}</span>
|
|
43
|
+
<span v-bind="blockEditableAttrs(blockId, 'description')">{{
|
|
44
|
+
blocks[blockId]!.settings.description
|
|
45
|
+
}}</span>
|
|
42
46
|
</li>
|
|
43
47
|
</template>
|
|
44
48
|
</ul>
|
|
@@ -27,24 +27,36 @@ const { editableAttrs } = useInlineEdit({
|
|
|
27
27
|
editorMode: () => !!props.editorMode,
|
|
28
28
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
29
29
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
30
|
-
onEditStart:
|
|
30
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
31
31
|
onEditEnd: () => emit('inline-edit-end'),
|
|
32
|
-
onUndoRedo:
|
|
32
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
33
33
|
})
|
|
34
34
|
</script>
|
|
35
35
|
|
|
36
36
|
<template>
|
|
37
|
-
<h3
|
|
37
|
+
<h3 class="article-subheading" :class="[`article-subheading--${variant}`]">
|
|
38
38
|
<!-- numbered: 实心绿色圆形+白色数字 (VigilKids) -->
|
|
39
|
-
<span
|
|
39
|
+
<span
|
|
40
|
+
v-if="variant === 'numbered' && s.number"
|
|
41
|
+
class="article-subheading__number"
|
|
42
|
+
v-bind="editableAttrs('number')"
|
|
43
|
+
>
|
|
40
44
|
{{ s.number }}
|
|
41
45
|
</span>
|
|
42
46
|
<!-- pill: 绿色药丸标签 (Visiva) -->
|
|
43
|
-
<span
|
|
47
|
+
<span
|
|
48
|
+
v-else-if="variant === 'pill' && s.label"
|
|
49
|
+
class="article-subheading__pill"
|
|
50
|
+
v-bind="editableAttrs('label')"
|
|
51
|
+
>
|
|
44
52
|
{{ s.label }}
|
|
45
53
|
</span>
|
|
46
54
|
<!-- icon: 浅色圆形+深色数字 (Visiva) -->
|
|
47
|
-
<span
|
|
55
|
+
<span
|
|
56
|
+
v-else-if="variant === 'icon' && s.number"
|
|
57
|
+
class="article-subheading__icon"
|
|
58
|
+
v-bind="editableAttrs('number')"
|
|
59
|
+
>
|
|
48
60
|
{{ s.number }}
|
|
49
61
|
</span>
|
|
50
62
|
<span v-bind="editableAttrs('title')">{{ s.title }}</span>
|
|
@@ -18,12 +18,15 @@ interface ColumnDef {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function parseJsonOrArray<T>(value: unknown): T[] {
|
|
21
|
-
if (Array.isArray(value))
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value as T[]
|
|
22
23
|
if (typeof value === 'string') {
|
|
23
24
|
try {
|
|
24
25
|
const parsed = JSON.parse(value)
|
|
25
|
-
if (Array.isArray(parsed))
|
|
26
|
-
|
|
26
|
+
if (Array.isArray(parsed))
|
|
27
|
+
return parsed as T[]
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
27
30
|
/* JSON 解析失败,返回空数组 */
|
|
28
31
|
}
|
|
29
32
|
}
|
|
@@ -32,13 +35,11 @@ function parseJsonOrArray<T>(value: unknown): T[] {
|
|
|
32
35
|
|
|
33
36
|
const isSticky = computed(() => String(s.value.variant) === 'sticky')
|
|
34
37
|
|
|
35
|
-
const columns = computed<ColumnDef[]>(() =>
|
|
36
|
-
parseJsonOrArray<ColumnDef>(s.value.columns),
|
|
37
|
-
)
|
|
38
|
+
const columns = computed<ColumnDef[]>(() => parseJsonOrArray<ColumnDef>(s.value.columns))
|
|
38
39
|
|
|
39
40
|
const rows = computed(() =>
|
|
40
41
|
props.blockOrder
|
|
41
|
-
.filter(
|
|
42
|
+
.filter(id => props.blocks[id]?.type === 'table-row')
|
|
42
43
|
.map((id) => {
|
|
43
44
|
const block = props.blocks[id]!
|
|
44
45
|
return parseJsonOrArray<string>(block.settings.cells)
|
|
@@ -47,7 +48,7 @@ const rows = computed(() =>
|
|
|
47
48
|
</script>
|
|
48
49
|
|
|
49
50
|
<template>
|
|
50
|
-
<div
|
|
51
|
+
<div class="contrast-table" :class="[{ 'sticky-table': isSticky }]">
|
|
51
52
|
<table>
|
|
52
53
|
<thead align="left">
|
|
53
54
|
<tr>
|
|
@@ -10,7 +10,7 @@ interface TocGroup {
|
|
|
10
10
|
text: string
|
|
11
11
|
anchor: string
|
|
12
12
|
hot: boolean
|
|
13
|
-
children: { id: string
|
|
13
|
+
children: { id: string, text: string, anchor: string }[]
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const props = defineProps<{
|
|
@@ -36,7 +36,8 @@ const tocGroups = computed<TocGroup[]>(() => {
|
|
|
36
36
|
|
|
37
37
|
for (const blockId of props.blockOrder) {
|
|
38
38
|
const block = props.blocks[blockId]
|
|
39
|
-
if (!block)
|
|
39
|
+
if (!block)
|
|
40
|
+
continue
|
|
40
41
|
|
|
41
42
|
if (block.type === 'toc-h2') {
|
|
42
43
|
groups.push({
|
|
@@ -46,7 +47,8 @@ const tocGroups = computed<TocGroup[]>(() => {
|
|
|
46
47
|
hot: Boolean(block.settings.hot),
|
|
47
48
|
children: [],
|
|
48
49
|
})
|
|
49
|
-
}
|
|
50
|
+
}
|
|
51
|
+
else if (block.type === 'toc-h3' && groups.length > 0) {
|
|
50
52
|
groups[groups.length - 1]!.children.push({
|
|
51
53
|
id: blockId,
|
|
52
54
|
text: String(block.settings.text || ''),
|
|
@@ -58,22 +60,24 @@ const tocGroups = computed<TocGroup[]>(() => {
|
|
|
58
60
|
return groups
|
|
59
61
|
})
|
|
60
62
|
|
|
61
|
-
const hotIconSrc
|
|
62
|
-
|
|
63
|
+
const hotIconSrc
|
|
64
|
+
= 'data:image/svg+xml,%3Csvg viewBox=\'0 0 20 20\' fill=\'none\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath d=\'M10.5479 11.9702C10.0966 11.9702 9.71456 12.5389 9.71456 13.2188C9.71456 13.8948 10.0966 14.4668 10.5479 14.4668C10.9992 14.4668 11.3819 13.8968 11.3819 13.2175C11.3819 12.5389 10.9999 11.9689 10.5485 11.9689L10.5479 11.9702ZM15.3977 3.54841C15.3258 3.46817 15.2338 3.40848 15.1312 3.37545C15.0286 3.34242 14.919 3.33723 14.8138 3.36042C14.6018 3.41108 14.4271 3.57374 14.3471 3.79507C14.1688 4.31816 14.0036 4.84565 13.8518 5.37704C13.6385 4.93438 13.3985 4.50972 13.1318 4.10374C12.1478 2.60444 10.7985 1.3318 8.88657 0.0985037C8.7956 0.0391916 8.69032 0.00547728 8.58182 0.000902909C8.47331 -0.00367146 8.36558 0.021063 8.26993 0.0725076C8.17116 0.129583 8.08767 0.209723 8.02661 0.306079C7.96554 0.402435 7.92871 0.512143 7.91927 0.625828C7.65327 3.22973 7.16395 5.86901 5.492 7.72696L5.27001 7.25364C5.0747 6.84432 4.89202 6.45901 4.7747 6.12901C4.64802 5.76569 4.27671 5.5837 3.94538 5.72237C3.7931 5.78848 3.67093 5.90898 3.60272 6.06034L3.46806 6.347C2.64741 8.08696 1.52544 10.4736 1.3561 12.4915C1.00477 16.7687 4.55669 19.4567 8.25059 19.9033C9.06192 20.0013 9.87722 20.0233 10.6919 19.9733C13.5965 19.784 17.125 18.1554 18.3143 14.1468C19.0843 11.5449 18.7837 7.21032 15.3977 3.54708V3.54774V3.54841ZM7.82794 15.1521C7.82794 15.4901 7.57528 15.7655 7.26462 15.7655C6.95464 15.7668 6.70329 15.4921 6.70196 15.1521V13.7588L5.542 13.7788V15.1861C5.542 15.5234 5.28934 15.7994 4.97868 15.7994C4.66936 15.8008 4.41669 15.5261 4.41536 15.1861V11.4189C4.41536 11.0802 4.66735 10.8056 4.97868 10.8056C5.28934 10.8056 5.54134 11.0802 5.54134 11.4189V12.5515L6.70196 12.5322V11.3855C6.70196 11.0469 6.95395 10.7716 7.26462 10.7716C7.57528 10.7716 7.82727 11.0469 7.82727 11.3849V15.1515H7.82794V15.1521ZM10.5479 15.6908C9.4679 15.6908 8.59059 14.5801 8.59059 13.2155C8.59059 11.8509 9.4679 10.7402 10.5479 10.7402C11.6278 10.7402 12.5078 11.8509 12.5078 13.2155C12.5065 14.5801 11.6278 15.6908 10.5479 15.6908V15.6908ZM15.6504 12.1022H14.9351V15.1188C14.9351 15.4568 14.6838 15.7321 14.3731 15.7321C14.0631 15.7334 13.8111 15.4588 13.8091 15.1188V12.1022H13.0951C13.0202 12.1022 12.9461 12.0862 12.8778 12.0553C12.8095 12.0244 12.7486 11.9792 12.6991 11.9229C12.5931 11.8023 12.5339 11.6475 12.5325 11.4869C12.5325 11.1482 12.7845 10.8736 13.0951 10.8736H15.6484C15.9591 10.8736 16.2104 11.1482 16.2104 11.4869C16.2099 11.6477 16.1506 11.8028 16.0437 11.9229C15.9951 11.9796 15.9348 12.0251 15.8669 12.0562C15.799 12.0872 15.7251 12.1032 15.6504 12.1029V12.1022Z\' fill=\'%23F74343\'/%3E%3C/svg%3E'
|
|
63
65
|
|
|
64
66
|
const { editableAttrs } = useInlineEdit({
|
|
65
67
|
editorMode: () => !!props.editorMode,
|
|
66
68
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
67
69
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
68
|
-
onEditStart:
|
|
70
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
69
71
|
onEditEnd: () => emit('inline-edit-end'),
|
|
70
|
-
onUndoRedo:
|
|
72
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
71
73
|
})
|
|
72
74
|
</script>
|
|
73
75
|
|
|
74
76
|
<template>
|
|
75
77
|
<ul class="level-1">
|
|
76
|
-
<li class="table-of-contents" v-bind="editableAttrs('title')">
|
|
78
|
+
<li class="table-of-contents" v-bind="editableAttrs('title')">
|
|
79
|
+
{{ title }}
|
|
80
|
+
</li>
|
|
77
81
|
<template v-for="group in tocGroups" :key="group.id">
|
|
78
82
|
<li :class="{ 'hot-link': group.hot }">
|
|
79
83
|
<a :href="`#${group.anchor}`">
|
|
@@ -85,7 +89,7 @@ const { editableAttrs } = useInlineEdit({
|
|
|
85
89
|
width="20"
|
|
86
90
|
height="20"
|
|
87
91
|
alt="hot"
|
|
88
|
-
|
|
92
|
+
>
|
|
89
93
|
</a>
|
|
90
94
|
</li>
|
|
91
95
|
<ul v-if="group.children.length > 0" class="level-2">
|
|
@@ -27,9 +27,9 @@ const { blockEditableAttrs } = useInlineEdit({
|
|
|
27
27
|
editorMode: () => !!props.editorMode,
|
|
28
28
|
onSettingUpdate: (key, value) => emit('update:setting', key, value),
|
|
29
29
|
onBlockSettingUpdate: (blockId, key, value) => emit('update:block-setting', blockId, key, value),
|
|
30
|
-
onEditStart:
|
|
30
|
+
onEditStart: key => emit('inline-edit-start', key),
|
|
31
31
|
onEditEnd: () => emit('inline-edit-end'),
|
|
32
|
-
onUndoRedo:
|
|
32
|
+
onUndoRedo: action => emit('undo-redo', action),
|
|
33
33
|
})
|
|
34
34
|
</script>
|
|
35
35
|
|