chore: 全量提交(推广页、宣传册、社交资源、视频与后台同步)
Made-with: Cursor
This commit is contained in:
@@ -1,17 +1,31 @@
|
||||
<template>
|
||||
<div class="page-builder-editor">
|
||||
<PageBuilderBlocks v-model:blocks="blocks" :site-id="siteId" />
|
||||
<el-collapse class="adv-collapse" accordion>
|
||||
<el-collapse-item title="高级:直接编辑 JSON(慎用)" name="json">
|
||||
<el-input
|
||||
v-model="jsonDraft"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="修改后会覆盖上方可视化内容"
|
||||
/>
|
||||
<el-button type="warning" style="margin-top: 8px" @click="applyJsonDraft">应用 JSON</el-button>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<div class="pbe-split">
|
||||
<div class="pbe-editor-col">
|
||||
<PageBuilderBlocks v-model:blocks="blocks" :site-id="siteId" />
|
||||
<el-collapse class="adv-collapse" accordion>
|
||||
<el-collapse-item title="高级:直接编辑 JSON(慎用)" name="json">
|
||||
<el-input
|
||||
v-model="jsonDraft"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="修改后会覆盖上方可视化内容"
|
||||
/>
|
||||
<el-button type="warning" style="margin-top: 8px" @click="applyJsonDraft">应用 JSON</el-button>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
<aside class="pbe-preview-col">
|
||||
<div class="pbe-preview-head">
|
||||
<span>实时预览</span>
|
||||
<el-text size="small" type="info">与前台样式接近,保存后线上一致</el-text>
|
||||
</div>
|
||||
<div class="pbe-preview-body">
|
||||
<BlockRenderer v-if="blocks.length" :blocks="blocks" />
|
||||
<el-empty v-else description="添加模块后此处显示效果" :image-size="80" />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -19,6 +33,8 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import PageBuilderBlocks from './PageBuilderBlocks.vue'
|
||||
import BlockRenderer from '@yh-web/components/blocks/BlockRenderer.vue'
|
||||
import '@yh-web/styles/page-animations.css'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: '' },
|
||||
@@ -105,6 +121,57 @@ function applyJsonDraft() {
|
||||
.page-builder-editor {
|
||||
width: 100%;
|
||||
}
|
||||
.pbe-split {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.pbe-editor-col {
|
||||
flex: 1 1 420px;
|
||||
min-width: 320px;
|
||||
}
|
||||
.pbe-preview-col {
|
||||
flex: 0 1 380px;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 8px;
|
||||
background: #0a0a12;
|
||||
color: #e8e8ef;
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
top: 8px;
|
||||
}
|
||||
.pbe-preview-head {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.pbe-preview-body {
|
||||
padding: 16px;
|
||||
max-height: min(62vh, 640px);
|
||||
overflow: auto;
|
||||
}
|
||||
.pbe-preview-body :deep(.builder-heading),
|
||||
.pbe-preview-body :deep(.builder-text),
|
||||
.pbe-preview-body :deep(.builder-links a),
|
||||
.pbe-preview-body :deep(.builder-btn) {
|
||||
color: inherit;
|
||||
}
|
||||
.pbe-preview-body :deep(.builder-text) {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
.pbe-preview-body :deep(.builder-links a) {
|
||||
color: #7eb8ff;
|
||||
}
|
||||
.pbe-preview-body :deep(hr) {
|
||||
border-color: rgba(255, 255, 255, 0.15) !important;
|
||||
}
|
||||
.adv-collapse {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './utils/disable-debug'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(ElementPlus)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* 禁止调试模式及右键 - 全局安全模块
|
||||
* 在页面加载时立即执行
|
||||
*/
|
||||
|
||||
// 禁止右键
|
||||
document.addEventListener('contextmenu', (e) => e.preventDefault())
|
||||
|
||||
// 禁止 F12、Ctrl+Shift+I/J/C 等开发者工具快捷键
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (
|
||||
e.key === 'F12' ||
|
||||
(e.ctrlKey && e.shiftKey && ['I', 'J', 'C'].includes(e.key.toUpperCase())) ||
|
||||
(e.ctrlKey && e.key === 'U')
|
||||
) {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
@@ -53,7 +53,7 @@
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="editId ? '编辑网页' : '新增网页'"
|
||||
:width="form.content_mode === 'builder' ? '1080px' : '720px'"
|
||||
:width="form.content_mode === 'builder' || form.content_mode === 'html' ? '1080px' : '720px'"
|
||||
top="4vh"
|
||||
@close="resetForm"
|
||||
>
|
||||
@@ -63,7 +63,15 @@
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-bottom: 16px"
|
||||
title="积木模式:从模块左侧 ⋮⋮ 手柄拖拽排序;链接可点「选择链接」选站内页或文件。"
|
||||
title="积木模式:左侧编辑、右侧实时预览;⋮⋮ 可拖拽排序;链接可「选择链接」。"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="form.content_mode === 'html'"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-bottom: 16px"
|
||||
title="HTML 模式:左侧源码、右侧预览(沙箱内不执行脚本,与线上可能略有差异)。"
|
||||
/>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="Slug" prop="slug">
|
||||
@@ -91,8 +99,21 @@
|
||||
<el-form-item label="发布到前台">
|
||||
<el-switch v-model="form.published" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.content_mode === 'html'" label="内容" prop="content">
|
||||
<el-input v-model="form.content" type="textarea" :rows="14" placeholder="直接编写 HTML" />
|
||||
<el-form-item v-if="form.content_mode === 'html'" label="内容" prop="content" class="html-form-item">
|
||||
<div class="html-split">
|
||||
<div class="html-editor">
|
||||
<el-input v-model="form.content" type="textarea" :rows="18" placeholder="直接编写 HTML" />
|
||||
</div>
|
||||
<div class="html-preview-wrap">
|
||||
<div class="html-preview-title">实时预览</div>
|
||||
<iframe
|
||||
class="html-preview-iframe"
|
||||
title="html-preview"
|
||||
sandbox=""
|
||||
:srcdoc="htmlPreviewSrcdoc"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="页面积木" class="builder-form-item page-builder-wrap">
|
||||
<PageBuilderEditor v-model="form.content" :site-id="siteId" />
|
||||
@@ -107,7 +128,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getSites } from '../../api/admin'
|
||||
import { getPages, createPage, updatePage, deletePage } from '../../api/admin'
|
||||
@@ -215,6 +236,13 @@ const form = reactive({
|
||||
published: true
|
||||
})
|
||||
|
||||
/** HTML 预览 iframe(沙箱禁用脚本,避免编辑时执行恶意片段) */
|
||||
const htmlPreviewSrcdoc = computed(() => {
|
||||
const raw = form.content || ''
|
||||
const body = raw.trim() ? raw : '<p style="color:#999">暂无内容</p>'
|
||||
return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{font-family:system-ui,sans-serif;padding:12px;margin:0;background:#fff;color:#222;line-height:1.5;}</style></head><body>${body}</body></html>`
|
||||
})
|
||||
|
||||
function insertBuilderTemplate() {
|
||||
form.content_mode = 'builder'
|
||||
if (!form.content?.trim()) {
|
||||
@@ -314,4 +342,43 @@ onMounted(() => {
|
||||
.page-builder-wrap {
|
||||
max-width: 100%;
|
||||
}
|
||||
.html-form-item :deep(.el-form-item__content) {
|
||||
display: block;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
.html-split {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
.html-editor {
|
||||
flex: 1 1 400px;
|
||||
min-width: 280px;
|
||||
}
|
||||
.html-preview-wrap {
|
||||
flex: 0 1 420px;
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: #fafafa;
|
||||
}
|
||||
.html-preview-title {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background: #fff;
|
||||
}
|
||||
.html-preview-iframe {
|
||||
width: 100%;
|
||||
min-height: 360px;
|
||||
height: 420px;
|
||||
border: none;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user