Browse Source

实现按规格下架功能的多项优化:支持多选、点击项勾选、默认全选、确认提示、点击外部关闭

Boss3
Default User 1 month ago
parent
commit
eb7a718add
  1. 89
      Reject.js
  2. 235
      supply.html

89
Reject.js

@ -1245,6 +1245,95 @@ app.post('/api/supplies/:id/unpublish', async (req, res) => {
}
});
// 按规格下架API - /api/supplies/:id/unpublish-spec
console.log('正在注册按规格下架API路由: /api/supplies/:id/unpublish-spec');
app.post('/api/supplies/:id/unpublish-spec', async (req, res) => {
console.log('收到按规格下架请求:', req.params.id, req.body);
try {
const connection = await pool.getConnection();
const productId = req.params.id;
const { specIndex } = req.body;
// 开始事务
await connection.beginTransaction();
// 检查当前状态
const [currentProduct] = await connection.query(
'SELECT spec_status, status FROM products WHERE id = ?',
[productId]
);
if (currentProduct.length === 0) {
await connection.rollback();
connection.release();
return sendResponse(res, false, null, '货源不存在');
}
// 解析spec_status为数组
let specStatuses = [];
if (currentProduct[0].spec_status) {
if (typeof currentProduct[0].spec_status === 'string') {
specStatuses = currentProduct[0].spec_status.split(',').map(s => s.trim());
} else if (Array.isArray(currentProduct[0].spec_status)) {
specStatuses = currentProduct[0].spec_status;
} else {
specStatuses = [currentProduct[0].spec_status];
}
}
// 确保specIndex是数组
let specIndices = specIndex;
if (!Array.isArray(specIndices)) {
specIndices = [specIndices];
}
// 将所有指定索引的规格状态改为1
specIndices.forEach(index => {
if (index >= 0 && index < specStatuses.length) {
specStatuses[index] = '1';
}
});
// 检查是否所有规格都已下架
const allUnpublished = specStatuses.every(status => status === '1');
// 更新规格状态
let updateQuery = 'UPDATE products SET spec_status = ?, updated_at = NOW() WHERE id = ?';
let updateParams = [specStatuses.join(','), productId];
// 如果所有规格都已下架,更新货源状态和label
if (allUnpublished) {
updateQuery = 'UPDATE products SET spec_status = ?, status = ?, label = 1, updated_at = NOW() WHERE id = ?';
updateParams = [specStatuses.join(','), 'sold_out', productId];
}
await connection.query(updateQuery, updateParams);
// 查询更新后的完整货源数据
const [updatedProduct] = await connection.query('SELECT * FROM products WHERE id = ?', [productId]);
// 提交事务
await connection.commit();
connection.release();
// 发送WebSocket消息通知所有客户端
broadcastMessage({
type: 'supply_status_change',
supplyId: productId,
action: 'unpublish_spec',
specIndex: specIndex,
status: allUnpublished ? 'sold_out' : currentProduct[0].status
});
// 返回更新后的完整货源数据
sendResponse(res, true, updatedProduct[0], '规格下架成功');
} catch (error) {
console.error('按规格下架失败:', error.message);
console.error('错误详情:', error);
sendResponse(res, false, null, '按规格下架失败');
}
});
// 上架货源API - /api/supplies/:id/publish
console.log('正在注册上架货源API路由: /api/supplies/:id/publish');
app.post('/api/supplies/:id/publish', async (req, res) => {

235
supply.html

@ -4806,12 +4806,34 @@
costprices = [supply.costprice || '0'];
}
// 解析规格状态数据
let specStatuses = [];
try {
if (supply.spec_status) {
if (typeof supply.spec_status === 'string') {
// 规格状态存储为英文逗号分隔的字符串
specStatuses = supply.spec_status.split(',').map(s => s.trim());
} else if (Array.isArray(supply.spec_status)) {
specStatuses = supply.spec_status;
} else {
specStatuses = [supply.spec_status];
}
}
} catch (e) {
specStatuses = [];
}
const maxLength = Math.max(specifications.length, quantities.length, costprices.length);
for (let i = 0; i < maxLength; i++) {
const spec = specifications[i] || '无';
const quantity = quantities[i] || '0';
const costprice = costprices[i] || '0';
specQuantityBoxes += `<div class="spec-quantity-box" style="border: 1px solid #f0f0f0; padding: 10px; border-radius: 8px; background-color: #fafafa; margin-bottom: 10px;">• 规格${i + 1}: ${spec} - 件数: ${quantity}件 - 采购价: ¥${costprice}</div>`;
const specStatus = specStatuses[i] || '0';
// 添加售空标识
const soldOutTag = specStatus === '1' ? '<span style="color: #f5222d; font-size: 12px; font-weight: 600; margin-left: 10px;">已售空</span>' : '';
specQuantityBoxes += `<div class="spec-quantity-box" style="border: 1px solid #f0f0f0; padding: 10px; border-radius: 8px; background-color: #fafafa; margin-bottom: 10px;">• 规格${i + 1}: ${spec} - 件数: ${quantity}件 - 采购价: ¥${costprice}${soldOutTag}</div>`;
}
return `
@ -6851,26 +6873,177 @@
});
}
// 下架货源
async function unpublishSupply(id) {
// 防止重复点击
if (isOperating) {
// 按规格下架弹窗
const unpublishSpecModal = document.createElement('div');
unpublishSpecModal.id = 'unpublishSpecModal';
unpublishSpecModal.className = 'select-modal';
unpublishSpecModal.innerHTML = `
<div class="select-content">
<div class="select-header">
<h3>选择要下架的规格</h3>
<button class="close-btn" onclick="hideUnpublishSpecModal()">×</button>
</div>
<div class="select-body">
<div id="unpublishSpecOptionsList" class="select-list"></div>
</div>
<div class="select-footer" style="padding: 16px 20px; display: flex; justify-content: space-between; border-top: 1px solid #f0f0f0; background-color: #fafafa;">
<button onclick="hideUnpublishSpecModal()" style="padding: 10px 24px; background-color: #f5f5f5; color: #666; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s ease;">取消</button>
<button onclick="confirmUnpublishSpec()" style="padding: 10px 24px; background-color: #1677ff; color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s ease;">确定</button>
</div>
</div>
`;
document.body.appendChild(unpublishSpecModal);
let currentUnpublishSupplyId = null;
let selectedSpecIndex = null;
// 显示按规格下架弹窗
function showUnpublishSpecModal(supplyId) {
currentUnpublishSupplyId = supplyId;
const supply = supplyData.supplies.find(s => String(s.id) === String(supplyId));
if (!supply) {
alert('找不到要下架的货源数据');
return;
}
// 解析规格
let specifications = [];
if (supply.specification) {
if (typeof supply.specification === 'string') {
specifications = supply.specification.split(',').filter(spec => spec.trim());
} else if (Array.isArray(supply.specification)) {
specifications = supply.specification;
} else {
specifications = [supply.specification];
}
}
// 解析件数
let quantities = [];
if (supply.quantity) {
if (typeof supply.quantity === 'string') {
quantities = supply.quantity.split(',').filter(qty => qty.trim());
} else if (Array.isArray(supply.quantity)) {
quantities = supply.quantity;
} else {
quantities = [supply.quantity];
}
}
// 解析采购价
let costprices = [];
if (supply.costprice) {
if (typeof supply.costprice === 'string') {
costprices = supply.costprice.split(',').filter(cp => cp.trim());
} else if (Array.isArray(supply.costprice)) {
costprices = supply.costprice;
} else {
costprices = [supply.costprice];
}
}
// 解析规格状态
let specStatuses = [];
if (supply.spec_status) {
if (typeof supply.spec_status === 'string') {
specStatuses = supply.spec_status.split(',').map(s => s.trim());
} else if (Array.isArray(supply.spec_status)) {
specStatuses = supply.spec_status;
} else {
specStatuses = [supply.spec_status];
}
}
// 生成规格选项
const optionsList = document.getElementById('unpublishSpecOptionsList');
optionsList.innerHTML = '';
for (let i = 0; i < specifications.length; i++) {
const spec = specifications[i] || `规格${i+1}`;
const qty = quantities[i] || '0件';
const cp = costprices[i] || '¥0';
const status = specStatuses[i] || '0';
const option = document.createElement('div');
option.className = 'select-item';
option.innerHTML = `
<div style="display: flex; align-items: center; gap: 10px;">
<input type="checkbox" name="specIndex" value="${i}" ${status === '1' ? 'disabled' : ''}>
<div style="flex: 1;">
<div style="font-weight: 600;">${spec}</div>
<div style="font-size: 12px; color: #666;">件数: ${qty} | 采购价: ${cp}</div>
</div>
${status === '1' ? '<span style="color: #f5222d; font-size: 12px; font-weight: 600;">已售空</span>' : ''}
</div>
`;
// 添加点击事件,点击整个项时切换checkbox状态
option.addEventListener('click', (e) => {
// 如果点击的是checkbox本身,不处理
if (e.target.type === 'checkbox') return;
const checkbox = option.querySelector('input[type="checkbox"]');
// 只有在checkbox未禁用时才切换状态
if (!checkbox.disabled) {
checkbox.checked = !checkbox.checked;
}
});
optionsList.appendChild(option);
}
// 默认勾选所有未禁用的规格
const checkboxes = optionsList.querySelectorAll('input[type="checkbox"]:not(:disabled)');
checkboxes.forEach(checkbox => {
checkbox.checked = true;
});
// 显示弹窗
document.getElementById('unpublishSpecModal').classList.add('active');
}
// 隐藏按规格下架弹窗
function hideUnpublishSpecModal() {
document.getElementById('unpublishSpecModal').classList.remove('active');
currentUnpublishSupplyId = null;
selectedSpecIndex = null;
}
// 确认按规格下架
async function confirmUnpublishSpec() {
// 获取所有选中的规格索引
const selectedCheckboxes = document.querySelectorAll('input[name="specIndex"]:checked');
if (selectedCheckboxes.length === 0) {
alert('请选择要下架的规格');
return;
}
// 将选中的索引收集到数组中
const selectedSpecIndices = Array.from(selectedCheckboxes).map(checkbox => parseInt(checkbox.value));
// 添加确认提示,防止误触
const isConfirmed = confirm('确定要下架所选规格吗?');
if (!isConfirmed) {
return;
}
if (confirm('确定要下架该货源吗?')) {
if (currentUnpublishSupplyId) {
try {
isOperating = true;
// 获取要下架的货源的原始数据
const originalSupply = supplyData.supplies.find(s => String(s.id) === String(id));
const originalSupply = supplyData.supplies.find(s => String(s.id) === String(currentUnpublishSupplyId));
if (!originalSupply) {
alert('找不到要下架的货源数据');
return;
}
const response = await fetch(`/api/supplies/${id}/unpublish`, {
method: 'POST'
const response = await fetch(`/api/supplies/${currentUnpublishSupplyId}/unpublish-spec`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ specIndex: selectedSpecIndices })
});
const result = await response.json();
if (result.success) {
@ -6878,31 +7051,44 @@
const userInfo = checkLogin();
if (userInfo) {
// 传入修改后的数据,包含状态变化
await logSupplyOperation('下架', id, originalSupply, result.data);
await logSupplyOperation('下架规格', currentUnpublishSupplyId, originalSupply, result.data);
}
// 向WebSocket服务器发送消息,通知其他客户端货源已下架
sendWebSocketMessage({
type: 'supply_status_change',
supplyId: id,
action: 'unpublish',
status: 'sold_out'
supplyId: currentUnpublishSupplyId,
action: 'unpublish_spec',
specIndex: selectedSpecIndex,
status: result.data.status
});
alert('下架成功');
alert('规格下架成功');
loadSupplies();
hideUnpublishSpecModal();
} else {
alert('下架失败: ' + (result.message || '未知错误'));
alert('规格下架失败: ' + (result.message || '未知错误'));
}
} catch (error) {
console.error('下架失败:', error);
alert('下架失败: 网络错误');
console.error('规格下架失败:', error);
alert('规格下架失败: 网络错误');
} finally {
isOperating = false;
}
}
}
// 下架货源
async function unpublishSupply(id) {
// 防止重复点击
if (isOperating) {
return;
}
// 显示按规格下架弹窗
showUnpublishSpecModal(id);
}
// 显示编辑货源
// 删除货源
async function deleteSupply(id) {
@ -8480,7 +8666,8 @@
window.onclick = function(event) {
// 确保只处理click事件,不处理拖拽选择文本导致的mouseup事件
if (event.type === 'click') {
const modals = document.querySelectorAll('.modal');
// 处理类名为modal的模态框
const modals = document.querySelectorAll('.modal, .select-modal');
modals.forEach(modal => {
if (event.target === modal) {
// 只在真正点击模态框背景时关闭,不在选择文本拖拽到外部时关闭
@ -8491,7 +8678,17 @@
const selection = window.getSelection();
if (selection && selection.toString()) return;
modal.style.display = 'none';
// 对于普通modal,使用display: none隐藏
if (modal.classList.contains('modal')) {
modal.style.display = 'none';
}
// 对于select-modal,使用移除active类来隐藏
else if (modal.classList.contains('select-modal')) {
modal.classList.remove('active');
// 重置相关变量
currentUnpublishSupplyId = null;
selectedSpecIndex = null;
}
}
});
}

Loading…
Cancel
Save