diff --git a/docs/developer-guide/_meta.json b/docs/developer-guide/_meta.json index f72e04ef..f267a81a 100644 --- a/docs/developer-guide/_meta.json +++ b/docs/developer-guide/_meta.json @@ -22,6 +22,12 @@ "label": "主题开发", "collapsed": true }, + { + "type": "dir", + "name": "shop", + "label": "商城开发", + "collapsed": true + }, { "type": "dir", "name": "restful-api", diff --git a/docs/developer-guide/index.md b/docs/developer-guide/index.md index b7dc0130..709754bf 100644 --- a/docs/developer-guide/index.md +++ b/docs/developer-guide/index.md @@ -1,5 +1,5 @@ --- title: 开发者指南 -description: Halo 开发者指南入口,涵盖核心系统、插件与主题开发、RESTful API、应用市场发布,以及 FormKit 表单和模型元数据扩展。 +description: Halo 开发者指南入口,涵盖核心系统、插件与主题开发、商城开发、RESTful API、应用市场发布,以及 FormKit 表单和模型元数据扩展。 overview: true --- diff --git a/docs/developer-guide/shop/_meta.json b/docs/developer-guide/shop/_meta.json new file mode 100644 index 00000000..59d5598c --- /dev/null +++ b/docs/developer-guide/shop/_meta.json @@ -0,0 +1,13 @@ +[ + { + "type": "file", + "name": "index", + "label": "内容导航" + }, + { + "type": "dir", + "name": "theme", + "label": "主题开发", + "collapsed": false + } +] diff --git a/docs/developer-guide/shop/index.md b/docs/developer-guide/shop/index.md new file mode 100644 index 00000000..f2baad0b --- /dev/null +++ b/docs/developer-guide/shop/index.md @@ -0,0 +1,11 @@ +--- +title: 商城开发 +description: Halo 商城版开发指南,涵盖商城主题适配、模板与数据、前台交互。 +overview: true +--- + +:::note 仅限 Halo 商城版 +本板块面向 [Halo 商城版](../../guide/prepare.md#发行版本)。主题开发文档以 **Halo 商城版 2.27** 为基线;使用其他版本时,请同时核对模板分支和对应版本的 API。 +::: + +商城主题沿用 Halo 的主题机制,可以复用内置商城模板,也可以自行实现商品展示和交易界面。从[主题开发概述](./theme/overview.md)开始,选择适合的开发方式。 diff --git a/docs/developer-guide/shop/theme/_meta.json b/docs/developer-guide/shop/theme/_meta.json new file mode 100644 index 00000000..fdb78f58 --- /dev/null +++ b/docs/developer-guide/shop/theme/_meta.json @@ -0,0 +1,13 @@ +[ + "overview", + "quick-start", + "template-reuse", + "pages-and-variables", + "finder-apis", + "components", + "styling", + "source-development", + "storefront-api", + "testing", + "api-changelog" +] diff --git a/docs/developer-guide/shop/theme/api-changelog.md b/docs/developer-guide/shop/theme/api-changelog.md new file mode 100644 index 00000000..66349767 --- /dev/null +++ b/docs/developer-guide/shop/theme/api-changelog.md @@ -0,0 +1,36 @@ +--- +title: 版本差异 +description: Halo 商城主题开发相关的 Finder 返回结构、商品永久链接、订阅和游客交易流程版本说明。 +--- + +本页记录与商城主题适配直接相关的变化。本系列代码示例以 **Halo 商城版 2.27** 为基线;完整接口模型见 [2.27 商城版 API](https://api.halo.run/?version=2.27-paid)。 + +## Halo 商城版 2.27 + +- 新增基于 handle 的商品详情入口 `/shop/products/{handle}`,商品响应提供 `permalink`。旧数字 ID 入口 `/shop/product/{id}` 在商品有 handle 时重定向到永久链接。 +- `productFinder` 新增 `getByHandle(handle)`。 +- 新增订阅商品和 `subscriptionFinder`。`/shop/subscription/{handle}` 复用 `shop/product.html`,内置 `shop-buy-box` 根据商品类型展示订阅计划。 +- 前台交易流程支持启用游客结算后的会话购物车、结算、订单访问和访问邮件找回。主题应使用匹配版本的组件与接口,处理游客邮箱、访问凭据和订单锁定状态。 +- 订单详情由 `shop/order.html` 渲染,需处理 `locked`、`order`、`fulfillments`、`canPay`、`canCancel` 等变量。 + +从旧版模板升级时,重点复查商品链接、购买组件、结算请求地址、订单权限恢复和支付状态处理。复制到主题中的模板不会随 Halo 升级自动更新。 + +## Halo 商城版 2.23.2 + +`productFinder.list(params)` 的返回值从 2.23.0、2.23.1 中的 `Page` 改为 `ListResult`: + +- 商品集合由 `content` 改为 `items`。 +- 返回页码由 `number`(从 0 开始)改为 `page`(从 1 开始)。 +- 总条数由 `totalElements` 改为 `total`。 + +`productCategoryFinder.list(params)` 仍返回 `Page`,不要对分类列表应用同样的字段替换。 + +## Halo 商城版 2.23 + +提供 `productFinder.list(params)` 和 `productCategoryFinder.list(params)`,用于在主题中分页查询商品与分类。 + +## 选择对应源码 + +从 [shop-templates 分支列表](https://github.com/lxware-dev/shop-templates/branches)选择与目标商城版本对应的 release 分支。`main` 为开发分支,可能包含暂未发布的功能。 + +主题采用源码二次开发时,模板、UI、客户端类型和后端需要作为一组验证。通用 Halo 主题能力的变化另见[主题 API 变更记录](../../theme/api-changelog.md)。 diff --git a/docs/developer-guide/shop/theme/components.md b/docs/developer-guide/shop/theme/components.md new file mode 100644 index 00000000..344e1342 --- /dev/null +++ b/docs/developer-guide/shop/theme/components.md @@ -0,0 +1,85 @@ +--- +title: 使用商城组件 +description: 在主题中挂载 Halo 商城购买、购物车、结算和支付 Web Components,正确传递上下文与 CSRF 参数。 +--- + +本页以 Halo 商城版 2.27 的内置组件为基线。组件使用 Svelte 的 [Custom Elements](https://svelte.dev/docs/svelte/custom-elements) 机制,可以直接嵌入 Thymeleaf 模板。 + +## 加载组件资源 + +复用内置组件时,在页面中引入一次: + +```html + + +``` + +这会加载 Halo 提供的 `/shop-dist/style.css` 和 `/shop-dist/main.iife.js`,内置引用附带 `site.version` 用于区分版本。 + +如果使用了[源码二次开发](./source-development.md)构建的组件,应改用主题自己的资源入口,避免重复注册相同标签。 + +## 组件与参数 + +下表使用 HTML 属性名。模板中的 `th:csrfToken` 最终对应 `csrftoken`,不要改写成 `csrf-token`。 + +| 标签 | 属性 | 用途 | +| -------------------- | ----------------------------------------- | ---------------------------- | +| `shop-buy-box` | `id`、`csrftoken` | 商品购买区域;`id` 为商品 ID | +| `shop-page-cart` | `csrftoken` | 当前顾客或游客会话的购物车 | +| `shop-page-checkout` | `contextid`、`csrftoken`、`authenticated` | 结算页 | +| `shop-page-payments` | `ordercode`、`csrftoken`、`authenticated` | 支付页 | + +`authenticated` 是字符串属性,组件以字符串 `'true'` 判断登录状态。应传入服务端变量,不要仅写一个没有值的 `authenticated` 属性。 + +### 商品购买区域 + +```html + +``` + +组件查询商品详情和 SKU 后处理规格选择、价格、数量、库存与购买入口。外链商品展示外部跳转;询价商品使用店铺的询价地址或联系方式。 + +2.27 中,订阅商品同样使用 `shop-buy-box`。组件内部查询订阅计划并展示档位、周期、试用和当前订阅状态;`SubscriptionBuyBox` 是内部组件,不是一个可直接挂载的独立 HTML 标签。 + +### 购物车 + +```html + +``` + +组件通过 `/shop/cart/items` 获取当前会话购物车。游客使用需启用游客结算;不要在模板中自行拼装其他客户的购物车数据。 + +### 结算页 + +```html + +``` + +该标签放在 `/shop/checkout/{contextId}` 对应的页面中。组件读取结算上下文,处理收货地址、备注、折扣和优惠券等交互。游客流程不使用登录用户的地址簿或客户优惠券。 + +Halo 商城版 2.27 尚未实现配送方式选择与运费计算,填写收货地址不代表支持这两项能力。 + +### 支付页 + +```html + +``` + +组件查询订单、选择支付方式、创建支付会话并检查支付状态。游客访问凭据失效时,提供邮箱找回访问链接的入口。 + +## 样式与语言 + +内置组件关闭 Shadow DOM,因此主题 CSS 可以影响其内部元素。优先通过 `--shop-*` CSS 变量调整外观,具体见[定制样式](./styling.md)。 + +内置 UI 支持 `en`、`es`、`zh-CN`、`zh-TW`。语言优先取 `language` Cookie,再取浏览器语言,未匹配时回退到英文。服务端模板文案与组件文案分别维护;只修改 `site.language` 或 `` 并不会替换全部 UI 文案。定制组件文案需要修改对应源码语言文件并重新构建。 diff --git a/docs/developer-guide/shop/theme/finder-apis.md b/docs/developer-guide/shop/theme/finder-apis.md new file mode 100644 index 00000000..86ac60cc --- /dev/null +++ b/docs/developer-guide/shop/theme/finder-apis.md @@ -0,0 +1,154 @@ +--- +title: Finder API +description: 在 Thymeleaf 中查询商城商品、分类和订阅计划,了解查询参数、返回模型与分页差异。 +--- + +商城 Finder 用于在首页、侧边栏或自定义页面中读取商品数据,不需要浏览器额外发送请求。以下示例以 Halo 商城版 2.27 为基线。 + +## productFinder.list(params) + +商品 Finder 自 Halo 商城版 2.23 起提供,**2.23.2 起返回 `ListResult`**;2.23.0 和 2.23.1 返回 `Page`,字段差异见[版本差异](./api-changelog.md#halo-商城版-2232)。2.27 支持下表中的参数。 + +### 参数 + +`params` 是一个 Map,支持: + +| 参数 | 类型 | 默认值与说明 | +| ---------------------- | ---------- | ------------------------------------------ | +| `page` | 整数 | 默认 1,从 1 开始 | +| `size` | 整数 | 默认 10,有效范围 1–999;超出范围回退到 10 | +| `sort` | 字符串列表 | 如 `{'id,desc'}`,未设置时不指定排序 | +| `keyword` | 字符串 | 商品标题关键词 | +| `categoryId` | 长整数 | 分类 ID,正数 | +| `minPrice`、`maxPrice` | 数值 | 价格筛选边界 | + +Finder 的每页数量限制与 `/shop/products` HTTP 路由的最大 100 条不同,不要混用。 + +### 返回值 + +返回 `ListResult`,只包含已发布商品。用 `items` 遍历,`page` 为从 1 开始的页码,`total` 为总条数。 + +```html +
+ +

暂无商品

+
+``` + +列表返回用于商品卡片的摘要数据。商品字段说明见[商品详情](./pages-and-variables.md#商品详情)。 + +## productFinder.getByHandle(handle) + +**自 Halo 商城版 2.27 起可用。** + +按字符串 `handle` 查询单个已发布商品,返回包含正文、图库和 SKU 的 `ProductResponse`。商品不存在、未发布或 handle 为空时无结果,模板应处理空值。 + +```html + + + +``` + +## productCategoryFinder.list(params) + +**自 Halo 商城版 2.23 起可用。** 2.27 中仍返回 `Page`。 + +### 参数 + +支持 `page`、`size`、`sort`,默认值与商品 Finder 相同;不支持商品关键词、价格或 `categoryId` 筛选。 + +### 返回值 + +分类分页与商品分页使用不同字段: + +| 含义 | 商品 `ListResult` | 分类 `Page` | +| ---------- | ----------------- | ------------------- | +| 当前页数据 | `items` | `content` | +| 当前页码 | `page`,从 1 开始 | `number`,从 0 开始 | +| 每页数量 | `size` | `size` | +| 总条数 | `total` | `totalElements` | +| 总页数 | `totalPages` | `totalPages` | + +两者的**输入参数 `page` 都从 1 开始**。分类 Finder 返回平铺分页,不等同于商品列表页的 `categoryTree`。 + +```html + +``` + +## subscriptionFinder.subscription(handle, customerId) + +**自 Halo 商城版 2.27 起可用。** + +查询已发布订阅商品的渲染数据;普通商品、未发布商品或不存在的 handle 无结果。 + +- `handle`:订阅商品的 handle。 +- `customerId`:商城客户的数字 ID,传 `null` 时仅查询公开计划信息。 + +`customerId` 不是 Halo 用户名,也不会自动取当前登录用户。主题展示公开订阅计划时传 `null` 即可;展示当前用户的订阅时,优先使用购买组件或登录态订阅商品接口,不要从查询参数读取客户 ID 传给 Finder。 + +### 返回值 + +| 字段 | 含义 | +| --------------------- | ---------------------------------------------------- | +| `product` | 商品详情 | +| `tiers` | 有已发布计划的档位列表 | +| `currentSubscription` | 指定客户的当前订阅;未传客户 ID 时为空 | +| `subscriptions` | 指定客户在该商品下的订阅列表;未传客户 ID 时为空列表 | +| `singleSubscription` | 该商品是否采用单订阅策略 | + +`tiers` 中每项包含 `id`、`name`、`handle`、`sortOrder`、`entitlements` 和 `plans`。计划包含 `id`、`variantId`、`name`、`price`、`billingMode`、`billingPeriod`、`periodCount`、试用与数量限制等信息。完整模型可查阅 [2.27 商城版 API](https://api.halo.run/?version=2.27-paid) 中的 `SubscriptionPlanResponse`。 + +```html + +
+

+
+

+
    +
  • + + +
  • +
+
+ 选择订阅计划 +
+
+``` + +上例只展示公开计划价格。试用资格、续费、升级补差价和数量限制应交给商城交易流程处理。 + +## subscriptionFinder.list(params) + +**自 Halo 商城版 2.27 起可用。** 支持 `page`、`size`,默认分别为 1 和 10,`size` 有效范围为 1–999。返回 `ListResult`,每项结构与上节相同,且不包含客户订阅数据。 + +:::warning 2.27 的分页限制 +此方法先获取一页已发布商品,再筛选其中的订阅商品。返回数量可能少于 `size`,`total` 为该页筛选后的数量,不能用来计算全站订阅商品总数或可靠的连续分页。如果需要展示确定的订阅商品入口,优先逐个按 handle 查询。 +::: + +它不支持商品 Finder 的关键词、价格、分类与排序参数。 diff --git a/docs/developer-guide/shop/theme/index.md b/docs/developer-guide/shop/theme/index.md new file mode 100644 index 00000000..e6f1b7ce --- /dev/null +++ b/docs/developer-guide/shop/theme/index.md @@ -0,0 +1,5 @@ +--- +title: 商城主题开发 +description: Halo 商城主题开发文档索引,包含模板适配、页面变量、Finder API、Web Components、样式和交易流程。 +overview: true +--- diff --git a/docs/developer-guide/shop/theme/overview.md b/docs/developer-guide/shop/theme/overview.md new file mode 100644 index 00000000..249ad49c --- /dev/null +++ b/docs/developer-guide/shop/theme/overview.md @@ -0,0 +1,42 @@ +--- +title: 开发概述 +description: 了解 Halo 商城主题的渲染方式、模板回退机制、开发路径与版本兼容要求。 +--- + +商城主题是普通 Halo 主题的一部分。你可以在现有主题的 `templates/shop/` 中添加商城页面,也可以开发以商城为主的完整主题。主题负责商品展示和顾客交互,商品、订单、支付与履约由 Halo 商城版提供。 + +本系列以 **Halo 商城版 2.27** 为基线。首次开发主题时,请先了解[主题目录结构](../../theme/structure.md)、[主题配置](../../theme/config.md)和 [Thymeleaf](../../theme/thymeleaf.md)。 + +## 页面如何渲染 + +Halo 商城版内置了 [shop-templates](https://github.com/lxware-dev/shop-templates) 提供的商城模板。访问商城时,优先使用当前主题中的同名模板;主题未提供的模板继续使用内置版本。 + +内置模板采用两种方式组合渲染: + +- **Thymeleaf**:在服务器端输出商品列表、商品详情正文和订单详情,便于展示内容和搜索引擎读取公开商品页面。 +- **Web Components**:在浏览器中处理购买规格、购物车、结算和支付等交互。内置组件由 Svelte 编译,使用这些组件的主题不需要采用 Svelte。 + +商品详情同时包含服务端商品内容和浏览器端购买组件。订单详情是 Thymeleaf 页面,不需要寻找一个 `shop-page-order` 组件。 + +## 选择开发方式 + +| 需求 | 方式 | 需要维护的内容 | +| ---------------------------------- | ---------------------------------------------------------------- | ------------------------------- | +| 只调整颜色、圆角或内容宽度 | [定制样式](./styling.md) | CSS 变量和少量样式覆盖 | +| 加入主题导航、页脚,统一页面布局 | [复用模板](./template-reuse.md) | 页面外壳和被覆盖的模块 | +| 自定义商品列表或详情,保留交易交互 | [页面与变量](./pages-and-variables.md) + [组件](./components.md) | 自定义 HTML、数据展示及组件挂载 | +| 大幅调整交易页面结构与交互 | [源码二次开发](./source-development.md) | UI 源码、构建产物及 API 兼容性 | +| 使用其他前端框架实现商城 | [前台接口与交易流程](./storefront-api.md) | 完整交易流程与错误处理 | + +通常可以先复用内置模板,再逐页替换确有定制需求的部分。未覆盖的模块能够继续使用 Halo 升级带来的更新;复制进主题的模板和 UI 源码则需要自行同步。 + +## 版本与参考资料 + +`shop-templates` 的 `main` 是开发分支,可能包含 Halo 商城版暂未发布的功能。开发前从[分支列表](https://github.com/lxware-dev/shop-templates/branches)选择与目标 Halo 商城版对应的 release 分支,不要将最新版源码与旧版后端直接混用。 + +- [基础示例主题](https://github.com/lxware-dev/theme-shop-starter):复用内置模板。 +- [二次开发示例主题](https://github.com/lxware-dev/theme-shop-advanced-starter):在主题中集成商城 UI 源码。 +- [Halo 商城版 2.27 RESTful API](https://api.halo.run/?version=2.27-paid):查询请求字段、响应模型与接口分组。 +- [版本差异](./api-changelog.md):查看本系列涉及的重要模板和 API 变化。 + +接下来按照[快速开始](./quick-start.md)完成第一个页面。 diff --git a/docs/developer-guide/shop/theme/pages-and-variables.md b/docs/developer-guide/shop/theme/pages-and-variables.md new file mode 100644 index 00000000..74d9b32c --- /dev/null +++ b/docs/developer-guide/shop/theme/pages-and-variables.md @@ -0,0 +1,149 @@ +--- +title: 页面与模板变量 +description: Halo 商城页面路由、模板文件、商品分页筛选、详情字段,以及结算和订单页面的变量说明。 +--- + +本页以 Halo 商城版 2.27 为基线。商品 handle 路由、订阅页面和游客订单支持自 2.27 起可用;早期版本差异见[版本差异](./api-changelog.md)。 + +## 页面路由 + +以下模板路径均相对于主题的 `templates/`: + +| 访问路径 | 模板 | 页面专属变量 | +| ----------------------------------------------------------------- | ------------------------------------ | -------------------------------------------------- | +| `/shop` | 无 | 重定向到 `/shop/products` | +| `/shop/products` | `shop/products.html` | `productPage`、`categoryTree` | +| `/shop/products/{handle}` | `shop/product.html` | `product` | +| `/shop/product/{id}` | `shop/product.html` | 兼容数字 ID 入口;有 handle 时重定向到商品永久链接 | +| `/shop/subscription/{handle}` | `shop/product.html` | `product`;仅接受已发布的订阅商品 | +| `/shop/cart` | `shop/cart.html` | 无购物车数据变量,由组件请求数据 | +| `/shop/checkout/{contextId}` | `shop/checkout.html` | `context`、`paymentMethods`、`authenticated` | +| `/shop/order/{orderCode}/payments` | `shop/payments.html` | `orderCode`、`authenticated` | +| `/shop/order/{orderCode}` | `shop/order.html` | 见下方订单页面说明 | +| `/shop/order/{orderCode}/payment-sessions/{sessionCode}/redirect` | `shop/payments/alipay-redirect.html` | `redirectionData`,用于支付宝跳转 | + +这些路由由商城提供,不需要在主题中另行注册。模板存在也不代表商城已启用;关闭商城时,受商城开关控制的路由会返回 404。 + +除专属变量外,还可以使用 Halo 的[全局变量](../../theme/global-variables.mdx),如 `site` 和 `theme`。提交表单或发起写请求时使用 `${_csrf.token}`。 + +## 商品列表 + +### 筛选与分页参数 + +`GET /shop/products` 接受以下查询参数: + +| 参数 | 含义 | +| ---------------------- | ------------------------------------------------------------------- | +| `page` | 页码,从 **1** 开始 | +| `size` | 每页数量,默认 10,最大 100 | +| `sort` | 如 `minPrice,asc`、`minPrice,desc`、`title,asc`;未传时按 `id` 降序 | +| `keyword` | 商品标题关键词 | +| `categoryId` | 分类 ID | +| `minPrice`、`maxPrice` | 价格筛选边界 | + +例如 `/shop/products?keyword=键盘&sort=minPrice,asc&page=1`。切换筛选条件时应清除旧的 `page`,分页跳转时则保留当前筛选条件。 + +`productPage` 为 `ListResult`,使用 `items` 获取当前页商品,`page`、`size`、`total` 分别表示页码、每页数量和总条数;可用 `hasNext`、`hasPrevious` 判断翻页按钮状态。 + +```html +
+

没有找到商品

+
+ + +
+
+``` + +`categoryTree` 是分类树列表。每项包含 `id`、`name`、`slug`、`image`、`description`、`parentId` 和 `children` 等字段。需要展示多级分类时自行递归渲染 `children`。 + +## 商品详情 + +`product` 是 `ProductResponse`。列表与详情共用该模型,但列表数据不包含完整详情内容;正文、图库与 SKU 应从详情页变量、`getByHandle` 或详情接口获取。 + +| 字段 | 用途 | +| ------------------------------------------- | ------------------------------------------------------------ | +| `id` | 商品 ID,用于商品接口和购买组件 | +| `handle`、`permalink` | 商品标识与后端生成的前台链接,输出链接时优先使用 `permalink` | +| `title`、`description` | 商品标题与简短介绍,使用 `th:text` 输出 | +| `coverImageUrl`、`images` | 封面与详情图库;为空时显示占位内容 | +| `content` | 商品详情 HTML,可使用 `th:utext` 渲染 | +| `productType` | 商品类型,包括订阅与外链商品等 | +| `specDefinition` | 规格定义,`usedForSku` 表示是否参与 SKU 选择 | +| `productVariants` | SKU 列表,包含价格、规格、库存与购买限制 | +| `minPrice`、`maxPrice` | 商品价格区间 | +| `minOriginalPrice`、`maxOriginalPrice` | 原价区间 | +| `externalLinkUrl`、`externalLinkButtonText` | 外链商品的目标地址与按钮文案 | + +```html +

+

+ +
+``` + +示例中的购买组件需要先加载商城脚本和样式。它会根据商品类型选择普通 SKU 或订阅计划交互,主题不需要另建订阅详情模板。 + +商品 ID、SKU ID、订阅计划 ID 是不同标识:购买请求使用 SKU ID;订阅商品使用计划的 `variantId`。不要把 `product.id` 或计划 `id` 直接用作购买参数。 + +## 结算与支付页面 + +`context.id` 是当前会话的结算上下文标识,由准备结算请求创建。不能随意构造 ID,也不能从另一浏览器会话复制结算链接使用;上下文缺失或不匹配时,结算页会重定向到购物车。 + +结算页的 `paymentMethods` 是当前销售渠道的公开支付方式。内置结算组件使用 `context.id` 再请求上下文,支付页则使用 `orderCode` 请求订单和可用支付方式。 + +`authenticated` 表示当前是否登录。它是展示提示,订单操作仍由服务端校验权限。完整过程见[前台接口与交易流程](./storefront-api.md)。 + +## 订单详情与游客访问 + +订单地址支持 HTML 与 JSON 两种响应;脚本读取 JSON 时显式发送 `Accept: application/json`。 + +| 变量 | 含义 | +| --------------------- | ------------------------------------ | +| `orderCode` | 订单号 | +| `locked` | 是否需要重新获取访问凭据 | +| `order` | 订单内容,仅在 `locked=false` 时提供 | +| `fulfillments` | 履约列表,仅在 `locked=false` 时提供 | +| `authenticated` | 当前登录用户是否为订单所有者 | +| `canPay`、`canCancel` | 是否可以显示付款、取消操作 | + +自定义页面必须先处理 `locked`,再读取订单内容: + +```html +
+

请输入下单时使用的邮箱以重新获取访问链接。

+
+ + + +
+
+
+

+ +
+``` + +订单金额中,`discountAmount` 和 `couponDiscountAmount` 分别表示普通优惠与优惠券抵扣,展示汇总时需要分别处理;应付金额使用 `payableAmount`,不要在主题中重新计算。 + +履约列表可能包含快递、自提、同城配送或虚拟交付。附件下载链接使用 `/shop/order/{orderCode}/digital-resources/{publicId}/download`;卡密与数字资源只应在订单授权成功后展示。 + +## 支付跳转中间页 + +`shop/payments/alipay-redirect.html` 接收支付接口生成的 `redirectionData`。通常保留内置模板即可,覆盖时需要保留该表单 HTML 的输出与自动提交行为,避免把它转义成普通文本。 + +不要把 `/shop/checkout/success` 当作通用支付成功地址。2.27 的正常流程根据订单状态跳转到订单页或支付页,主题应遵循后端返回的跳转地址。 diff --git a/docs/developer-guide/shop/theme/quick-start.md b/docs/developer-guide/shop/theme/quick-start.md new file mode 100644 index 00000000..1702399a --- /dev/null +++ b/docs/developer-guide/shop/theme/quick-start.md @@ -0,0 +1,57 @@ +--- +title: 快速开始 +description: 准备 Halo 商城版开发环境,在主题中复用商品列表模板并验证适配结果。 +--- + +本页适用于 Halo 商城版 2.27,目标是在现有主题中完成第一个商城页面。 + +## 准备开发环境 + +1. 安装并激活 [Halo 商城版](../../../guide/shop/prepare.mdx),按[主题开发准备](../../theme/prepare.mdx)配置本地主题目录。 +2. 在控制台创建并发布测试商品,设置 SKU、价格和库存。需要测试交易时,再配置[支付方式](../../../guide/shop/payments.mdx)与[销售渠道](../../../guide/shop/sales-channels.mdx)。 +3. 激活待适配的主题,访问 `/shop/products`,确认内置页面能显示商品。 +4. 按目标 Halo 版本选择 [shop-templates 分支](https://github.com/lxware-dev/shop-templates/branches),用于查看相应模板和样式。 + +单纯复用内置模板不需要安装商城 UI 依赖或运行其构建任务。主题原有静态资源仍按自己的构建流程处理。 + +## 创建商品列表页面 + +在主题中创建 `templates/shop/products.html`: + +```html + + + + + + + + + + + +
+
+ +
+ + +``` + +这里的 `shop/modules/products` 由 Halo 提供,无需复制到主题。页面稳定后,可以把示例中的页头替换为主题自己的[布局片段](../../theme/page-layout.md)。 + +## 验证效果 + +重新访问 `/shop/products`,确认页头已经出现,商品分类、搜索、排序和分页仍能工作。点击商品后应进入详情页;此时详情页仍使用内置模板。 + +在浏览器开发者工具中检查: + +- `/shop-dist/style.css` 与 `/shop-dist/main.iife.js` 请求成功。 +- 商品链接使用后端提供的 `permalink`,能打开对应商品。 +- 控制台没有模板错误或重复注册自定义元素的错误。 + +## 继续适配 + +按相同方式创建 `product.html`、`cart.html`、`checkout.html`、`payments.html` 和 `order.html`,分别引入对应的 `shop/modules/` 模块。模板名与访问地址并不完全相同,具体对应关系见[页面与变量](./pages-and-variables.md)。 + +如果只想修改默认页面颜色和宽度,可以直接添加[样式覆盖文件](./styling.md),无需创建全部页面。 diff --git a/docs/developer-guide/shop/theme/source-development.md b/docs/developer-guide/shop/theme/source-development.md new file mode 100644 index 00000000..101dce2f --- /dev/null +++ b/docs/developer-guide/shop/theme/source-development.md @@ -0,0 +1,93 @@ +--- +title: 源码二次开发 +description: 将 shop-templates 的 Svelte UI 集成到 Halo 主题,处理 workspace 依赖、资源构建、图库与升级维护。 +--- + +当需要改变交易组件内部结构或交互时,可以把 [shop-templates](https://github.com/lxware-dev/shop-templates) 的 UI 源码集成到主题。以下以 Halo 商城版 2.27 为基线;可结合[二次开发示例主题](https://github.com/lxware-dev/theme-shop-advanced-starter)阅读。 + +## 准备源码与工具 + +需要熟悉 pnpm workspace、主题静态资源构建和 Svelte。也可以采用其他框架,但需要自行实现[前台交易流程](./storefront-api.md)。 + +先从[分支列表](https://github.com/lxware-dev/shop-templates/branches)选择与运行版本一致的 release 分支。`main` 可能包含尚未发布的功能,记录所用提交,便于以后同步更新。 + +把选定版本的 **整个 `ui/` 目录**复制到主题根目录,保留其中的配置和本地依赖包。`@halo-dev/api-client` 在该项目中通过本地 `.tgz` 引用,不能只复制 `ui/src/`,也不要直接替换为不含商城模型的客户端版本。 + +## 合并 workspace 与依赖 + +在主题的 `pnpm-workspace.yaml` 中加入 `ui`,保留主题已有配置: + +```yaml +packages: + - ui +``` + +这只是 workspace 的目录部分。还需要核对所选源码的根配置:如果 `ui` 的构建配置或依赖使用了 `vite-plus`、`catalog:`、`overrides` 等内容,将对应依赖与配置合并到主题中。不要只复制上面的目录配置就假设构建环境已经完整。 + +在主题根 `package.json` 的 `dependencies` 中加入: + +```json +{ + "@halo-dev/shop-ui": "workspace:*" +} +``` + +随后安装依赖并构建 UI: + +```bash +pnpm install +pnpm --filter @halo-dev/shop-ui run build +``` + +使用所选源码和主题声明的 Node.js、pnpm 与构建工具版本。依赖清单或锁文件变化应由包管理器生成。 + +## 接入主题资源入口 + +在主题 JavaScript 或 TypeScript 入口加入: + +```ts +import "@halo-dev/shop-ui"; +import "@halo-dev/shop-ui/dist/index.css"; +``` + +UI 包会注册购买、购物车、结算和支付组件。然后按照主题原有方式构建资源,例如: + +```bash +pnpm build +``` + +主题应按上述顺序先构建 UI,再构建引用它的主题入口。 + +资源输出位置与模板引用方式参见[主题 Vite 集成](../../theme/vite.md)。不要手动修改构建后的 JavaScript 或 CSS。 + +## 处理图库与模板 + +复制或改写需要接管的 `templates/shop/` 页面与模块,并使用主题自己的脚本和样式入口替换内置资源引用。 + +:::warning 避免重复注册组件 +加载自己构建的 `@halo-dev/shop-ui` 后,不要再加载 `shop/modules/script` 提供的内置脚本。否则两份代码会注册相同的 `shop-*` 自定义元素。 +::: + +此外,`shop-ui` 入口只注册商城组件;默认商品详情图库还使用 Swiper 和图片放大。需要保留这部分效果时,参考同版本仓库的根 `src/index.ts`,安装并初始化对应的 Swiper、medium-zoom 依赖,或替换为主题已有图库。 + +逐个检查未覆盖的页面:它们仍可能通过内置页面外壳加载内置脚本。如果采用统一布局或全局入口注册自编译组件,应确保这些页面不会再次加载内置组件。 + +## 开发与验证 + +UI 的常用命令: + +```bash +pnpm --filter @halo-dev/shop-ui run check +pnpm --filter @halo-dev/shop-ui run dev +pnpm --filter @halo-dev/shop-ui run build +``` + +UI 开发服务器便于调试组件,但其代理配置不等同于完整 Halo 主题环境。特别是 2.27 前台购物车、结算和订单请求使用 `/shop/**`,需要检查代理是否覆盖该路径;登录、Cookie、CSRF 和服务器表单跳转仍应在真实 Halo 主题页面验证。 + +## 维护改动 + +- UI 文案位于 `ui/src/i18n/locales/`,修改时同步所支持的语言。 +- 样式位于 `ui/src/styles/`,优先保留 `shop-` 前缀以减少主题冲突。 +- 价格显示需要精确小数处理,最终金额使用后端计算结果。 +- 升级时同时比较模板、组件属性、API 模型与依赖包;仅更新生成的客户端并不能保证交易流程兼容。 +- 将需要覆盖的模板和构建产物纳入主题打包规则,按[测试与发布](./testing.md)完成验证。 diff --git a/docs/developer-guide/shop/theme/storefront-api.md b/docs/developer-guide/shop/theme/storefront-api.md new file mode 100644 index 00000000..ee8b47f0 --- /dev/null +++ b/docs/developer-guide/shop/theme/storefront-api.md @@ -0,0 +1,169 @@ +--- +title: 前台接口与交易流程 +description: Halo 商城前台商品、购物车、结算、支付和游客订单接口的使用流程,以及 Cookie、CSRF 与金额处理要求。 +--- + +本页以 **Halo 商城版 2.27** 为基线,面向需要自行实现交互的主题。仅调整页面外观时,优先使用[内置组件](./components.md)。2.27 新增的游客购物车、结算与订单访问能力不能直接用于旧版后端。 + +商品、订阅和结算等 RESTful API 的请求字段与响应模型见 [Halo 商城版 2.27 RESTful API](https://api.halo.run/?version=2.27-paid),可在 **User-center API V1alpha1** 中查找 `EcommerceProductUc`、`EcommerceSubscriptionUc`、`EcommerceCheckoutUc` 等标签。该站点的 2.27 定义不包含本页列出的 `/shop/**` 页面与交易路由,这部分应结合匹配版本的 [shop-templates](https://github.com/lxware-dev/shop-templates) 使用。 + +## 选择接口与身份 + +| 接口范围 | 典型用途 | +| --------------------------------------------- | ---------------------------------------------------- | +| `/shop/**` | 浏览器前台页面、会话购物车、结算表单、订单与支付操作 | +| `/apis/uc.api.ecommerce.halo.run/v1alpha1/**` | 商品查询、订阅计划、登录用户地址簿和优惠券等 | +| Console 商城 API | 管理端操作,不应作为顾客前台的调用入口 | + +不能仅凭 `uc.api` 前缀推断接口必须登录或允许匿名,具体由接口决定。商品详情和订阅商品公开计划可供匿名访问;用户地址簿和客户优惠券依赖登录身份。 + +普通前台购物车和交易使用当前会话。店铺启用游客结算后,游客可购买支持游客流程的商品;未开启时,需要先登录。**订阅商品不支持游客购买**,应引导登录后继续。 + +## Cookie 与 CSRF + +浏览器请求应与 Halo 保持同源并携带会话 Cookie。不要把管理端 Personal Access Token 写入主题脚本。 + +普通 HTML 表单带上隐藏字段: + +```html + +``` + +JavaScript 写请求可以使用内置组件采用的 `X-XSRF-TOKEN` 请求头。下面的函数可放在商品详情模板中,调用时传入用户选中的 SKU ID 与数量: + +```html + +``` + +调用方需要显示成功或失败结果。CSRF 失效时应让用户刷新当前页面,不要复用其他会话的令牌。 + +## 查询商品与订阅计划 + +常用只读接口: + +| 方法与路径 | 用途 | +| --------------------------------------------------------------------------------- | ---------------------------------------- | +| `GET /apis/uc.api.ecommerce.halo.run/v1alpha1/products/{id}?includeVariants=true` | 获取商品详情与 SKU | +| `GET /apis/uc.api.ecommerce.halo.run/v1alpha1/subscription-products/{id}` | 获取公开订阅计划及当前登录客户的订阅信息 | +| `GET /apis/uc.api.ecommerce.halo.run/v1alpha1/ecommerceconfigs/shop` | 获取店铺询价地址和联系信息 | + +订阅商品响应包含 `plans`、`currentSubscription`、`subscriptions` 和 `singleSubscription`。游客获得公开计划,客户订阅为空。新购订阅使用所选计划的 `variantId`;切换已有订阅是另一条业务流程,不能简单当作再买一次。可以复用内置购买组件,或引导到 `/uc/shop/subscriptions/{subscriptionId}/change?planId={planId}` 处理变更。 + +## 购物车 + +2.27 的前台购物车接口同时支持登录用户和开启游客结算后的游客会话: + +| 方法与路径 | 数据 | +| ------------------------------ | --------------------------------------------------------------- | +| `GET /shop/cart/items` | 返回当前购物车商品列表 | +| `POST /shop/cart/items` | JSON:`productVariantId`、`quantity` | +| `PUT /shop/cart/items/{id}` | JSON:更新该购物车项的数量 | +| `DELETE /shop/cart/items/{id}` | 删除购物车项,成功返回 204 | +| `POST /shop/cart` | 表单:`productVariantId`、`quantity`、`_csrf`;成功跳转到购物车 | + +登录用户的购物车项 `id` 是购物车记录的 ID;游客会话使用 SKU ID 作为购物车项 `id`。两种身份都应使用 `GET /shop/cart/items` 返回的 `id` 更新或删除购物车项,不要自行推导标识。 + +## 准备结算 + +`POST /shop/checkout/prepare` 接受 `application/x-www-form-urlencoded` 表单。成功后由服务端建立会话结算上下文,并重定向到 `/shop/checkout/{contextId}`。 + +立即购买时,`source=BUY_NOW`,每一项传 SKU ID 和数量: + +```html +
+ + + + + +
+``` + +上例便于在开发环境验证表单字段。正式购买界面应把 SKU ID 输入替换为规格选择产生的隐藏字段,并显示价格、库存与数量限制。 + +购物车结算使用 `source=CART`,对应字段为 `items[0].cartItemId` 和 `items[0].quantity`;多项依次使用 `[1]`、`[2]` 等索引。`cartItemId` 使用当前购物车接口返回的 `id`,登录用户和游客都遵循这一规则。两个来源不要混用字段。 + +如果在动作地址上附加 `redirect_uri` 以便登录后返回当前页面,使用 `encodeURIComponent` 编码完整 URL,保留原页面的查询参数。 + +## 读取和提交结算 + +| 方法与路径 | 用途 | +| --------------------------------------------------------------------------- | --------------------------------------------------- | +| `GET /shop/checkout/{contextId}/context` | 读取当前会话结算上下文和计算结果 | +| `POST /shop/checkout/{contextId}/discount` | JSON `{ "code": "优惠码" }`,应用折扣 | +| `DELETE /shop/checkout/{contextId}/discount` | 移除折扣 | +| `PUT /apis/uc.api.ecommerce.halo.run/v1alpha1/checkout/{contextId}/coupons` | 登录客户应用优惠券,JSON 字段为 `customerCouponIds` | +| `POST /shop/checkout/{contextId}/submit` | 提交结算表单并创建订单 | + +结算提交同样使用表单编码。主要字段包括: + +- `items[i].productVariantId`、`items[i].quantity`。 +- `selectedAddressId`,或 `userAddress.*` 中的新地址字段。 +- `customerNotes`、`saveAsNewAddress`。 +- 游客的 `email`,以及 `_csrf`。 + +模型中还包含 `items[i].selectedShippingRateId`,但 Halo 商城版 2.27 尚未实现配送方式选择与运费计算,不应依赖该字段完成配送计费。 + +地址字段和必填约束应按 API 模型 `CheckoutSubmitRequest`、`UserAddressUpsertRequest` 处理。游客不能借用用户地址簿,提交时使用自己的联系信息;订阅商品要求登录。 + +展示金额时读取结算响应中的 `calculateResult`,优惠券可能部分成功,应同时处理 `appliedCoupons` 与 `rejectedCoupons`。重新计算失败时,保留用户已填写的表单并展示错误,不要把已有内容替换成空白页。 + +提交成功返回重定向:待支付订单进入支付页,已经支付的订单进入对应订单页。游客跳转还可能携带访问令牌,**应遵循服务端返回地址**。使用原生表单可以直接完成页面跳转;若改用 `fetch`,需要自行处理浏览器导航,不能把最终 HTML 当作 JSON 解析。 + +## 支付 + +订单创建后按以下顺序处理: + +1. `GET /shop/order/{orderCode}`,携带 `Accept: application/json` 获取订单。 +2. `GET /shop/order/{orderCode}/payment-methods`,获取该订单可用支付方式。 +3. `POST /shop/order/{orderCode}/initiate-payment`,JSON 包含 `paymentMethodId`、`returnUrl`,按需提供 `preferredResponseType`。 +4. 按响应中的支付渠道、响应类型和 `payload` 展示二维码、表单、跳转或人工付款说明。 +5. 使用返回的 `sessionCode` 请求 `GET /shop/order/{orderCode}/payment-sessions/{sessionCode}/status`,在终态停止轮询并更新订单显示。 + +不要把支付链接返回、打开支付 App 或回到 `returnUrl` 当作已付款。支付结果以服务端状态为准,人工转账类方式还需等待确认。切换支付方式产生新会话后,轮询应跟随新的 `sessionCode`,重新计算超时。 + +微信 H5、微信内置浏览器、桌面二维码和支付宝表单跳转的处理不同。没有自行实现这些分支的需求时,保留内置支付组件。 + +## 游客订单与履约 + +2.27 中,游客订单依赖下单后或邮件中的访问凭据。有效的 `access` 查询参数由服务端处理并写入订单访问 Cookie,再跳转到不含该参数的地址。主题应保留这一导航过程,不要把订单号本身视为授权凭据。 + +无权限或凭据失效时,订单 JSON 请求可能返回 **404**;HTML 订单页则可能提供 `locked=true` 的找回界面。自定义支付页应提供找回入口,而非只针对 401、403 处理。 + +| 方法与路径 | 用途 | +| ------------------------------------------------------------------- | ---------------------------- | +| `POST /shop/order/{orderCode}/access-link` | 提交下单邮箱重新获取访问邮件 | +| `POST /shop/order/{orderCode}/cancel` | 取消允许取消的订单 | +| `GET /shop/order/{orderCode}/fulfillments` | 获取履约信息 | +| `GET /shop/order/{orderCode}/digital-resources/{publicId}/download` | 通过订单授权下载数字资源 | +| `GET /shop/order/{orderCode}/items/{orderItemId}/cdks` | 获取订单项对应的卡密 | + +找回接口支持表单和 JSON。JSON 请求使用 `Accept: application/json`,成功返回 204;提示应为“如果邮箱与订单一致,访问链接已发送”,不要宣称已验证订单归属或一定发送成功。 + +订单金额、支付状态、可取消状态和履约结果都应使用服务端数据。主题只负责呈现与发起操作,不在浏览器中推导最终授权或支付成功状态。 diff --git a/docs/developer-guide/shop/theme/styling.md b/docs/developer-guide/shop/theme/styling.md new file mode 100644 index 00000000..24e95d8e --- /dev/null +++ b/docs/developer-guide/shop/theme/styling.md @@ -0,0 +1,71 @@ +--- +title: 定制样式 +description: 通过 custom-styles 模板和商城 CSS 变量定制品牌色、容器宽度、表单、暗色模式和响应式布局。 +--- + +本页以 Halo 商城版 2.27 内置模板的样式为基线。 + +## 添加样式覆盖文件 + +在主题中创建 `templates/shop/modules/custom-styles.html`。内置商城页面会在基础资源之后引用它,因此只调整外观时,无需复制所有页面: + +```html + +``` + +也可以在该文件中引入独立 CSS,路径遵循[主题静态资源](../../theme/static-resources.md)规则: + +```html + +``` + +如果你覆盖了完整页面,需要在自定义页面中保留 `shop/modules/custom-styles` 的引用,或者从自己的样式入口引入这些规则。 + +## 常用变量 + +| 变量 | 用途 | +| ---------------------------------------------------------------------------- | ---------------------------------------- | +| `--shop-color-primary`、`--shop-color-primary-content` | 主按钮背景和文字 | +| `--shop-color-primary-light` | 选中状态的浅色背景 | +| `--shop-color-accent` | 强调色及部分悬停状态 | +| `--shop-color-base-100`、`--shop-color-base-200`、`--shop-color-base-300` | 基础背景层级 | +| `--shop-color-base-content` | 基础文字颜色 | +| `--shop-gray-50` 至 `--shop-gray-900` | 文字、边框、占位和背景使用的灰阶 | +| `--shop-color-success`、`--shop-color-warning`、`--shop-color-error` | 状态颜色 | +| `--shop-border-radius`、`--shop-border-radius-sm`、`--shop-border-radius-lg` | 圆角 | +| `--shop-spacing-xs` 至 `--shop-spacing-xl` | 间距 | +| `--shop-font-family`、`--shop-font-size-base` | 字体与基础字号 | +| `--shop-container-max` | `.shop-entry` 内容最大宽度,默认 `80rem` | +| `--shop-sticky-offset` | 吸顶区域距离顶部的偏移,默认 `100px` | + +完整变量见匹配版本源码中的 `ui/src/styles/_vars.scss`,模块样式位于 `ui/src/styles/`。 + +## 避免样式冲突 + +商城组件没有 Shadow DOM 隔离。主题中针对 `button`、`input`、`table`、`img` 的全局规则会影响商城,应在实际页面检查。 + +需要精确覆盖时,使用 `.shop-entry` 或商城组件标签限定范围。例如: + +```css +.shop-entry .shop-product-card__image img { + object-fit: contain; +} +``` + +结构类名会随模板版本变化,升级时需要复查。不要依赖生成的 Svelte 类名,也不要给所有按钮或输入框增加全站覆盖规则。 + +## 暗色模式与窄屏 + +内置变量以浅色为基线。支持暗色模式的主题应在自己的暗色选择器下同时调整背景、文字、灰阶、边框和状态色;只改变背景可能导致文字或表单不可读。 + +针对固定头部调整 `--shop-sticky-offset`,并在窄屏检查:商品图库、规格按钮、购物车数量输入、地址表单、支付二维码、订单金额和长卡密均可正常查看。避免通过固定宽度限制整个结算区域。 diff --git a/docs/developer-guide/shop/theme/template-reuse.md b/docs/developer-guide/shop/theme/template-reuse.md new file mode 100644 index 00000000..80141961 --- /dev/null +++ b/docs/developer-guide/shop/theme/template-reuse.md @@ -0,0 +1,73 @@ +--- +title: 复用与覆盖模板 +description: 通过同名模板覆盖、Thymeleaf 模块复用和主题布局组合,逐步适配 Halo 商城页面。 +--- + +本页以 Halo 商城版 2.27 的内置模板为基线。 + +## 页面与模块的关系 + +主题内的商城文件可以按以下方式组织;只需创建要覆盖的文件: + +```text +templates/ +└── shop/ + ├── products.html + ├── product.html + ├── cart.html + ├── checkout.html + ├── payments.html + ├── order.html + ├── payments/ + │ └── alipay-redirect.html + └── modules/ + ├── custom-styles.html + └── products.html +``` + +`shop/products.html` 是完整页面,`shop/modules/products.html` 是商品列表内容模块。覆盖完整页面适合添加主题布局;覆盖内容模块适合改变列表或表单的 HTML 结构。 + +| 模块路径 | 用途 | +| ---------------------------- | ----------------------------------------------- | +| `shop/modules/style` | 引入内置商城 CSS | +| `shop/modules/script` | 引入内置 JavaScript,注册商城组件和图库相关功能 | +| `shop/modules/custom-styles` | 在内置样式之后追加主题样式 | +| `shop/modules/products` | 商品筛选、列表和分页 | +| `shop/modules/product` | 商品图库、介绍、购买组件和正文 | +| `shop/modules/cart` | 挂载购物车组件 | +| `shop/modules/checkout` | 挂载结算组件 | +| `shop/modules/payments` | 挂载支付组件 | +| `shop/modules/order` | 订单状态、金额、履约信息和访问链接找回表单 | + +## 复用内置模块 + +在主题页面中使用 Thymeleaf 的模板表达式: + +```html + + + +``` + +其中商品详情模块需要 `product` 变量,应在商品详情页中使用。在首页插入这个模块时,需要先通过 [Finder API](./finder-apis.md)查询商品并显式提供 `product`。 + +购物车、结算、支付模块也有各自的数据要求,不能只复制标签到任意页面就获得完整上下文。参见[组件参数](./components.md)。 + +## 覆盖一个内容模块 + +例如,创建主题内的 `templates/shop/modules/products.html` 后,内置商品列表页和主题自己的商品列表页都会优先引用该文件。你可以使用 `productPage` 和 `categoryTree` 自行渲染列表,其余页面保持内置实现。 + +:::warning 避免模板递归 +不要在主题的 `shop/modules/products.html` 中再次执行 `th:replace="~{shop/modules/products}"`。同名解析会重新命中自身,无法借此引用被覆盖的内置版本。需要保留原内容时,从匹配版本的模板复制后修改,或仅覆盖页面外壳。 +::: + +## 接入主题布局 + +将内置模块放入主题现有的内容插槽,并按主题约定引入商城 CSS 和脚本。接入时检查: + +- 布局中的 ``、`` 和 `` 各保留一份,内容模块不要再套完整 HTML 文档。 +- 商城脚本和组件只注册一次,不要同时加载内置脚本和自己打包的同名组件。 +- 自定义页面仍引用 `custom-styles`,或者通过主题自己的 CSS 入口加载商城覆盖样式。 +- 主题的全局按钮、表单、图片和排版规则可能影响商城组件,需要验证交易页面。 + +CSS 变量和布局调整见[定制样式](./styling.md)。 diff --git a/docs/developer-guide/shop/theme/testing.md b/docs/developer-guide/shop/theme/testing.md new file mode 100644 index 00000000..d5afa019 --- /dev/null +++ b/docs/developer-guide/shop/theme/testing.md @@ -0,0 +1,60 @@ +--- +title: 测试与发布 +description: Halo 商城主题从商品展示到游客结算、支付、订单履约的验证清单,以及打包、升级与常见问题排查。 +--- + +以下检查面向 Halo 商城版 2.27。根据主题支持的商品与支付方式准备测试数据,在开发环境完成完整流程。 + +## 准备测试数据 + +至少准备有图和无图商品、多 SKU 商品、库存不足商品、外链商品、询价商品,以及主题声明支持的订阅商品。实体商品验证收货地址和发货信息,虚拟商品配置数字资源或卡密;使用测试数据验证订单内容。Halo 商城版 2.27 尚未实现配送方式选择与运费计算。 + +分别测试登录用户、未登录访客、开启游客结算后的游客。订阅商品还需要已有订阅和无订阅的登录用户。 + +## 页面与交互检查 + +| 场景 | 检查内容 | +| -------- | ------------------------------------------------------------ | +| 商品列表 | 空列表、关键词、分类、排序、分页;翻页保留筛选条件 | +| 商品详情 | 正文、无图占位、图库、永久链接、SKU 切换与数量限制 | +| 订阅商品 | 档位和周期、数量上下限、试用提示、已有订阅入口;游客需要登录 | +| 购物车 | 添加、修改、删除、空购物车、库存或价格发生变化 | +| 结算 | 新地址、地址簿、游客邮箱、备注、优惠码和优惠券拒绝原因 | +| 支付 | 可用方式、支付失败、超时、切换方式、刷新和返回页面 | +| 订单 | 待支付、已支付、取消、退款、优惠明细、快递和虚拟履约信息 | +| 游客订单 | 下单后访问、邮件访问、失效链接、邮箱找回、附件下载和卡密展示 | + +结算上下文依赖会话,补测刷新、浏览器后退、重新发起结算、会话过期等情况。订单价格发生变化或商品失效时,应展示服务端错误并允许顾客恢复操作。 + +## 样式与兼容性 + +- 桌面与窄屏分别检查列表、图库、表单、支付二维码和订单长内容。 +- 使用主题支持的浅色、暗色和语言配置检查按钮、输入框、状态提示。 +- 从浏览器查看页面源代码,确认商品标题、介绍和正文已由 Thymeleaf 输出。 +- 检查同一页没有重复注册 `shop-*` 组件,商城静态资源成功加载。 +- 覆盖模板后仍保留 CSRF 字段,交易请求正确携带会话 Cookie。 +- 测试主题声明支持的支付环境,包括需要支持时的微信内置浏览器和手机浏览器。 + +## 常见问题 + +| 现象 | 排查方向 | +| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | +| 所有商城页面返回 404 | 确认运行商城版、商城已启用,路径与目标版本匹配 | +| 自定义模板未生效 | 检查激活主题、`templates/shop/` 路径及文件名;开发环境模板缓存设置见[主题开发准备](../../theme/prepare.mdx) | +| 出现 `shop-*` 标签但没有内容 | 检查 JavaScript 加载、组件注册及必需属性 | +| 自定义元素重复注册 | 是否同时加载内置脚本和自编译的商城 UI | +| 商品图库不工作 | 源码开发时是否遗漏根入口中的 Swiper 和图片放大初始化 | +| 组件样式缺失或错乱 | 检查 CSS 引用、加载顺序和主题全局样式冲突 | +| 结算页跳回购物车 | 结算上下文不存在、过期或与当前会话不匹配 | +| 订单 JSON 请求返回 HTML | 设置 `Accept: application/json`,并检查是否跳转到登录页 | +| 游客订单 JSON 返回 404 | 检查访问链接及 Cookie,提供邮箱找回入口 | +| 写请求返回 403 | 检查登录要求、会话和 CSRF 令牌,必要时刷新页面 | +| 分类列表不显示 | 分类 Finder 使用 `content`,商品 Finder 使用 `items` | + +## 打包与升级 + +按[主题打包规范](../../theme/packaging.md)和[发布检查清单](../../theme/release-checklist.md)生成安装包。复用内置模板时,只打包主题实际覆盖的文件;源码开发时,先构建 UI 与主题资源,再确认安装包包含模板引用的最终文件。 + +在主题说明中标明“需要 Halo 商城版”与已验证的最低版本。不要仅凭 `theme.yaml` 的版本约束推断安装环境具备商城能力。 + +发布前把安装包安装到干净的目标版本实例中验证,避免本地开发目录中的额外文件掩盖缺失资源。升级 Halo 后,优先对照[版本差异](./api-changelog.md),复测被主题覆盖的模板和交易流程。 diff --git a/docs/guide/shop/theme-dev.mdx b/docs/guide/shop/theme-dev.mdx index 651aef48..44c6b7b2 100644 --- a/docs/guide/shop/theme-dev.mdx +++ b/docs/guide/shop/theme-dev.mdx @@ -1,11 +1,17 @@ --- title: 商城主题开发 -description: Halo 商城页面主题定制:内置模板、示例主题仓库与二次开发参考。 +description: Halo 商城主题定制入口,了解内置模板并前往开发者指南进行主题适配与二次开发。 --- 默认情况下,我们已经在 Halo 本身内置了一套默认的商城模板,如果当前主题没有提供对应的模板文件,则会使用内置的模板文件。 -如果你需要定制商城页面或者开发一套全新的商城主题,可以参考以下 GitHub 仓库: +如果你需要定制商城页面或者开发一套全新的商城主题,请阅读开发者指南中的[商城主题开发](../../developer-guide/shop/theme/overview.md)。 + +- [快速开始](../../developer-guide/shop/theme/quick-start.md):准备环境并适配第一个商城页面。 +- [复用与覆盖模板](../../developer-guide/shop/theme/template-reuse.md):将内置商城页面融入现有主题。 +- [源码二次开发](../../developer-guide/shop/theme/source-development.md):接管商城前端的布局和交互。 + +相关示例仓库: - [lxware-dev/shop-templates](https://github.com/lxware-dev/shop-templates):Halo 商城基础模板与主题适配文档 - [lxware-dev/theme-shop-starter](https://github.com/lxware-dev/theme-shop-starter):Halo 商城的基础示例主题 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f3adacd2..e1487d37 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,6 @@ +allowBuilds: + "@parcel/watcher": true + "esbuild": true + "workerd": true minimumReleaseAgeExclude: - "@iconify-json/simple-icons@1.2.99"