Files
crawler-plugin/app/assets/dedupe.js
2026-03-28 15:42:02 +08:00

2 lines
10 KiB
JavaScript

import { d as z, o as W, a as n, c as o, b as q, B as J, e as s, F as C, r as F, t as i, f as b, n as I, w as P, v as B, g as u, h as L, i as x, E as r, j as O, k as G, l as Q, m as X, p as Y, _ as Z, q as ee, s as se } from "./pywebview-DdV1XQSM.js"; const te = { class: "page-shell module-page" }, le = { class: "main-content" }, ae = { class: "left-panel" }, ne = { class: "upload-zone" }, oe = { class: "selected-files clean-placeholder" }, re = { key: 0, class: "more-line" }, ie = { key: 1 }, ue = { class: "option-group column-option-group" }, ce = { class: "column-toolbar" }, de = { class: "column-count" }, ve = { key: 0, class: "column-grid" }, pe = ["value"], fe = { key: 1, class: "empty-column-state" }, _e = { class: "option-group" }, me = { class: "run-row" }, he = ["disabled"], ge = { class: "loading-msg" }, ye = { class: "right-panel" }, be = { class: "task-list-wrap" }, we = { class: "clean-result-summary" }, ke = { class: "summary-card" }, Ce = { class: "summary-card" }, Ie = { class: "summary-card" }, xe = { class: "result-list-wrap" }, Ee = { key: 0, class: "empty-tasks" }, De = { key: 1, class: "task-list clean-result-list" }, Fe = { class: "left" }, Pe = ["title"], Be = { key: 0, class: "files" }, Se = { key: 1, class: "files" }, Ue = { class: "task-right" }, Ne = ["onClick"], $e = ["onClick"], Ae = z({ __name: "BrandDedupeTab", setup(Ke) { const p = u([]), c = u([]), f = u([]), w = u(""), d = u([]), S = u(!1), _ = u(!0), m = u(!0), h = u(!1), k = u([]), g = u({ total: 0, successCount: 0, failedCount: 0, items: [] }), E = L(() => f.value.slice(0, 8)); function $() { c.value = [...p.value] } function A() { c.value = [] } async function U(l) { const e = x(); if (!e?.upload_file_to_java) throw new Error("当前桌面端未提供文件上传桥接能力"); const t = []; for (const a of l) { const v = typeof a == "string" ? a : a.absolutePath, j = typeof a == "string" ? void 0 : a.relativePath, y = await e.upload_file_to_java(v, j); if (!y?.success || !y.data) throw new Error(y?.error || y?.message || `上传失败:${v}`); t.push(y.data) } return t } async function N(l) { const e = await Y(l); if (!e.headers?.length) { r.error("读取 Excel 表头失败"), p.value = [], c.value = []; return } const t = ["id", "ASIN", "国家", "价格", "卖家名称"], a = [...t.filter(v => e.headers?.includes(v)), ...(e.headers || []).filter(v => !t.includes(v))]; p.value = a, c.value = t.filter(v => a.includes(v)) } async function H(l, e) { f.value = l, w.value = "", d.value = await U(l), d.value.length > 0 && await N(d.value[0].fileKey), r.success(e) } async function K() { const l = x(); if (!l?.select_brand_xlsx_files) { r.warning("当前环境不支持文件选择,请在本机客户端中打开"); return } try { const e = await l.select_brand_xlsx_files(); if (!e?.length) return; await H(e, `已选择 ${e.length} 个待清洗文件`) } catch (e) { r.error(e instanceof Error ? e.message : "选择失败") } } async function R() { const l = x(); if (!l?.select_brand_folder) { r.warning("当前环境不支持文件夹选择,请在本机客户端中打开"); return } try { const e = await l.select_brand_folder(); if (!e) return; w.value = e.split(/[/\\]/).filter(Boolean).pop() || ""; const t = await O(e); if (t.success && t.items?.length) { f.value = t.items.map(a => a.relativePath), d.value = await U(t.items), d.value.length > 0 && await N(d.value[0].fileKey), r.success(`已选择文件夹内 ${t.items.length} 个 xlsx 文件`); return } r.warning(t.error || "该文件夹下没有 xlsx 文件") } catch (e) { r.error(e instanceof Error ? e.message : "选择失败") } } async function V() { if (!d.value.length) { r.warning("请先选择待清洗 Excel 文件或文件夹"); return } if (!c.value.length) { r.warning("请至少选择一列保留列"); return } if (!S.value && !_.value && !m.value) { r.warning("请至少选择一种 ID 保留规则"); return } try { h.value = !0; const l = await G({ files: d.value.map(e => ({ fileKey: e.fileKey, originalFilename: e.originalFilename, relativePath: e.relativePath })), selectedColumns: c.value, keepIntegerIds: S.value, keepUnderscoreIds: _.value, keepIntegerMainIdsWhenNoSubIds: m.value, archiveName: d.value.some(e => !!e.relativePath) && w.value ? w.value : void 0 }); g.value = l, k.value = l.items || [], await D(), r.success("数据去重完成") } catch (l) { r.error(l instanceof Error ? l.message : "去重失败") } finally { h.value = !1 } } async function D() { try { const l = await Q(); k.value = l.items || [], g.value = { total: l.items?.length || 0, successCount: l.items?.filter(e => e.success).length || 0, failedCount: l.items?.filter(e => !e.success).length || 0, items: l.items || [] } } catch { } } async function M(l) { try { await X(l), await D(), r.success("已删除") } catch (e) { r.error(e instanceof Error ? e.message : "删除失败") } } async function T(l) { const e = x(); if (!l.downloadUrl) { r.warning("当前结果没有下载地址"); return } const t = l.outputFilename || `${new Date().toISOString().slice(0, 10).replace(/-/g, "")}_cleaned.xlsx`; if (e?.save_file_from_url) { const a = await e.save_file_from_url(l.downloadUrl, t); a.success ? r.success(`已保存:${a.path || t}`) : a.error && a.error !== "用户取消" && r.error(a.error); return } window.open(l.downloadUrl, "_blank") } return W(() => { D().catch(() => { }) }), (l, e) => (n(), o("div", te, [q(J, { active: "dedupe" }), s("div", le, [s("aside", ae, [e[7] || (e[7] = s("div", { class: "section-title" }, "选择文件", -1)), s("div", ne, [e[3] || (e[3] = s("div", { class: "hint" }, "选择需要清洗的 Excel 文件或文件夹,后续将按所选规则输出处理结果。", -1)), s("div", { class: "btns" }, [s("button", { type: "button", class: "opt-btn", onClick: K }, "选择 Excel 文件"), s("button", { type: "button", class: "opt-btn", onClick: R }, "选择文件夹")]), s("div", oe, [f.value.length ? (n(), o(C, { key: 0 }, [(n(!0), o(C, null, F(E.value, t => (n(), o("span", { key: t }, i(t), 1))), 128)), f.value.length > E.value.length ? (n(), o("span", re, " 还有 " + i(f.value.length - E.value.length) + " 个文件未展开显示 ", 1)) : b("", !0)], 64)) : (n(), o("span", ie, "暂未选择待清洗文件"))])]), e[8] || (e[8] = s("div", { class: "section-title" }, "保留列设置", -1)), s("div", ue, [s("div", ce, [s("span", de, "已选择 " + i(c.value.length) + " / " + i(p.value.length) + " 列", 1), s("div", { class: "column-actions" }, [s("button", { type: "button", class: "opt-btn small", onClick: $ }, "全选"), s("button", { type: "button", class: "opt-btn small", onClick: A }, "清空")])]), p.value.length ? (n(), o("div", ve, [(n(!0), o(C, null, F(p.value, t => (n(), o("label", { key: t, class: I(["column-item", { active: c.value.includes(t) }]) }, [P(s("input", { "onUpdate:modelValue": e[0] || (e[0] = a => c.value = a), type: "checkbox", value: t }, null, 8, pe), [[B, c.value]]), s("span", null, i(t), 1)], 2))), 128))])) : (n(), o("div", fe, "请选择 Excel 文件后读取表头并填充可保留列")), e[4] || (e[4] = s("div", { class: "desc" }, "读取首个 Excel 文件的第一行作为表头,用于动态选择需要保留的列。", -1))]), e[9] || (e[9] = s("div", { class: "section-title" }, "ID 保留规则", -1)), s("div", _e, [s("label", { class: I(["radio-item", { active: _.value }]) }, [P(s("input", { "onUpdate:modelValue": e[1] || (e[1] = t => _.value = t), type: "checkbox" }, null, 512), [[B, _.value]]), e[5] || (e[5] = s("div", null, [s("span", { class: "label" }, "保留子链接"), s("div", { class: "desc" }, "例如 1_1、2_3 这种带下划线的子项 ID")], -1))], 2), s("label", { class: I(["radio-item", { active: m.value }]) }, [P(s("input", { "onUpdate:modelValue": e[2] || (e[2] = t => m.value = t), type: "checkbox" }, null, 512), [[B, m.value]]), e[6] || (e[6] = s("div", null, [s("span", { class: "label" }, "仅剩主链接无子链接"), s("div", { class: "desc" }, "当某个主链接只有 13 这类主 ID、没有 13_1 这类对应子 ID 时,自动保留该主 ID")], -1))], 2)]), s("div", me, [s("button", { type: "button", class: "btn-run", disabled: h.value, onClick: V }, i(h.value ? "清洗中..." : "开始清洗"), 9, he), s("span", ge, i(h.value ? "正在处理文件并上传结果,请稍候…" : "选择文件、列和 ID 规则后即可执行清洗,结果会显示在右侧供下载"), 1)])]), s("section", ye, [e[14] || (e[14] = s("div", { class: "panel-header" }, "去重结果", -1)), s("div", be, [s("div", we, [s("div", ke, [e[10] || (e[10] = s("span", { class: "summary-label" }, "已处理文件", -1)), s("strong", null, i(g.value.total), 1)]), s("div", Ce, [e[11] || (e[11] = s("span", { class: "summary-label" }, "成功结果", -1)), s("strong", null, i(g.value.successCount), 1)]), s("div", Ie, [e[12] || (e[12] = s("span", { class: "summary-label" }, "失败文件", -1)), s("strong", null, i(g.value.failedCount), 1)])]), s("div", xe, [e[13] || (e[13] = s("div", { class: "result-list-header" }, [s("span", null, "处理后的 Excel 列表")], -1)), k.value.length === 0 ? (n(), o("div", Ee, " 暂无去重结果,完成数据去重后会在这里展示输出文件 ")) : (n(), o("ul", De, [(n(!0), o(C, null, F(k.value, t => (n(), o("li", { key: `${t.resultId || t.outputFilename || t.sourceFilename}`, class: "task-item" }, [s("div", Fe, [s("span", { class: "id", title: t.sourceFilename }, i(t.sourceFilename || "-"), 9, Pe), t.outputFilename ? (n(), o("div", Be, "输出文件:" + i(t.outputFilename), 1)) : b("", !0), t.error ? (n(), o("div", Se, "错误信息:" + i(t.error), 1)) : b("", !0)]), s("div", Ue, [s("span", { class: I(["status", t.success ? "success" : "failed"]) }, i(t.success ? "已完成" : "失败"), 3), t.success && t.downloadUrl ? (n(), o("button", { key: 0, type: "button", class: "download", onClick: a => T(t) }, " 下载文件 ", 8, Ne)) : b("", !0), t.resultId ? (n(), o("button", { key: 1, type: "button", class: "btn-delete", onClick: a => M(t.resultId) }, " 删除 ", 8, $e)) : b("", !0)])]))), 128))]))])])])])])) } }), He = Z(Ae, [["__scopeId", "data-v-6b14da88"]]); ee(He).use(se).mount("#app");