提交工作更新
This commit is contained in:
@@ -114,6 +114,7 @@
|
||||
<div class="tab" data-tab="dedupe-total-data">数据去重总数据</div>
|
||||
<div class="tab" data-tab="shop-keys">店铺密钥管理</div>
|
||||
<div class="tab" data-tab="shop-manage">店铺管理</div>
|
||||
<div class="tab" data-tab="skip-price-asin">跳过跟价 ASIN</div>
|
||||
<div class="tab" data-tab="history">查看生成记录</div>
|
||||
<div class="tab" data-tab="version">版本管理</div>
|
||||
</div>
|
||||
@@ -421,6 +422,79 @@
|
||||
</div>
|
||||
|
||||
<!-- 查看生成记录 -->
|
||||
<div id="panel-skip-price-asin" class="tab-panel">
|
||||
<div class="form-box">
|
||||
<h3 style="margin-bottom:16px;font-size:15px;">新增 ASIN</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="min-width:220px;">
|
||||
<label>分组</label>
|
||||
<div style="display:flex;gap:8px;align-items:center;">
|
||||
<select id="skipPriceAsinGroupSelect" style="min-width:150px;">
|
||||
<option value="">请选择分组</option>
|
||||
</select>
|
||||
<button class="btn btn-secondary" id="btnManageSkipPriceAsinGroups" type="button">管理分组</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="min-width:180px;">
|
||||
<label>店铺名</label>
|
||||
<input type="text" id="skipPriceAsinShopName" placeholder="请输入店铺名称">
|
||||
</div>
|
||||
<div class="form-group" style="min-width:220px;">
|
||||
<label>国家</label>
|
||||
<select id="skipPriceAsinCountries" multiple size="5">
|
||||
<option value="DE">德国</option>
|
||||
<option value="UK">英国</option>
|
||||
<option value="FR">法国</option>
|
||||
<option value="IT">意大利</option>
|
||||
<option value="ES">西班牙</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="min-width:180px;">
|
||||
<label>ASIN</label>
|
||||
<input type="text" id="skipPriceAsinValue" placeholder="请输入 ASIN">
|
||||
</div>
|
||||
<button class="btn" id="btnCreateSkipPriceAsin">新增 ASIN</button>
|
||||
</div>
|
||||
<p class="msg" id="msgSkipPriceAsin"></p>
|
||||
</div>
|
||||
<div class="panel-box">
|
||||
<h3 style="margin-bottom:16px;font-size:15px;">店铺列表</h3>
|
||||
<div class="form-row" style="margin-bottom:12px;">
|
||||
<div class="form-group" style="min-width:180px;">
|
||||
<label>分组</label>
|
||||
<select id="skipPriceAsinFilterGroupId">
|
||||
<option value="">全部分组</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="min-width:220px;">
|
||||
<label>店铺名</label>
|
||||
<input type="text" id="skipPriceAsinFilterShopName" placeholder="请输入店铺名">
|
||||
</div>
|
||||
<div class="form-group" style="min-width:220px;">
|
||||
<label>ASIN</label>
|
||||
<input type="text" id="skipPriceAsinFilterAsin" placeholder="请输入 ASIN">
|
||||
</div>
|
||||
<button class="btn" id="btnSearchSkipPriceAsin">查询</button>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>分组</th>
|
||||
<th>店铺名</th>
|
||||
<th>德国</th>
|
||||
<th>英国</th>
|
||||
<th>法国</th>
|
||||
<th>意大利</th>
|
||||
<th>西班牙</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="skipPriceAsinListBody"></tbody>
|
||||
</table>
|
||||
<div class="pagination" id="skipPriceAsinPagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="panel-history" class="tab-panel">
|
||||
<div class="form-box">
|
||||
<h3 style="margin-bottom:16px;font-size:15px;">筛选条件</h3>
|
||||
@@ -627,6 +701,23 @@
|
||||
<script>
|
||||
(function() {
|
||||
// Tab 切换
|
||||
var ADMIN_TAB_ACCESS_CONFIG = {
|
||||
'columns': { superAdminOnly: true },
|
||||
'shop-keys': { superAdminOnly: true },
|
||||
'dedupe-total-data': { columnKey: 'admin_dedupe_total_data', routePath: 'dedupe-total-data' },
|
||||
'shop-manage': { columnKey: 'admin_shop_manage', routePath: 'shop-manage' },
|
||||
'skip-price-asin': { columnKey: 'admin_skip_price_asin', routePath: 'skip-price-asin' }
|
||||
};
|
||||
function runTabLoader(tabName) {
|
||||
if (tabName === 'users') { loadUsers(1); loadColumnsForPermission(); }
|
||||
else if (tabName === 'columns') loadColumns();
|
||||
else if (tabName === 'dedupe-total-data') loadDedupeTotalData(1);
|
||||
else if (tabName === 'shop-keys') loadShopKeys(1);
|
||||
else if (tabName === 'shop-manage') loadShopManage(1);
|
||||
else if (tabName === 'skip-price-asin') loadSkipPriceAsin(1);
|
||||
else if (tabName === 'history') loadHistory(1);
|
||||
else if (tabName === 'version') loadVersions();
|
||||
}
|
||||
document.querySelectorAll('.tab').forEach(function(t) {
|
||||
t.onclick = function() {
|
||||
document.querySelectorAll('.tab').forEach(function(x) { x.classList.remove('active'); });
|
||||
@@ -634,13 +725,7 @@
|
||||
t.classList.add('active');
|
||||
var id = 'panel-' + t.dataset.tab;
|
||||
document.getElementById(id).classList.add('active');
|
||||
if (t.dataset.tab === 'users') { loadUsers(1); loadColumnsForPermission(); }
|
||||
else if (t.dataset.tab === 'columns') loadColumns();
|
||||
else if (t.dataset.tab === 'dedupe-total-data') loadDedupeTotalData(1);
|
||||
else if (t.dataset.tab === 'shop-keys') loadShopKeys(1);
|
||||
else if (t.dataset.tab === 'shop-manage') loadShopManage(1);
|
||||
else if (t.dataset.tab === 'history') loadHistory(1);
|
||||
else if (t.dataset.tab === 'version') loadVersions();
|
||||
runTabLoader(t.dataset.tab);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -695,7 +780,7 @@
|
||||
bindUserActions();
|
||||
updateCreateFormByRole();
|
||||
updateUserFilterByRole();
|
||||
updateDedupeTotalDataAccess();
|
||||
refreshAdminTabAccess();
|
||||
loadCurrentUserAdminPermissions();
|
||||
})
|
||||
.catch(function() {
|
||||
@@ -794,39 +879,31 @@
|
||||
if (usersPanel) usersPanel.classList.add('active');
|
||||
}
|
||||
}
|
||||
function updateAdminOnlyAccess() {
|
||||
var canUse = currentUserRole === 'super_admin';
|
||||
applyTabAccess('columns', canUse);
|
||||
applyTabAccess('shop-keys', canUse);
|
||||
function hasAdminTabAccess(tabName) {
|
||||
var config = ADMIN_TAB_ACCESS_CONFIG[tabName];
|
||||
if (!config) return true;
|
||||
if (currentUserRole === 'super_admin') return true;
|
||||
if (config.superAdminOnly) return false;
|
||||
return !!currentUserAdminPermissionKeys[config.columnKey] ||
|
||||
!!currentUserAdminPermissionRoutes[config.routePath];
|
||||
}
|
||||
function updateDedupeTotalDataAccess() {
|
||||
var canUse = currentUserRole === 'super_admin' ||
|
||||
!!currentUserAdminPermissionKeys['admin_dedupe_total_data'] ||
|
||||
!!currentUserAdminPermissionRoutes['dedupe-total-data'];
|
||||
applyTabAccess('dedupe-total-data', canUse);
|
||||
}
|
||||
function updateShopManageAccess() {
|
||||
var canUse = currentUserRole === 'super_admin' ||
|
||||
!!currentUserAdminPermissionKeys['admin_shop_manage'] ||
|
||||
!!currentUserAdminPermissionRoutes['shop-manage'];
|
||||
applyTabAccess('shop-manage', canUse);
|
||||
function refreshAdminTabAccess() {
|
||||
Object.keys(ADMIN_TAB_ACCESS_CONFIG).forEach(function(tabName) {
|
||||
applyTabAccess(tabName, hasAdminTabAccess(tabName));
|
||||
});
|
||||
}
|
||||
function loadCurrentUserAdminPermissions() {
|
||||
currentUserAdminPermissionKeys = {};
|
||||
currentUserAdminPermissionRoutes = {};
|
||||
updateAdminOnlyAccess();
|
||||
if (!currentUserId || currentUserRole === 'super_admin') {
|
||||
updateDedupeTotalDataAccess();
|
||||
updateShopManageAccess();
|
||||
refreshAdminTabAccess();
|
||||
return;
|
||||
}
|
||||
fetch('/api/admin/user/' + currentUserId + '/column-permissions?menu_type=admin')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(res) {
|
||||
if (!res.success) {
|
||||
updateAdminOnlyAccess();
|
||||
updateDedupeTotalDataAccess();
|
||||
updateShopManageAccess();
|
||||
refreshAdminTabAccess();
|
||||
return;
|
||||
}
|
||||
(res.items || []).forEach(function(item) {
|
||||
@@ -835,14 +912,10 @@
|
||||
if (columnKey) currentUserAdminPermissionKeys[columnKey] = true;
|
||||
if (routePath) currentUserAdminPermissionRoutes[routePath] = true;
|
||||
});
|
||||
updateAdminOnlyAccess();
|
||||
updateDedupeTotalDataAccess();
|
||||
updateShopManageAccess();
|
||||
refreshAdminTabAccess();
|
||||
})
|
||||
.catch(function() {
|
||||
updateAdminOnlyAccess();
|
||||
updateDedupeTotalDataAccess();
|
||||
updateShopManageAccess();
|
||||
refreshAdminTabAccess();
|
||||
});
|
||||
}
|
||||
var allColumnsList = [];
|
||||
@@ -1604,7 +1677,11 @@
|
||||
var createSel = document.getElementById('shopManageGroupSelect');
|
||||
var editSel = document.getElementById('editShopManageGroupSelect');
|
||||
var filterSel = document.getElementById('shopManageFilterGroupId');
|
||||
var skipCreateSel = document.getElementById('skipPriceAsinGroupSelect');
|
||||
var skipFilterSel = document.getElementById('skipPriceAsinFilterGroupId');
|
||||
var selectedFilterId = filterSel ? filterSel.value : '';
|
||||
var selectedSkipCreateId = skipCreateSel ? skipCreateSel.value : '';
|
||||
var selectedSkipFilterId = skipFilterSel ? skipFilterSel.value : '';
|
||||
var createOpts = ['<option value="">请选择分组</option>'];
|
||||
var filterOpts = ['<option value="">全部分组</option>'];
|
||||
shopManageGroups.forEach(function(g) {
|
||||
@@ -1614,10 +1691,14 @@
|
||||
});
|
||||
createSel.innerHTML = createOpts.join('');
|
||||
editSel.innerHTML = createOpts.join('');
|
||||
if (skipCreateSel) skipCreateSel.innerHTML = createOpts.join('');
|
||||
if (filterSel) filterSel.innerHTML = filterOpts.join('');
|
||||
if (skipFilterSel) skipFilterSel.innerHTML = filterOpts.join('');
|
||||
if (selectedCreateId != null) createSel.value = String(selectedCreateId);
|
||||
if (selectedEditId != null) editSel.value = String(selectedEditId);
|
||||
if (filterSel && selectedFilterId) filterSel.value = selectedFilterId;
|
||||
if (skipCreateSel && selectedSkipCreateId) skipCreateSel.value = selectedSkipCreateId;
|
||||
if (skipFilterSel && selectedSkipFilterId) skipFilterSel.value = selectedSkipFilterId;
|
||||
}
|
||||
|
||||
function loadShopManageGroups(selectedCreateId, selectedEditId) {
|
||||
@@ -1682,6 +1763,7 @@
|
||||
loadShopManageGroups().then(function() {
|
||||
renderShopManageGroupRows();
|
||||
loadShopManage(shopManagePage);
|
||||
loadSkipPriceAsin(skipPriceAsinPage);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1799,6 +1881,7 @@
|
||||
loadShopManageGroups().then(function() {
|
||||
renderShopManageGroupRows();
|
||||
loadShopManage(shopManagePage);
|
||||
loadSkipPriceAsin(skipPriceAsinPage);
|
||||
});
|
||||
})
|
||||
.catch(function() {
|
||||
@@ -1888,6 +1971,143 @@
|
||||
};
|
||||
|
||||
// ========== 版本管理 ==========
|
||||
// ========== 跳过跟价 ASIN ==========
|
||||
var skipPriceAsinPage = 1, skipPriceAsinPageSize = 15;
|
||||
var skipPriceCountryColumns = [
|
||||
{ code: 'DE', field: 'asin_de', label: '德国' },
|
||||
{ code: 'UK', field: 'asin_uk', label: '英国' },
|
||||
{ code: 'FR', field: 'asin_fr', label: '法国' },
|
||||
{ code: 'IT', field: 'asin_it', label: '意大利' },
|
||||
{ code: 'ES', field: 'asin_es', label: '西班牙' }
|
||||
];
|
||||
function buildSkipPriceAsinQuery(page) {
|
||||
var query = 'page=' + (page || 1) + '&page_size=' + skipPriceAsinPageSize;
|
||||
var groupId = (document.getElementById('skipPriceAsinFilterGroupId').value || '').trim();
|
||||
var shopName = (document.getElementById('skipPriceAsinFilterShopName').value || '').trim();
|
||||
var asin = (document.getElementById('skipPriceAsinFilterAsin').value || '').trim();
|
||||
if (groupId) query += '&group_id=' + encodeURIComponent(groupId);
|
||||
if (shopName) query += '&shop_name=' + encodeURIComponent(shopName);
|
||||
if (asin) query += '&asin=' + encodeURIComponent(asin);
|
||||
return query;
|
||||
}
|
||||
function renderSkipPriceAsinCell(item, country) {
|
||||
var value = item[country.field] || '';
|
||||
if (!value) return '<span style="color:#999;">-</span>';
|
||||
return '<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">' +
|
||||
'<span>' + value + '</span>' +
|
||||
'<button class="btn btn-sm btn-danger" data-skip-price-asin-delete="' + item.id + '" data-country="' + country.code + '" data-shop-name="' + (item.shop_name || '').replace(/"/g, '"') + '">删除</button>' +
|
||||
'</div>';
|
||||
}
|
||||
function bindSkipPriceAsinActions() {
|
||||
document.querySelectorAll('[data-skip-price-asin-delete]').forEach(function(btn) {
|
||||
btn.onclick = function() {
|
||||
var shopName = (btn.dataset.shopName || '').replace(/"/g, '"');
|
||||
var countryCode = btn.dataset.country || '';
|
||||
if (!confirm('确定删除店铺“' + shopName + '”在 ' + countryCode + ' 的 ASIN 吗?')) return;
|
||||
fetch('/api/admin/skip-price-asin/' + btn.dataset.skipPriceAsinDelete + '/country/' + countryCode, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(res) {
|
||||
if (res.success) loadSkipPriceAsin(skipPriceAsinPage);
|
||||
else alert(res.error || '删除失败');
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
function loadSkipPriceAsin(page) {
|
||||
skipPriceAsinPage = page || 1;
|
||||
fetch('/api/admin/skip-price-asins?' + buildSkipPriceAsinQuery(skipPriceAsinPage))
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(res) {
|
||||
var tbody = document.getElementById('skipPriceAsinListBody');
|
||||
if (!res.success) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="empty-tip">加载失败: ' + (res.error || '') + '</td></tr>';
|
||||
return;
|
||||
}
|
||||
var items = res.items || [];
|
||||
if (items.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="empty-tip">暂无数据</td></tr>';
|
||||
} else {
|
||||
tbody.innerHTML = items.map(function(item, index) {
|
||||
var rowNo = (skipPriceAsinPage - 1) * skipPriceAsinPageSize + index + 1;
|
||||
return '<tr><td>' + rowNo + '</td><td>' + (item.group_name || '') + '</td><td>' + (item.shop_name || '') + '</td>' +
|
||||
skipPriceCountryColumns.map(function(country) {
|
||||
return '<td>' + renderSkipPriceAsinCell(item, country) + '</td>';
|
||||
}).join('') +
|
||||
'</tr>';
|
||||
}).join('');
|
||||
}
|
||||
renderPagination('skipPriceAsinPagination', res.total, res.page, res.page_size, loadSkipPriceAsin);
|
||||
bindSkipPriceAsinActions();
|
||||
})
|
||||
.catch(function() {
|
||||
document.getElementById('skipPriceAsinListBody').innerHTML = '<tr><td colspan="8" class="empty-tip">请求失败</td></tr>';
|
||||
});
|
||||
}
|
||||
document.getElementById('btnManageSkipPriceAsinGroups').onclick = openShopManageGroupModal;
|
||||
document.getElementById('btnSearchSkipPriceAsin').onclick = function() {
|
||||
loadSkipPriceAsin(1);
|
||||
};
|
||||
document.getElementById('skipPriceAsinFilterShopName').addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
loadSkipPriceAsin(1);
|
||||
}
|
||||
});
|
||||
document.getElementById('skipPriceAsinFilterAsin').addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
loadSkipPriceAsin(1);
|
||||
}
|
||||
});
|
||||
document.getElementById('btnCreateSkipPriceAsin').onclick = function() {
|
||||
var groupId = (document.getElementById('skipPriceAsinGroupSelect').value || '').trim();
|
||||
var shopName = (document.getElementById('skipPriceAsinShopName').value || '').trim();
|
||||
var asin = (document.getElementById('skipPriceAsinValue').value || '').trim();
|
||||
var countries = Array.from(document.getElementById('skipPriceAsinCountries').selectedOptions).map(function(option) {
|
||||
return option.value;
|
||||
});
|
||||
var msgEl = document.getElementById('msgSkipPriceAsin');
|
||||
msgEl.textContent = '';
|
||||
msgEl.className = 'msg';
|
||||
if (!groupId || !shopName || !asin || !countries.length) {
|
||||
msgEl.textContent = '请完整填写分组、店铺名、国家和 ASIN';
|
||||
msgEl.className = 'msg err';
|
||||
return;
|
||||
}
|
||||
fetch('/api/admin/skip-price-asin', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
group_id: Number(groupId),
|
||||
shop_name: shopName,
|
||||
countries: countries,
|
||||
asin: asin
|
||||
})
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(res) {
|
||||
if (!res.success) {
|
||||
msgEl.textContent = res.error || '保存失败';
|
||||
msgEl.className = 'msg err';
|
||||
return;
|
||||
}
|
||||
document.getElementById('skipPriceAsinGroupSelect').value = '';
|
||||
document.getElementById('skipPriceAsinShopName').value = '';
|
||||
document.getElementById('skipPriceAsinValue').value = '';
|
||||
Array.from(document.getElementById('skipPriceAsinCountries').options).forEach(function(option) {
|
||||
option.selected = false;
|
||||
});
|
||||
msgEl.textContent = res.msg || '保存成功';
|
||||
msgEl.className = 'msg ok';
|
||||
loadSkipPriceAsin(1);
|
||||
})
|
||||
.catch(function() {
|
||||
msgEl.textContent = '请求失败';
|
||||
msgEl.className = 'msg err';
|
||||
});
|
||||
};
|
||||
function loadVersions() {
|
||||
fetch('/api/admin/versions')
|
||||
.then(function(r) { return r.json(); })
|
||||
@@ -2199,6 +2419,7 @@
|
||||
loadShopKeys(1);
|
||||
loadShopManageGroups();
|
||||
loadShopManage(1);
|
||||
loadSkipPriceAsin(1);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user