Browse Source

实现管理员统计页面及相关功能

Boss3
Default User 2 months ago
parent
commit
fd310a2881
  1. 1132
      Management.html
  2. 19
      Reject.html
  3. 189
      Reject.js
  4. 12
      login.html
  5. 52
      supply.html

1132
Management.html

File diff suppressed because it is too large

19
Reject.html

@ -1038,6 +1038,7 @@
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
">
<button class="type-btn" id="managementBtn" style="border-right: 1px solid rgba(0, 0, 0, 0.1);">货源管理</button>
<button class="type-btn active" data-type="supply">货源审核</button>
<button class="type-btn" data-type="supplier">供应商审核</button>
<button class="type-btn" id="createSupplyBtn" style="background-color: #52c41a; color: white; border-left: 1px solid rgba(0, 0, 0, 0.1);">创建货源</button>
@ -1308,6 +1309,18 @@
window.location.href = 'login.html';
});
// 根据角色控制货源管理按钮的显示
const managementBtnEl = document.getElementById('managementBtn');
if (managementBtnEl) {
if (parsedUserInfo.projectName === '管理员') {
// 管理员显示按钮
managementBtnEl.style.display = 'block';
} else {
// 非管理员隐藏按钮
managementBtnEl.style.display = 'none';
}
}
return true;
}
@ -1343,6 +1356,7 @@
const statusBtns = document.querySelectorAll('.status-btn');
// 创建货源相关DOM元素
const createSupplyBtnEl = document.getElementById('createSupplyBtn');
const managementBtnEl = document.getElementById('managementBtn');
const createSupplyModalEl = document.getElementById('createSupplyModal');
const supplyNameEl = document.getElementById('supplyName');
const supplyPriceEl = document.getElementById('supplyPrice');
@ -1555,6 +1569,11 @@
}
});
// 货源管理按钮点击事件 - 跳转到Management.html页面
managementBtnEl.addEventListener('click', () => {
window.location.href = 'Management.html';
});
// 创建货源按钮点击事件 - 跳转到supply.html页面
createSupplyBtnEl.addEventListener('click', () => {
window.location.href = 'supply.html';

189
Reject.js

@ -104,7 +104,7 @@ setInterval(async () => {
// 批量更新商品状态为sold_out
for (const product of productsToOffline) {
await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
'UPDATE products SET status = ?, label = 1, updated_at = NOW() WHERE id = ?',
['sold_out', product.id]
);
@ -1320,11 +1320,18 @@ app.put('/api/supplies/:id', async (req, res) => {
return sendResponse(res, false, null, '货源不存在');
}
// 更新状态
// 更新状态,当状态为sold_out时同时锁定label
if (status === 'sold_out') {
await connection.query(
'UPDATE products SET status = ?, label = 1, updated_at = NOW() WHERE id = ?',
[status, productId]
);
} else {
await connection.query(
'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
[status, productId]
);
}
// 提交事务
await connection.commit();
@ -1631,9 +1638,183 @@ app.get('/api/suppliers', async (req, res) => {
}
});
// 首页路由
// 管理员统计 - 获取货源创建统计数据
app.get('/api/admin/stats/supplies', async (req, res) => {
try {
const { filter } = req.query;
const connection = await pool.getConnection();
// 计算时间范围
let timeCondition = '';
const now = new Date();
if (filter === 'today') {
// 今天
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
timeCondition = `AND created_at >= '${today.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'yesterday') {
// 昨天
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
timeCondition = `AND created_at >= '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${today.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'beforeYesterday') {
// 前天
const beforeYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 2);
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
timeCondition = `AND created_at >= '${beforeYesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'week') {
// 本周
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'month') {
// 本月
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'custom') {
// 自定义时间范围
const { startDate, endDate } = req.query;
if (startDate) {
// 开始日期,格式化为YYYY-MM-DD 00:00:00
const start = new Date(startDate);
start.setHours(0, 0, 0, 0);
timeCondition += `AND created_at >= '${start.toISOString().slice(0, 19).replace('T', ' ')}'`;
}
if (endDate) {
// 结束日期,格式化为YYYY-MM-DD 23:59:59
const end = new Date(endDate);
end.setHours(23, 59, 59, 999);
timeCondition += `AND created_at <= '${end.toISOString().slice(0, 19).replace('T', ' ')}'`;
}
}
// 获取每个卖家创建的货源数量,关联users表获取nickName
// 替换created_at为p.created_at,避免歧义
const chartTimeCondition = timeCondition.replace(/created_at/g, 'p.created_at');
const [chartData] = await connection.query(`
SELECT p.sellerId, u.nickName, COUNT(*) as count
FROM products p
LEFT JOIN users u ON p.sellerId = u.userId
WHERE 1=1 ${chartTimeCondition}
GROUP BY p.sellerId, u.nickName
ORDER BY count DESC
`);
// 获取总体统计信息
const [totalSuppliesResult] = await connection.query(`
SELECT COUNT(*) as total FROM products WHERE 1=1 ${timeCondition}
`);
const totalSupplies = totalSuppliesResult[0].total;
const [totalUsersResult] = await connection.query(`
SELECT COUNT(DISTINCT sellerId) as total FROM products WHERE 1=1 ${timeCondition}
`);
const totalUsers = totalUsersResult[0].total;
const avgPerUser = totalUsers > 0 ? totalSupplies / totalUsers : 0;
connection.release();
sendResponse(res, true, {
chartData,
stats: {
totalSupplies,
totalUsers,
avgPerUser
}
}, '获取统计数据成功');
} catch (error) {
console.error('获取统计数据失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '获取统计数据失败');
}
});
// 管理员统计 - 获取指定卖家的货源列表
app.get('/api/admin/supplies', async (req, res) => {
try {
const { sellerId, filter } = req.query;
const connection = await pool.getConnection();
// 计算时间范围
let timeCondition = '';
const now = new Date();
if (filter === 'today') {
// 今天
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
timeCondition = `AND created_at >= '${today.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'yesterday') {
// 昨天
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
timeCondition = `AND created_at >= '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${today.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'beforeYesterday') {
// 前天
const beforeYesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 2);
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
timeCondition = `AND created_at >= '${beforeYesterday.toISOString().slice(0, 19).replace('T', ' ')}' AND created_at < '${yesterday.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'week') {
// 本周
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
timeCondition = `AND created_at >= '${weekAgo.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'month') {
// 本月
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
timeCondition = `AND created_at >= '${monthAgo.toISOString().slice(0, 19).replace('T', ' ')}'`;
} else if (filter === 'custom') {
// 自定义时间范围
const { startDate, endDate } = req.query;
if (startDate) {
// 开始日期,格式化为YYYY-MM-DD 00:00:00
const start = new Date(startDate);
start.setHours(0, 0, 0, 0);
timeCondition += `AND created_at >= '${start.toISOString().slice(0, 19).replace('T', ' ')}'`;
}
if (endDate) {
// 结束日期,格式化为YYYY-MM-DD 23:59:59
const end = new Date(endDate);
end.setHours(23, 59, 59, 999);
timeCondition += `AND created_at <= '${end.toISOString().slice(0, 19).replace('T', ' ')}'`;
}
}
// 获取指定卖家的货源列表,关联users表获取创建人姓名
// 替换created_at为p.created_at,避免歧义
const suppliesTimeCondition = timeCondition.replace(/created_at/g, 'p.created_at');
const [supplies] = await connection.query(`
SELECT p.*, u.nickName
FROM products p
LEFT JOIN users u ON p.sellerId = u.userId
WHERE p.sellerId = ? ${suppliesTimeCondition}
ORDER BY p.created_at DESC
`, [sellerId]);
connection.release();
sendResponse(res, true, { supplies }, '获取货源列表成功');
} catch (error) {
console.error('获取货源列表失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '获取货源列表失败');
}
});
// 首页路由 - 根据角色重定向
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'Reject.html'));
// 注意:这里无法直接获取localStorage中的用户信息,因为localStorage是客户端的
// 实际生产环境中,应该通过cookie或token来获取用户角色信息
// 这里我们默认跳转到登录页面,让客户端处理角色跳转
res.sendFile(path.join(__dirname, 'login.html'));
});
// 管理员统计页面路由
app.get('/admin-stats', (req, res) => {
res.sendFile(path.join(__dirname, 'Management.html'));
});
// 货源管理页面路由
app.get('/management', (req, res) => {
res.sendFile(path.join(__dirname, 'Management.html'));
});
// 错误处理中间件

12
login.html

@ -227,8 +227,18 @@
localStorage.setItem('userInfo', JSON.stringify(result.data.userInfo));
localStorage.setItem('token', result.data.token);
// 跳转到审核页面
// 根据角色跳转到不同页面
const userInfo = result.data.userInfo;
if (userInfo.projectName === '管理员') {
// 管理员跳转到管理页面
window.location.href = 'Management.html';
} else if (userInfo.projectName === '采购员') {
// 采购员跳转到供应页面
window.location.href = 'supply.html';
} else {
// 其他角色默认跳转到审核页面
window.location.href = 'Reject.html';
}
} else {
// 登录失败
loginError.textContent = result.message || '登录失败,请检查用户名和密码';

52
supply.html

@ -5023,8 +5023,45 @@
if (freshnessDisplay) freshnessDisplay.textContent = supply.freshness || '请选择新鲜程度';
selectedFreshness = supply.freshness || '';
// 复制图片
supplyData.uploadedImages = supply.images || supply.imageUrls || supply.imageList || [];
// 复制图片/视频,确保正确解析JSON字符串
let mediaUrls = [];
console.log('复制货源时的imageUrls数据:', supply.imageUrls);
console.log('复制货源时的imageUrls类型:', typeof supply.imageUrls);
if (supply.images) {
mediaUrls = Array.isArray(supply.images) ? supply.images : [];
console.log('使用supply.images:', mediaUrls);
} else if (supply.imageUrls) {
if (Array.isArray(supply.imageUrls)) {
mediaUrls = supply.imageUrls;
console.log('imageUrls是数组:', mediaUrls);
} else if (typeof supply.imageUrls === 'string') {
// 尝试解析JSON字符串
try {
console.log('尝试解析JSON字符串:', supply.imageUrls);
mediaUrls = JSON.parse(supply.imageUrls);
console.log('解析结果:', mediaUrls);
// 确保解析后是数组
if (!Array.isArray(mediaUrls)) {
console.log('解析结果不是数组,重置为空数组');
mediaUrls = [];
}
} catch (e) {
console.error('解析imageUrls失败:', e);
mediaUrls = [];
}
}
} else if (supply.imageList) {
mediaUrls = Array.isArray(supply.imageList) ? supply.imageList : [];
console.log('使用supply.imageList:', mediaUrls);
}
console.log('最终的mediaUrls:', mediaUrls);
console.log('mediaUrls类型:', typeof mediaUrls);
console.log('mediaUrls是否为数组:', Array.isArray(mediaUrls));
supplyData.uploadedImages = mediaUrls;
console.log('supplyData.uploadedImages:', supplyData.uploadedImages);
renderUploadedImages();
// 规格、件数和采购价 - 支持中文逗号分隔和英文逗号分隔字符串
@ -5662,19 +5699,28 @@
const uploadArea = document.getElementById('uploadImages');
uploadArea.innerHTML = '';
console.log('renderUploadedImages被调用,mediaUrls:', supplyData.uploadedImages);
supplyData.uploadedImages.forEach((mediaUrl, index) => {
console.log('处理媒体URL:', mediaUrl, '索引:', index);
const mediaElement = document.createElement('div');
mediaElement.className = 'upload-image';
let mediaContent;
if (mediaUrl.startsWith('data:video/')) {
// 检查是否为视频文件(支持data:video/和普通视频URL)
const isVideo = mediaUrl.startsWith('data:video/') || mediaUrl.match(/\.(mp4|mov|avi|wmv|flv|webm|mkv)$/i);
if (isVideo) {
// 视频文件
console.log('检测到视频URL:', mediaUrl);
mediaContent = `
<video src="${mediaUrl}" alt="商品视频" onclick="previewImage('${mediaUrl}')" controls style="width: 100%; height: 100%; object-fit: cover;"></video>
<div class="delete-image" onclick="deleteUploadedImage(${index})"></div>
`;
} else {
// 图片文件
console.log('检测到图片URL:', mediaUrl);
mediaContent = `
<img src="${mediaUrl}" alt="商品图片" onclick="previewImage('${mediaUrl}')">
<div class="delete-image" onclick="deleteUploadedImage(${index})"></div>

Loading…
Cancel
Save