LikeShop 首页装修模版复用二开:页面方案一键保存、活动场景快速套用

一、原生装修的边界:能装一次,不能复用一次

LikeShop 的 DIY 装修能力是完整的。组件在 web/components/decorate 下按 pc-widgets 两套体系组织,数据结构定义在 web/utils/decorate.ts,每个组件由 title、name、content、styles 四部分构成,装修结果以 JSON 形式持久化。多商户版的店铺主页也支持商家自主装修。

但这套能力解决的是“怎么装”,没有解决“装完怎么留着下次用”。大促前花两小时拖出来的首页,活动结束后被新装修覆盖,JSON 被新数据顶掉;下次大促想复用上次的版式,只能凭记忆重新拖一遍。组件本身是可复用的,页面方案是不可复用的——这是原生装修最实际的一个缺口。

更麻烦的是多店铺场景。同一个商户开了三家店,三家店的首页版本可以共用一套“大促模版”,但原生没有模版概念,每家店都要单独拖一遍。平台想给所有商家下发一套“双11标准首页”,也只能靠文档截图加人工照做。

二开的核心策略是:不修改原生装修编辑器和组件代码,在装修数据之上加一层“模版层”。模版不是装修 JSON 的简单拷贝,而是脱敏后的可复用方案——商品ID、优惠券ID 这些店铺特有的数据被抽象成占位符,套用时再按当前店铺的实际数据替换。

二、模版数据模型:模版、组件快照、变量占位三层

2.1 模版主表

模版以“场景 + 页面类型”为组织维度,一个模版对应一套完整的页面方案:

sql

CREATE TABLE `ls_decorate_template` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `template_name` varchar(100) NOT NULL COMMENT '模板名称',
  `template_code` varchar(50) NOT NULL COMMENT '模板编码,导出导入的标识',
  `page_type` varchar(30) NOT NULL DEFAULT 'home' COMMENT '页面类型:home/category/custom',
  `scene_tag` varchar(30) NOT NULL DEFAULT 'daily' COMMENT '场景标签:daily/double11/spring_festival/new_year',
  `cover_image` varchar(500) NOT NULL DEFAULT '' COMMENT '模板封面图',
  `decoration_json` longtext NOT NULL COMMENT '装修数据,已脱敏,含占位符',
  `variable_schema` text COMMENT '变量定义,JSON格式',
  `version` varchar(20) NOT NULL DEFAULT '1.0' COMMENT '模板版本号',
  `scope_type` tinyint NOT NULL DEFAULT 1 COMMENT '适用范围:1-平台公共,2-商户私有',
  `owner_shop_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '归属店铺,scope_type=2时使用',
  `allow_shop_levels` varchar(255) NOT NULL DEFAULT '' COMMENT '允许使用的商户等级',
  `use_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '被套用次数',
  `status` tinyint NOT NULL DEFAULT 1,
  `create_time` int UNSIGNED NOT NULL DEFAULT 0,
  `update_time` int UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_template_code` (`template_code`),
  KEY `idx_scene_page` (`scene_tag`, `page_type`),
  KEY `idx_owner` (`owner_shop_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='装修模板主表';

template_code 是导出导入的稳定标识。模板在店铺之间、系统之间流转时,自增 ID 会变,但 template_code 不变。导入时按 template_code 判断是“新增”还是“覆盖更新”,而不是按名称匹配——名称可以改,编码是模板的身份。

scope_type 区分平台公共模板和商户私有模板。平台公共模板由平台运营维护,所有符合条件的商户可套用;商户私有模板只属于创建它的商户,其他商户不可见。这个边界是多店铺场景下模板权限的基础。

2.2 变量定义

模板脱敏的关键是变量占位。装修 JSON 中的商品 ID、优惠券 ID、分类 ID 被替换为 {{goods_id_1}} 这样的占位符,模板套用时按当前店铺的实际数据替换:

sql

CREATE TABLE `ls_decorate_template_variable` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `template_id` int UNSIGNED NOT NULL,
  `var_key` varchar(50) NOT NULL COMMENT '变量键,如 goods_id_1',
  `var_type` varchar(20) NOT NULL COMMENT '类型:goods/coupon/category/custom',
  `var_label` varchar(50) NOT NULL COMMENT '展示名称,如 主推商品',
  `default_value` varchar(100) NOT NULL DEFAULT '' COMMENT '默认值',
  `is_required` tinyint NOT NULL DEFAULT 1 COMMENT '套用时是否必须指定',
  `create_time` int UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_template_var` (`template_id`, `var_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='模板变量定义表';

变量分两类:必须指定的(如主推商品的 goods_id,不指定模板就没有意义)和可留空的(如某个营销位,留空则组件隐藏)。套用模板时,前端弹出变量填写面板,运营为每个必填变量选择当前店铺的商品或券,系统替换后生成最终装修 JSON。

2.3 套用记录表

sql

CREATE TABLE `ls_decorate_template_apply_log` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `template_id` int UNSIGNED NOT NULL,
  `shop_id` int UNSIGNED NOT NULL COMMENT '套用的店铺',
  `decoration_id` int UNSIGNED NOT NULL COMMENT '生成的装修配置ID',
  `variable_values` text COMMENT '本次套用的变量值快照',
  `operator_id` int UNSIGNED NOT NULL DEFAULT 0,
  `create_time` int UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `idx_template` (`template_id`),
  KEY `idx_shop` (`shop_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='模板套用记录表';

套用记录的价值在于可追溯——某个店铺的首页是哪次套用生成的、用了哪些变量值,都有记录。出问题时能定位到“是这个模板的这组变量导致的”,而不是面对一个被手工改过的装修 JSON 无从下手。

三、保存为模板:从装修 JSON 到脱敏方案

3.1 脱敏不是删数据,是替换为占位符

“保存为模板”最粗糙的实现是直接拷贝当前店铺的装修 JSON。这样拷出来的模板里硬编码着 A 店铺的商品 ID,B 店铺套用后展示的还是 A 店铺的商品,模板复用就变成了“错误复用”。

正确的脱敏逻辑是识别并替换店铺特有的实体引用:遍历装修 JSON 中的组件,对商品组件、优惠券组件、分类组件中的 ID 字段,替换为 {{var_key}},同时在变量定义表中登记这个变量。图片这类资源 URL 保留原样,但模板套用后应提示运营“图片可能需要替换为当前店铺的素材”,而不是自动替换——自动替换图片的误判率太高。

php

public function extractVariables(&$json, $templateId)
{
    foreach ($json['widgets'] as &$widget) {
        if ($widget['name'] === 'goods-list') {
            foreach ($widget['content']['goods'] as $i => &$goods) {
                $varKey = 'goods_id_' . ($i + 1);
                $this->registerVariable($templateId, $varKey, 'goods',
                    '商品位' . ($i + 1), $goods['goods_id']);
                $goods['goods_id'] = '{{' . $varKey . '}}';
            }
        }
        if ($widget['name'] === 'coupon') {
            foreach ($widget['content']['coupons'] as $i => &$coupon) {
                $varKey = 'coupon_id_' . ($i + 1);
                $this->registerVariable($templateId, $varKey, 'coupon',
                    '优惠券位' . ($i + 1), $coupon['coupon_id']);
                $coupon['coupon_id'] = '{{' . $varKey . '}}';
            }
        }
    }
}

3.2 保存时的校验

保存为模板前,必须校验三件事:装修 JSON 的结构完整性(能否被解析)、变量提取后是否还有残留的硬编码店铺数据、模板名称和编码是否重复。结构不完整的 JSON 不能存为模板——一个坏模板被套用 N 次,就是 N 个坏页面。

3.3 版本管理

模板修改后,version 递增,旧版本不覆盖,而是保留在历史记录中。已套用旧版本的店铺不受影响,只有新套用才使用新版本。这是模板和装修配置的关键差异——装修配置改了就改了,模板需要“向下兼容已套用的实例”。

四、一键套用:变量替换与差异提示

4.1 套用的执行流程

套用模板分为四步:选择模板 → 填写变量 → 预览效果 → 确认生成。填写变量时,前端按 variable_schema 动态渲染表单,商品类变量调起商品选择器,优惠券类调起券选择器。变量填完后,服务端执行占位符替换,生成最终的装修 JSON,写入 ls_shop_decoration 并生成新的版本记录。

php

public function applyTemplate($templateId, $shopId, $variableValues, $operatorId)
{
    $template = DecorateTemplateModel::find($templateId);
    $json = $template['decoration_json'];

    // 校验必填变量
    $schema = json_decode($template['variable_schema'], true);
    foreach ($schema as $var) {
        if ($var['is_required'] && empty($variableValues[$var['var_key']])) {
            throw new \Exception("请填写变量:{$var['var_label']}");
        }
    }

    // 占位符替换
    foreach ($variableValues as $key => $value) {
        $json = str_replace('{{' . $key . '}}', $value, $json);
    }

    // 写入装修配置,走版本管理的 saveConfig
    $result = ShopDecorationService::saveConfig(
        $shopId, $template['page_type'],
        json_decode($json, true),
        json_decode($template['theme_config'] ?? '{}', true),
        $operatorId
    );

    // 记录套用日志
    DecorateTemplateApplyLogModel::create([
        'template_id'     => $templateId,
        'shop_id'         => $shopId,
        'decoration_id'   => $result['decoration_id'],
        'variable_values' => json_encode($variableValues, JSON_UNESCAPED_UNICODE),
        'operator_id'     => $operatorId,
        'create_time'     => time(),
    ]);

    return $result;
}

套用是覆盖,但覆盖前保留版本历史。套用会取消当前生效版本、创建新版本,旧版本仍在 ls_shop_decoration_history 中,运营套用后不满意可以回滚。没有版本历史的“一键覆盖”是危险的——模板套错了,当前装修就没了。

4.2 套用后的差异提示

模板中的商品可能在当前店铺不存在、优惠券可能已过期。套用完成后,系统应给出一份差异清单:哪些变量对应的实体在当前店铺不可用、哪些组件因为数据缺失会被降级隐藏。运营看到清单后可以决定“先套用再补数据”还是“先补数据再套用”。

降级处理不能报错。商品下架了,组件应该隐藏或展示“商品已下架”的占位,而不是让整个页面白屏。模板套用的容错边界是“页面能打开,缺失的部分降级”,而不是“数据不全就拒绝套用”。

五、导出导入与跨店铺复用

5.1 导出包的结构

模板导出为一个 JSON 包,包含模板主数据、变量定义和装修 JSON:

json

{
  "format_version": "1.0",
  "template_code": "double11_2026_home",
  "template_name": "2026双11大促首页",
  "page_type": "home",
  "scene_tag": "double11",
  "theme_config": {},
  "decoration_json": {},
  "variables": []
}

format_version 是导入兼容性的闸门。不同 LikeShop 版本的装修组件结构可能不同,导入时先校验 format_version,不兼容的包直接拒绝并提示“该模板由更高版本导出,请升级后再导入”,而不是导入一个半坏的模板。

5.2 导入的冲突处理

导入时按 template_code 判断:编码不存在则新增,已存在则按导入者的选择“覆盖更新”或“另存为新模板”。覆盖更新保留原模板的套用记录,但已套用的店铺不会自动更新——它们用的是旧版本,新版本只影响后续套用。这个规则必须明确,否则平台更新一个公共模板,所有店铺的首页被静默改变,是运营事故。

5.3 跨店铺复用的权限边界

  • 商户私有模板:仅归属店铺可见、可套用。同商户下的其他店铺可以申请“复制为我的模板”,复制后生成新的 template_code,与原模板脱钩。
  • 平台公共模板:所有符合 allow_shop_levels 的商户可见、可套用,但不可编辑。平台运营在后台维护公共模板,商户只能套用。
  • 跨商户的模板流转通过导出导入完成,不走直接引用。直接引用会让 A 商户的模板修改影响 B 商户的页面,模板的独立性被破坏。
六、后台配置与开发注意事项

6.1 后台配置清单

配置项类型说明
模板功能开关开关关闭后装修页不出现“保存为模板”入口
模板套用权限按商户等级哪些等级的商户可套用平台公共模板
单商户模板数量上限数字防止私有模板无限增长,默认 50
导出权限开关是否允许商户导出私有模板
导入冲突策略单选覆盖 / 另存为新模板
套用后差异提示开关默认开启
模板版本保留数数字默认保留最近 10 个版本

6.2 开发注意事项

模板不是装修 JSON 的拷贝,必须脱敏。直接拷贝会把源店铺的商品 ID、优惠券 ID 硬编码进模板,套用后展示的是别人的商品。商品、券、分类这三类实体引用必须替换为占位符,图片资源保留但提示替换。

套用必须走版本管理,覆盖前保留历史。模板套用是“一键替换当前装修”,如果没有版本历史,套错了就回不去。复用原生 ls_shop_decoration_history 的版本机制,套用生成新版本,旧版本可回滚。

导出包的 format_version 不能省。装修组件的 JSON 结构会随版本变化,没有版本标识的导入包在新旧版本之间流转时,可能产生“组件认识但字段对不上”的半坏模板。导入时校验版本,不兼容直接拒绝,比导入一个坏模板安全。

公共模板的更新不能静默影响已套用的店铺。已套用旧版本的店铺继续用旧版本,新版本只影响后续套用。如果平台需要强制更新,应该提供“批量重新套用”的显式操作,而不是让模板更新自动穿透。

兼容性。新增的模板主表、变量表、套用记录表独立于 ls_shop_decoration 和原生的装修编辑器。模板的保存和套用通过独立的 Service 实现,不修改原生组件的渲染逻辑和 web/utils/decorate.ts 的数据结构。模板功能关闭时,装修页行为与原生完全一致。导出导入作为可选的跨系统流转能力,不参与日常的模板套用链路。

功能原型来自 LikeShop 最新版本更新,单商户和多商户版均已适配,感兴趣可以去他们 demo 站看看实际效果。

0
0
0
0
评论
未登录
暂无评论