提交工作更新

This commit is contained in:
super
2026-04-13 13:02:41 +08:00
parent 728eea4b83
commit bbc833f2c9
18 changed files with 917 additions and 42 deletions

View File

@@ -19,6 +19,8 @@ ALLOWED_DEDUPE_TOTAL_DATA_ADMIN_USERNAMES = {'刘丽泓'}
def _can_access_dedupe_total_data(role, current_row):
_, _, denied = _ensure_admin_menu_access('dedupe-total-data')
return denied is None
if role == 'super_admin':
return True
if role != 'admin' or not current_row:
@@ -27,6 +29,7 @@ def _can_access_dedupe_total_data(role, current_row):
def _ensure_dedupe_total_data_access():
return _ensure_admin_menu_access('dedupe-total-data')
role, current_row = get_current_admin_role()
if not _can_access_dedupe_total_data(role, current_row):
return None, None, (jsonify({'success': False, 'error': '无权限访问数据去重总数据'}), 403)
@@ -41,6 +44,24 @@ except ImportError:
admin_api = Blueprint('admin_api', __name__, url_prefix='/api/admin')
ADMIN_MENU_ACCESS_CONFIG = {
'dedupe-total-data': {
'column_key': 'admin_dedupe_total_data',
'route_path': 'dedupe-total-data',
'error': '鏃犳潈闄愯闂暟鎹幓閲嶆€绘暟鎹?',
},
'shop-manage': {
'column_key': 'admin_shop_manage',
'route_path': 'shop-manage',
'error': '鏃犳潈闄愯闂簵閾虹鐞嗘ā鍧?',
},
'skip-price-asin': {
'column_key': 'admin_skip_price_asin',
'route_path': 'skip-price-asin',
'error': '鏃犳潈闄愯闂烦杩囪窡浠稟SIN 妯″潡',
},
}
def _safe_version_key(version):
"""将版本号转为安全的 OSS 对象名部分"""
@@ -90,6 +111,47 @@ def _sync_user_column_permissions(user_id, column_ids):
return error_response, status
def _get_current_admin_permission_sets():
user_id = session.get('user_id')
if not user_id:
return set(), set()
conn = get_db()
try:
with conn.cursor() as cur:
cur.execute(
"""SELECT c.column_key, c.route_path
FROM user_column_permission ucp
INNER JOIN columns c ON c.id = ucp.column_id
WHERE ucp.user_id = %s AND c.menu_type = 'admin'""",
(user_id,),
)
rows = cur.fetchall()
finally:
conn.close()
key_set = {(row.get('column_key') or '').strip() for row in rows if (row.get('column_key') or '').strip()}
route_set = {(row.get('route_path') or '').strip() for row in rows if (row.get('route_path') or '').strip()}
return key_set, route_set
def _ensure_admin_menu_access(*menu_names):
role, current_row = get_current_admin_role()
if role == 'super_admin':
return role, current_row, None
if role != 'admin' or not current_row:
return role, current_row, (jsonify({'success': False, 'error': '闇€瑕佺鐞嗗憳鏉冮檺'}), 403)
key_set, route_set = _get_current_admin_permission_sets()
for menu_name in menu_names:
config = ADMIN_MENU_ACCESS_CONFIG.get(menu_name) or {}
column_key = (config.get('column_key') or '').strip()
route_path = (config.get('route_path') or '').strip()
if (column_key and column_key in key_set) or (route_path and route_path in route_set):
return role, current_row, None
fallback_menu = menu_names[0] if menu_names else ''
fallback_config = ADMIN_MENU_ACCESS_CONFIG.get(fallback_menu) or {}
error_message = fallback_config.get('error') or '鏃犳潈闄愯闂綋鍓嶆ā鍧?'
return role, current_row, (jsonify({'success': False, 'error': error_message}), 403)
# ---------- 用户管理 ----------
@admin_api.route('/users')
@@ -936,6 +998,9 @@ def delete_dedupe_total_data(item_id):
@admin_api.route('/shop-manages')
@admin_required
def list_shop_manages():
_, _, denied = _ensure_admin_menu_access('shop-manage')
if denied:
return denied
page = max(1, int(request.args.get('page', 1)))
page_size = min(100, max(1, int(request.args.get('page_size', 15))))
group_id_raw = (request.args.get('group_id') or '').strip()
@@ -986,6 +1051,9 @@ def list_shop_manages():
@admin_api.route('/shop-manage', methods=['POST'])
@admin_required
def create_shop_manage():
_, _, denied = _ensure_admin_menu_access('shop-manage')
if denied:
return denied
data = request.get_json() or {}
payload = {
'groupId': data.get('group_id'),
@@ -1022,6 +1090,9 @@ def create_shop_manage():
@admin_api.route('/shop-manage/<int:item_id>', methods=['PUT'])
@admin_required
def update_shop_manage(item_id):
_, _, denied = _ensure_admin_menu_access('shop-manage')
if denied:
return denied
data = request.get_json() or {}
payload = {
'groupId': data.get('group_id'),
@@ -1057,6 +1128,9 @@ def update_shop_manage(item_id):
@admin_api.route('/shop-manage/<int:item_id>', methods=['DELETE'])
@admin_required
def delete_shop_manage(item_id):
_, _, denied = _ensure_admin_menu_access('shop-manage')
if denied:
return denied
result, error_response, status = _proxy_backend_java(
'DELETE',
f'/api/admin/shop-manages/{item_id}',
@@ -1072,6 +1146,9 @@ def delete_shop_manage(item_id):
@admin_api.route('/shop-manage-groups')
@admin_required
def list_shop_manage_groups():
_, _, denied = _ensure_admin_menu_access('shop-manage', 'skip-price-asin')
if denied:
return denied
result, error_response, status = _proxy_backend_java(
'GET',
'/api/admin/shop-manages/groups',
@@ -1093,6 +1170,9 @@ def list_shop_manage_groups():
@admin_api.route('/shop-manage-group', methods=['POST'])
@admin_required
def create_shop_manage_group():
_, _, denied = _ensure_admin_menu_access('shop-manage', 'skip-price-asin')
if denied:
return denied
data = request.get_json() or {}
payload = {'groupName': (data.get('group_name') or '').strip()}
result, error_response, status = _proxy_backend_java(
@@ -1118,6 +1198,9 @@ def create_shop_manage_group():
@admin_api.route('/shop-manage-group/<int:item_id>', methods=['PUT'])
@admin_required
def update_shop_manage_group(item_id):
_, _, denied = _ensure_admin_menu_access('shop-manage', 'skip-price-asin')
if denied:
return denied
data = request.get_json() or {}
payload = {'groupName': (data.get('group_name') or '').strip()}
result, error_response, status = _proxy_backend_java(
@@ -1140,9 +1223,123 @@ def update_shop_manage_group(item_id):
})
@admin_api.route('/skip-price-asins')
@admin_required
def list_skip_price_asins():
_, _, denied = _ensure_admin_menu_access('skip-price-asin')
if denied:
return denied
page = max(1, int(request.args.get('page', 1)))
page_size = min(100, max(1, int(request.args.get('page_size', 15))))
group_id_raw = (request.args.get('group_id') or '').strip()
shop_name = (request.args.get('shop_name') or '').strip()
asin = (request.args.get('asin') or '').strip()
params = {'page': page, 'pageSize': page_size}
if group_id_raw:
try:
group_id = int(group_id_raw)
if group_id > 0:
params['groupId'] = group_id
except (TypeError, ValueError):
pass
if shop_name:
params['shopName'] = shop_name
if asin:
params['asin'] = asin
result, error_response, status = _proxy_backend_java(
'GET',
'/api/admin/skip-price-asins',
params=params,
)
if error_response is not None:
return error_response, status
payload = result.get('data') or {}
items = [
{
'id': item.get('id'),
'group_id': item.get('groupId'),
'group_name': item.get('groupName') or '',
'shop_name': item.get('shopName') or '',
'asin_de': item.get('asinDe') or '',
'asin_uk': item.get('asinUk') or '',
'asin_fr': item.get('asinFr') or '',
'asin_it': item.get('asinIt') or '',
'asin_es': item.get('asinEs') or '',
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
'updated_at': (item.get('updatedAt') or '').replace('T', ' ')[:16],
}
for item in (payload.get('items') or [])
]
return jsonify({
'success': True,
'items': items,
'total': payload.get('total') or 0,
'page': payload.get('page') or page,
'page_size': payload.get('pageSize') or page_size,
})
@admin_api.route('/skip-price-asin', methods=['POST'])
@admin_required
def create_skip_price_asin():
_, _, denied = _ensure_admin_menu_access('skip-price-asin')
if denied:
return denied
data = request.get_json() or {}
payload = {
'groupId': data.get('group_id'),
'shopName': (data.get('shop_name') or '').strip(),
'countries': data.get('countries') or [],
'asin': (data.get('asin') or '').strip(),
}
result, error_response, status = _proxy_backend_java(
'POST',
'/api/admin/skip-price-asins',
json_data=payload,
)
if error_response is not None:
return error_response, status
item = result.get('data') or {}
return jsonify({
'success': True,
'msg': result.get('message') or '淇濆瓨鎴愬姛',
'item': {
'id': item.get('id'),
'group_id': item.get('groupId'),
'group_name': item.get('groupName') or '',
'shop_name': item.get('shopName') or '',
'asin_de': item.get('asinDe') or '',
'asin_uk': item.get('asinUk') or '',
'asin_fr': item.get('asinFr') or '',
'asin_it': item.get('asinIt') or '',
'asin_es': item.get('asinEs') or '',
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
'updated_at': (item.get('updatedAt') or '').replace('T', ' ')[:16],
},
})
@admin_api.route('/skip-price-asin/<int:item_id>/country/<country>', methods=['DELETE'])
@admin_required
def delete_skip_price_asin_country(item_id, country):
_, _, denied = _ensure_admin_menu_access('skip-price-asin')
if denied:
return denied
result, error_response, status = _proxy_backend_java(
'DELETE',
f'/api/admin/skip-price-asins/{item_id}/countries/{country}',
)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'msg': result.get('message') or '鍒犻櫎鎴愬姛'})
@admin_api.route('/shop-manage-group/<int:item_id>', methods=['DELETE'])
@admin_required
def delete_shop_manage_group(item_id):
_, _, denied = _ensure_admin_menu_access('shop-manage', 'skip-price-asin')
if denied:
return denied
result, error_response, status = _proxy_backend_java(
'DELETE',
f'/api/admin/shop-manages/groups/{item_id}',

View File

@@ -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, '&quot;') + '">删除</button>' +
'</div>';
}
function bindSkipPriceAsinActions() {
document.querySelectorAll('[data-skip-price-asin-delete]').forEach(function(btn) {
btn.onclick = function() {
var shopName = (btn.dataset.shopName || '').replace(/&quot;/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>