1. 技术栈
| 技术 | 版本 | 用途 |
| Tailwind CSS | v3.x | 原子化 CSS 框架,优先使用 |
| XPUI | 自研 | 唯一UI框架,封装通用样式与组件 |
| Font Awesome | v6.x | 图标库 |
| jQuery | v3.x | DOM 操作与 Ajax 请求 |
| Layer | v3.x | 弹窗组件 |
| ThinkPHP 模板 | v8 | 服务端模板引擎 |
重要规则:禁止引入任何第三方 UI 框架(如 Bootstrap、Element UI),仅允许使用 TailwindCSS + XPUI。
2. 命名规范(xp- 前缀)
所有 class 和 id 必须以 xp- 为前缀,使用短横线连接,全小写。禁止驼峰、下划线、拼音。
| 类型 | 正确示例 | 错误示例 |
| 布局容器 | xp-container, xp-wrapper | container, wrapper_1, rongQi |
| 页面头部 | xp-header, xp-header-nav | header, HeaderNav, header_nav |
| 导航菜单 | xp-nav, xp-nav-item | nav, navItem, nav_item |
| 表单元素 | xp-form, xp-input, xp-btn | form, inputBox, submit_Btn |
| 表格 | xp-table, xp-table-th | table, TableHead, table_head |
| 卡片 | xp-card, xp-card-body | card, cardBody, card_body |
| 弹窗 | xp-modal, xp-modal-title | modal, modalTitle, modal_title |
| 列表 | xp-list, xp-list-item | list, listItem, list_item |
| 侧边栏 | xp-sidebar, xp-sidebar-menu | sidebar, SidebarMenu, sidebar_menu |
| 页脚 | xp-footer, xp-footer-link | footer, FooterLink, footer_link |
3. 代码分离原则
XPCMS 前端严格遵循 HTML / CSS / JS 完全独立文件 的开发模式:
| 规则 | 说明 | 违规示例 |
| 禁止内联样式 | 不允许 style="..." 属性 | <div style="color:red"> ❌ |
| 禁止内联事件 | 不允许 onclick="..." 等属性 | <button onclick="doSomething()"> ❌ |
| CSS 独立文件 | 业务样式写入 /static/应用名/css/ | <style> 在HTML中 ❌ |
| JS 独立文件 | 业务脚本写入 /static/应用名/js/ | <script> 在HTML中 ❌ |
| 事件委托绑定 | JS中用选择器绑定事件 | $(document).on('click', '.xp-btn', fn) ✅ |
4. 静态资源路径规范
| 资源类型 | 存放路径 | 访问URL |
| 静态根目录 | public/static/ | /static/ |
| XPUI 框架 | public/static/xpui/ | /static/xpui/ |
| 业务 CSS | public/static/应用名/css/ | /static/应用名/css/ |
| 业务 JS | public/static/应用名/js/ | /static/应用名/js/ |
| 业务图片 | public/static/应用名/images/ | /static/应用名/images/ |
5. 主题开发规范
每个主题位于 themes/frontend/主题标识/,核心文件为 theme.json。
主题目录结构
themes/frontend/your-theme/
├── theme.json # 主题配置文件(必填)
├── theme.png # 主题预览图
├── view/ # 模板视图目录
│ ├── index.html # 首页模板
│ ├── list.html # 列表页模板
│ ├── show.html # 详情页模板
│ └── common/ # 公共模板片段
│ ├── _header.html
│ └── _footer.html
├── static/ # 主题静态资源
│ ├── css/
│ │ └── theme.css
│ ├── js/
│ │ └── theme.js
│ └── images/
│ └── logo.png
└── config/ # 主题配置面板
└── config.php
theme.json 配置
{
"name": "主题名称",
"version": "1.0.0",
"author": "作者",
"description": "主题描述",
"screenshot": "theme.png",
"templates": {
"index": "view/index.html",
"list": "view/list.html",
"show": "view/show.html"
}
}
FrontendConfig 全局变量(15 个)
所有主题模板中可直接使用以下系统变量:
| 变量名 | 说明 | 示例 |
| {$site_name} | 站点名称 | XPCMS 官网 |
| {$site_logo} | 站点 Logo URL | /static/images/logo.png |
| {$site_keywords} | 站点关键词 | CMS,内容管理 |
| {$site_description} | 站点描述 | XPCMS 内容管理系统 |
| {$site_icp} | ICP 备案号 | 粤ICP备xxxxxx号 |
| {$site_copyright} | 版权信息 | © 2025 XPCMS |
| {$theme_path} | 当前主题资源路径 | /themes/frontend/default |
| {$static_path} | 静态资源路径 | /static |
| {$category_tree} | 分类树数据 | 多维数组 |
| {$nav_menu} | 导航菜单数据 | 关联数组 |
| {$page_title} | 当前页面标题 | 新闻列表 |
| {$current_category} | 当前分类信息 | 关联数组 |
| {$breadcrumb} | 面包屑导航数据 | 数组 |
| {$friend_links} | 友情链接列表 | 数组 |
| {$ad_list} | 广告位数据 | 按位置分组 |
6. 栏目分类体系
XPCMS 将栏目分为三大类别,每种类型有不同的模板处理逻辑和子分类结构。
三大类别
| 分类类型 | type 值 | 核心特征 | 典型场景 |
| 单页 | page | 单个页面,无列表,内容编辑器编辑 | 关于我们、联系我们 |
| 列表 | list | 有文章列表,支持分页,点击进入详情 | 新闻中心、产品展示 |
| 链接 | link | 跳转到外部URL,无内容 | 友情链接、外部导航 |
列表类型的子分类
| 子类型 | sub_type | 说明 |
| 文章 | article | 标准图文内容,有标题/封面/正文 |
| 图集 | gallery | 多图展示,以大图轮播方式呈现 |
| 视频 | video | 视频内容,内嵌播放器 |
| 下载 | download | 文件下载,含下载链接和文件信息 |
| 产品 | product | 产品展示,含价格/规格/参数 |
三类区别对比
| 特性 | 单页 | 列表 | 链接 |
| 文章列表 | ❌ 无 | ✅ 有 | ❌ 无 |
| 详情页 | ❌ 无 | ✅ 有 | ❌ 无 |
| 内容编辑器 | ✅ 富文本 | ❌ 无 | ❌ 无 |
| 跳转链接 | ❌ | ❌ | ✅ 跳转外部URL |
| 子分类 | ❌ 不支持 | ✅ 无限层级 | ❌ 不支持 |
| SEO TDK | ✅ 自定义 | ✅ 自定义 | ❌ 无 |
7. ThinkPHP 模板语法速查
| 语法 | 说明 | 示例 |
| {$var} | 变量输出 | {$site_name} |
| {$var|default='默认'} | 默认值 | {$title|default='无标题'} |
| {$var|raw} | 不转义HTML输出 | {$content|raw} |
| {$var|date='Y-m-d'} | 日期格式化 | {$create_time|date='Y-m-d'} |
| {$var|substr=0,100} | 字符串截取 | {$title|substr=0,50} |
| {$var|number_format=2} | 数字格式化 | {$price|number_format=2} |
| {$var|count} | 数组计数 | {$list|count} |
volist 循环
{volist name="list" id="vo" key="k"}
<!-- $vo 为当前项,$k 为索引(从1开始),$i 为索引(从0开始) -->
<div class="xp-list-item">
<a href="{$vo.url}">{$vo.title}</a>
<span>{$vo.create_time|date='Y-m-d'}</span>
</div>
{/volist}
<!-- 空数据提示 -->
{volist name="list" id="vo" empty="<p>暂无数据</p>"}
...
{/volist}
if 条件判断
{if condition="$vo.status == 1"}
<span class="text-green-600">已发布</span>
{elseif condition="$vo.status == 0" /}
<span class="text-gray-400">草稿</span>
{else /}
<span class="text-red-500">已下架</span>
{/if}
json_encode 与 literal
<!-- 将 PHP 变量转为 JSON 输出到 JS -->
<script>
window.xpConfig = {:json_encode($config)};
window.xpData = {:json_encode($data)};
</script>
<!-- 阻止模板解析(literal标签内的内容原样输出) -->
{literal}
<style>/* 这里的CSS不会被模板引擎解析 */</style>
<script>/* 这里的JS不会被模板引擎解析 */</script>
{/literal}
include 引入模板
<!-- 引入公共头部 -->
{include file="common/_header" /}
<!-- 引入公共底部 -->
{include file="common/_footer" /}
<!-- 引入并传参 -->
{include file="common/_sidebar" active="news" /}
8. 后台视图模板编写规范
8.1 列表页模板结构
{extend name="common/_layout" /}
{block name="content"}
<!-- 搜索区域 -->
<div class="xp-card xp-mb-16">
<div class="xp-card-body">
<form class="xp-form xp-form-inline" id="xpSearchForm">
<div class="xp-form-group">
<label class="xp-form-label">关键词</label>
<input type="text" name="keyword" class="xp-input" placeholder="请输入关键词">
</div>
<button type="submit" class="xp-btn xp-btn-primary">搜索</button>
</form>
</div>
</div>
<!-- 操作栏 -->
<div class="xp-toolbar xp-mb-16">
<button class="xp-btn xp-btn-success" id="xpBtnAdd">新增</button>
<button class="xp-btn xp-btn-danger" id="xpBtnBatchDelete">批量删除</button>
</div>
<!-- 数据表格 -->
<table class="xp-table" id="xpDataTable">
<thead>
<tr>
<th><input type="checkbox" id="xpCheckAll"></th>
<th>ID</th>
<th>标题</th>
<th>状态</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="xpTableBody"></tbody>
</table>
<!-- 分页 -->
<div id="xpPagination" class="xp-pagination"></div>
{/block}
8.2 表单页模板结构
{extend name="common/_layout" /}
{block name="content"}
<form class="xp-form" id="xpForm" method="post">
<div class="xp-card">
<div class="xp-card-header">基本信息</div>
<div class="xp-card-body">
<!-- 隐藏的ID字段,编辑时带上 -->
<input type="hidden" name="id" value="{$data.id|default=''}">
<div class="xp-form-group">
<label class="xp-form-label xp-form-required">标题</label>
<input type="text" name="title" class="xp-input"
value="{$data.title|default=''}" placeholder="请输入标题">
</div>
<div class="xp-form-group">
<label class="xp-form-label">分类</label>
<select name="category_id" class="xp-select">
<option value="">请选择分类</option>
{volist name="category_list" id="cat"}
<option value="{$cat.id}"
{if condition="$data.category_id == $cat.id"}selected{/if}>
{$cat.name}
</option>
{/volist}
</select>
</div>
<div class="xp-form-group">
<label class="xp-form-label">内容</label>
<textarea name="content" class="xp-textarea xp-editor"
rows="10">{$data.content|default=''}</textarea>
</div>
</div>
<div class="xp-card-footer">
<button type="submit" class="xp-btn xp-btn-primary">保存</button>
<button type="button" class="xp-btn xp-btn-default" onclick="history.back()">返回</button>
</div>
</div>
</form>
{/block}
8.3 弹窗表单结构
<!-- 弹窗HTML模板,默认隐藏 -->
<div id="xpModalTemplate" style="display:none;">
<form class="xp-form xp-p-20" id="xpModalForm">
<input type="hidden" name="id">
<div class="xp-form-group">
<label class="xp-form-label">名称</label>
<input type="text" name="name" class="xp-input">
</div>
</form>
</div>
9. 前台最佳实践
9.1 面包屑导航
<div class="xp-breadcrumb">
<a href="/">首页</a>
{if condition="isset($breadcrumb)"}
{volist name="breadcrumb" id="bc"}
<span>/</span>
<a href="{$bc.url}">{$bc.name}</a>
{/volist}
{/if}
<span>/</span>
<span>{$page_title|default=''}</span>
</div>
9.2 卡片布局
<div class="xp-card xp-card-shadow xp-card-hover">
<div class="xp-card-image">
<img src="{$vo.cover_image}" alt="{$vo.title}">
</div>
<div class="xp-card-body">
<h3 class="xp-card-title">{$vo.title|substr=0,40}</h3>
<p class="xp-card-desc">{$vo.description|substr=0,100}</p>
<div class="xp-card-meta">
<span>{$vo.create_time|date='Y-m-d'}</span>
<span>{$vo.views} 阅读</span>
</div>
</div>
</div>
9.3 响应式布局
<!-- 桌面端2列,平板2列,手机1列 -->
<div class="xp-grid xp-grid-cols-1 md:xp-grid-cols-2 lg:xp-grid-cols-3 xp-gap-6">
{volist name="list" id="vo"}
<div class="xp-card">...</div>
{/volist}
</div>
9.4 移动端适配要点
| 要点 | 说明 |
| Viewport | 必须设置 <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 断点 | sm:640px / md:768px / lg:1024px / xl:1280px |
| 触控区域 | 按钮/链接最小触控面积为 44×44px |
| 图片 | 使用 srcset 响应式图片或 CDN 裁剪 |
| 导航 | 移动端使用汉堡菜单,桌面端展开导航 |
| 表格 | 移动端表格横向滚动或卡片化展示 |