diff --git a/Management.html b/Management.html
new file mode 100644
index 0000000..879a002
--- /dev/null
+++ b/Management.html
@@ -0,0 +1,1132 @@
+
+
+
+
+
+ 管理货源页面
+
+
+
+
+
+
+
+
+
+
+
+
+
+
管理员统计页面
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Reject.html b/Reject.html
index 8f7739f..5e7915b 100644
--- a/Reject.html
+++ b/Reject.html
@@ -1038,6 +1038,7 @@
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
">
+
@@ -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';
diff --git a/Reject.js b/Reject.js
index 45ae2c1..234e554 100644
--- a/Reject.js
+++ b/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, '货源不存在');
}
- // 更新状态
- await connection.query(
- 'UPDATE products SET status = ?, updated_at = NOW() WHERE id = ?',
- [status, productId]
- );
+ // 更新状态,当状态为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'));
});
// 错误处理中间件
diff --git a/login.html b/login.html
index 1c14435..a23655d 100644
--- a/login.html
+++ b/login.html
@@ -227,8 +227,18 @@
localStorage.setItem('userInfo', JSON.stringify(result.data.userInfo));
localStorage.setItem('token', result.data.token);
- // 跳转到审核页面
- window.location.href = 'Reject.html';
+ // 根据角色跳转到不同页面
+ 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 || '登录失败,请检查用户名和密码';
diff --git a/supply.html b/supply.html
index b3ed115..77ff557 100644
--- a/supply.html
+++ b/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 = `
`;
} else {
// 图片文件
+ console.log('检测到图片URL:', mediaUrl);
mediaContent = `