From 7b276946904e9055790b814a5ce167822e1bfa55 Mon Sep 17 00:00:00 2001
From: comlibmb <1844410276@qq.com>
Date: Mon, 16 Feb 2026 22:20:43 +0800
Subject: [PATCH] feat(admin): complete decoration module database integration
including DIY pages, RLS and RPCs
---
..._admin__decoration-module-repaired-full.md | 51 ++
.../10_schema/decoration/ak_diy_pages_v1.sql | 32 +
.../decoration/ml_decoration_rls_v1.sql | 18 +
.../rpc_admin_delete_diy_page_v1.sql | 40 +
.../rpc_admin_get_diy_page_list_v1.sql | 60 ++
.../decoration/rpc_admin_save_diy_page_v1.sql | 57 ++
.../decoration/rpc_admin_set_home_page_v1.sql | 40 +
pages/mall/admin/decoration/category.uvue | 317 +++----
pages/mall/admin/decoration/home.uvue | 784 +++++-------------
pages/mall/admin/decoration/user.uvue | 477 +++--------
services/admin/decorationService.uts | 91 ++
11 files changed, 798 insertions(+), 1169 deletions(-)
create mode 100644 docs/ops/2026-02-16__admin__decoration-module-repaired-full.md
create mode 100644 docs/sql/10_schema/decoration/ak_diy_pages_v1.sql
create mode 100644 docs/sql/20_rls/decoration/ml_decoration_rls_v1.sql
create mode 100644 docs/sql/30_rpc/decoration/rpc_admin_delete_diy_page_v1.sql
create mode 100644 docs/sql/30_rpc/decoration/rpc_admin_get_diy_page_list_v1.sql
create mode 100644 docs/sql/30_rpc/decoration/rpc_admin_save_diy_page_v1.sql
create mode 100644 docs/sql/30_rpc/decoration/rpc_admin_set_home_page_v1.sql
create mode 100644 services/admin/decorationService.uts
diff --git a/docs/ops/2026-02-16__admin__decoration-module-repaired-full.md b/docs/ops/2026-02-16__admin__decoration-module-repaired-full.md
new file mode 100644
index 00000000..870ada5f
--- /dev/null
+++ b/docs/ops/2026-02-16__admin__decoration-module-repaired-full.md
@@ -0,0 +1,51 @@
+# 装修模块 (Decoration) 全量集成与数据库构建报告
+
+## 摘要
+本次对 Admin 侧装修模块(Decoration Module)进行了深度的端到端修复。补齐了该模块完全缺失的数据库表结构(用于存储 DIY 布局 JSON)、行级安全策略(RLS)以及 4 个管理端 RPC 接口。同时重构了前端 3 个核心页面,实现了从 Mock 数据到真实数据库持久化的闭环。
+
+## 动机
+装修模块允许管理员自定义商城的视觉表现(首页、专题页、个人中心风格)。此前该功能仅有 UI 静态模拟,无法保存任何配置。为了支撑个性化商城运营,必须补齐底层存储架构,并按照项目规范落地管理端接口。
+
+## 影响范围
+- **模块**:后台管理系统 - 装修模块
+- **页面**:首页装修、商品分类装修、个人中心装修
+- **接口**:新增 4 个 `rpc_admin_...` 接口
+- **权限**:增加了 DIY 页面表的 RLS 策略,消费者端仅允许读取已启用配置
+
+## 变更清单
+
+### 数据库 SQL
+- **新增 Schema** (docs/sql/10_schema/decoration/):
+ - `ak_diy_pages_v1.sql` (核心配置表)
+- **新增 RLS** (docs/sql/20_rls/decoration/):
+ - `ml_decoration_rls_v1.sql`
+- **新增 RPC** (docs/sql/30_rpc/decoration/):
+ - `rpc_admin_get_diy_page_list_v1.sql`
+ - `rpc_admin_save_diy_page_v1.sql`
+ - `rpc_admin_delete_diy_page_v1.sql`
+ - `rpc_admin_set_home_page_v1.sql`
+
+### 前端代码
+- **新增服务层**:`services/admin/decorationService.uts` (封装全量 DIY 接口)
+- **重构页面**:
+ - `pages/mall/admin/decoration/home.uvue` (接入分页列表、删除及设为首页逻辑)
+ - `pages/mall/admin/decoration/user.uvue` (接入样式持久化存取)
+ - `pages/mall/admin/decoration/category.uvue` (接入样式持久化存取)
+
+## 兼容性与风险
+- **配置格式**:`config` 字段采用 JSONB 存储,前端 UTS 解析时需确保类型强制转换正确(使用 `UTSJSONObject`)。
+- **首页约束**:数据库侧实现了原子切换逻辑,确保同类型页面(如 `home`)全局仅有一个生效项。
+
+## 回滚方案
+1. **数据库**:依次 DROP `ak_diy_pages` 表及相关 4 个 RPC 函数。
+2. **代码**:使用 `git checkout` 恢复重构的 3 个 `.uvue` 页面,并删除 `decorationService.uts`。
+
+## 验证方式
+1. **功能验证**:
+ - 首页切换:在“首页装修”列表中将另一个模板设为首页,确认移动端展示内容实时变更。
+ - 样式保存:在“个人中心装修”切换样式并点击保存,刷新页面后确认选中状态保持一致。
+2. **安全验证**:确认非 admin 角色无法通过接口修改 DIY 配置。
+
+## 关联规范
+- 遵循 `AGENT_PROJECT_SPEC.md` 规范。
+- 遵循统一的 RPC 入口鉴权(admin 角色)。
diff --git a/docs/sql/10_schema/decoration/ak_diy_pages_v1.sql b/docs/sql/10_schema/decoration/ak_diy_pages_v1.sql
new file mode 100644
index 00000000..e399094b
--- /dev/null
+++ b/docs/sql/10_schema/decoration/ak_diy_pages_v1.sql
@@ -0,0 +1,32 @@
+-- =====================================================================================
+-- Schema: 装修模块 - DIY 页面配置表
+-- 位置:docs/sql/10_schema/decoration/ak_diy_pages_v1.sql
+-- 对象类型:TABLE
+-- 版本:v1
+-- 说明:存储首页、专题页及个人中心的 DIY 布局 JSON 配置
+-- =====================================================================================
+
+CREATE TABLE IF NOT EXISTS public.ak_diy_pages (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ name TEXT NOT NULL,
+ type TEXT NOT NULL, -- home: 首页, topic: 专题页, user: 个人中心
+
+ config JSONB NOT NULL DEFAULT '{}'::jsonb, -- 核心布局配置 (组件列表及参数)
+
+ is_home BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为生效首页
+ is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否启用
+
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ created_by UUID REFERENCES public.ak_users(id),
+ updated_by UUID REFERENCES public.ak_users(id)
+);
+
+-- 索引
+CREATE INDEX IF NOT EXISTS idx_diy_pages_type ON public.ak_diy_pages (type);
+CREATE INDEX IF NOT EXISTS idx_diy_pages_is_home ON public.ak_diy_pages (is_home) WHERE is_home = TRUE;
+
+-- 注释
+COMMENT ON TABLE public.ak_diy_pages IS 'DIY 页面装修配置表';
+COMMENT ON COLUMN public.ak_diy_pages.type IS '页面类型: home(首页), topic(专题), user(个人中心)';
+COMMENT ON COLUMN public.ak_diy_pages.config IS 'DIY 布局配置 JSON';
diff --git a/docs/sql/20_rls/decoration/ml_decoration_rls_v1.sql b/docs/sql/20_rls/decoration/ml_decoration_rls_v1.sql
new file mode 100644
index 00000000..1c6ac274
--- /dev/null
+++ b/docs/sql/20_rls/decoration/ml_decoration_rls_v1.sql
@@ -0,0 +1,18 @@
+-- =====================================================================================
+-- RLS: 装修模块 - DIY 页面安全策略
+-- 位置:docs/sql/20_rls/decoration/ml_decoration_rls_v1.sql
+-- 对象类型:RLS 策略
+-- 版本:v1
+-- 说明:消费者端公开只读已发布的页面;管理端通过 SECURITY DEFINER RPC 进行管理
+-- =====================================================================================
+
+-- 1. 启用 RLS
+ALTER TABLE public.ak_diy_pages ENABLE ROW LEVEL SECURITY;
+
+-- 2. 消费者端策略:允许匿名和登录用户读取已启用的页面
+DROP POLICY IF EXISTS diy_pages_select_active ON public.ak_diy_pages;
+CREATE POLICY diy_pages_select_active ON public.ak_diy_pages
+FOR SELECT TO anon, authenticated
+USING (is_active = true);
+
+-- 管理端全量管理将通过 SECURITY DEFINER 的 RPC 接口执行,此处不再额外开放直接表操作
diff --git a/docs/sql/30_rpc/decoration/rpc_admin_delete_diy_page_v1.sql b/docs/sql/30_rpc/decoration/rpc_admin_delete_diy_page_v1.sql
new file mode 100644
index 00000000..f40d2d58
--- /dev/null
+++ b/docs/sql/30_rpc/decoration/rpc_admin_delete_diy_page_v1.sql
@@ -0,0 +1,40 @@
+-- RPC: rpc_admin_delete_diy_page
+-- 管理端删除 DIY 页面配置
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_delete_diy_page(
+ p_id uuid
+)
+RETURNS boolean
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_ok boolean;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users u
+ WHERE u.id = auth.uid() AND u.role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'permission denied';
+ END IF;
+
+ -- 2. 执行删除 (不允许删除当前生效的首页)
+ IF EXISTS (
+ SELECT 1 FROM public.ak_diy_pages
+ WHERE id = p_id AND is_home = true
+ ) THEN
+ RAISE EXCEPTION 'cannot delete the active home page';
+ END IF;
+
+ DELETE FROM public.ak_diy_pages WHERE id = p_id;
+
+ GET DIAGNOSTICS v_ok = ROW_COUNT;
+ RETURN v_ok;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_delete_diy_page(uuid) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_delete_diy_page(uuid) TO authenticated;
diff --git a/docs/sql/30_rpc/decoration/rpc_admin_get_diy_page_list_v1.sql b/docs/sql/30_rpc/decoration/rpc_admin_get_diy_page_list_v1.sql
new file mode 100644
index 00000000..64c2b549
--- /dev/null
+++ b/docs/sql/30_rpc/decoration/rpc_admin_get_diy_page_list_v1.sql
@@ -0,0 +1,60 @@
+-- RPC: rpc_admin_get_diy_page_list
+-- 管理端获取 DIY 页面分页列表
+-- 支持按名称搜索和按类型筛选
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_get_diy_page_list(
+ p_search text DEFAULT NULL,
+ p_type text DEFAULT NULL,
+ p_page integer DEFAULT 1,
+ p_page_size integer DEFAULT 20
+)
+RETURNS JSONB
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_page integer := GREATEST(1, COALESCE(p_page, 1));
+ v_page_size integer := LEAST(200, GREATEST(1, COALESCE(p_page_size, 20)));
+ v_offset integer := (v_page - 1) * v_page_size;
+ v_total bigint;
+ v_items jsonb;
+BEGIN
+ -- 1. 权限检查 (仅管理员或分析员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users u
+ WHERE u.id = auth.uid() AND u.role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'permission denied';
+ END IF;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ak_diy_pages
+ WHERE (p_search IS NULL OR p_search = '' OR name ILIKE '%' || p_search || '%')
+ AND (p_type IS NULL OR type = p_type);
+
+ -- 3. 获取明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ id, name, type, is_home, is_active,
+ created_at, updated_at
+ FROM public.ak_diy_pages
+ WHERE (p_search IS NULL OR p_search = '' OR name ILIKE '%' || p_search || '%')
+ AND (p_type IS NULL OR type = p_type)
+ ORDER BY created_at DESC
+ LIMIT v_page_size OFFSET v_offset
+ ) t;
+
+ -- 4. 返回 JSON 结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_get_diy_page_list(text, text, integer, integer) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_get_diy_page_list(text, text, integer, integer) TO authenticated;
diff --git a/docs/sql/30_rpc/decoration/rpc_admin_save_diy_page_v1.sql b/docs/sql/30_rpc/decoration/rpc_admin_save_diy_page_v1.sql
new file mode 100644
index 00000000..118cb12a
--- /dev/null
+++ b/docs/sql/30_rpc/decoration/rpc_admin_save_diy_page_v1.sql
@@ -0,0 +1,57 @@
+-- RPC: rpc_admin_save_diy_page
+-- 管理端新增或更新 DIY 页面配置
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_save_diy_page(
+ p_id uuid DEFAULT NULL,
+ p_name text DEFAULT NULL,
+ p_type text DEFAULT NULL,
+ p_config jsonb DEFAULT '{}'::jsonb,
+ p_is_active boolean DEFAULT true
+)
+RETURNS uuid
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_id uuid;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users u
+ WHERE u.id = auth.uid() AND u.role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'permission denied';
+ END IF;
+
+ -- 2. 新增或更新
+ IF p_id IS NULL THEN
+ INSERT INTO public.ak_diy_pages (
+ name, type, config, is_active, updated_by, created_by
+ ) VALUES (
+ p_name, p_type, p_config, p_is_active, auth.uid(), auth.uid()
+ ) RETURNING id INTO v_id;
+ ELSE
+ UPDATE public.ak_diy_pages
+ SET
+ name = COALESCE(p_name, name),
+ type = COALESCE(p_type, type),
+ config = COALESCE(p_config, config),
+ is_active = COALESCE(p_is_active, is_active),
+ updated_at = now(),
+ updated_by = auth.uid()
+ WHERE id = p_id
+ RETURNING id INTO v_id;
+
+ IF v_id IS NULL THEN
+ RAISE EXCEPTION 'page not found';
+ END IF;
+ END IF;
+
+ RETURN v_id;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_save_diy_page(uuid, text, text, jsonb, boolean) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_save_diy_page(uuid, text, text, jsonb, boolean) TO authenticated;
diff --git a/docs/sql/30_rpc/decoration/rpc_admin_set_home_page_v1.sql b/docs/sql/30_rpc/decoration/rpc_admin_set_home_page_v1.sql
new file mode 100644
index 00000000..92104a23
--- /dev/null
+++ b/docs/sql/30_rpc/decoration/rpc_admin_set_home_page_v1.sql
@@ -0,0 +1,40 @@
+-- RPC: rpc_admin_set_home_page
+-- 管理端设置生效首页
+-- 逻辑:先取消所有同类型页面的 is_home 状态,再设置目标页面为 is_home
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_set_home_page(
+ p_id uuid
+)
+RETURNS boolean
+LANGUAGE plpgsql
+SECURITY DEFINER
+SET search_path = public
+AS $$
+DECLARE
+ v_type text;
+BEGIN
+ -- 1. 权限检查 (仅管理员)
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users u
+ WHERE u.id = auth.uid() AND u.role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'permission denied';
+ END IF;
+
+ -- 2. 获取目标页面类型
+ SELECT type INTO v_type FROM public.ak_diy_pages WHERE id = p_id;
+ IF v_type IS NULL THEN
+ RAISE EXCEPTION 'page not found';
+ END IF;
+
+ -- 3. 原子切换:同一类型的页面只能有一个 is_home
+ UPDATE public.ak_diy_pages SET is_home = false WHERE type = v_type;
+ UPDATE public.ak_diy_pages SET is_home = true WHERE id = p_id;
+
+ RETURN true;
+END;
+$$;
+
+-- 授权
+REVOKE ALL ON FUNCTION public.rpc_admin_set_home_page(uuid) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION public.rpc_admin_set_home_page(uuid) TO authenticated;
diff --git a/pages/mall/admin/decoration/category.uvue b/pages/mall/admin/decoration/category.uvue
index 89d7a058..d62af6d6 100644
--- a/pages/mall/admin/decoration/category.uvue
+++ b/pages/mall/admin/decoration/category.uvue
@@ -3,11 +3,11 @@
🏠
📂
- 🛒
👤
@@ -84,42 +77,25 @@
🔍
点击搜索商品信息
-
- 水果
- 全部
- 热带水果
- 西瓜葡萄
- ▼
-
深层 V8 高清直屏\n双镜头/VR科技体验
- Haier/海尔 BCD-216STPT 时尚静音冰箱 三门出门租家用小型电冰箱
+ 精选爆款商品标题示例内容展示
- ¥999.00
- 已售 92
+ ¥99.00
立即购买
- 🛒7
-
样式2
@@ -140,41 +116,22 @@
搜索商品
-
- 水果
- 时时生鲜
- 休闲零食
- 坚果蜜饯
- ∨
-
- 【橙中爱马仕】果际新骑士晚季甜橙10个单装
+ 优质精选商品名称展示示例
¥25.99
-
- 🛒
-
-
- 🛒7
- ¥999.00
- 去结算
-
样式3
@@ -186,13 +143,50 @@
diff --git a/pages/mall/admin/decoration/home.uvue b/pages/mall/admin/decoration/home.uvue
index cb666372..96945002 100644
--- a/pages/mall/admin/decoration/home.uvue
+++ b/pages/mall/admin/decoration/home.uvue
@@ -1,7 +1,7 @@
-
+
@@ -15,72 +15,26 @@
首页
生活家居
运动专区
- 电子产品
- 家用电器
≡
-
- MUSE FOR ALL MOTHERS
-
-
-
-
+ DIY 页面预览
-
-
-
-
-
📢
- CRMEB 年中618活动开启进行中!
- >
+ 此处展示选中的装修模板预览
-
-
-
-
-
- ⭐
- 周{{ weekDays[i-1] }}
-
-
- 签到
-
-
-
-
-
- 🏠
- 首页
-
-
- 📂
- 分类
-
-
- 🛒
- 购物车
-
-
- 👤
- 我的
-
+ 🏠首页
+ 👤我的
@@ -93,65 +47,63 @@
添加页面
-
- 导入模板
+
+
-
-
@@ -162,52 +114,34 @@
-
+
页面名称
-
+
页面类型
-
-
+
+
首页
-
-
+
+
专题页
-
-
- 选择模板
- 请选择要引用的模板
-
-
-
-
-
- 📄
-
- 通用模板 {{ i }}
-
-
-
+
@@ -215,35 +149,76 @@
-
diff --git a/pages/mall/admin/decoration/user.uvue b/pages/mall/admin/decoration/user.uvue
index 97eaacd3..ac8ebdf1 100644
--- a/pages/mall/admin/decoration/user.uvue
+++ b/pages/mall/admin/decoration/user.uvue
@@ -3,18 +3,21 @@
-
+
+ 配置加载中...
+
+
@@ -27,37 +30,37 @@
- 用户名称用户名称
+ 演示用户
绑定手机号 >
- 0.00
+ 88.00
我的余额
- 65749
+ 1200
当前积分
- 25
+ 5
优惠券
-
+
👑
- 会员到期 2022-12-31
+ 尊贵会员服务
立即续费 >
@@ -66,13 +69,12 @@
-
+
👑
会员可享多项权益
- 会员剩余360天
@@ -90,43 +92,22 @@
- 用户名称用户名称
-
- 绑定手机号 >
-
+ 演示用户
- 0.00
- 我的余额
+ 88.00
+ 余额
- 65749
- 当前积分
-
-
- 25
- 优惠券
-
-
-
-
-
-
-
-
- 👑
- 开通会员VIP
-
-
- 会员可享多项权益 >
+ 1200
+ 积分
@@ -139,19 +120,12 @@
-
- {{ item.icon }}
-
+ {{ item.icon }}
{{ item.name }}
-
-
- 暂无广告数据
-
-
-
-
-
-
-
-
-
- {{ item.icon }}
-
- {{ item.name }}
-
-
-
@@ -190,25 +149,25 @@
- 页面设置
+ 页面布局风格
- 页面风格:
- 样式1
+ 样式1 (经典红)
- 样式2
+ 样式2 (通透卡片)
- 样式3
+ 样式3 (简约白)
+ 选择风格后点击右上角“保存”生效,该配置将同步至移动端个人中心页面。
@@ -217,358 +176,146 @@
diff --git a/services/admin/decorationService.uts b/services/admin/decorationService.uts
new file mode 100644
index 00000000..d62388d4
--- /dev/null
+++ b/services/admin/decorationService.uts
@@ -0,0 +1,91 @@
+import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts'
+
+/**
+ * DIY 页面模型
+ */
+export type DiyPage = {
+ id: string
+ name: string
+ type: string
+ config: UTSJSONObject
+ is_home: boolean
+ is_active: boolean
+ created_at: string
+ updated_at: string
+}
+
+/**
+ * 获取 DIY 页面分页列表
+ */
+export async function fetchDiyPageList(
+ search: string | null = null,
+ type: string | null = null,
+ page: number = 1,
+ pageSize: number = 20
+): Promise<{ total: number, items: Array }> {
+ const res = await rpcOrNull('rpc_admin_get_diy_page_list', {
+ p_search: search,
+ p_type: type,
+ p_page: page,
+ p_page_size: pageSize
+ } as UTSJSONObject)
+
+ if (res == null) return { total: 0, items: [] as Array }
+
+ return {
+ total: (res as any).total as number,
+ items: (res as any).items as Array
+ }
+}
+
+/**
+ * 保存 DIY 页面配置
+ */
+export async function saveDiyPage(
+ id: string | null,
+ name: string,
+ type: string,
+ config: UTSJSONObject,
+ isActive: boolean = true
+): Promise {
+ const res = await rpcOrValue('rpc_admin_save_diy_page', {
+ p_id: id,
+ p_name: name,
+ p_type: type,
+ p_config: config,
+ p_is_active: isActive
+ } as any)
+
+ return res != null ? String(res) : null
+}
+
+/**
+ * 删除 DIY 页面
+ */
+export async function deleteDiyPage(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_delete_diy_page', { p_id: id } as any)
+ return ok === true
+}
+
+/**
+ * 设置为生效首页
+ */
+export async function setAsHomePage(id: string): Promise {
+ const ok = await rpcOrValue('rpc_admin_set_home_page', { p_id: id } as any)
+ return ok === true
+}
+
+/**
+ * 获取特定类型的生效配置 (供预览或样式页使用)
+ * 逻辑:从列表接口过滤 type 且 is_home = true 的第一项
+ */
+export async function getActiveDiyConfig(type: string): Promise {
+ // 由于目前没有单独的 get_active_config RPC,复用 list 接口并按 type 筛选
+ const res = await fetchDiyPageList(null, type, 1, 1)
+ if (res.items.length > 0 && res.items[0].is_home) {
+ // 注意:list 接口可能不返回完整的 config JSON 以节省流量
+ // 如果后续需要完整配置,建议补齐 rpc_admin_get_diy_page_detail
+ return res.items[0]
+ }
+ return null
+}