feat: 前台动态路由与积木页面、网页路径/发布/模式、PAGE_BUILDER 文档
Made-with: Cursor
This commit is contained in:
@@ -17,9 +17,24 @@
|
||||
<el-table-column label="ID" width="240">
|
||||
<template #default="{ row }">{{ row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="slug" label="Slug" width="120" />
|
||||
<el-table-column prop="title" label="标题" width="160" />
|
||||
<el-table-column prop="type" label="类型" width="100">
|
||||
<el-table-column prop="slug" label="Slug" width="100" />
|
||||
<el-table-column label="前台路径" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.route_path || '/' + (row.slug || '') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" width="140" />
|
||||
<el-table-column label="模式" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.content_mode === 'builder'" type="warning" size="small">积木</el-tag>
|
||||
<el-tag v-else type="info" size="small">HTML</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布" width="70">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.published === false" type="danger" size="small">否</el-tag>
|
||||
<el-tag v-else type="success" size="small">是</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.type === 'homepage'" type="success" size="small">首页</el-tag>
|
||||
<el-tag v-else size="small">页面</el-tag>
|
||||
@@ -35,10 +50,13 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="editId ? '编辑网页' : '新增网页'" width="560px" @close="resetForm">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-dialog v-model="dialogVisible" :title="editId ? '编辑网页' : '新增网页'" width="720px" @close="resetForm">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="Slug" prop="slug">
|
||||
<el-input v-model="form.slug" placeholder="如 about、index" :disabled="!!editId" />
|
||||
<el-input v-model="form.slug" placeholder="如 about、index(index 为首页数据,一般不单独走路由)" :disabled="!!editId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="前台路径">
|
||||
<el-input v-model="form.route_path" placeholder="留空则自动为 /{slug},可填如 /download 或 /about/us" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="form.title" placeholder="页面标题" />
|
||||
@@ -49,8 +67,18 @@
|
||||
<el-option label="首页" value="homepage" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容模式">
|
||||
<el-select v-model="form.content_mode" placeholder="模式" style="width: 200px">
|
||||
<el-option label="HTML 富文本" value="html" />
|
||||
<el-option label="积木组装(JSON)" value="builder" />
|
||||
</el-select>
|
||||
<el-button type="primary" link style="margin-left: 12px" @click="insertBuilderTemplate">插入积木模板</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="发布到前台">
|
||||
<el-switch v-model="form.published" />
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="content">
|
||||
<el-input v-model="form.content" type="textarea" :rows="8" placeholder="HTML 或 JSON" />
|
||||
<el-input v-model="form.content" type="textarea" :rows="12" placeholder="HTML 模式直接写 HTML;积木模式为 JSON,见项目 docs/PAGE_BUILDER.md" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -107,7 +135,80 @@ const dialogVisible = ref(false)
|
||||
const editId = ref('')
|
||||
const submitting = ref(false)
|
||||
const formRef = ref(null)
|
||||
const form = reactive({ site_id: '', slug: '', title: '', type: 'page', content: '' })
|
||||
const builderTemplate = () =>
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
blocks: [
|
||||
{
|
||||
id: 'h1',
|
||||
type: 'heading',
|
||||
props: { text: '页面标题', level: 2 },
|
||||
animation: { enter: 'fadeIn', delay_ms: 0, duration_ms: 600 }
|
||||
},
|
||||
{
|
||||
id: 't1',
|
||||
type: 'text',
|
||||
props: { text: '在此编辑说明文字,可在后台修改 JSON 调整模块与动画。' },
|
||||
animation: { enter: 'slideUp', delay_ms: 100, duration_ms: 500 }
|
||||
},
|
||||
{
|
||||
id: 'links',
|
||||
type: 'link_list',
|
||||
props: {
|
||||
items: [
|
||||
{ label: '回首页', url: '/' },
|
||||
{ label: '示例外链', url: '#', target: '_blank' }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'btn',
|
||||
type: 'button',
|
||||
props: { text: '主要按钮', url: '#', variant: 'primary' }
|
||||
},
|
||||
{ id: 'sp', type: 'spacer', props: { height: 24 } },
|
||||
{
|
||||
id: 'sec',
|
||||
type: 'section',
|
||||
props: { padding: '24px 0', maxWidth: '720px' },
|
||||
children: [
|
||||
{
|
||||
id: 'sub',
|
||||
type: 'text',
|
||||
props: { html: '<p>区块内可嵌套子模块(<strong>section → children</strong>)。</p>' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
|
||||
const form = reactive({
|
||||
site_id: '',
|
||||
slug: '',
|
||||
title: '',
|
||||
type: 'page',
|
||||
content: '',
|
||||
content_mode: 'html',
|
||||
route_path: '',
|
||||
published: true
|
||||
})
|
||||
|
||||
function insertBuilderTemplate() {
|
||||
form.content_mode = 'builder'
|
||||
if (!form.content?.trim()) {
|
||||
form.content = builderTemplate()
|
||||
} else {
|
||||
ElMessageBox.confirm('将用模板覆盖当前内容?', '提示', { type: 'warning' })
|
||||
.then(() => {
|
||||
form.content = builderTemplate()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
const rules = {
|
||||
slug: [{ required: true, message: '请输入 slug', trigger: 'blur' }],
|
||||
title: [{ required: true, message: '请输入标题', trigger: 'blur' }]
|
||||
@@ -120,6 +221,9 @@ const openDialog = (row) => {
|
||||
form.title = row ? row.title : ''
|
||||
form.type = row ? row.type || 'page' : 'page'
|
||||
form.content = row ? row.content || '' : ''
|
||||
form.content_mode = row?.content_mode || 'html'
|
||||
form.route_path = row?.route_path || ''
|
||||
form.published = row?.published !== false
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -128,6 +232,9 @@ const resetForm = () => {
|
||||
form.title = ''
|
||||
form.type = 'page'
|
||||
form.content = ''
|
||||
form.content_mode = 'html'
|
||||
form.route_path = ''
|
||||
form.published = true
|
||||
editId.value = ''
|
||||
}
|
||||
|
||||
@@ -135,11 +242,20 @@ const submitForm = async () => {
|
||||
await formRef.value?.validate()
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload = {
|
||||
slug: form.slug,
|
||||
title: form.title,
|
||||
type: form.type,
|
||||
content: form.content,
|
||||
content_mode: form.content_mode,
|
||||
route_path: form.route_path || undefined,
|
||||
published: form.published
|
||||
}
|
||||
if (editId.value) {
|
||||
await updatePage(editId.value, { slug: form.slug, title: form.title, type: form.type, content: form.content })
|
||||
await updatePage(editId.value, payload)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await createPage({ ...form, site_id: siteId.value })
|
||||
await createPage({ ...payload, site_id: siteId.value })
|
||||
ElMessage.success('创建成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
|
||||
Reference in New Issue
Block a user