主题开发指南
主题开发指南将帮助您为 Yuan-ICP 系统创建自定义主题,包括主题结构、开发流程、最佳实践和示例代码。
📋 主题系统概览
Yuan-ICP 采用模块化的主题架构,支持:
- 多主题切换:在多个主题之间快速切换
- 响应式设计:支持各种设备尺寸
- 模板引擎:基于PHP的模板系统
- 资源管理:CSS、JavaScript、图片等资源管理
- 配置系统:主题级别的配置选项
主题系统架构
themes/
├── default/ # 默认主题
│ ├── theme.json # 主题配置文件
│ ├── css/ # 样式文件
│ ├── js/ # JavaScript文件
│ ├── images/ # 图片资源
│ └── templates/ # 模板文件
└── custom/ # 自定义主题
├── theme.json # 主题配置
├── css/ # 样式文件
├── js/ # 脚本文件
└── templates/ # 模板文件
🎨 创建新主题
1. 创建主题目录
首先在 themes/
目录下创建新的主题文件夹:
bash
mkdir themes/my-theme
cd themes/my-theme
mkdir css js images templates
2. 创建主题配置文件
创建 theme.json
文件:
json
{
"name": "我的主题",
"version": "1.0.0",
"description": "一个自定义的Yuan-ICP主题",
"author": "您的姓名",
"license": "MIT",
"requires": {
"system_version": "1.0.0"
},
"supports": {
"responsive": true,
"dark_mode": false,
"customization": true
},
"templates": [
"header",
"footer",
"home",
"apply",
"query",
"result"
],
"assets": {
"css": [
"css/main.css",
"css/responsive.css"
],
"js": [
"js/main.js"
]
}
}
3. 主题目录结构
my-theme/
├── theme.json # 主题配置文件
├── css/ # 样式文件目录
│ ├── main.css # 主样式文件
│ ├── components.css # 组件样式
│ └── responsive.css # 响应式样式
├── js/ # JavaScript文件目录
│ ├── main.js # 主脚本文件
│ └── utils.js # 工具函数
├── images/ # 图片资源目录
│ ├── logo.png # 主题logo
│ └── icons/ # 图标文件
└── templates/ # 模板文件目录
├── header.php # 页面头部
├── footer.php # 页面底部
├── home.php # 首页模板
├── apply.php # 申请页面
├── query.php # 查询页面
└── result.php # 结果页面
📝 模板开发
模板系统基础
Yuan-ICP 使用基于PHP的模板系统,支持以下功能:
- 数据传递:从控制器传递数据到模板
- 条件渲染:根据数据状态显示不同内容
- 循环渲染:渲染列表数据
- 模板继承:基础模板和子模板
基础模板结构
页面头部模板 (header.php)
php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($data['page_title'] ?? 'Yuan-ICP') ?></title>
<!-- 主题CSS文件 -->
<?php foreach ($this->getThemeAssets('css') as $css): ?>
<link rel="stylesheet" href="<?= $css ?>">
<?php endforeach; ?>
<!-- 系统CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css">
<!-- 自定义样式 -->
<style>
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">
<img src="<?= $this->getThemeAsset('images/logo.png') ?>" alt="Logo" height="30">
<?= htmlspecialchars($data['config']['site_name'] ?? 'Yuan-ICP') ?>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="/">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/apply.php">申请备案</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/query.php">查询备案</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/announcements.php">公告</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 主要内容容器 -->
<main class="main-content">
页面底部模板 (footer.php)
php
</main>
<!-- 页脚 -->
<footer class="footer bg-dark text-light py-4 mt-5">
<div class="container">
<div class="row">
<div class="col-md-6">
<h5><?= htmlspecialchars($data['config']['site_name'] ?? 'Yuan-ICP') ?></h5>
<p class="text-muted">专业的虚拟ICP备案管理系统</p>
</div>
<div class="col-md-6 text-md-end">
<p class="text-muted">
© <?= date('Y') ?> <?= htmlspecialchars($data['config']['site_name'] ?? 'Yuan-ICP') ?>.
保留所有权利。
</p>
<p class="text-muted">
<a href="https://github.com/bbb-lsy07/Yuan-ICP" class="text-light">
<i class="fab fa-github"></i> GitHub
</a>
</p>
</div>
</div>
</div>
</footer>
<!-- 主题JavaScript文件 -->
<?php foreach ($this->getThemeAssets('js') as $js): ?>
<script src="<?= $js ?>"></script>
<?php endforeach; ?>
<!-- 系统JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- 自定义脚本 -->
<script>
// 主题特定的JavaScript代码
document.addEventListener('DOMContentLoaded', function() {
// 初始化主题功能
initTheme();
});
function initTheme() {
// 主题初始化逻辑
console.log('主题已初始化');
}
</script>
</body>
</html>
首页模板 (home.php)
php
<!-- 英雄区域 -->
<section class="hero bg-primary text-white py-5">
<div class="container text-center">
<h1 class="display-4 mb-4">欢迎使用 Yuan-ICP</h1>
<p class="lead mb-4">专业的虚拟ICP备案管理系统,为您提供完整的备案服务体验</p>
<div class="hero-buttons">
<a href="/apply.php" class="btn btn-light btn-lg me-3">
<i class="fas fa-file-alt me-2"></i>申请备案
</a>
<a href="/query.php" class="btn btn-outline-light btn-lg">
<i class="fas fa-search me-2"></i>查询备案
</a>
</div>
</div>
</section>
<!-- 统计信息 -->
<section class="stats py-5">
<div class="container">
<div class="row text-center">
<div class="col-md-3 mb-4">
<div class="stat-card">
<i class="fas fa-file-alt fa-3x text-primary mb-3"></i>
<h3><?= number_format($data['stats']['total'] ?? 0) ?></h3>
<p class="text-muted">总申请数</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="stat-card">
<i class="fas fa-clock fa-3x text-warning mb-3"></i>
<h3><?= number_format($data['stats']['pending'] ?? 0) ?></h3>
<p class="text-muted">待审核</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="stat-card">
<i class="fas fa-check-circle fa-3x text-success mb-3"></i>
<h3><?= number_format($data['stats']['approved'] ?? 0) ?></h3>
<p class="text-muted">已通过</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="stat-card">
<i class="fas fa-times-circle fa-3x text-danger mb-3"></i>
<h3><?= number_format($data['stats']['rejected'] ?? 0) ?></h3>
<p class="text-muted">已驳回</p>
</div>
</div>
</div>
</div>
</section>
<!-- 公告列表 -->
<?php if (!empty($data['announcements'])): ?>
<section class="announcements py-5 bg-light">
<div class="container">
<h2 class="text-center mb-5">最新公告</h2>
<div class="row">
<?php foreach (array_slice($data['announcements'], 0, 6) as $announcement): ?>
<div class="col-md-6 col-lg-4 mb-4">
<div class="announcement-card card h-100">
<div class="card-body">
<?php if ($announcement['is_pinned']): ?>
<span class="badge bg-warning mb-2">
<i class="fas fa-thumbtack"></i> 置顶
</span>
<?php endif; ?>
<h5 class="card-title"><?= htmlspecialchars($announcement['title']) ?></h5>
<p class="card-text text-muted">
<?= htmlspecialchars(substr($announcement['content'], 0, 100)) ?>...
</p>
<small class="text-muted">
<i class="fas fa-calendar me-1"></i>
<?= date('Y-m-d', strtotime($announcement['created_at'])) ?>
</small>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="text-center mt-4">
<a href="/announcements.php" class="btn btn-outline-primary">
查看所有公告 <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
</div>
</section>
<?php endif; ?>
<!-- 功能特色 -->
<section class="features py-5">
<div class="container">
<h2 class="text-center mb-5">系统特色</h2>
<div class="row">
<div class="col-md-4 mb-4">
<div class="feature-card text-center">
<i class="fas fa-shield-alt fa-3x text-primary mb-3"></i>
<h4>安全可靠</h4>
<p class="text-muted">内置安全机制,保护您的数据安全</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card text-center">
<i class="fas fa-mobile-alt fa-3x text-success mb-3"></i>
<h4>响应式设计</h4>
<p class="text-muted">完美支持各种设备,提供一致的用户体验</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card text-center">
<i class="fas fa-cogs fa-3x text-info mb-3"></i>
<h4>高度可定制</h4>
<p class="text-muted">支持主题切换和插件扩展</p>
</div>
</div>
</div>
</div>
</section>
🎨 CSS样式开发
主样式文件 (css/main.css)
css
/* 主题变量定义 */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
--light-color: #f8f9fa;
--dark-color: #343a40;
--font-family-sans-serif: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--font-family-monospace: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--border-radius: 0.375rem;
--box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--transition: all 0.15s ease-in-out;
}
/* 全局样式 */
body {
font-family: var(--font-family-sans-serif);
line-height: 1.6;
color: var(--dark-color);
}
/* 导航栏样式 */
.navbar-brand img {
transition: var(--transition);
}
.navbar-brand:hover img {
transform: scale(1.05);
}
/* 英雄区域样式 */
.hero {
background: linear-gradient(135deg, var(--primary-color) 0%, #0056b3 100%);
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('../images/hero-bg.svg') center/cover;
opacity: 0.1;
}
.hero .container {
position: relative;
z-index: 1;
}
.hero-buttons .btn {
padding: 0.75rem 2rem;
font-weight: 600;
border-radius: var(--border-radius);
transition: var(--transition);
}
.hero-buttons .btn:hover {
transform: translateY(-2px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
/* 统计卡片样式 */
.stat-card {
padding: 2rem 1rem;
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
transition: var(--transition);
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.stat-card h3 {
color: var(--primary-color);
font-weight: 700;
margin-bottom: 0.5rem;
}
/* 公告卡片样式 */
.announcement-card {
border: none;
box-shadow: var(--box-shadow);
transition: var(--transition);
}
.announcement-card:hover {
transform: translateY(-3px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.announcement-card .card-title {
color: var(--dark-color);
font-weight: 600;
}
/* 功能特色样式 */
.feature-card {
padding: 2rem 1rem;
}
.feature-card i {
color: var(--primary-color);
margin-bottom: 1rem;
}
.feature-card h4 {
color: var(--dark-color);
font-weight: 600;
margin-bottom: 1rem;
}
/* 页脚样式 */
.footer {
background: linear-gradient(135deg, var(--dark-color) 0%, #495057 100%);
}
.footer a {
text-decoration: none;
transition: var(--transition);
}
.footer a:hover {
opacity: 0.8;
}
/* 按钮样式增强 */
.btn {
border-radius: var(--border-radius);
transition: var(--transition);
font-weight: 500;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
transform: translateY(-1px);
}
/* 表单样式 */
.form-control {
border-radius: var(--border-radius);
border: 1px solid #ced4da;
transition: var(--transition);
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/* 卡片样式 */
.card {
border: none;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
transition: var(--transition);
}
.card:hover {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
响应式样式 (css/responsive.css)
css
/* 移动端优化 */
@media (max-width: 768px) {
.hero h1 {
font-size: 2rem;
}
.hero .lead {
font-size: 1rem;
}
.hero-buttons .btn {
display: block;
width: 100%;
margin-bottom: 1rem;
}
.stat-card {
margin-bottom: 1rem;
}
.announcement-card {
margin-bottom: 1rem;
}
}
/* 平板端优化 */
@media (min-width: 769px) and (max-width: 1024px) {
.hero h1 {
font-size: 2.5rem;
}
.hero-buttons .btn {
margin-bottom: 0.5rem;
}
}
/* 大屏幕优化 */
@media (min-width: 1025px) {
.hero {
padding: 6rem 0;
}
.hero h1 {
font-size: 3.5rem;
}
.hero .lead {
font-size: 1.25rem;
}
.stat-card {
padding: 3rem 2rem;
}
}
/* 超小屏幕优化 */
@media (max-width: 576px) {
.container {
padding-left: 15px;
padding-right: 15px;
}
.navbar-brand img {
height: 25px;
}
.hero {
padding: 3rem 0;
}
.hero h1 {
font-size: 1.75rem;
}
.hero .lead {
font-size: 0.9rem;
}
}
/* 横屏模式优化 */
@media (orientation: landscape) and (max-height: 500px) {
.hero {
padding: 2rem 0;
}
.hero h1 {
font-size: 2rem;
margin-bottom: 1rem;
}
.hero .lead {
margin-bottom: 1rem;
}
}
/* 高分辨率屏幕优化 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.hero::before {
background-image: url('../images/hero-bg@2x.svg');
}
.navbar-brand img {
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
}
/* 打印样式 */
@media print {
.navbar,
.hero,
.footer,
.btn {
display: none !important;
}
.main-content {
margin: 0 !important;
padding: 0 !important;
}
.card {
border: 1px solid #000 !important;
box-shadow: none !important;
}
}
🔧 JavaScript开发
主脚本文件 (js/main.js)
javascript
/**
* Yuan-ICP 主题主脚本文件
*/
// 主题命名空间
window.YuanICPTheme = {
// 配置选项
config: {
debug: false,
animationDuration: 300,
scrollOffset: 100
},
// 初始化函数
init: function() {
this.initComponents();
this.bindEvents();
this.initAnimations();
if (this.config.debug) {
console.log('Yuan-ICP 主题已初始化');
}
},
// 初始化组件
initComponents: function() {
this.initTooltips();
this.initPopovers();
this.initScrollSpy();
this.initBackToTop();
},
// 绑定事件
bindEvents: function() {
this.bindScrollEvents();
this.bindFormEvents();
this.bindNavigationEvents();
},
// 初始化动画
initAnimations: function() {
this.initScrollAnimations();
this.initHoverEffects();
},
// 初始化工具提示
initTooltips: function() {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
},
// 初始化弹出框
initPopovers: function() {
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
},
// 初始化滚动监听
initScrollSpy: function() {
const scrollSpyElement = document.querySelector('[data-bs-spy="scroll"]');
if (scrollSpyElement) {
new bootstrap.ScrollSpy(scrollSpyElement);
}
},
// 初始化返回顶部按钮
initBackToTop: function() {
const backToTopBtn = document.createElement('button');
backToTopBtn.innerHTML = '<i class="fas fa-arrow-up"></i>';
backToTopBtn.className = 'btn btn-primary back-to-top';
backToTopBtn.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
display: none;
width: 50px;
height: 50px;
border-radius: 50%;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
`;
document.body.appendChild(backToTopBtn);
// 显示/隐藏返回顶部按钮
window.addEventListener('scroll', () => {
if (window.pageYOffset > this.config.scrollOffset) {
backToTopBtn.style.display = 'block';
} else {
backToTopBtn.style.display = 'none';
}
});
// 返回顶部功能
backToTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
},
// 绑定滚动事件
bindScrollEvents: function() {
let ticking = false;
function updateScroll() {
// 滚动时的处理逻辑
ticking = false;
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(updateScroll);
ticking = true;
}
}
window.addEventListener('scroll', requestTick);
},
// 绑定表单事件
bindFormEvents: function() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', this.handleFormSubmit.bind(this));
form.addEventListener('input', this.handleFormInput.bind(this));
});
},
// 处理表单提交
handleFormSubmit: function(event) {
const form = event.target;
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 提交中...';
}
// 表单验证逻辑
if (!this.validateForm(form)) {
event.preventDefault();
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = '提交';
}
}
},
// 处理表单输入
handleFormInput: function(event) {
const input = event.target;
const formGroup = input.closest('.form-group');
if (formGroup) {
this.validateField(input);
}
},
// 表单验证
validateForm: function(form) {
let isValid = true;
const inputs = form.querySelectorAll('input[required], textarea[required], select[required]');
inputs.forEach(input => {
if (!this.validateField(input)) {
isValid = false;
}
});
return isValid;
},
// 字段验证
validateField: function(field) {
const value = field.value.trim();
const formGroup = field.closest('.form-group');
const errorElement = formGroup.querySelector('.invalid-feedback');
// 移除之前的验证状态
field.classList.remove('is-valid', 'is-invalid');
if (errorElement) {
errorElement.remove();
}
// 验证逻辑
let isValid = true;
let errorMessage = '';
if (field.hasAttribute('required') && !value) {
isValid = false;
errorMessage = '此字段为必填项';
} else if (field.type === 'email' && value && !this.isValidEmail(value)) {
isValid = false;
errorMessage = '请输入有效的邮箱地址';
} else if (field.type === 'url' && value && !this.isValidUrl(value)) {
isValid = false;
errorMessage = '请输入有效的URL地址';
}
// 应用验证结果
if (isValid) {
field.classList.add('is-valid');
} else {
field.classList.add('is-invalid');
this.showFieldError(formGroup, errorMessage);
}
return isValid;
},
// 显示字段错误
showFieldError: function(formGroup, message) {
const errorElement = document.createElement('div');
errorElement.className = 'invalid-feedback';
errorElement.textContent = message;
formGroup.appendChild(errorElement);
},
// 邮箱验证
isValidEmail: function(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
// URL验证
isValidUrl: function(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
},
// 绑定导航事件
bindNavigationEvents: function() {
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
navLinks.forEach(link => {
link.addEventListener('click', this.handleNavClick.bind(this));
});
},
// 处理导航点击
handleNavClick: function(event) {
const link = event.target;
const href = link.getAttribute('href');
// 平滑滚动到锚点
if (href.startsWith('#')) {
event.preventDefault();
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
},
// 初始化滚动动画
initScrollAnimations: function() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
}
});
}, observerOptions);
const animatedElements = document.querySelectorAll('.animate-on-scroll');
animatedElements.forEach(el => observer.observe(el));
},
// 初始化悬停效果
initHoverEffects: function() {
const hoverElements = document.querySelectorAll('.hover-effect');
hoverElements.forEach(element => {
element.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.05)';
});
element.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
});
},
// 工具函数
utils: {
// 防抖函数
debounce: function(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// 节流函数
throttle: function(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
// 格式化日期
formatDate: function(date) {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(date));
},
// 格式化数字
formatNumber: function(num) {
return new Intl.NumberFormat('zh-CN').format(num);
}
}
};
// 页面加载完成后初始化主题
document.addEventListener('DOMContentLoaded', function() {
YuanICPTheme.init();
});
// 导出主题对象(用于模块化开发)
if (typeof module !== 'undefined' && module.exports) {
module.exports = YuanICPTheme;
}
🎯 主题配置系统
主题配置选项
php
// 主题配置示例
$theme_config = [
'colors' => [
'primary' => '#007bff',
'secondary' => '#6c757d',
'success' => '#28a745',
'danger' => '#dc3545',
'warning' => '#ffc107',
'info' => '#17a2b8'
],
'typography' => [
'font_family' => 'Segoe UI, Tahoma, Geneva, Verdana, sans-serif',
'font_size_base' => '16px',
'line_height_base' => '1.6'
],
'layout' => [
'container_max_width' => '1200px',
'sidebar_width' => '250px',
'header_height' => '70px'
],
'features' => [
'dark_mode' => false,
'animations' => true,
'responsive' => true
]
];
动态配置加载
php
// 从数据库加载主题配置
function loadThemeConfig($theme_name) {
$db = db();
$stmt = $db->prepare("
SELECT config_key, config_value
FROM system_config
WHERE config_key LIKE 'theme_{$theme_name}_%'
");
$stmt->execute();
$config = [];
while ($row = $stmt->fetch()) {
$key = str_replace("theme_{$theme_name}_", '', $row['config_key']);
$config[$key] = $row['config_value'];
}
return $config;
}
🚀 主题开发最佳实践
1. 设计原则
- 一致性:保持整个主题的设计风格一致
- 可用性:确保用户界面易于使用
- 可访问性:支持不同用户的需求
- 性能:优化加载速度和响应时间
2. 代码规范
- 命名规范:使用有意义的类名和ID
- 注释规范:为复杂代码添加注释
- 文件组织:合理组织CSS和JavaScript文件
- 版本控制:使用语义化版本号
3. 性能优化
- CSS优化:合并和压缩CSS文件
- JavaScript优化:异步加载非关键脚本
- 图片优化:使用适当的图片格式和大小
- 缓存策略:合理设置缓存头
4. 兼容性考虑
- 浏览器支持:支持主流浏览器的最新版本
- 响应式设计:确保在各种设备上的显示效果
- 渐进增强:基础功能在所有环境下可用
🔧 主题测试和调试
测试环境
bash
# 创建测试环境
mkdir theme-test
cd theme-test
# 复制主题文件
cp -r ../themes/my-theme ./
# 启动本地服务器
php -S localhost:8000
调试工具
javascript
// 启用调试模式
YuanICPTheme.config.debug = true;
// 控制台日志
console.log('主题状态:', YuanICPTheme);
// 性能监控
console.time('主题初始化');
YuanICPTheme.init();
console.timeEnd('主题初始化');
常见问题排查
- 样式不生效:检查CSS文件路径和语法
- JavaScript错误:查看浏览器控制台错误信息
- 响应式问题:测试不同屏幕尺寸
- 性能问题:使用浏览器开发者工具分析
📚 相关文档
🎯 总结
通过本指南,您可以:
- 创建自定义主题:按照规范创建完整的主题
- 优化用户体验:提供美观、易用的界面
- 扩展系统功能:通过主题系统扩展功能
- 维护主题代码:遵循最佳实践,便于维护
记住,好的主题不仅仅是美观,更重要的是提供良好的用户体验和功能完整性。通过不断测试和优化,您可以创建出优秀的Yuan-ICP主题。