小氢云商城模板开发完全指南
本文面向第三方模板开发者与站点二次开发者,系统梳理小氢云商城(Go 架构)下的模板开发规范、接口调用方式与上架流程。读完本文,你应该能独立完成一个可安装的静态模板,并正确对接商城核心业务接口。
一、先搞清楚:模板是什么
在小氢云商城里,「模板」指的是替换或扩展前台展示层的前端工程。用户访问商城时,系统通过路由映射把 URL 指向你打包好的 index.html,你的页面再通过 REST API 与后端通信,完成商品展示、下单、支付、查单等完整链路。
当前系统支持两类模板形态:
| 形态 | 技术栈 | 适用场景 |
|---|---|---|
| 静态 SPA 模板(推荐) | Vue / React / UniApp H5 打包产物 | 现代商城前台、高度自定义 UI |
| 经典 PHP 模板(兼容晴玖规范) | PHP + HTML + route.php |
轻量改造、沿用旧版模板生态 |
小氢云商城后端为 Go + Gin,接口统一走
/api/*。PHP 模板中的/?mod=route&p=Goods等旧式路由,在新系统中应改为前端路由 + REST API 的方式实现。
二、模板目录结构规范
2.1 静态 SPA 模板(主流方式)
从模板商店安装后,系统会将 zip 包解压到 doc/{template_slug}/ 目录,并自动创建路由映射。
标准目录结构:
my-template/
├── index.html # 入口文件(必需,文件名必须为 index)
├── index.png # 预览图(建议 800×600px,上架用)
├── assets/ # JS / CSS 打包产物(UniApp / Vite 默认目录)
│ ├── index-xxx.js
│ └── index-xxx.css
├── static/ # 图片、字体等静态资源(可选)
│ └── images/
└── conf.json # 模板配置(可选,见下文)
硬性要求:
- 包内必须存在名为
index的首页文件(index.html/index.htm/index.php,优先.html) template_slug在授权站配置,仅允许英文和数字,全局唯一- 静态资源与
index.html放在同一目录树下,便于路由中间件自动解析
2.2 经典 PHP 模板(晴玖兼容)
若你沿用晴玖商城模板规范,目录如下:
template/{应用标识}/
├── index.php # 模板入口
├── index.png # 预览图
├── route.php # 路由跳转(旧式页面导航)
├── conf.json # 模板配置
└── help.html # 帮助文档(有扩展功能时建议提供)
route.php 示例(旧式跳转,新开发建议用前端路由替代):
<?php
switch ($_GET['p']) {
case 'Goods':
header("Location: " . ROOT_DIR . "#/pages/shop/shop?gid=" . $_GET['gid']);
break;
case 'Order':
header("Location: " . ROOT_DIR . "#/pages/order/order");
break;
default:
header("Location: " . ROOT_DIR);
break;
}
三、模板配置文件 conf.json
conf.json 让站长在后台可视化修改模板参数,无需改代码。
{
"name": "极简商城模板",
"version": "1.0.0",
"type": "1",
"content": "一款简洁现代的商城前台模板",
"extend": [
{
"type": 1,
"name": "主题色",
"value": "#1890ff",
"Tips": "全局主色调,支持 HEX 色值"
},
{
"type": 2,
"name": "首页布局",
"value": "1",
"data": {
"1": "单列瀑布流",
"2": "双列网格",
"3": "分类优先"
}
}
]
}
| 字段 | 说明 |
|---|---|
name |
模板名称 |
version |
版本号 |
type |
模板类型标识 |
content |
模板描述 |
extend[].type=1 |
文本输入框 |
extend[].type=2 |
下拉选择框,需配 data 选项 |
四、接口调用规范
4.1 基础约定
| 项目 | 规范 |
|---|---|
| 基础路径 | {站点域名}/api |
| 请求格式 | Content-Type: application/json |
| 成功标识 | code === 200(部分旧接口兼容 code === 1) |
| 失败标识 | code === -1,错误信息在 msg 字段 |
| 登录认证 | 请求头 user-token: Bearer {token} |
统一响应结构:
{
"code": 200,
"msg": "操作成功",
"data": { ... }
}
4.2 获取站点基础信息(模板初始化必调)
模板加载后,第一件事应拉取站点配置,用于渲染 Logo、公告、默认商品图、登录状态等。
// 获取网站基础信息(含 sitename、notice、def_img、User 等)
const res = await fetch('/api/inform', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const { code, data } = await res.json();
// 获取前端公共配置
const config = await fetch('/api/getConfig', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
/api/inform支持可选认证:携带user-token时返回当前用户信息;不传则为游客模式。
4.3 用户登录与 Token 管理
登录:
const res = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'demo',
password: '123456',
code: '' // 验证码(若后台开启)
})
});
const { code, data } = await res.json();
if (code === 200) {
localStorage.setItem('user-token', data.token);
}
携带 Token 发起请求:
const token = localStorage.getItem('user-token');
const res = await fetch('/api/getUserInfo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'user-token': `Bearer ${token}`
}
});
Token 无感刷新:
当接口返回 code === 401 时,调用 POST /api/refresh-token(同样携带 user-token 头),拿到新 token 后重试原请求。
4.4 封装通用请求函数(推荐)
const API_BASE = window.location.origin;
async function api(url, params = {}, method = 'POST') {
const token = localStorage.getItem('user-token');
const headers = { 'Content-Type': 'application/json' };
if (token) headers['user-token'] = `Bearer ${token.trim()}`;
const options = { method, headers };
if (method !== 'GET') options.body = JSON.stringify(params);
const res = await fetch(API_BASE + url, options);
const data = await res.json();
if (data.code === 401) {
// 触发登录弹窗或跳转登录页
throw new Error('登录已过期');
}
if (data.code !== 200 && data.code !== 1) {
throw new Error(data.msg || '请求失败');
}
return data;
}
// 使用示例
const goods = await api('/api/getGoods', { cid: 1, page: 1, limit: 20 });
const detail = await api('/api/getGoodsById', { id: 123 });
4.5 核心业务接口速查
商品与分类
| 接口 | 方法 | 说明 |
|---|---|---|
/api/class |
Any | 获取商品分类 |
/api/getGoods |
Any | 分页商品列表(cid, page, limit, keyword) |
/api/getGoodsById |
Any | 商品详情(id) |
/api/searchCourse |
Any | 搜索商品 |
/api/review/list |
Any | 商品评价列表 |
下单与订单
| 接口 | 方法 | 说明 |
|---|---|---|
/api/add |
Any | 创建订单 |
/api/OrderPrice |
Any | 计算订单价格 |
/api/PreviewPrice |
Any | 价格预览(含优惠券等) |
/api/queryOrder |
Any | 查询订单 |
/api/getOrderDetail |
Any | 订单详情 |
/api/orderByKey |
Any | 游客查单(按 key) |
支付
| 接口 | 方法 | 说明 |
|---|---|---|
/api/PaymentWay |
Any | 获取可用支付方式 |
/api/pay |
GET | 统一支付入口 |
用户中心
| 接口 | 方法 | 说明 |
|---|---|---|
/api/UserData |
Any | 用户数据概览 |
/api/getUserInfo |
Any | 当前用户信息 |
/api/coupon/my |
Any | 我的优惠券 |
/api/cart/list |
GET | 购物车列表 |
完整接口清单见项目
doc/api/client/目录,按模块分为商品、订单、支付、认证等文档。
4.6 图片地址处理
- 本地上传图片路径形如
/static/img/xxx.png,直接拼接站点域名即可,无需走代理 - 相对路径统一用
window.location.origin拼接,避免硬编码域名 - 商品默认图优先取
/api/inform返回的def_img字段
function resolveImage(url) {
if (!url) return '';
if (url.startsWith('http')) return url;
return window.location.origin + (url.startsWith('/') ? url : '/' + url);
}
五、路由映射与静态资源(关键!)
模板安装后,系统会在路由映射中创建一条记录(默认路由 /,初始状态为关闭,需站长手动启用)。
5.1 子路由下的静态资源自动重写
若你的模板映射到 /myshop(非根路径),系统会自动重写 HTML 中的绝对路径:
<!-- 打包原始 -->
<script src="/assets/index.js"></script>
<!-- 访问 /myshop 时自动变为 -->
<script src="/myshop/assets/index.js"></script>
支持自动重写的目录前缀:/assets/、/static/、/js/、/css/、/images/、/img/、/fonts/、/libs/、/lib/
5.2 获取当前路由前缀
HTML 中可使用 [router] 占位符,服务端渲染时替换为当前路由:
<div id="app" data-base="[router]"></div>
<!-- 映射到 /myshop 时变为 data-base="/myshop" -->
JS 中读取:
const basePath = document.getElementById('app').dataset.base || '';
// 用于 Vue Router 的 base 配置
5.3 常见踩坑
| 问题 | 原因 | 解决 |
|---|---|---|
| 页面白屏,JS 404 | 静态资源绝对路径未重写 | 确保 HTML 引用为 /assets/xxx 格式;或映射到根路由 / |
| JS 动态加载 404 | 代码中硬编码了 /assets/ |
改为相对路径,或用 [router] 获取前缀 |
| 接口跨域 | 开发时前后端不同端口 | 开发环境配置代理,或直连后端 127.0.0.1:8080 |
| 登录态丢失 | Token 未持久化 | 写入 localStorage,请求头带 user-token |
六、开发流程与上架规范
6.1 完整开发流程
1. 在授权站创建模板项目,配置 template_slug
↓
2. 本地开发前端(Vue / UniApp H5 等)
↓
3. 打包产物确保含 index.html + assets/
↓
4. 本地测试:手动创建路由映射指向打包目录
↓
5. 打包 zip 上传授权站,提交审核
↓
6. 站长在「模板商店」安装 → 启用路由映射
6.2 本地调试建议
方式一:路由映射调试(推荐)
- 将打包产物放到后端
doc/my-template/目录 - 管理后台 → 路由映射 → 新建映射
- 路由:
/my-template - 文件路径:
my-template/index.html - 设备类型:
pc/pe/def - 状态:启用
- 路由:
- 访问
http://127.0.0.1:8080/my-template验证
方式二:开发服务器 + API 直连
// vite.config.js
export default {
server: {
proxy: {
'/api': 'http://127.0.0.1:8080'
}
}
}
6.3 提审清单
提交模板商店审核前,请逐项确认:
- 包含
index.html(或index.htm/index.php)入口文件 - 预览图
index.png(建议 800×600px) - 路由映射测试通过,静态资源全部 200
- 商品列表、详情、下单、查单核心链路跑通
- 登录 / 游客双模式均正常
- 移动端适配(若
template_device=mobile) -
conf.json格式正确(若有配置项) - 帮助文档
help.html(若有扩展功能) - 无硬编码第三方域名、无安全隐患
上架参数:
| 字段 | 要求 |
|---|---|
| 项目类型 | 选择「模板」 |
| 安装路径 | 填写 / |
| 设备类型 | universal / mobile / desktop |
| template_slug | 英文数字,全局唯一 |
七、扩展开发方式
7.1 插件式扩展(复杂功能)
需要拼团、砍价、自定义支付等能力时,建议开发应用商店插件,在模板中通过接口调用:
GET ?AppApies&identification={插件标识}&name={方法名}
插件控制器规范:
/includes/lib/soft/controller/{标识}/index.php
7.2 内置式扩展(简单功能)
在模板包内直接新增 PHP 文件,通过 URL 访问:
/?mod={扩展文件名}
查找顺序:
/template/{应用标识}/{扩展文件}.php/template/default/{扩展文件}.php
新架构下更推荐纯前端 + REST API 方案,PHP 扩展仅作兼容保留。
八、开发规范与最佳实践
8.1 代码规范
- 不要硬编码站点域名,统一用
window.location.origin - 图片路径走
/static/img/直链,不走/api/image/proxy - 富文本渲染(UniApp PE 端)统一用
<u-parse :content="xxx" :selectable="true"> - 登录态优先读
localStorage,401 时引导重新登录 - 接口兼容同时判断
code === 200和code === 1
8.2 性能建议
- 静态资源打包压缩,图片适当 WebP
- 商品列表分页加载,避免一次拉全量
- 站点配置(
/api/inform)可缓存 60 秒,开发模式建议 1 秒 - 按需加载路由模块,控制首屏 JS 体积
8.3 安全建议
- 不在前端存储敏感信息(密码、API 密钥)
- 用户输入做 XSS 过滤后再渲染
- 涉及资金的接口必须带 Token,不做前端金额计算终裁
九、快速上手示例:最小可用模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的商城模板</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; background: #f5f5f5; }
.header { background: #fff; padding: 16px; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.goods-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; padding: 16px; }
.goods-card { background: #fff; border-radius: 8px; overflow: hidden; cursor: pointer; }
.goods-card img { width: 100%; aspect-ratio: 1; object-fit: cover; }
.goods-card .info { padding: 8px 12px; }
.goods-card .name { font-size: 14px; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.goods-card .price { color: #ff4d4f; font-weight: 600; margin-top: 4px; }
</style>
</head>
<body>
<div class="header"><h1 id="site-name">加载中...</h1></div>
<div class="goods-list" id="goods-list"></div>
<script>
const API = window.location.origin;
async function api(url, params = {}) {
const token = localStorage.getItem('user-token');
const headers = { 'Content-Type': 'application/json' };
if (token) headers['user-token'] = 'Bearer ' + token.trim();
const res = await fetch(API + url, { method: 'POST', headers, body: JSON.stringify(params) });
return res.json();
}
async function init() {
// 1. 站点信息
const site = await api('/api/inform');
if (site.code === 200 || site.code === 1) {
document.getElementById('site-name').textContent = site.data?.sitename || '商城';
}
// 2. 商品列表
const goods = await api('/api/getGoods', { page: 1, limit: 20 });
const list = goods.data?.list || goods.data || [];
const container = document.getElementById('goods-list');
list.forEach(item => {
const img = item.image?.startsWith('http') ? item.image : API + (item.image || '/static/img/default.png');
container.innerHTML += `
<div class="goods-card" onclick="location.href='?id=${item.id}'">
<img src="${img}" alt="${item.name}" loading="lazy">
<div class="info">
<div class="name">${item.name}</div>
<div class="price">¥${item.price}</div>
</div>
</div>`;
});
}
init().catch(err => console.error('初始化失败:', err));
</script>
</body>
</html>
将上述文件保存为 index.html,配合路由映射即可在本地验证。
十、相关资源
| 资源 | 说明 |
|---|---|
| 前台接口文档 | 项目 doc/api/client/ 目录 |
| 接口测试守则 | doc/api/接口测试守则.md |
| 路由映射静态资源说明 | 后端 UniApp路由映射静态资源解决方案.txt |
| 应用商店开发平台 | https://appstor.79tian.com/ |
| 开发文档中心 | https://appstor.79tian.com/docs |
总结
小氢云商城模板开发的本质是:用任意前端技术栈打包出 SPA,通过路由映射挂载到商城域名下,用标准 REST API 驱动业务。
记住三个关键点:
- 入口文件必须叫
index,静态资源与之间目录 - 接口走
/api/*,认证头用user-token: Bearer {token} - 非根路由映射时,依赖系统自动重写
/assets/等静态路径
按本文规范开发并通过提审清单自检,你的模板就能顺利上架并被站长一键安装使用。
如有疑问,欢迎在评论区留言或加入开发者交流群。