388 lines
19 KiB
PHP
Executable File
388 lines
19 KiB
PHP
Executable File
<h1 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||
<i class="fas fa-cog text-primary mr-3"></i>
|
||
系统设置
|
||
</h1>
|
||
|
||
<!-- 设置表单 -->
|
||
<form id="settingsForm" class="space-y-6">
|
||
<!-- Logo设置 -->
|
||
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
|
||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||
<i class="fas fa-image text-primary mr-2"></i>
|
||
Logo设置
|
||
</h3>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<!-- Logo -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">网站Logo</label>
|
||
<div class="flex items-center gap-4">
|
||
<div class="flex-shrink-0">
|
||
<img id="site_logo_preview" src="<?= htmlspecialchars($settings['site_logo'] ?? '') ?>"
|
||
alt="Logo预览"
|
||
class="w-32 h-32 object-contain border border-gray-200 rounded-lg bg-gray-50 p-2"
|
||
style="<?= empty($settings['site_logo'] ?? '') ? 'display:none;' : '' ?>">
|
||
</div>
|
||
<div class="flex-1">
|
||
<input type="file" id="site_logo_file" name="site_logo_file" accept="image/jpeg,image/png,image/gif,image/webp"
|
||
class="hidden" onchange="handleLogoUpload(this, 'site_logo')">
|
||
<button type="button" onclick="document.getElementById('site_logo_file').click()"
|
||
class="w-full px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors">
|
||
<i class="fas fa-upload mr-2"></i>选择图片
|
||
</button>
|
||
<input type="hidden" id="site_logo" name="site_logo" value="<?= htmlspecialchars($settings['site_logo'] ?? '') ?>">
|
||
<p class="text-xs text-gray-500 mt-2">建议尺寸:200x60px,支持JPG/PNG/GIF/WEBP,前端统一使用此Logo</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Favicon -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">网站图标 (Favicon)</label>
|
||
<div class="flex items-center gap-4">
|
||
<div class="flex-shrink-0">
|
||
<img id="site_favicon_preview" src="<?= htmlspecialchars($settings['site_favicon'] ?? '') ?>"
|
||
alt="Favicon预览"
|
||
class="w-16 h-16 object-contain border border-gray-200 rounded-lg bg-gray-50 p-2"
|
||
style="<?= empty($settings['site_favicon'] ?? '') ? 'display:none;' : '' ?>">
|
||
</div>
|
||
<div class="flex-1">
|
||
<input type="file" id="site_favicon_file" name="site_favicon_file" accept="image/x-icon,image/vnd.microsoft.icon,image/png"
|
||
class="hidden" onchange="handleLogoUpload(this, 'site_favicon')">
|
||
<button type="button" onclick="document.getElementById('site_favicon_file').click()"
|
||
class="w-full px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors">
|
||
<i class="fas fa-upload mr-2"></i>选择图标
|
||
</button>
|
||
<input type="hidden" id="site_favicon" name="site_favicon" value="<?= htmlspecialchars($settings['site_favicon'] ?? '') ?>">
|
||
<p class="text-xs text-gray-500 mt-2">建议尺寸:32x32px,支持ICO/PNG格式</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 网站基本信息 -->
|
||
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
|
||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||
<i class="fas fa-info-circle text-primary mr-2"></i>
|
||
网站基本信息
|
||
</h3>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label for="site_title" class="block text-sm font-medium text-gray-700 mb-1">网站标题</label>
|
||
<input type="text" id="site_title" name="site_title"
|
||
value="<?= htmlspecialchars($settings['site_title'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="请输入网站标题">
|
||
</div>
|
||
<div>
|
||
<label for="site_description" class="block text-sm font-medium text-gray-700 mb-1">网站描述</label>
|
||
<textarea id="site_description" name="site_description" rows="3"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors resize-none"
|
||
placeholder="请输入网站描述"><?= htmlspecialchars($settings['site_description'] ?? '') ?></textarea>
|
||
<p class="text-xs text-gray-500 mt-1">用于SEO优化,建议控制在150字以内</p>
|
||
</div>
|
||
<div>
|
||
<label for="site_keywords" class="block text-sm font-medium text-gray-700 mb-1">网站关键词</label>
|
||
<input type="text" id="site_keywords" name="site_keywords"
|
||
value="<?= htmlspecialchars($settings['site_keywords'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="请输入关键词,用逗号分隔">
|
||
<p class="text-xs text-gray-500 mt-1">多个关键词用逗号分隔</p>
|
||
</div>
|
||
<div>
|
||
<label for="site_copyright" class="block text-sm font-medium text-gray-700 mb-1">版权信息</label>
|
||
<input type="text" id="site_copyright" name="site_copyright"
|
||
value="<?= htmlspecialchars($settings['site_copyright'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="请输入版权信息">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="flex justify-end gap-3 pt-4">
|
||
<button type="button" onclick="resetSettings()"
|
||
class="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors">
|
||
<i class="fas fa-undo mr-2"></i>重置
|
||
</button>
|
||
<button type="button" id="saveSettingsBtn"
|
||
class="px-6 py-2 bg-primary hover:bg-primary/90 text-white rounded-lg shadow hover:shadow-md transition-all duration-200">
|
||
<i class="fas fa-save mr-2"></i>保存设置
|
||
</button>
|
||
</div>
|
||
|
||
<!-- SMTP 邮件配置 -->
|
||
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
|
||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||
<i class="fas fa-envelope text-primary mr-2"></i>
|
||
SMTP 邮件配置
|
||
</h3>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">SMTP 服务器</label>
|
||
<input type="text" id="smtp_host" name="smtp_host" value="<?= htmlspecialchars($settings['smtp_host'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="如 smtp.gmail.com">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">端口</label>
|
||
<input type="number" id="smtp_port" name="smtp_port" value="<?= htmlspecialchars($settings['smtp_port'] ?? '465') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="465">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">加密方式</label>
|
||
<select id="smtp_encryption" name="smtp_encryption"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors">
|
||
<option value="ssl" <?= ($settings['smtp_encryption'] ?? 'ssl') === 'ssl' ? 'selected' : '' ?>>SSL (端口465)</option>
|
||
<option value="tls" <?= ($settings['smtp_encryption'] ?? '') === 'tls' ? 'selected' : '' ?>>TLS (端口587)</option>
|
||
<option value="none" <?= ($settings['smtp_encryption'] ?? '') === 'none' ? 'selected' : '' ?>>无加密 (端口25)</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">SMTP 用户名</label>
|
||
<input type="text" id="smtp_user" name="smtp_user" value="<?= htmlspecialchars($settings['smtp_user'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="your@email.com">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">SMTP 密码</label>
|
||
<input type="password" id="smtp_pass" name="smtp_pass" value="<?= htmlspecialchars($settings['smtp_pass'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="密码或应用专用密码">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">发件人地址</label>
|
||
<input type="text" id="smtp_from" name="smtp_from" value="<?= htmlspecialchars($settings['smtp_from'] ?? '') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="noreply@yourdomain.com(留空则用SMTP用户名)">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">发件人名称</label>
|
||
<input type="text" id="smtp_from_name" name="smtp_from_name" value="<?= htmlspecialchars($settings['smtp_from_name'] ?? 'PK10') ?>"
|
||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors"
|
||
placeholder="PK10">
|
||
</div>
|
||
<div class="flex items-end">
|
||
<button type="button" onclick="testSmtp()"
|
||
class="w-full px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors">
|
||
<i class="fas fa-paper-plane mr-2"></i>发送测试邮件
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<p class="text-xs text-gray-500 mt-3">
|
||
<i class="fas fa-info-circle mr-1"></i>
|
||
常用配置:Gmail(smtp.gmail.com:465/SSL)、Outlook(smtp-mail.outlook.com:587/TLS)、QQ邮箱(smtp.qq.com:465/SSL)、163邮箱(smtp.163.com:465/SSL)
|
||
</p>
|
||
</div>
|
||
</form>
|
||
|
||
<script>
|
||
function testSmtp(){
|
||
const email=prompt('输入接收测试邮件的邮箱地址:');
|
||
if(!email)return;
|
||
// 先保存当前配置再测试
|
||
const data={};
|
||
document.querySelectorAll('#settingsForm [name^="smtp_"]').forEach(el=>{data[el.name]=el.value;});
|
||
fetch('/admin/settings/save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})
|
||
.then(()=>fetch('/admin/settings/smtp-test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({email})}))
|
||
.then(r=>r.json())
|
||
.then(d=>{showMessage(d.message,d.success?'success':'error');})
|
||
.catch(()=>{showMessage('测试失败','error');});
|
||
}
|
||
</script>
|
||
|
||
<script>
|
||
// Logo上传处理
|
||
function handleLogoUpload(input, logoType) {
|
||
const file = input.files[0];
|
||
if (!file) return;
|
||
|
||
// 验证文件大小(5MB)
|
||
if (file.size > 5 * 1024 * 1024) {
|
||
alert('文件大小不能超过5MB');
|
||
input.value = '';
|
||
return;
|
||
}
|
||
|
||
// 验证文件类型
|
||
const allowedTypes = logoType === 'site_favicon'
|
||
? ['image/x-icon', 'image/vnd.microsoft.icon', 'image/png']
|
||
: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||
|
||
if (!allowedTypes.includes(file.type)) {
|
||
alert('不支持的文件类型');
|
||
input.value = '';
|
||
return;
|
||
}
|
||
|
||
// 预览图片
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
const previewId = logoType + '_preview';
|
||
const previewImg = document.getElementById(previewId);
|
||
if (previewImg) {
|
||
previewImg.src = e.target.result;
|
||
previewImg.style.display = '';
|
||
}
|
||
};
|
||
reader.readAsDataURL(file);
|
||
|
||
// 立即上传
|
||
uploadLogo(file, logoType);
|
||
}
|
||
|
||
// 上传Logo到服务器
|
||
function uploadLogo(file, logoType) {
|
||
const formData = new FormData();
|
||
formData.append(logoType + '_file', file);
|
||
formData.append('logo_type', logoType);
|
||
|
||
// 显示上传中状态
|
||
const saveBtn = document.getElementById('saveSettingsBtn');
|
||
const originalText = saveBtn.innerHTML;
|
||
saveBtn.disabled = true;
|
||
saveBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>上传中...';
|
||
|
||
fetch('/admin/settings/save', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
// 更新隐藏字段的值
|
||
const hiddenInput = document.getElementById(logoType);
|
||
const previewImg = document.getElementById(logoType + '_preview');
|
||
if (data.data && data.data[logoType]) {
|
||
if (hiddenInput) hiddenInput.value = data.data[logoType];
|
||
if (previewImg) {
|
||
previewImg.src = data.data[logoType];
|
||
previewImg.style.display = '';
|
||
}
|
||
}
|
||
|
||
// 显示成功提示
|
||
showMessage('Logo上传成功', 'success');
|
||
} else {
|
||
alert('上传失败: ' + (data.message || '未知错误'));
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Upload error:', error);
|
||
alert('上传失败,请稍后重试');
|
||
})
|
||
.finally(() => {
|
||
saveBtn.disabled = false;
|
||
saveBtn.innerHTML = originalText;
|
||
});
|
||
}
|
||
|
||
// 保存设置
|
||
document.getElementById('saveSettingsBtn').addEventListener('click', function() {
|
||
const form = document.getElementById('settingsForm');
|
||
const formData = new FormData(form);
|
||
|
||
// 将FormData转换为JSON对象
|
||
const data = {};
|
||
for (let [key, value] of formData.entries()) {
|
||
// 跳过文件输入
|
||
if (key.endsWith('_file')) continue;
|
||
data[key] = value;
|
||
}
|
||
|
||
// 发送保存请求
|
||
const saveBtn = this;
|
||
const originalText = saveBtn.innerHTML;
|
||
saveBtn.disabled = true;
|
||
saveBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>保存中...';
|
||
|
||
fetch('/admin/settings/save', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.status === 'success') {
|
||
showMessage('设置保存成功', 'success');
|
||
// 如果有返回的数据,更新页面
|
||
if (data.data) {
|
||
updateSettingsPreview(data.data);
|
||
}
|
||
} else {
|
||
showMessage('保存失败: ' + (data.message || '未知错误'), 'error');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Save error:', error);
|
||
showMessage('保存失败,请稍后重试', 'error');
|
||
})
|
||
.finally(() => {
|
||
saveBtn.disabled = false;
|
||
saveBtn.innerHTML = originalText;
|
||
});
|
||
});
|
||
|
||
// 重置设置
|
||
function resetSettings() {
|
||
if (confirm('确定要重置所有设置吗?')) {
|
||
location.reload();
|
||
}
|
||
}
|
||
|
||
// 更新预览
|
||
function updateSettingsPreview(settings) {
|
||
const logoPreview = document.getElementById('site_logo_preview');
|
||
if (logoPreview) {
|
||
if (settings.site_logo) {
|
||
logoPreview.src = settings.site_logo;
|
||
logoPreview.style.display = '';
|
||
} else {
|
||
logoPreview.src = '';
|
||
logoPreview.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
const faviconPreview = document.getElementById('site_favicon_preview');
|
||
if (faviconPreview) {
|
||
if (settings.site_favicon) {
|
||
faviconPreview.src = settings.site_favicon;
|
||
faviconPreview.style.display = '';
|
||
} else {
|
||
faviconPreview.src = '';
|
||
faviconPreview.style.display = 'none';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示消息提示
|
||
function showMessage(message, type = 'success') {
|
||
// 创建提示元素
|
||
const messageEl = document.createElement('div');
|
||
messageEl.className = `fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 ${
|
||
type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white'
|
||
}`;
|
||
messageEl.innerHTML = `
|
||
<div class="flex items-center">
|
||
<i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'} mr-2"></i>
|
||
<span>${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(messageEl);
|
||
|
||
// 3秒后自动移除
|
||
setTimeout(() => {
|
||
messageEl.remove();
|
||
}, 3000);
|
||
}
|
||
|
||
// 页面加载时获取设置
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 设置已经在页面加载时通过PHP变量传递,无需额外请求
|
||
});
|
||
</script>
|
||
|