后台管理接口修复 后端BUG修复
This commit is contained in:
@@ -8,7 +8,7 @@ import threading
|
||||
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter
|
||||
from flask import Blueprint, request, jsonify, session, current_app, g
|
||||
from flask import Blueprint, request, jsonify, session, current_app, g, Response
|
||||
|
||||
import pymysql
|
||||
from werkzeug.security import generate_password_hash
|
||||
@@ -149,14 +149,24 @@ def _proxy_backend_java(method, path, *, params=None, json_data=None, files=None
|
||||
|
||||
|
||||
def _format_permission_item(item):
|
||||
created_at = item.get('createdAt')
|
||||
if created_at in (None, ''):
|
||||
created_at = item.get('created_at')
|
||||
if hasattr(created_at, 'strftime'):
|
||||
created_at_text = created_at.strftime('%Y-%m-%d %H:%M')
|
||||
else:
|
||||
created_at_text = (str(created_at).replace('T', ' ')[:16]) if created_at else ''
|
||||
sort_order = item.get('sortOrder')
|
||||
if sort_order is None:
|
||||
sort_order = item.get('sort_order')
|
||||
return {
|
||||
'id': item.get('id'),
|
||||
'name': item.get('name') or '',
|
||||
'column_key': item.get('columnKey') or '',
|
||||
'menu_type': item.get('menuType') or 'app',
|
||||
'route_path': item.get('routePath') or '',
|
||||
'sort_order': item.get('sortOrder') if item.get('sortOrder') is not None else 0,
|
||||
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
|
||||
'column_key': item.get('columnKey') or item.get('column_key') or '',
|
||||
'menu_type': item.get('menuType') or item.get('menu_type') or 'app',
|
||||
'route_path': item.get('routePath') or item.get('route_path') or '',
|
||||
'sort_order': sort_order if sort_order is not None else 0,
|
||||
'created_at': created_at_text,
|
||||
}
|
||||
|
||||
|
||||
@@ -792,11 +802,9 @@ def create_user():
|
||||
(username, pwd_hash, is_admin, want_role, want_created_by),
|
||||
)
|
||||
new_uid = cur.lastrowid
|
||||
_set_user_column_permissions(cur, new_uid, column_ids)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
error_response, status = _sync_user_column_permissions(new_uid, column_ids)
|
||||
if error_response is not None:
|
||||
return error_response, status
|
||||
return jsonify({'success': True, 'msg': '用户创建成功'})
|
||||
except pymysql.IntegrityError:
|
||||
return jsonify({'success': False, 'error': '用户名已存在'})
|
||||
@@ -807,7 +815,7 @@ def create_user():
|
||||
@admin_api.route('/user/<int:uid>', methods=['PUT'])
|
||||
@admin_required
|
||||
def update_user(uid):
|
||||
"""更新用户(密码、角色)"""
|
||||
"""更新用户(密码、角色、栏目权限)"""
|
||||
data = request.get_json() or {}
|
||||
_, _, denied = _ensure_admin_menu_access('users')
|
||||
if denied:
|
||||
@@ -817,7 +825,7 @@ def update_user(uid):
|
||||
role, current_row = get_current_admin_role()
|
||||
if not role:
|
||||
return jsonify({'success': False, 'error': '需要管理员权限'}), 403
|
||||
if want_role is None and not password:
|
||||
if want_role is None and not password and 'column_ids' not in data:
|
||||
return jsonify({'success': False, 'error': '请提供要修改的内容'})
|
||||
try:
|
||||
conn = get_db()
|
||||
@@ -852,13 +860,10 @@ def update_user(uid):
|
||||
"UPDATE users SET is_admin = %s, role = %s WHERE id = %s",
|
||||
(is_admin, want_role, uid),
|
||||
)
|
||||
column_ids = data.get('column_ids')
|
||||
if 'column_ids' in data:
|
||||
_set_user_column_permissions(cur, uid, data.get('column_ids'))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
if column_ids is not None:
|
||||
error_response, status = _sync_user_column_permissions(uid, column_ids)
|
||||
if error_response is not None:
|
||||
return error_response, status
|
||||
return jsonify({'success': True, 'msg': '更新成功'})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
@@ -1139,15 +1144,7 @@ def get_user_columns(uid):
|
||||
if denied:
|
||||
return denied
|
||||
menu_type = (request.args.get('menu_type') or '').strip()
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'GET',
|
||||
f'/api/admin/permission-users/{uid}/columns',
|
||||
params={'menuType': menu_type} if menu_type else None,
|
||||
)
|
||||
if error_response is not None:
|
||||
return error_response, status
|
||||
payload = result.get('data') or {}
|
||||
return jsonify({'success': True, 'column_ids': payload.get('columnIds') or []})
|
||||
return jsonify({'success': True, 'column_ids': _get_local_user_column_ids(uid, menu_type or None)})
|
||||
|
||||
|
||||
@admin_api.route('/user/<int:uid>/column-permissions')
|
||||
@@ -1157,14 +1154,8 @@ def get_user_column_permissions(uid):
|
||||
if denied:
|
||||
return denied
|
||||
menu_type = (request.args.get('menu_type') or '').strip()
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'GET',
|
||||
f'/api/admin/permission-users/{uid}/column-permissions',
|
||||
params={'menuType': menu_type} if menu_type else None,
|
||||
)
|
||||
if error_response is not None:
|
||||
return error_response, status
|
||||
return jsonify({'success': True, 'items': [_format_permission_item(item) for item in (result.get('data') or [])]})
|
||||
items = _get_local_user_column_permission_items(uid, menu_type or None)
|
||||
return jsonify({'success': True, 'items': [_format_permission_item(item) for item in items]})
|
||||
|
||||
|
||||
def _set_user_column_permissions(cur, user_id, column_ids):
|
||||
@@ -2006,18 +1997,18 @@ def list_skip_price_asins():
|
||||
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 current_row and current_row.get('id'):
|
||||
params['operatorId'] = current_row.get('id')
|
||||
params['superAdmin'] = 'true' if role == 'super_admin' else 'false'
|
||||
params = {'page': page, 'page_size': page_size, 'pageSize': page_size}
|
||||
params.update(_skip_price_asin_auth_params(role, current_row))
|
||||
if group_id_raw:
|
||||
try:
|
||||
group_id = int(group_id_raw)
|
||||
if group_id > 0:
|
||||
params['group_id'] = group_id
|
||||
params['groupId'] = group_id
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
if shop_name:
|
||||
params['shop_name'] = shop_name
|
||||
params['shopName'] = shop_name
|
||||
if asin:
|
||||
params['asin'] = asin
|
||||
@@ -2059,6 +2050,64 @@ def list_skip_price_asins():
|
||||
})
|
||||
|
||||
|
||||
def _skip_price_asin_auth_params(role, current_row):
|
||||
operator_id = current_row.get('id') if current_row else None
|
||||
super_admin = 'true' if role == 'super_admin' else 'false'
|
||||
return {
|
||||
'operator_id': operator_id,
|
||||
'operatorId': operator_id,
|
||||
'super_admin': super_admin,
|
||||
'superAdmin': super_admin,
|
||||
}
|
||||
|
||||
|
||||
@admin_api.route('/skip-price-asins/export')
|
||||
@login_required
|
||||
def export_skip_price_asins():
|
||||
role, current_row, denied = _ensure_backend_menu_access('skip-price-asin')
|
||||
if denied:
|
||||
return denied
|
||||
params = _skip_price_asin_auth_params(role, current_row)
|
||||
group_id_raw = (request.args.get('group_id') or request.args.get('groupId') or '').strip()
|
||||
shop_name = (request.args.get('shop_name') or request.args.get('shopName') or '').strip()
|
||||
asin = (request.args.get('asin') or '').strip()
|
||||
if group_id_raw:
|
||||
params['group_id'] = group_id_raw
|
||||
params['groupId'] = group_id_raw
|
||||
if shop_name:
|
||||
params['shop_name'] = shop_name
|
||||
params['shopName'] = shop_name
|
||||
if asin:
|
||||
params['asin'] = asin
|
||||
|
||||
url = f"{backend_java_base_url}/api/admin/skip-price-asins/export"
|
||||
try:
|
||||
resp = _get_backend_java_session().get(url, params=params, timeout=60)
|
||||
except requests.RequestException:
|
||||
return jsonify({'success': False, 'error': 'backend-java 服务不可用'}), 502
|
||||
if resp.status_code >= 400:
|
||||
try:
|
||||
data = resp.json()
|
||||
error = data.get('message') or data.get('error') or '导出失败'
|
||||
except ValueError:
|
||||
error = '导出失败'
|
||||
return jsonify({'success': False, 'error': error}), resp.status_code
|
||||
|
||||
headers = {}
|
||||
disposition = resp.headers.get('Content-Disposition')
|
||||
if disposition:
|
||||
headers['Content-Disposition'] = disposition
|
||||
return Response(
|
||||
resp.content,
|
||||
status=resp.status_code,
|
||||
headers=headers,
|
||||
content_type=resp.headers.get(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@admin_api.route('/skip-price-asin', methods=['POST'])
|
||||
@login_required
|
||||
def create_skip_price_asin():
|
||||
@@ -2092,10 +2141,7 @@ def create_skip_price_asin():
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'POST',
|
||||
'/api/admin/skip-price-asins',
|
||||
params={
|
||||
'operatorId': current_row.get('id') if current_row else None,
|
||||
'superAdmin': 'true' if role == 'super_admin' else 'false',
|
||||
},
|
||||
params=_skip_price_asin_auth_params(role, current_row),
|
||||
json_data=payload,
|
||||
)
|
||||
if error_response is not None:
|
||||
@@ -2134,10 +2180,7 @@ def delete_skip_price_asin_country(item_id, country):
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'DELETE',
|
||||
f'/api/admin/skip-price-asins/{item_id}/countries/{country}',
|
||||
params={
|
||||
'operatorId': current_row.get('id') if current_row else None,
|
||||
'superAdmin': 'true' if role == 'super_admin' else 'false',
|
||||
},
|
||||
params=_skip_price_asin_auth_params(role, current_row),
|
||||
)
|
||||
if error_response is not None:
|
||||
return error_response, status
|
||||
@@ -2177,10 +2220,7 @@ def update_skip_price_asin_country(item_id, country):
|
||||
result, error_response, status = _proxy_backend_java(
|
||||
'PUT',
|
||||
f'/api/admin/skip-price-asins/{item_id}/countries/{country}',
|
||||
params={
|
||||
'operatorId': current_row.get('id') if current_row else None,
|
||||
'superAdmin': 'true' if role == 'super_admin' else 'false',
|
||||
},
|
||||
params=_skip_price_asin_auth_params(role, current_row),
|
||||
json_data=payload,
|
||||
)
|
||||
if error_response is not None:
|
||||
@@ -2240,11 +2280,9 @@ def import_skip_price_asins():
|
||||
group_id_raw = (request.form.get('group_id') or '').strip()
|
||||
if not group_id_raw:
|
||||
return jsonify({'success': False, 'error': '请先选择分组'})
|
||||
params = {
|
||||
'groupId': group_id_raw,
|
||||
'operatorId': current_row.get('id') if current_row else None,
|
||||
'superAdmin': 'true' if role == 'super_admin' else 'false',
|
||||
}
|
||||
params = _skip_price_asin_auth_params(role, current_row)
|
||||
params['group_id'] = group_id_raw
|
||||
params['groupId'] = group_id_raw
|
||||
files = {
|
||||
'file': (file_storage.filename, file_storage.stream, file_storage.mimetype or 'application/octet-stream')
|
||||
}
|
||||
@@ -2295,11 +2333,9 @@ def delete_import_skip_price_asins():
|
||||
group_id_raw = (request.form.get('group_id') or '').strip()
|
||||
if not group_id_raw:
|
||||
return jsonify({'success': False, 'error': '请先选择分组'})
|
||||
params = {
|
||||
'groupId': group_id_raw,
|
||||
'operatorId': current_row.get('id') if current_row else None,
|
||||
'superAdmin': 'true' if role == 'super_admin' else 'false',
|
||||
}
|
||||
params = _skip_price_asin_auth_params(role, current_row)
|
||||
params['group_id'] = group_id_raw
|
||||
params['groupId'] = group_id_raw
|
||||
files = {
|
||||
'file': (file_storage.filename, file_storage.stream, file_storage.mimetype or 'application/octet-stream')
|
||||
}
|
||||
@@ -2584,3 +2620,46 @@ def update_query_asin_country(item_id, country):
|
||||
'msg': result.get('message') or '保存成功',
|
||||
'item': _format_query_asin_item(result.get('data') or {}),
|
||||
})
|
||||
|
||||
|
||||
def _get_local_user_column_ids(user_id, menu_type=None):
|
||||
conn = get_db()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
sql = """
|
||||
SELECT c.id
|
||||
FROM user_column_permission ucp
|
||||
INNER JOIN columns c ON c.id = ucp.column_id
|
||||
WHERE ucp.user_id = %s
|
||||
"""
|
||||
params = [user_id]
|
||||
if menu_type:
|
||||
sql += " AND c.menu_type = %s"
|
||||
params.append(menu_type)
|
||||
sql += " ORDER BY c.sort_order ASC, c.id ASC"
|
||||
cur.execute(sql, params)
|
||||
rows = cur.fetchall()
|
||||
return [int(row['id']) for row in rows if row.get('id') is not None]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _get_local_user_column_permission_items(user_id, menu_type=None):
|
||||
conn = get_db()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
sql = """
|
||||
SELECT c.id, c.name, c.column_key, c.menu_type, c.route_path, c.sort_order, c.created_at
|
||||
FROM user_column_permission ucp
|
||||
INNER JOIN columns c ON c.id = ucp.column_id
|
||||
WHERE ucp.user_id = %s
|
||||
"""
|
||||
params = [user_id]
|
||||
if menu_type:
|
||||
sql += " AND c.menu_type = %s"
|
||||
params.append(menu_type)
|
||||
sql += " ORDER BY c.sort_order ASC, c.id ASC"
|
||||
cur.execute(sql, params)
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@@ -1856,16 +1856,82 @@
|
||||
minimumPriceMappings: minimumPriceMappings
|
||||
};
|
||||
}
|
||||
function buildSkipPriceAsinQuery(page) {
|
||||
var query = 'page=' + (page || 1) + '&page_size=' + skipPriceAsinPageSize;
|
||||
function buildSkipPriceAsinFilterQuery() {
|
||||
var query = '';
|
||||
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);
|
||||
if (groupId) query += (query ? '&' : '') + 'group_id=' + encodeURIComponent(groupId);
|
||||
if (shopName) query += (query ? '&' : '') + 'shop_name=' + encodeURIComponent(shopName);
|
||||
if (asin) query += (query ? '&' : '') + 'asin=' + encodeURIComponent(asin);
|
||||
return query;
|
||||
}
|
||||
function buildSkipPriceAsinOperatorQuery() {
|
||||
var query = '';
|
||||
if (currentUserId) query += 'operator_id=' + encodeURIComponent(currentUserId);
|
||||
if (currentUserRole === 'super_admin') query += (query ? '&' : '') + 'super_admin=true';
|
||||
return query;
|
||||
}
|
||||
function buildSkipPriceAsinQuery(page) {
|
||||
var query = 'page=' + (page || 1) + '&page_size=' + skipPriceAsinPageSize;
|
||||
var filterQuery = buildSkipPriceAsinFilterQuery();
|
||||
if (filterQuery) query += '&' + filterQuery;
|
||||
var operatorQuery = buildSkipPriceAsinOperatorQuery();
|
||||
if (operatorQuery) query += '&' + operatorQuery;
|
||||
return query;
|
||||
}
|
||||
function extractDownloadFilename(disposition, fallbackName) {
|
||||
var fallback = fallbackName || 'download.xlsx';
|
||||
if (!disposition) return fallback;
|
||||
var utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i);
|
||||
if (utf8Match && utf8Match[1]) {
|
||||
try {
|
||||
return decodeURIComponent(utf8Match[1]);
|
||||
} catch (e) { }
|
||||
}
|
||||
var plainMatch = disposition.match(/filename=\"?([^\";]+)\"?/i);
|
||||
if (plainMatch && plainMatch[1]) return plainMatch[1];
|
||||
return fallback;
|
||||
}
|
||||
function triggerBrowserDownload(blob, filename) {
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename || 'download.xlsx';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
|
||||
}
|
||||
function exportSkipPriceAsin() {
|
||||
var query = buildSkipPriceAsinFilterQuery();
|
||||
var operatorQuery = buildSkipPriceAsinOperatorQuery();
|
||||
if (operatorQuery) query += (query ? '&' : '') + operatorQuery;
|
||||
var url = '/api/admin/skip-price-asins/export' + (query ? ('?' + query) : '');
|
||||
fetch(url)
|
||||
.then(function (response) {
|
||||
var contentType = response.headers.get('content-type') || '';
|
||||
if (!response.ok || contentType.indexOf('application/json') >= 0) {
|
||||
return response.json().then(function (res) {
|
||||
throw new Error((res && (res.error || res.msg)) || '导出失败');
|
||||
}).catch(function (err) {
|
||||
throw err instanceof Error ? err : new Error('导出失败');
|
||||
});
|
||||
}
|
||||
return response.blob().then(function (blob) {
|
||||
return {
|
||||
blob: blob,
|
||||
filename: extractDownloadFilename(response.headers.get('content-disposition'), 'skip-price-asin.xlsx')
|
||||
};
|
||||
});
|
||||
})
|
||||
.then(function (payload) {
|
||||
triggerBrowserDownload(payload.blob, payload.filename);
|
||||
})
|
||||
.catch(function (err) {
|
||||
alert((err && err.message) || '导出失败');
|
||||
});
|
||||
}
|
||||
function renderSkipPriceAsinCell(item, country) {
|
||||
var asinValue = item[country.field] || '';
|
||||
var minimumPriceValue = formatSkipPriceMinimumPrice(item[country.minimumPriceField]);
|
||||
@@ -1917,7 +1983,8 @@
|
||||
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, {
|
||||
var operatorQuery = buildSkipPriceAsinOperatorQuery();
|
||||
fetch('/api/admin/skip-price-asin/' + btn.dataset.skipPriceAsinDelete + '/country/' + countryCode + (operatorQuery ? ('?' + operatorQuery) : ''), {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
@@ -2184,6 +2251,8 @@
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('group_id', groupId);
|
||||
if (currentUserId) formData.append('operator_id', currentUserId);
|
||||
if (currentUserRole === 'super_admin') formData.append('super_admin', 'true');
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', deleteMode ? '/api/admin/skip-price-asins/delete-import' : '/api/admin/skip-price-asins/import', true);
|
||||
xhr.upload.onprogress = function (event) {
|
||||
@@ -2267,7 +2336,8 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
fetch('/api/admin/skip-price-asin/' + itemId + '/country/' + countryCode, {
|
||||
var operatorQuery = buildSkipPriceAsinOperatorQuery();
|
||||
fetch('/api/admin/skip-price-asin/' + itemId + '/country/' + countryCode + (operatorQuery ? ('?' + operatorQuery) : ''), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -2294,6 +2364,9 @@
|
||||
document.getElementById('btnSearchSkipPriceAsin').onclick = function () {
|
||||
loadSkipPriceAsin(1);
|
||||
};
|
||||
document.getElementById('btnExportSkipPriceAsin').onclick = function () {
|
||||
exportSkipPriceAsin();
|
||||
};
|
||||
document.getElementById('skipPriceAsinFilterShopName').addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
@@ -2334,7 +2407,8 @@
|
||||
fallbackAsin = asinMappings[countryCode] || '';
|
||||
return !!fallbackAsin;
|
||||
});
|
||||
fetch('/api/admin/skip-price-asin', {
|
||||
var operatorQuery = buildSkipPriceAsinOperatorQuery();
|
||||
fetch('/api/admin/skip-price-asin' + (operatorQuery ? ('?' + operatorQuery) : ''), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -3429,8 +3503,10 @@
|
||||
}
|
||||
|
||||
// 初始化
|
||||
var adminCurrentUserPromise = null;
|
||||
function loadAdminCurrentUser() {
|
||||
fetch('/api/admin/current-user')
|
||||
if (adminCurrentUserPromise) return adminCurrentUserPromise;
|
||||
adminCurrentUserPromise = fetch('/api/admin/current-user')
|
||||
.then(function (r) {
|
||||
if (r.status === 404) {
|
||||
return fetch('/api/auth/check')
|
||||
@@ -3474,7 +3550,7 @@
|
||||
updateShopManageGroupButtonsAccess();
|
||||
})
|
||||
.catch(function () {
|
||||
fetch('/api/auth/check')
|
||||
return fetch('/api/auth/check')
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
document.getElementById('adminCurrentUsername').textContent = res && res.logged_in ? '当前用户' : '未登录';
|
||||
@@ -3486,7 +3562,13 @@
|
||||
document.getElementById('adminCurrentUserRole').style.display = 'none';
|
||||
updateShopManageGroupButtonsAccess();
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
if (!currentUserId && currentUserRole !== 'super_admin') {
|
||||
adminCurrentUserPromise = null;
|
||||
}
|
||||
});
|
||||
return adminCurrentUserPromise;
|
||||
}
|
||||
|
||||
document.getElementById('btnAdminLogout').onclick = function () {
|
||||
@@ -3514,10 +3596,11 @@
|
||||
});
|
||||
};
|
||||
|
||||
loadAdminCurrentUser();
|
||||
loadUserOptions();
|
||||
loadColumnsForPermission();
|
||||
loadShopManageGroups();
|
||||
loadAdminMenus();
|
||||
loadAdminCurrentUser().finally(function () {
|
||||
loadUserOptions();
|
||||
loadColumnsForPermission();
|
||||
loadShopManageGroups();
|
||||
loadAdminMenus();
|
||||
});
|
||||
}) ();
|
||||
|
||||
|
||||
@@ -1202,6 +1202,7 @@
|
||||
<input type="text" id="skipPriceAsinFilterAsin" placeholder="请输入 ASIN">
|
||||
</div>
|
||||
<button class="btn" id="btnSearchSkipPriceAsin">查询</button>
|
||||
<button class="btn btn-secondary" id="btnExportSkipPriceAsin" type="button">导出 XLSX</button>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
Reference in New Issue
Block a user