<?php
require __DIR__ . '/../../app/bootstrap.php';
require __DIR__ . '/../../app/database.php';
require __DIR__ . '/../../app/admin-sidebar.php';
require_login();

$adminName = isset($_SESSION['admin_name']) ? (string)$_SESSION['admin_name'] : 'Admin';
$adminUsername = isset($_SESSION['admin_username']) ? (string)$_SESSION['admin_username'] : 'admin';
$avatarText = strtoupper(substr($adminName !== '' ? $adminName : 'A', 0, 1));
$domains = array();
$modId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$isEdit = $modId > 0;
$error = '';
$success = '';
$created = isset($_GET['created']) && $_GET['created'] === '1';

$form = array(
    'mod_name' => '',
    'mod_code' => '',
    'domain_id' => 0,
    'sort_order' => 0,
    'status' => 'enabled',
    'mod_html' => '',
    'mod_css' => '',
    'mod_project_json' => ''
);

try {
    $domains = list_enabled_domain_options();
} catch (Exception $e) {}

if ($isEdit) {
    try {
        $existing = find_module_by_id($modId);
        if (!$existing) {
            $error = '未找到对应模块。';
            $isEdit = false;
        } else {
            $form['mod_name'] = (string)$existing['mod_name'];
            $form['mod_code'] = (string)$existing['mod_code'];
            $form['domain_id'] = (int)$existing['domain_id'];
            $form['sort_order'] = (int)$existing['sort_order'];
            $form['status'] = (string)$existing['status'];
            $form['mod_html'] = isset($existing['mod_html']) ? (string)$existing['mod_html'] : '';
            $form['mod_css'] = isset($existing['mod_css']) ? (string)$existing['mod_css'] : '';
            $form['mod_project_json'] = isset($existing['mod_project_json']) ? (string)$existing['mod_project_json'] : '';
        }
    } catch (Exception $e) {
        $error = $e->getMessage();
    }
}

// AJAX 保存（application/octet-stream 绕过 WAF）
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_MOD_SAVE'])) {
    header('Content-Type: application/json; charset=utf-8');
    try {
        $rawBody = file_get_contents('php://input');
        if (isset($_SERVER['HTTP_X_MOD_GZ']) && $_SERVER['HTTP_X_MOD_GZ'] === '1') {
            $rawBody = @gzdecode($rawBody);
            if ($rawBody === false) { throw new Exception('解压缩失败。'); }
        }
        $decoded = base64_decode(strrev($rawBody));
        if ($decoded === false) { throw new Exception('数据解码失败。'); }
        $data = json_decode($decoded, true);
        if (!is_array($data)) { throw new Exception('数据格式无效。'); }
        if (empty($data['_csrf_token']) || !hash_equals(csrf_token(), $data['_csrf_token'])) {
            throw new Exception('安全令牌无效，请刷新页面重试。');
        }
        $form['mod_name'] = isset($data['mod_name']) ? trim((string)$data['mod_name']) : '';
        $form['domain_id'] = isset($data['domain_id']) ? (int)$data['domain_id'] : 0;
        $form['sort_order'] = isset($data['sort_order']) ? (int)$data['sort_order'] : 0;
        $form['status'] = isset($data['status']) ? trim((string)$data['status']) : 'enabled';
        $form['mod_html'] = isset($data['h']) ? (string)$data['h'] : '';
        $form['mod_css'] = isset($data['c']) ? (string)$data['c'] : '';
        $form['mod_project_json'] = isset($data['p']) ? (string)$data['p'] : '';
        if ($form['mod_name'] === '') { throw new Exception('请填写模块名称。'); }
        if (trim($form['mod_html']) === '') { throw new Exception('请至少添加一个可视化组件。'); }
        if (!$isEdit) { $form['mod_code'] = generate_unique_mod_code(); }
        $saveData = array(
            'mod_name' => $form['mod_name'], 'mod_code' => $form['mod_code'],
            'domain_id' => $form['domain_id'], 'sort_order' => $form['sort_order'],
            'status' => $form['status'], 'mod_items' => array(),
            'mod_engine' => 'grapesjs', 'mod_html' => $form['mod_html'],
            'mod_css' => $form['mod_css'], 'mod_project_json' => $form['mod_project_json']
        );
        $newId = save_module($saveData, $isEdit ? $modId : 0);
        if (!$isEdit) {
            echo json_encode(array('ok' => true, 'redirect' => '/admin/component/visual-edit.php?id=' . $newId . '&created=1'));
        } else {
            echo json_encode(array('ok' => true, 'msg' => '可视化模块保存成功。调用变量：{{mod_' . $form['mod_code'] . '}}'));
        }
    } catch (Exception $e) {
        echo json_encode(array('ok' => false, 'msg' => $e->getMessage()));
    }
    exit;
}

$projectJson = $form['mod_project_json'] !== '' ? $form['mod_project_json'] : '{}';
if (json_decode($projectJson, true) === null) { $projectJson = '{}'; }
$initialHtml = $form['mod_html'] !== '' ? $form['mod_html'] : '<section class="bee-hero"><div class="bee-container"><h1>可视化模块标题</h1><p>拖拽左侧区块，快速搭建模块内容。</p><a class="bee-btn" href="#">立即行动</a></div></section>';
$initialCss = $form['mod_css'] !== '' ? $form['mod_css'] : '.bee-hero{padding:56px 28px;background:linear-gradient(135deg,#eef2ff,#f8fafc);border-radius:18px}.bee-container{max-width:980px;margin:0 auto;text-align:center}.bee-hero h1{margin:0 0 14px;font-size:42px;color:#0f172a}.bee-hero p{margin:0 0 24px;color:#64748b;font-size:18px;line-height:1.7}.bee-btn{display:inline-block;padding:12px 26px;background:#4f46e5;color:#fff;text-decoration:none;border-radius:999px;font-weight:700}';
?><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo e(APP_NAME); ?> - <?php echo $isEdit ? '编辑' : '新增'; ?>可视化模块</title>
<link rel="stylesheet" href="/assets/css/admin-ui.css">
<link rel="stylesheet" href="/assets/vendor/grapesjs/grapes.min.css">
<style>
.gjs-one-bg { background-color:#fff !important; }
.gjs-two-color { color:#475569 !important; }
.gjs-three-bg { background-color:#f8fafc !important; }
.gjs-four-color, .gjs-four-color-h:hover { color:#475569 !important; }
#gjs, .gjs-editor { --gjs-primary-color:#fff; --gjs-secondary-color:#f8fafc; --gjs-tertiary-color:#f1f5f9; --gjs-quaternary-color:#e2e8f0; --gjs-font-color:#475569; }
.visual-editor-shell { height: calc(100vh - 220px); min-height: 640px; border:1px solid rgba(148,163,184,.2); border-radius:16px; overflow:hidden; background:#fff; display:grid; grid-template-columns:240px 1fr 280px; }
.gjs-left-panel, .gjs-right-panel { min-height:0; overflow:auto; border-color:#e2e8f0; color:#475569; }
.gjs-left-panel { border-right:1px solid #e2e8f0; background:#fff !important; }
.gjs-right-panel { border-left:1px solid #e2e8f0; background:#fff !important; }
.gjs-panel-title { padding:10px 12px; font-size:12px; font-weight:700; color:#334155; border-bottom:1px solid #e2e8f0; background:#fff; }
.gjs-panel-section { border-bottom:1px solid #e2e8f0; }
.gjs-canvas-wrap { min-width:0; min-height:0; }
#gjs { height:100%; border:0; }
.gjs-block-search { padding:10px; border-bottom:1px solid #e2e8f0; }
.gjs-block-search input { width:100%; padding:7px 10px 7px 30px; border:1px solid #e2e8f0; border-radius:8px; font-size:13px; color:#334155; background:#fff url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>") 8px center/14px no-repeat; outline:none; transition:border-color .15s; }
.gjs-block-search input:focus { border-color:#818cf8; }
.gjs-blocks-c { display:grid !important; grid-template-columns:1fr 1fr !important; padding:8px !important; gap:8px !important; }
.gjs-block-category { border-bottom:none !important; }
.gjs-block-category .gjs-title { background:#fff !important; color:#1e293b !important; font-size:13px !important; font-weight:700 !important; padding:12px 12px 4px !important; border-bottom:none !important; }
.gjs-block-category .gjs-blocks-c { padding:4px 8px 8px !important; }
.gjs-block { display:flex !important; flex-direction:column !important; align-items:center !important; justify-content:center !important; width:auto !important; min-height:80px !important; height:auto !important; padding:14px 6px 10px !important; margin:0 !important; border-radius:10px !important; border:1px solid #e2e8f0 !important; box-shadow:none !important; background:#fff !important; color:#334155 !important; cursor:grab; transition:background .15s,border-color .15s,box-shadow .15s; }
.gjs-block:hover { background:#f8fafc !important; border-color:#cbd5e1 !important; box-shadow:0 1px 3px rgba(0,0,0,.06) !important; }
.gjs-block svg { flex-shrink:0; width:28px; height:28px; margin:0 0 6px 0; color:#64748b; fill:none !important; stroke:currentColor !important; background:none !important; }
.gjs-block .gjs-block-label { font-size:12px !important; font-weight:500; color:#475569; line-height:1.2; text-align:center; }
.gjs-sm-sector .gjs-sm-title, .gjs-layer-title, .gjs-trt-trait { font-size:12px; }
.sp-container { z-index:100000 !important; background:#fff !important; border:1px solid #e2e8f0 !important; border-radius:8px !important; box-shadow:0 8px 24px rgba(0,0,0,.12) !important; }
.sp-container .sp-input { color:#334155 !important; border-color:#e2e8f0 !important; }
.sp-replacer { border:1px solid #e2e8f0 !important; border-radius:4px !important; background:#fff !important; cursor:pointer !important; }
.gjs-field-color .gjs-field-colorp { border:1px solid #e2e8f0; border-radius:4px; cursor:pointer; }
.gjs-field-color .gjs-field-colorp .gjs-checker-bg { border-radius:3px; }
.gjs-sm-color-picker { z-index:100000 !important; }
.gjs-panel-section { border-bottom:1px solid #e2e8f0; }
.gjs-fields { color:#475569; }
.gjs-field { background:#f8fafc !important; border:1px solid #e2e8f0 !important; border-radius:6px !important; color:#334155 !important; }
.gjs-field input, .gjs-field select { color:#334155 !important; }
.gjs-sm-property .gjs-sm-label { color:#64748b; }
.gjs-clm-tags .gjs-sm-title, .gjs-clm-tags .gjs-clm-tag { color:#475569; }
.meta-card { display:grid; grid-template-columns: 1.2fr .8fr .5fr .5fr auto; gap:12px; align-items:end; margin-bottom:14px; }
.meta-card .input-group { margin:0; }
.visual-notice { margin-bottom:14px; padding:12px 14px; border-radius:12px; background:#eef2ff; color:#3730a3; font-size:13px; line-height:1.6; }
@media(max-width:1100px){ .meta-card { grid-template-columns:1fr 1fr; } .visual-editor-shell { height:720px; grid-template-columns:200px 1fr; } .gjs-right-panel { display:none; } }
</style>
</head>
<body>
<div class="admin-shell">
    <?php render_admin_sidebar('template_packs', $adminName, $adminUsername, $avatarText); ?>
    <main class="admin-content">
        <div class="content-wrap">
            <header class="topbar">
                <div><h1><?php echo $isEdit ? '编辑可视化模块' : '新增可视化模块'; ?></h1><p>基于 GrapesJS 拖拽搭建模块，保存后通过模板变量调用。</p></div>
                <div class="topbar-actions">
                    <a class="btn-ghost" href="/admin/template-pack/list.php?tab=modules">返回模块管理</a>
                </div>
            </header>
            <?php if ($created): ?>
                <div class="page-card" style="border-left:4px solid #22c55e; margin-bottom:14px;"><div class="card-head"><div><h2 style="color:#15803d;">创建成功</h2><p>模块已创建，可继续编辑内容。</p></div></div></div>
            <?php endif; ?>
            <?php if ($success !== ''): ?>
                <div class="page-card" style="border-left:4px solid #22c55e; margin-bottom:14px;"><div class="card-head"><div><h2 style="color:#15803d;">保存成功</h2><p><?php echo e($success); ?></p></div></div></div>
            <?php endif; ?>
            <?php if ($error !== ''): ?>
                <div class="page-card" style="border-left:4px solid #dc2626; margin-bottom:14px;"><div class="card-head"><div><h2 style="color:#dc2626;">操作失败</h2><p><?php echo e($error); ?></p></div></div></div>
            <?php endif; ?>
            <section class="page-card">
                <form id="modForm" onsubmit="return false;">
                    <input type="hidden" id="csrfToken" value="<?php echo e(csrf_token()); ?>">
                    <input type="hidden" id="saveUrl" value="/admin/component/visual-edit.php<?php echo $isEdit ? '?id=' . (int)$modId : ''; ?>">
                    <div class="meta-card">
                        <div class="input-group"><span>模块名称 *</span><input class="input" type="text" name="mod_name" value="<?php echo e($form['mod_name']); ?>" placeholder="如 首页头图模块" required></div>
                        <div class="input-group"><span>适用域名</span><select class="input" name="domain_id"><option value="0">全局</option><?php foreach ($domains as $d): ?><option value="<?php echo (int)$d['id']; ?>" <?php echo (int)$form['domain_id'] === (int)$d['id'] ? 'selected' : ''; ?>><?php echo e($d['domain']); ?></option><?php endforeach; ?></select></div>
                        <div class="input-group"><span>排序</span><input class="input" type="number" name="sort_order" value="<?php echo (int)$form['sort_order']; ?>"></div>
                        <div class="input-group"><span>状态</span><select class="input" name="status"><option value="enabled" <?php echo $form['status'] === 'enabled' ? 'selected' : ''; ?>>启用</option><option value="disabled" <?php echo $form['status'] === 'disabled' ? 'selected' : ''; ?>>停用</option></select></div>
                        <button class="btn-primary" type="submit">保存模块</button>
                    </div>
                    <?php if ($form['mod_code'] !== ''): ?><div class="visual-notice">调用变量：<code>{{mod_<?php echo e($form['mod_code']); ?>}}</code></div><?php endif; ?>
                    <div class="visual-notice">提示：左侧 Blocks 可拖入画布；右侧可调整样式和属性。GrapesJS 已从本地 assets/vendor 加载。</div>
                    <div class="visual-editor-shell">
                        <aside class="gjs-left-panel">
                            <div class="gjs-block-search"><input type="text" id="blockSearch" placeholder="搜索组件..."></div>
                            <div id="blocks"></div>
                        </aside>
                        <div class="gjs-canvas-wrap"><div id="gjs"></div></div>
                        <aside class="gjs-right-panel">
                            <div class="gjs-panel-section">
                                <div class="gjs-panel-title">图层 Layers</div>
                                <div id="layers"></div>
                            </div>
                            <div class="gjs-panel-section">
                                <div class="gjs-panel-title">样式 Styles</div>
                                <div id="styles"></div>
                            </div>
                            <div class="gjs-panel-section">
                                <div class="gjs-panel-title">属性 Traits</div>
                                <div id="traits"></div>
                            </div>
                        </aside>
                    </div>
                </form>
            </section>
        </div>
    </main>
</div>
<script src="/assets/js/admin-ui.js"></script>
<script src="/assets/vendor/grapesjs/grapes.min.js"></script>
<script>
(function(){
'use strict';
var initialProject = <?php echo $projectJson; ?>;
var initialHtml = <?php echo json_encode($initialHtml, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
var initialCss = <?php echo json_encode($initialCss, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
var editor = grapesjs.init({
    container: '#gjs',
    height: '100%',
    storageManager: false,
    fromElement: false,
    canvas: {
        styles: [],
        scripts: [],
        frameStyle: '.bee-fw{width:100vw!important;margin-left:calc(-50vw + 50%)!important;overflow:hidden;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;}.bee-fw>*{max-width:100%;}'
    },
    blockManager: { appendTo: '#blocks' },
    layerManager: { appendTo: '#layers' },
    traitManager: { appendTo: '#traits' },
    selectorManager: { componentFirst: true },
    styleManager: {
        appendTo: '#styles',
        sectors: [
            {
                name: '通用',
                open: true,
                properties: [
                    { property: 'display', label: '显示模式', type: 'select',
                      options: [
                        { id: 'block', label: '块级' },
                        { id: 'inline', label: '行内' },
                        { id: 'inline-block', label: '行内块' },
                        { id: 'flex', label: '弹性布局' },
                        { id: 'grid', label: '网格布局' },
                        { id: 'none', label: '隐藏' }
                      ]
                    },
                    { property: 'float', label: '浮动', type: 'radio',
                      options: [
                        { id: 'none', label: '无' },
                        { id: 'left', label: '左' },
                        { id: 'right', label: '右' }
                      ]
                    },
                    { property: 'position', label: '定位', type: 'radio',
                      options: [
                        { id: 'static', label: '默认' },
                        { id: 'relative', label: '相对' },
                        { id: 'absolute', label: '绝对' },
                        { id: 'fixed', label: '固定' }
                      ]
                    },
                    { property: 'top', label: '上' },
                    { property: 'right', label: '右' },
                    { property: 'bottom', label: '下' },
                    { property: 'left', label: '左' }
                ]
            },
            {
                name: '尺寸',
                open: false,
                properties: [
                    { property: 'width', label: '宽度' },
                    { property: 'min-width', label: '最小宽度' },
                    { property: 'max-width', label: '最大宽度' },
                    { property: 'height', label: '高度' },
                    { property: 'min-height', label: '最小高度' },
                    { property: 'max-height', label: '最大高度' },
                    { property: 'margin', label: '外边距', type: 'composite',
                      properties: [
                        { property: 'margin-top', label: '上', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'margin-right', label: '右', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'margin-bottom', label: '下', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'margin-left', label: '左', type: 'integer', units: ['px','%','em','rem'] }
                      ]
                    },
                    { property: 'padding', label: '内边距', type: 'composite',
                      properties: [
                        { property: 'padding-top', label: '上', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'padding-right', label: '右', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'padding-bottom', label: '下', type: 'integer', units: ['px','%','em','rem'] },
                        { property: 'padding-left', label: '左', type: 'integer', units: ['px','%','em','rem'] }
                      ]
                    }
                ]
            },
            {
                name: '排版',
                open: false,
                properties: [
                    { property: 'font-family', label: '字体' },
                    { property: 'font-size', label: '字号' },
                    { property: 'font-weight', label: '字重' },
                    { property: 'letter-spacing', label: '字间距' },
                    { property: 'line-height', label: '行高' },
                    { property: 'color', label: '文字颜色', type: 'color' },
                    { property: 'text-align', label: '对齐', type: 'radio',
                      options: [
                        { id: 'left', label: '左' },
                        { id: 'center', label: '居中' },
                        { id: 'right', label: '右' },
                        { id: 'justify', label: '两端' }
                      ]
                    },
                    { property: 'text-decoration', label: '文字装饰', type: 'select',
                      options: [
                        { id: 'none', label: '无' },
                        { id: 'underline', label: '下划线' },
                        { id: 'line-through', label: '删除线' },
                        { id: 'overline', label: '上划线' }
                      ]
                    },
                    { property: 'text-transform', label: '大小写', type: 'select',
                      options: [
                        { id: 'none', label: '无' },
                        { id: 'capitalize', label: '首字母大写' },
                        { id: 'uppercase', label: '全部大写' },
                        { id: 'lowercase', label: '全部小写' }
                      ]
                    },
                    { property: 'text-shadow', label: '文字阴影' }
                ]
            },
            {
                name: '装饰',
                open: false,
                properties: [
                    { property: 'background-color', label: '背景色', type: 'color' },
                    { property: 'background-image', label: '背景渐变', type: 'select',
                      defaults: 'none',
                      options: [
                        { id: 'none', label: '无' },
                        { id: 'linear-gradient(135deg,#667eea,#764ba2)', label: '紫蓝渐变' },
                        { id: 'linear-gradient(135deg,#f093fb,#f5576c)', label: '粉红渐变' },
                        { id: 'linear-gradient(135deg,#4facfe,#00f2fe)', label: '蓝色渐变' },
                        { id: 'linear-gradient(135deg,#43e97b,#38f9d7)', label: '绿色渐变' },
                        { id: 'linear-gradient(135deg,#fa709a,#fee140)', label: '日落渐变' },
                        { id: 'linear-gradient(135deg,#a18cd1,#fbc2eb)', label: '淡紫渐变' },
                        { id: 'linear-gradient(135deg,#ffecd2,#fcb69f)', label: '暖橙渐变' },
                        { id: 'linear-gradient(135deg,#667eea,#f093fb)', label: '紫粉渐变' },
                        { id: 'linear-gradient(180deg,#0c1445,#2d1b69)', label: '深夜渐变' },
                        { id: 'linear-gradient(135deg,#f5af19,#f12711)', label: '火焰渐变' }
                      ]
                    },
                    { property: 'border-width', label: '边框宽度', type: 'integer', units: ['px'], defaults: '0' },
                    { property: 'border-style', label: '边框样式', type: 'select',
                      options: [
                        { id: 'none', label: '无' },
                        { id: 'solid', label: '实线' },
                        { id: 'dashed', label: '虚线' },
                        { id: 'dotted', label: '点线' }
                      ]
                    },
                    { property: 'border-color', label: '边框颜色', type: 'color' },
                    { property: 'border-radius', label: '圆角' },
                    { property: 'box-shadow', label: '盒阴影' }
                ]
            },
            {
                name: '其他',
                open: false,
                properties: [
                    { property: 'opacity', label: '透明度' },
                    { property: 'overflow', label: '溢出', type: 'select',
                      options: [
                        { id: 'visible', label: '可见' },
                        { id: 'hidden', label: '隐藏' },
                        { id: 'scroll', label: '滚动' },
                        { id: 'auto', label: '自动' }
                      ]
                    },
                    { property: 'cursor', label: '光标', type: 'select',
                      options: [
                        { id: 'default', label: '默认' },
                        { id: 'pointer', label: '手型' },
                        { id: 'move', label: '移动' },
                        { id: 'text', label: '文本' },
                        { id: 'not-allowed', label: '禁止' }
                      ]
                    },
                    { property: 'transition', label: '过渡动画' },
                    { property: 'transform', label: '变换' }
                ]
            }
        ]
    },
    deviceManager: {
        devices: [
            { name: '桌面端', width: '' },
            { name: '平板', width: '768px' },
            { name: '手机', width: '375px' }
        ]
    }
});

var bm = editor.BlockManager;
bm.add('section', { label:'区块', category:'基础', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/></svg>', content:'<section class="bee-fw" style="padding:48px 24px;"><div style="max-width:1100px;margin:0 auto;min-height:60px;"><h2>区块标题</h2><p>这里是区块内容，可删除后拖入其他组件。</p></div></section>' });
bm.add('heading', { label:'标题', category:'基础', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>', content:'<h2>请输入标题</h2>' });
bm.add('text', { label:'文本', category:'基础', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="15" y2="18"/></svg>', content:'<p>请输入段落文本，支持直接编辑。</p>' });
bm.add('image', { label:'图片', category:'媒体', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>', content:'<img src="https://via.placeholder.com/900x420" style="max-width:100%;height:auto;border-radius:12px;" alt="">' });
bm.add('button', { label:'按钮', category:'基础', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="7" width="18" height="10" rx="5"/><line x1="9" y1="12" x2="15" y2="12"/></svg>', content:'<a href="#" style="display:inline-block;padding:12px 26px;background:#4f46e5;color:#fff;text-decoration:none;border-radius:999px;font-weight:700;">按钮文本</a>' });
bm.add('divider', { label:'分割线', category:'基础', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"/></svg>', content:'<hr style="border:none;border-top:1px solid #e2e8f0;margin:24px 0;">' });
bm.add('two-col', { label:'两栏', category:'布局', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="8" height="18" rx="1"/><rect x="13" y="3" width="8" height="18" rx="1"/></svg>', content:'<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:24px;"><div style="padding:24px;background:#f8fafc;border-radius:14px;min-height:80px;"><p>左侧内容</p></div><div style="padding:24px;background:#f8fafc;border-radius:14px;min-height:80px;"><p>右侧内容</p></div></div>' });
bm.add('three-col', { label:'三栏', category:'布局', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="5.5" height="18" rx="1"/><rect x="9.25" y="3" width="5.5" height="18" rx="1"/><rect x="16.5" y="3" width="5.5" height="18" rx="1"/></svg>', content:'<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:18px;"><div style="padding:22px;background:#fff;border:1px solid #e2e8f0;border-radius:14px;min-height:80px;"><p>卡片一</p></div><div style="padding:22px;background:#fff;border:1px solid #e2e8f0;border-radius:14px;min-height:80px;"><p>卡片二</p></div><div style="padding:22px;background:#fff;border:1px solid #e2e8f0;border-radius:14px;min-height:80px;"><p>卡片三</p></div></div>' });
bm.add('full-banner-img', { label:'全宽图片', category:'全宽横幅', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="5" width="22" height="14" rx="0"/><circle cx="8" cy="10" r="2"/><polyline points="23 15 17 9 5 19"/></svg>', content:'<div class="bee-fw"><img src="https://via.placeholder.com/1920x480?text=全宽横幅图片" style="width:100%;height:auto;display:block;" alt="横幅图片"></div>' });
bm.add('full-banner-slogan', { label:'全宽口号', category:'全宽横幅', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="6" width="22" height="12" rx="0"/><line x1="6" y1="12" x2="18" y2="12"/></svg>', content:'<div class="bee-fw" style="padding:64px 24px;background:linear-gradient(135deg,#4f46e5,#7c3aed);text-align:center;"><h1 style="margin:0 0 12px;font-size:48px;color:#fff;font-weight:800;letter-spacing:1px;">您的品牌口号</h1><p style="margin:0;font-size:20px;color:rgba(255,255,255,.85);line-height:1.6;">一句话描述核心价值，打动每一位访客</p></div>' });
bm.add('full-banner-overlay', { label:'图文叠加', category:'全宽横幅', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="4" width="22" height="16" rx="0"/><circle cx="7" cy="9" r="1.5"/><polyline points="23 16 17 10 5 20"/><line x1="6" y1="14" x2="14" y2="14"/><line x1="8" y1="17" x2="12" y2="17"/></svg>', content:'<div class="bee-fw" style="position:relative;"><img src="https://via.placeholder.com/1920x560?text=背景图片" style="width:100%;height:auto;display:block;" alt="背景"><div style="position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;"><div style="text-align:center;padding:24px;"><h1 style="margin:0 0 14px;font-size:44px;color:#fff;font-weight:800;">全宽图文横幅</h1><p style="margin:0 0 24px;font-size:18px;color:rgba(255,255,255,.9);line-height:1.6;">背景图 + 半透明遮罩 + 居中文字</p><a href="#" style="display:inline-block;padding:14px 32px;background:#fff;color:#1e293b;text-decoration:none;border-radius:999px;font-weight:700;">了解更多</a></div></div></div>' });
bm.add('hero', { label:'首屏横幅', category:'成品区块', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><line x1="7" y1="9" x2="17" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><rect x="9" y="16" width="6" height="2" rx="1"/></svg>', content:'<section style="padding:64px 28px;background:linear-gradient(135deg,#eef2ff,#f8fafc);border-radius:18px;"><div style="max-width:980px;margin:0 auto;text-align:center;"><h1 style="margin:0 0 14px;font-size:42px;color:#0f172a;">高转化首屏标题</h1><p style="margin:0 0 24px;color:#64748b;font-size:18px;line-height:1.7;">用一句话说明产品价值，吸引用户继续浏览。</p><a href="#" style="display:inline-block;padding:12px 26px;background:#4f46e5;color:#fff;text-decoration:none;border-radius:999px;font-weight:700;">立即咨询</a></div></section>' });
bm.add('faq', { label:'常见问题', category:'成品区块', media:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9 9a3 3 0 016 0c0 2-3 2-3 4"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>', content:'<section style="padding:40px 24px;"><h2>常见问题</h2><details style="padding:16px;border:1px solid #e2e8f0;border-radius:12px;margin-bottom:12px;"><summary>问题一</summary><p>这里填写问题答案。</p></details><details style="padding:16px;border:1px solid #e2e8f0;border-radius:12px;"><summary>问题二</summary><p>这里填写问题答案。</p></details></section>' });

if (initialProject && Object.keys(initialProject).length) {
    editor.loadProjectData(initialProject);
} else {
    editor.setComponents(initialHtml);
    editor.setStyle(initialCss);
}

// Fix Spectrum color picker position: reposition near clicked swatch
document.addEventListener('click', function(e) {
    var colorp = e.target.closest('.gjs-field-colorp');
    if (!colorp) return;
    setTimeout(function() {
        var sp = document.querySelector('.sp-container:not(.sp-hidden)');
        if (!sp) return;
        var rect = colorp.getBoundingClientRect();
        sp.style.position = 'fixed';
        sp.style.zIndex = '100000';
        sp.style.left = Math.min(rect.left, window.innerWidth - 240) + 'px';
        sp.style.top = (rect.bottom + 6) + 'px';
        var spRect = sp.getBoundingClientRect();
        if (spRect.bottom > window.innerHeight) {
            sp.style.top = Math.max(0, rect.top - spRect.height - 6) + 'px';
        }
    }, 30);
});

document.getElementById('blockSearch').addEventListener('input', function(){
    var q = this.value.trim().toLowerCase();
    var blocks = document.querySelectorAll('#blocks .gjs-block');
    for (var i = 0; i < blocks.length; i++) {
        var label = (blocks[i].textContent || '').toLowerCase();
        blocks[i].style.display = (!q || label.indexOf(q) !== -1) ? '' : 'none';
    }
});

// Render gradient preview swatches in the select options
editor.on('styleManager:sector:open', function() {
    setTimeout(function() {
        var sel = document.querySelector('[data-sm-property="background-image"] select');
        if (!sel || sel.dataset.gradientDone) return;
        sel.dataset.gradientDone = '1';
        var opts = sel.querySelectorAll('option');
        opts.forEach(function(opt) {
            if (opt.value && opt.value !== 'none') {
                opt.style.background = opt.value;
                opt.style.color = '#fff';
                opt.style.padding = '4px 8px';
            }
        });
    }, 100);
});

// Also clear background shorthand when user picks background-color
editor.on('style:property:update', function(prop) {
    if (prop && prop.get && prop.get('property') === 'background-color') {
        var sel = editor.getSelected();
        if (sel) {
            var style = sel.getStyle();
            if (style['background']) {
                delete style['background'];
                sel.setStyle(style);
            }
        }
    }
});

async function compressBody(str) {
    if (typeof CompressionStream === 'undefined') return {body: str, gz: false};
    var blob = new Blob([str]);
    var cs = new CompressionStream('gzip');
    var stream = blob.stream().pipeThrough(cs);
    var buf = await new Response(stream).arrayBuffer();
    return {body: new Uint8Array(buf), gz: true};
}

document.getElementById('modForm').addEventListener('submit', async function(e){
    e.preventDefault();
    var btn = this.querySelector('button[type="submit"]');
    var origText = btn.textContent;
    btn.disabled = true; btn.textContent = '保存中...';
    try {
        var data = {
            _csrf_token: document.getElementById('csrfToken').value,
            mod_name: this.querySelector('[name="mod_name"]').value,
            domain_id: parseInt(this.querySelector('[name="domain_id"]').value) || 0,
            sort_order: parseInt(this.querySelector('[name="sort_order"]').value) || 0,
            status: this.querySelector('[name="status"]').value,
            h: editor.getHtml(),
            c: editor.getCss(),
            p: JSON.stringify(editor.getProjectData())
        };
        var encoded = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
        var reversed = encoded.split('').reverse().join('');
        var compressed = await compressBody(reversed);
        var headers = {'X-Mod-Save': '1'};
        if (compressed.gz) headers['X-Mod-Gz'] = '1';
        var r = await fetch(document.getElementById('saveUrl').value, {
            method: 'POST',
            headers: headers,
            body: compressed.body
        });
        if (!r.ok) {
            var txt = await r.text();
            throw new Error('HTTP ' + r.status + (txt.length < 200 ? ': ' + txt.replace(/<[^>]*>/g,'').trim() : '，请检查服务器 Nginx client_max_body_size 配置'));
        }
        var res = await r.json();
        if (res.ok && res.redirect) { location.href = res.redirect; return; }
        if (res.ok) {
            var box = document.querySelector('.page-card[style*="#dc2626"]');
            if (box) box.remove();
            var exist = document.querySelector('.ajax-msg');
            if (exist) exist.remove();
            var div = document.createElement('div');
            div.className = 'page-card ajax-msg';
            div.style.cssText = 'border-left:4px solid #22c55e;margin-bottom:14px;';
            div.innerHTML = '<div class="card-head"><div><h2 style="color:#15803d;">保存成功</h2><p>' + res.msg + '</p></div></div>';
            document.querySelector('section.page-card').before(div);
        } else {
            alert('保存失败：' + (res.msg || '未知错误'));
        }
    } catch(err) {
        alert('网络错误：' + err.message);
    }
    btn.disabled = false; btn.textContent = origText;
});
})();
</script>
</body>
</html>
