增加公共下载进度、增加接收SKU、密钥分别存放
This commit is contained in:
4
app/.env
4
app/.env
@@ -12,8 +12,8 @@ zn_username=%E8%87%AA%E5%8A%A8%E5%8C%96_Robot
|
||||
client_name=ShuFuAI
|
||||
|
||||
# java_api_base=http://47.111.163.154:18080
|
||||
# java_api_base=http://127.0.0.1:18080
|
||||
java_api_base=http://121.196.149.225:18080
|
||||
java_api_base=http://127.0.0.1:18080
|
||||
# java_api_base=http://121.196.149.225:18080
|
||||
|
||||
# 与 Java 后端共享的 JWT 签名密钥,必须与 backend-java 的 AIIMAGE_JWT_SECRET 完全一致
|
||||
AIIMAGE_JWT_SECRET=please-change-this-secret-please-rotate-at-least-32-bytes
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
app/assets/appearance-patent-efxpNAXi.css
Normal file
1
app/assets/appearance-patent-efxpNAXi.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{b8 as r}from"./pywebview-3bFwh9Cg.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e};
|
||||
1
app/assets/brand-jr47vQUd.js
Normal file
1
app/assets/brand-jr47vQUd.js
Normal file
@@ -0,0 +1 @@
|
||||
import{bN as r}from"./java-modules-DzYrjxib.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e};
|
||||
1
app/assets/collect-data-6h7Mh27f.css
Normal file
1
app/assets/collect-data-6h7Mh27f.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/collect-data.js
Normal file
1
app/assets/collect-data.js
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/convert-6oxZNMye.css
Normal file
1
app/assets/convert-6oxZNMye.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/dedupe-DpfPYQDv.css
Normal file
1
app/assets/dedupe-DpfPYQDv.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/delete-brand-DZ3x5hOX.css
Normal file
1
app/assets/delete-brand-DZ3x5hOX.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/patrol-delete-Cqhg2qXm.css
Normal file
1
app/assets/patrol-delete-Cqhg2qXm.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/product-risk-BQIMXTON.css
Normal file
1
app/assets/product-risk-BQIMXTON.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/query-asin-CECO5lUB.css
Normal file
1
app/assets/query-asin-CECO5lUB.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.module-page[data-v-a27e815e]{min-height:100vh;background:#1a1a1a}.main-content[data-v-a27e815e]{display:flex;height:calc(100vh - 56px);min-height:calc(100vh - 56px)}.left-panel[data-v-a27e815e]{width:400px;background:#1e1e1e;padding:20px;overflow-y:auto;border-right:1px solid #2a2a2a}.right-panel[data-v-a27e815e]{flex:1;min-width:0;background:#1a1a1a;display:flex;flex-direction:column}.section-title[data-v-a27e815e],.subsection-title[data-v-a27e815e]{font-size:13px;color:#bbb;margin-bottom:10px}.upload-zone[data-v-a27e815e]{border:1px dashed #3a3a3a;border-radius:10px;padding:18px;background:#252525;margin-bottom:18px}.hint[data-v-a27e815e],.loading-msg[data-v-a27e815e],.files[data-v-a27e815e],.muted[data-v-a27e815e]{color:#888;font-size:12px;line-height:1.5}.link[data-v-a27e815e]{color:#6ea8fe;text-decoration:none}.link[data-v-a27e815e]:hover{color:#9fc5ff}.btns[data-v-a27e815e],.run-row[data-v-a27e815e]{display:flex;gap:10px;flex-wrap:wrap}.opt-btn[data-v-a27e815e],.btn-run[data-v-a27e815e],.btn-delete[data-v-a27e815e],.download[data-v-a27e815e]{border:none;cursor:pointer;border-radius:7px}.opt-btn[data-v-a27e815e]{padding:8px 14px;color:#ccc;background:#2a2a2a;border:1px solid #3a3a3a}.btn-run[data-v-a27e815e]{padding:10px 18px;color:#fff;background:#3498db;font-weight:600}.btn-queue[data-v-a27e815e]{background:#27ae60}.btn-run[data-v-a27e815e]:disabled{opacity:.55;cursor:not-allowed}.selected-files[data-v-a27e815e]{margin-top:14px;color:#999;font-size:12px;word-break:break-all}.selected-files span[data-v-a27e815e]{display:block;margin:4px 0}.parse-card[data-v-a27e815e],.queue-payload[data-v-a27e815e]{margin-top:14px;padding:12px;border:1px solid #2a2a2a;border-radius:8px;background:#202020;color:#b8c1cc;font-size:12px}.queue-payload[data-v-a27e815e]{max-height:220px;overflow:auto;color:#8fd3ff;white-space:pre-wrap}.panel-header[data-v-a27e815e]{padding:16px 20px;border-bottom:1px solid #2a2a2a;font-size:15px;font-weight:600;color:#ddd}.task-list-wrap[data-v-a27e815e]{flex:1;padding:16px 20px;overflow:auto}.clean-result-summary[data-v-a27e815e]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:16px}.summary-card[data-v-a27e815e]{padding:14px 16px;border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e}.summary-card strong[data-v-a27e815e]{display:block;margin-top:8px;color:#eaf4ff;font-size:22px}.summary-label[data-v-a27e815e]{color:#8d8d8d;font-size:12px}.result-list-wrap[data-v-a27e815e]{border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e;min-height:180px;margin:0 0 16px}.result-list-header[data-v-a27e815e]{display:flex;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #2a2a2a;color:#ddd;font-size:14px}.empty-tasks[data-v-a27e815e]{color:#666;font-size:13px;padding:18px;text-align:center}.task-list[data-v-a27e815e]{list-style:none;margin:0;padding:12px}.task-item[data-v-a27e815e]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid #2a2a2a;border-radius:8px;margin-bottom:8px;background:#222}.left[data-v-a27e815e]{flex:1;min-width:0}.id[data-v-a27e815e]{color:#e0e0e0;font-size:13px;font-weight:600}.task-right[data-v-a27e815e]{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.status[data-v-a27e815e]{padding:4px 10px;border-radius:6px;font-size:12px}.status.success[data-v-a27e815e]{background:#2ecc712e;color:#2ecc71}.status.failed[data-v-a27e815e]{background:#e74c3c2e;color:#ff6b6b}.status.running[data-v-a27e815e]{background:#3498db2e;color:#3498db}.status.pending[data-v-a27e815e]{background:#95a5a62e;color:#bdc3c7}.result-hint[data-v-a27e815e]{margin-top:6px;color:#e0b96d}.file-progress[data-v-a27e815e]{margin-top:8px;max-width:520px}.file-progress-meta[data-v-a27e815e]{display:flex;justify-content:space-between;gap:12px;color:#d8c278;font-size:12px}.file-progress-track[data-v-a27e815e]{margin-top:5px;height:8px;border-radius:999px;overflow:hidden;background:#303030;border:1px solid #3b3b3b}.file-progress-bar[data-v-a27e815e]{height:100%;border-radius:inherit;background:linear-gradient(90deg,#4aa3ff,#f0c75e);transition:width .25s ease}.file-progress-count[data-v-a27e815e]{margin-top:4px;color:#858585;font-size:11px}.download[data-v-a27e815e]{padding:6px 10px;color:#d6ecff;background:#3498db2e}.btn-delete[data-v-a27e815e]{padding:6px 10px;color:#ff8f8f;background:#e74c3c1f}@media(max-width:1100px){.main-content[data-v-a27e815e]{flex-direction:column;height:auto}.left-panel[data-v-a27e815e]{width:100%;border-right:none;border-bottom:1px solid #2a2a2a}.clean-result-summary[data-v-a27e815e]{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
1
app/assets/similar-asin-CVYG_iIM.css
Normal file
1
app/assets/similar-asin-CVYG_iIM.css
Normal file
@@ -0,0 +1 @@
|
||||
.module-page[data-v-39de0e76]{min-height:100vh;background:#1a1a1a}.main-content[data-v-39de0e76]{display:flex;height:calc(100vh - 56px);min-height:calc(100vh - 56px)}.left-panel[data-v-39de0e76]{width:400px;background:#1e1e1e;padding:20px;overflow-y:auto;border-right:1px solid #2a2a2a}.right-panel[data-v-39de0e76]{flex:1;min-width:0;background:#1a1a1a;display:flex;flex-direction:column}.section-title[data-v-39de0e76],.subsection-title[data-v-39de0e76]{font-size:13px;color:#bbb;margin-bottom:10px}.upload-zone[data-v-39de0e76]{border:1px dashed #3a3a3a;border-radius:10px;padding:18px;background:#252525;margin-bottom:18px}.hint[data-v-39de0e76],.loading-msg[data-v-39de0e76],.files[data-v-39de0e76],.muted[data-v-39de0e76]{color:#888;font-size:12px;line-height:1.5}.link[data-v-39de0e76]{color:#6ea8fe;text-decoration:none}.link[data-v-39de0e76]:hover{color:#9fc5ff}.btns[data-v-39de0e76],.run-row[data-v-39de0e76]{display:flex;gap:10px;flex-wrap:wrap}.opt-btn[data-v-39de0e76],.btn-run[data-v-39de0e76],.btn-delete[data-v-39de0e76],.download[data-v-39de0e76]{border:none;cursor:pointer;border-radius:7px}.opt-btn[data-v-39de0e76]{padding:8px 14px;color:#ccc;background:#2a2a2a;border:1px solid #3a3a3a}.btn-run[data-v-39de0e76]{padding:10px 18px;color:#fff;background:#3498db;font-weight:600}.btn-queue[data-v-39de0e76]{background:#27ae60}.btn-run[data-v-39de0e76]:disabled{opacity:.55;cursor:not-allowed}.selected-files[data-v-39de0e76]{margin-top:14px;color:#999;font-size:12px;word-break:break-all}.selected-files span[data-v-39de0e76]{display:block;margin:4px 0}.parse-card[data-v-39de0e76],.queue-payload[data-v-39de0e76]{margin-top:14px;padding:12px;border:1px solid #2a2a2a;border-radius:8px;background:#202020;color:#b8c1cc;font-size:12px}.queue-payload[data-v-39de0e76]{max-height:220px;overflow:auto;color:#8fd3ff;white-space:pre-wrap}.panel-header[data-v-39de0e76]{padding:16px 20px;border-bottom:1px solid #2a2a2a;font-size:15px;font-weight:600;color:#ddd}.task-list-wrap[data-v-39de0e76]{flex:1;padding:16px 20px;overflow:auto}.clean-result-summary[data-v-39de0e76]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:16px}.summary-card[data-v-39de0e76]{padding:14px 16px;border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e}.summary-card strong[data-v-39de0e76]{display:block;margin-top:8px;color:#eaf4ff;font-size:22px}.summary-label[data-v-39de0e76]{color:#8d8d8d;font-size:12px}.result-list-wrap[data-v-39de0e76]{border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e;min-height:180px;margin:0 0 16px}.result-list-header[data-v-39de0e76]{display:flex;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #2a2a2a;color:#ddd;font-size:14px}.empty-tasks[data-v-39de0e76]{color:#666;font-size:13px;padding:18px;text-align:center}.task-list[data-v-39de0e76]{list-style:none;margin:0;padding:12px}.task-item[data-v-39de0e76]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid #2a2a2a;border-radius:8px;margin-bottom:8px;background:#222}.left[data-v-39de0e76]{flex:1;min-width:0}.id[data-v-39de0e76]{color:#e0e0e0;font-size:13px;font-weight:600}.task-right[data-v-39de0e76]{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.status[data-v-39de0e76]{padding:4px 10px;border-radius:6px;font-size:12px}.status.success[data-v-39de0e76]{background:#2ecc712e;color:#2ecc71}.status.failed[data-v-39de0e76]{background:#e74c3c2e;color:#ff6b6b}.status.running[data-v-39de0e76]{background:#3498db2e;color:#3498db}.status.pending[data-v-39de0e76]{background:#95a5a62e;color:#bdc3c7}.result-hint[data-v-39de0e76]{margin-top:6px;color:#e0b96d}.file-progress[data-v-39de0e76]{margin-top:8px;max-width:520px}.file-progress-meta[data-v-39de0e76]{display:flex;justify-content:space-between;gap:12px;color:#d8c278;font-size:12px}.file-progress-track[data-v-39de0e76]{margin-top:5px;height:8px;border-radius:999px;overflow:hidden;background:#303030;border:1px solid #3b3b3b}.file-progress-bar[data-v-39de0e76]{height:100%;border-radius:inherit;background:linear-gradient(90deg,#4aa3ff,#f0c75e);transition:width .25s ease}.file-progress-count[data-v-39de0e76]{margin-top:4px;color:#858585;font-size:11px}.download[data-v-39de0e76]{padding:6px 10px;color:#d6ecff;background:#3498db2e}.btn-delete[data-v-39de0e76]{padding:6px 10px;color:#ff8f8f;background:#e74c3c1f}@media(max-width:1100px){.main-content[data-v-39de0e76]{flex-direction:column;height:auto}.left-panel[data-v-39de0e76]{width:100%;border-right:none;border-bottom:1px solid #2a2a2a}.clean-result-summary[data-v-39de0e76]{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/split-CR1PtDQE.css
Normal file
1
app/assets/split-CR1PtDQE.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/zh-cn-z8KAT-CE.js
Normal file
1
app/assets/zh-cn-z8KAT-CE.js
Normal file
File diff suppressed because one or more lines are too long
128
app/main.py
128
app/main.py
@@ -4,6 +4,8 @@ import json
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
import uuid
|
||||
import ctypes
|
||||
os.makedirs(cache_path,exist_ok=True)
|
||||
if not debug:
|
||||
today = datetime.datetime.now().strftime("%Y_%m_%d")
|
||||
@@ -186,17 +188,7 @@ class WindowAPI:
|
||||
if not result:
|
||||
return {'success': False, 'error': '用户取消'}
|
||||
path = result[0] if isinstance(result, (list, tuple)) else result
|
||||
try:
|
||||
import requests
|
||||
resp = requests.get(url.strip(), timeout=120, stream=True)
|
||||
resp.raise_for_status()
|
||||
with open(path, 'wb') as f:
|
||||
for chunk in resp.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
return {'success': True, 'path': path}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': str(e)}
|
||||
return self._download_url_to_path(url, path)
|
||||
|
||||
def save_file_from_url_new(self, url, default_filename='download.bin'):
|
||||
"""弹窗选择保存位置,从 url 下载文件并保存。根据文件后缀动态设置保存类型。"""
|
||||
@@ -230,18 +222,124 @@ class WindowAPI:
|
||||
return {'success': False, 'error': '用户取消'}
|
||||
|
||||
path = result[0] if isinstance(result, (list, tuple)) else result
|
||||
return self._download_url_to_path(url, path)
|
||||
|
||||
def save_file_from_url_with_progress(self, url, default_filename='download.bin', download_id=''):
|
||||
"""弹窗选择保存位置,从 url 下载文件并通过前端事件上报下载进度。"""
|
||||
print("调用到save_file_from_url_with_progress方法")
|
||||
if not url or not url.strip():
|
||||
return {'success': False, 'error': '下载地址为空'}
|
||||
|
||||
filename = str(default_filename or 'download.bin').strip() or 'download.bin'
|
||||
ext = os.path.splitext(filename)[1].lower()
|
||||
if ext == '.zip':
|
||||
file_types = ('ZIP 压缩包 (*.zip)', '所有文件 (*.*)')
|
||||
elif ext in ('.xlsx', '.xls'):
|
||||
file_types = ('Excel 文件 (*.xlsx;*.xls)', '所有文件 (*.*)')
|
||||
elif ext == '.csv':
|
||||
file_types = ('CSV 文件 (*.csv)', '所有文件 (*.*)')
|
||||
elif ext == '.txt':
|
||||
file_types = ('文本文件 (*.txt)', '所有文件 (*.*)')
|
||||
elif ext == '.json':
|
||||
file_types = ('JSON 文件 (*.json)', '所有文件 (*.*)')
|
||||
else:
|
||||
file_types = ('所有文件 (*.*)',)
|
||||
|
||||
result = self._window.create_file_dialog(
|
||||
webview.SAVE_DIALOG,
|
||||
save_filename=filename,
|
||||
file_types=file_types
|
||||
)
|
||||
if not result:
|
||||
return {'success': False, 'error': '用户取消'}
|
||||
|
||||
path = result[0] if isinstance(result, (list, tuple)) else result
|
||||
progress_id = str(download_id or uuid.uuid4())
|
||||
return self._download_url_to_path(url, path, progress_id)
|
||||
|
||||
def _download_url_to_path(self, url, path, download_id=''):
|
||||
"""先下载到临时文件,完整下载后再替换为最终文件,避免用户打开半成品。"""
|
||||
final_path = os.path.abspath(path)
|
||||
final_dir = os.path.dirname(final_path) or os.getcwd()
|
||||
final_name = os.path.basename(final_path)
|
||||
temp_path = os.path.join(final_dir, f".{final_name}.crdownload")
|
||||
last_progress_emit = 0.0
|
||||
last_progress_percent = -1
|
||||
try:
|
||||
import requests
|
||||
os.makedirs(final_dir, exist_ok=True)
|
||||
if os.path.exists(temp_path):
|
||||
os.remove(temp_path)
|
||||
resp = requests.get(url.strip(), timeout=120, stream=True)
|
||||
resp.raise_for_status()
|
||||
with open(path, 'wb') as f:
|
||||
total = int(resp.headers.get('Content-Length') or 0)
|
||||
downloaded = 0
|
||||
self._emit_download_progress(download_id, 'running', final_path, downloaded, total)
|
||||
with open(temp_path, 'wb') as f:
|
||||
self._hide_file(temp_path)
|
||||
for chunk in resp.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
return {'success': True, 'path': path}
|
||||
downloaded += len(chunk)
|
||||
percent = int(downloaded * 100 / total) if total else 0
|
||||
now_time = time.time()
|
||||
if percent != last_progress_percent or now_time - last_progress_emit >= 0.2:
|
||||
self._emit_download_progress(download_id, 'running', final_path, downloaded, total)
|
||||
last_progress_emit = now_time
|
||||
last_progress_percent = percent
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
self._show_file(temp_path)
|
||||
os.replace(temp_path, final_path)
|
||||
self._emit_download_progress(download_id, 'success', final_path, downloaded, total)
|
||||
return {'success': True, 'path': final_path}
|
||||
except Exception as e:
|
||||
try:
|
||||
if os.path.exists(temp_path):
|
||||
os.remove(temp_path)
|
||||
except Exception:
|
||||
pass
|
||||
self._emit_download_progress(download_id, 'failed', final_path, 0, 0, str(e))
|
||||
return {'success': False, 'error': str(e)}
|
||||
|
||||
def _emit_download_progress(self, download_id, status, path, downloaded, total, error=''):
|
||||
if not download_id:
|
||||
return
|
||||
payload = {
|
||||
'id': download_id,
|
||||
'status': status,
|
||||
'path': path,
|
||||
'downloaded': downloaded,
|
||||
'total': total,
|
||||
'percent': round(downloaded * 100 / total, 1) if total else 0,
|
||||
'error': error,
|
||||
}
|
||||
script = (
|
||||
"window.dispatchEvent(new CustomEvent('pywebview-download-progress', "
|
||||
f"{{ detail: {json.dumps(payload, ensure_ascii=False)} }}));"
|
||||
)
|
||||
try:
|
||||
self._window.evaluate_js(script)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _hide_file(self, path):
|
||||
"""Windows 下隐藏临时下载文件,减少用户误打开半成品的机会。"""
|
||||
if os.name != 'nt':
|
||||
return
|
||||
try:
|
||||
ctypes.windll.kernel32.SetFileAttributesW(path, 0x02)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _show_file(self, path):
|
||||
"""恢复普通文件属性,避免最终保存文件被隐藏。"""
|
||||
if os.name != 'nt':
|
||||
return
|
||||
try:
|
||||
ctypes.windll.kernel32.SetFileAttributesW(path, 0x80)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def save_template_xlsx(self):
|
||||
"""弹窗选择保存位置,将品牌文档格式模板 xlsx 保存到用户选择的位置。"""
|
||||
template_name = '品牌文档格式_模板.xlsx'
|
||||
@@ -440,7 +538,7 @@ def main():
|
||||
|
||||
api = WindowAPI(window)
|
||||
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
|
||||
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new,api.get_device_id)
|
||||
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new,api.save_file_from_url_with_progress,api.get_device_id)
|
||||
webview.start(
|
||||
debug=True,
|
||||
storage_path=cache_path,
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>格式转换 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/convert.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-3bFwh9Cg.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-D__LhhPM.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-B7UKcPVJ.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/convert-BKSNvX8i.css">
|
||||
<link rel="modulepreload" crossorigin href="/assets/java-modules-DzYrjxib.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-jr47vQUd.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/convert-6oxZNMye.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>数据去重 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-3bFwh9Cg.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-D__LhhPM.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-B7UKcPVJ.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-DNlVfFj-.css">
|
||||
<link rel="modulepreload" crossorigin href="/assets/java-modules-DzYrjxib.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-jr47vQUd.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-DpfPYQDv.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>删除品牌 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/delete-brand.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-3bFwh9Cg.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-D__LhhPM.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/java-modules-DzYrjxib.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-jr47vQUd.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/categorized-timers-JPA-olTr.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-B7UKcPVJ.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-BJod9K65.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-DZ3x5hOX.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>数据拆分 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/split.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-3bFwh9Cg.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-D__LhhPM.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-B7UKcPVJ.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/split-BoBVrLdC.css">
|
||||
<link rel="modulepreload" crossorigin href="/assets/java-modules-DzYrjxib.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-jr47vQUd.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/split-CR1PtDQE.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Reference in New Issue
Block a user