初始提交
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkAuth();
|
||||
loadConfig();
|
||||
loadGroups();
|
||||
|
||||
document.getElementById('logout').addEventListener('click', logout);
|
||||
document.getElementById('featureToggle').addEventListener('change', toggleFeature);
|
||||
document.getElementById('addGroupBtn').addEventListener('click', addGroup);
|
||||
document.getElementById('uploadBtn').addEventListener('click', () => document.getElementById('fileInput').click());
|
||||
document.getElementById('fileInput').addEventListener('change', handleFileSelect);
|
||||
document.getElementById('cropCancel').addEventListener('click', closeCropModal);
|
||||
document.getElementById('cropConfirm').addEventListener('click', cropAndUpload);
|
||||
});
|
||||
|
||||
let cropper = null;
|
||||
let selectedFile = null;
|
||||
|
||||
function checkAuth() {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
window.location.href = '/login';
|
||||
return;
|
||||
}
|
||||
// 验证token有效性(可选)
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/config');
|
||||
const config = await response.json();
|
||||
document.getElementById('featureToggle').checked = config.feature_enabled === 'true';
|
||||
} catch (error) {
|
||||
console.error('加载配置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleFeature() {
|
||||
const enabled = document.getElementById('featureToggle').checked;
|
||||
try {
|
||||
await fetch('/api/config', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
},
|
||||
body: JSON.stringify({ key: 'feature_enabled', value: enabled.toString() })
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('更新配置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGroups() {
|
||||
try {
|
||||
const response = await fetch('/api/groups');
|
||||
const groups = await response.json();
|
||||
renderGroups(groups);
|
||||
updateGroupSelect(groups);
|
||||
} catch (error) {
|
||||
console.error('加载分组失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderGroups(groups) {
|
||||
const container = document.getElementById('groupsList');
|
||||
container.innerHTML = '';
|
||||
|
||||
groups.forEach(group => {
|
||||
const groupItem = document.createElement('div');
|
||||
groupItem.className = 'group-item';
|
||||
groupItem.innerHTML = `
|
||||
<div>
|
||||
<strong>${group.name}</strong>
|
||||
<span style="margin-left: 10px; color: #666;">展示时间: ${group.display_seconds}秒</span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-danger" onclick="deleteGroup(${group.id})">删除</button>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(groupItem);
|
||||
});
|
||||
}
|
||||
|
||||
function updateGroupSelect(groups) {
|
||||
const select = document.getElementById('groupSelect');
|
||||
select.innerHTML = '<option value="">请选择分组</option>';
|
||||
|
||||
groups.forEach(group => {
|
||||
const option = document.createElement('option');
|
||||
option.value = group.id;
|
||||
option.textContent = group.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
async function addGroup() {
|
||||
const name = prompt('请输入分组名称:');
|
||||
if (!name) return;
|
||||
|
||||
const displaySeconds = prompt('请输入展示时间(秒):', '30');
|
||||
if (!displaySeconds) return;
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await fetch('/api/groups', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ name, display_seconds: parseInt(displaySeconds) })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
loadGroups();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('添加分组失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteGroup(id) {
|
||||
if (!confirm('确定要删除这个分组吗?')) return;
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await fetch(`/api/groups/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
loadGroups();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除分组失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFileSelect(e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
selectedFile = file;
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
const cropImage = document.getElementById('cropImage');
|
||||
cropImage.src = e.target.result;
|
||||
|
||||
document.getElementById('cropModal').style.display = 'block';
|
||||
|
||||
if (cropper) {
|
||||
cropper.destroy();
|
||||
}
|
||||
|
||||
cropper = new Cropper(cropImage, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
autoCropArea: 1,
|
||||
ready: function() {
|
||||
// 裁剪器就绪
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
e.target.value = '';
|
||||
}
|
||||
|
||||
function closeCropModal() {
|
||||
document.getElementById('cropModal').style.display = 'none';
|
||||
if (cropper) {
|
||||
cropper.destroy();
|
||||
cropper = null;
|
||||
}
|
||||
selectedFile = null;
|
||||
}
|
||||
|
||||
async function cropAndUpload() {
|
||||
if (!cropper || !selectedFile) return;
|
||||
|
||||
const groupId = document.getElementById('groupSelect').value;
|
||||
if (!groupId) {
|
||||
alert('请先选择分组');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取裁剪后的canvas
|
||||
const canvas = cropper.getCroppedCanvas({
|
||||
width: 280,
|
||||
height: 280
|
||||
});
|
||||
|
||||
// 压缩图片
|
||||
const compressedBlob = await compressImage(canvas, 0.7);
|
||||
|
||||
// 创建FormData
|
||||
const formData = new FormData();
|
||||
formData.append('image', compressedBlob, selectedFile.name);
|
||||
formData.append('group_id', groupId);
|
||||
|
||||
// 上传
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await fetch('/api/upload', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
closeCropModal();
|
||||
loadImages(groupId);
|
||||
alert('上传成功');
|
||||
} else {
|
||||
alert('上传失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('上传失败:', error);
|
||||
alert('上传失败');
|
||||
}
|
||||
}
|
||||
|
||||
function compressImage(canvas, quality) {
|
||||
return new Promise((resolve) => {
|
||||
canvas.toBlob((blob) => {
|
||||
resolve(blob);
|
||||
}, 'image/jpeg', quality);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadImages(groupId) {
|
||||
if (!groupId) {
|
||||
document.getElementById('imageList').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/images?group_id=${groupId}`);
|
||||
const images = await response.json();
|
||||
renderImages(images);
|
||||
} catch (error) {
|
||||
console.error('加载图片失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderImages(images) {
|
||||
const container = document.getElementById('imageList');
|
||||
container.innerHTML = '';
|
||||
|
||||
images.forEach(image => {
|
||||
const imageCard = document.createElement('div');
|
||||
imageCard.className = `image-card ${image.enabled ? '' : 'disabled'}`;
|
||||
imageCard.innerHTML = `
|
||||
<img src="/uploads/${image.filename}" alt="${image.original_name}">
|
||||
<div class="actions">
|
||||
<button class="btn btn-success" onclick="toggleImage(${image.id}, ${image.enabled ? 0 : 1})">
|
||||
${image.enabled ? '下架' : '上架'}
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="deleteImage(${image.id})">删除</button>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(imageCard);
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleImage(id, enable) {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const endpoint = enable ? 'enable' : 'disable';
|
||||
const response = await fetch(`/api/images/${id}/${endpoint}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const groupId = document.getElementById('groupSelect').value;
|
||||
loadImages(groupId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新图片状态失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteImage(id) {
|
||||
if (!confirm('确定要删除这张图片吗?')) return;
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await fetch(`/api/images/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const groupId = document.getElementById('groupSelect').value;
|
||||
loadImages(groupId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除图片失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 分组选择变化时加载图片
|
||||
document.getElementById('groupSelect').addEventListener('change', function() {
|
||||
loadImages(this.value);
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadDisplayData();
|
||||
});
|
||||
|
||||
let currentGroupIndex = 0;
|
||||
let groups = [];
|
||||
let displayTimer = null;
|
||||
|
||||
async function loadDisplayData() {
|
||||
try {
|
||||
const response = await fetch('/api/display');
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.enabled) {
|
||||
document.getElementById('feature-disabled').style.display = 'block';
|
||||
document.getElementById('image-display').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('feature-disabled').style.display = 'none';
|
||||
document.getElementById('image-display').style.display = 'block';
|
||||
|
||||
groups = data.groups;
|
||||
if (groups.length === 0) {
|
||||
document.getElementById('group-name').textContent = '暂无分组';
|
||||
return;
|
||||
}
|
||||
|
||||
showGroup(0);
|
||||
startRotation();
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showGroup(index) {
|
||||
if (index >= groups.length) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
currentGroupIndex = index;
|
||||
const group = groups[index];
|
||||
|
||||
document.getElementById('group-name').textContent = group.name;
|
||||
|
||||
const grid = document.getElementById('image-grid');
|
||||
grid.innerHTML = '';
|
||||
|
||||
group.images.forEach(image => {
|
||||
const imageItem = document.createElement('div');
|
||||
imageItem.className = 'image-item';
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = `/uploads/${image.filename}`;
|
||||
img.alt = image.original_name;
|
||||
img.loading = 'lazy';
|
||||
|
||||
// 长按保存功能
|
||||
let pressTimer;
|
||||
img.addEventListener('touchstart', function(e) {
|
||||
pressTimer = setTimeout(function() {
|
||||
saveImage(`/uploads/${image.filename}`, image.original_name);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
img.addEventListener('touchend', function() {
|
||||
clearTimeout(pressTimer);
|
||||
});
|
||||
|
||||
img.addEventListener('touchmove', function() {
|
||||
clearTimeout(pressTimer);
|
||||
});
|
||||
|
||||
imageItem.appendChild(img);
|
||||
grid.appendChild(imageItem);
|
||||
});
|
||||
|
||||
// 更新倒计时
|
||||
updateTimer(group.display_seconds);
|
||||
}
|
||||
|
||||
function updateTimer(seconds) {
|
||||
const timerElement = document.getElementById('group-timer');
|
||||
let remaining = seconds;
|
||||
|
||||
if (displayTimer) {
|
||||
clearInterval(displayTimer);
|
||||
}
|
||||
|
||||
timerElement.textContent = `剩余时间: ${remaining}秒`;
|
||||
|
||||
displayTimer = setInterval(function() {
|
||||
remaining--;
|
||||
if (remaining <= 0) {
|
||||
clearInterval(displayTimer);
|
||||
showGroup(currentGroupIndex + 1);
|
||||
return;
|
||||
}
|
||||
timerElement.textContent = `剩余时间: ${remaining}秒`;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function startRotation() {
|
||||
if (groups.length <= 1) return;
|
||||
|
||||
// 自动轮播已在updateTimer中处理
|
||||
}
|
||||
|
||||
function saveImage(url, filename) {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename || 'image.jpg';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const errorDiv = document.getElementById('error');
|
||||
|
||||
loginForm.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
localStorage.setItem('token', data.token);
|
||||
window.location.href = '/admin';
|
||||
} else {
|
||||
errorDiv.textContent = data.error;
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
errorDiv.textContent = '登录失败,请重试';
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user