From 30b8c910e893e71191bf12c992148b5cf3f218f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Mon, 7 Sep 2026 16:35:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E6=A1=8C=E9=9D=A2=E7=AB=AF?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=20Web=20=E7=8B=AC=E7=AB=8B=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E9=80=82=E9=85=8D=E2=80=94=E2=80=94=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E9=99=8D=E7=BA=A7=E6=A1=A5+=E7=99=BB=E5=BD=95=E6=80=81?= =?UTF-8?q?=E5=BC=95=E5=AF=BC+=E9=A1=B5=E9=9D=A2=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 web-fallback.ts:无 pywebview 时以浏览器能力降级(文件选择/上传/模板下载/配置), 其余桌面能力(enqueue_json/vc_*/folder 选择等)保持 undefined 交由页面既有请在本地客户端中打开提示 - 新增 ensure-auth.ts:无 uid 时经 /newApi/check_login 恢复登录态,无 token/失效跳登录页; 21 个入口 main.ts 挂载前先引导(桌面端已注入 uid 时零开销) - pywebview.ts:getPywebviewApi() 在纯浏览器环境返回降级桥;新增 isDesktopRuntime 标志 (首页更新面板/退出链接据此区分桌面与 Web);hasPywebview 语义修正为真实桥判定 - 登录页跳转、首页登出链接、品牌检测页 hasBridge 判定(select_brand_folder 存在性)适配 Web --- frontend-vue/src/amazon-console-main.ts | 7 +- frontend-vue/src/appearance-patent-main.ts | 7 +- frontend-vue/src/brand-main.ts | 7 +- frontend-vue/src/collect-data-main.ts | 7 +- frontend-vue/src/convert-main.ts | 7 +- frontend-vue/src/dedupe-main.ts | 7 +- frontend-vue/src/delete-brand-main.ts | 7 +- frontend-vue/src/home-main.ts | 7 +- frontend-vue/src/image-main.ts | 9 + frontend-vue/src/image-video-main.ts | 7 +- .../pages/brand/components/BrandBrandTab.vue | 3 +- .../src/pages/home/DesktopHomePage.vue | 22 ++- .../src/pages/image-video/ImageVideoPage.vue | 23 ++- .../src/pages/login/DesktopLoginPage.vue | 4 +- frontend-vue/src/patrol-delete-main.ts | 7 +- frontend-vue/src/price-track-main.ts | 7 +- frontend-vue/src/product-risk-main.ts | 7 +- frontend-vue/src/publish-main.ts | 7 +- frontend-vue/src/query-asin-main.ts | 7 +- frontend-vue/src/shared/auth/ensure-auth.ts | 59 ++++++ frontend-vue/src/shared/bridges/pywebview.ts | 28 ++- .../src/shared/bridges/web-fallback.ts | 168 ++++++++++++++++++ frontend-vue/src/shop-data-crawl-main.ts | 7 +- frontend-vue/src/shop-match-main.ts | 7 +- frontend-vue/src/similar-asin-main.ts | 7 +- frontend-vue/src/split-main.ts | 7 +- frontend-vue/src/variant-collection-main.ts | 7 +- frontend-vue/src/withdraw-main.ts | 7 +- 28 files changed, 424 insertions(+), 32 deletions(-) create mode 100644 frontend-vue/src/image-main.ts create mode 100644 frontend-vue/src/shared/auth/ensure-auth.ts create mode 100644 frontend-vue/src/shared/bridges/web-fallback.ts diff --git a/frontend-vue/src/amazon-console-main.ts b/frontend-vue/src/amazon-console-main.ts index 5885b4fd..9fa97767 100644 --- a/frontend-vue/src/amazon-console-main.ts +++ b/frontend-vue/src/amazon-console-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import AmazonConsolePage from '@/pages/amazon/AmazonConsolePage.vue' -createApp(AmazonConsolePage).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(AmazonConsolePage).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/appearance-patent-main.ts b/frontend-vue/src/appearance-patent-main.ts index 8cb25e12..72380e08 100644 --- a/frontend-vue/src/appearance-patent-main.ts +++ b/frontend-vue/src/appearance-patent-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import BrandAppearancePatentTab from '@/pages/brand/components/BrandAppearancePatentTab.vue' -createApp(BrandAppearancePatentTab).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandAppearancePatentTab).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/brand-main.ts b/frontend-vue/src/brand-main.ts index 80ec854a..cf3f956a 100644 --- a/frontend-vue/src/brand-main.ts +++ b/frontend-vue/src/brand-main.ts @@ -1,4 +1,5 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import zhCn from 'element-plus/es/locale/lang/zh-cn' import dayjs from 'dayjs' @@ -9,4 +10,8 @@ import BrandBrandTab from '@/pages/brand/components/BrandBrandTab.vue' dayjs.locale('zh-cn') -createApp(BrandBrandTab).use(ElementPlus, { locale: zhCn }).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandBrandTab).use(ElementPlus, { locale: zhCn }).mount('#app') + } +}) diff --git a/frontend-vue/src/collect-data-main.ts b/frontend-vue/src/collect-data-main.ts index 59db957f..871f60ac 100644 --- a/frontend-vue/src/collect-data-main.ts +++ b/frontend-vue/src/collect-data-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import BrandCollectDataTab from '@/pages/brand/components/BrandCollectDataTab.vue' -createApp(BrandCollectDataTab).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandCollectDataTab).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/convert-main.ts b/frontend-vue/src/convert-main.ts index 68ec465b..71f65b6c 100644 --- a/frontend-vue/src/convert-main.ts +++ b/frontend-vue/src/convert-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import BrandConvertTab from '@/pages/brand/components/BrandConvertTab.vue' -createApp(BrandConvertTab).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandConvertTab).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/dedupe-main.ts b/frontend-vue/src/dedupe-main.ts index 75f0a219..6164e33f 100644 --- a/frontend-vue/src/dedupe-main.ts +++ b/frontend-vue/src/dedupe-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import BrandDedupeTab from '@/pages/brand/components/BrandDedupeTab.vue' -createApp(BrandDedupeTab).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandDedupeTab).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/delete-brand-main.ts b/frontend-vue/src/delete-brand-main.ts index 7c3a7f40..c9ee4e7c 100644 --- a/frontend-vue/src/delete-brand-main.ts +++ b/frontend-vue/src/delete-brand-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import BrandDeleteBrandTab from '@/pages/brand/components/BrandDeleteBrandTab.vue' -createApp(BrandDeleteBrandTab).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(BrandDeleteBrandTab).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/home-main.ts b/frontend-vue/src/home-main.ts index a46f9476..d4c3034d 100644 --- a/frontend-vue/src/home-main.ts +++ b/frontend-vue/src/home-main.ts @@ -1,8 +1,13 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import zhCn from 'element-plus/es/locale/lang/zh-cn' import 'element-plus/dist/index.css' import '@/styles/main.css' import DesktopHomePage from '@/pages/home/DesktopHomePage.vue' -createApp(DesktopHomePage).use(ElementPlus, { locale: zhCn }).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(DesktopHomePage).use(ElementPlus, { locale: zhCn }).mount('#app') + } +}) diff --git a/frontend-vue/src/image-main.ts b/frontend-vue/src/image-main.ts new file mode 100644 index 00000000..4d4d2104 --- /dev/null +++ b/frontend-vue/src/image-main.ts @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' +import ImageWorkbenchPage from '@/pages/image/ImageWorkbenchPage.vue' + +ensureAuth().then((ok) => { + if (ok) { + createApp(ImageWorkbenchPage).mount('#app') + } +}) diff --git a/frontend-vue/src/image-video-main.ts b/frontend-vue/src/image-video-main.ts index 8aaff334..84749385 100644 --- a/frontend-vue/src/image-video-main.ts +++ b/frontend-vue/src/image-video-main.ts @@ -1,7 +1,12 @@ import { createApp } from 'vue' +import { ensureAuth } from '@/shared/auth/ensure-auth' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@/styles/main.css' import ImageVideoPage from '@/pages/image-video/ImageVideoPage.vue' -createApp(ImageVideoPage).use(ElementPlus).mount('#app') +ensureAuth().then((ok) => { + if (ok) { + createApp(ImageVideoPage).use(ElementPlus).mount('#app') + } +}) diff --git a/frontend-vue/src/pages/brand/components/BrandBrandTab.vue b/frontend-vue/src/pages/brand/components/BrandBrandTab.vue index 4be4205a..06fad2db 100644 --- a/frontend-vue/src/pages/brand/components/BrandBrandTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandBrandTab.vue @@ -148,7 +148,8 @@ const runMode = ref<'immediate' | 'queue'>('immediate') const submitting = ref(false) const tasks = ref([]) -const hasBridge = computed(() => Boolean(getPywebviewApi())) +// 品牌检测由桌面客户端本地执行:以"文件夹选择"桥(Web 降级桥不提供)判定桌面运行时,浏览器下仅可预览 +const hasBridge = computed(() => Boolean(getPywebviewApi()?.select_brand_folder)) const hasUid = computed(() => { const raw = typeof window === 'undefined' ? '' : window.localStorage.getItem('uid') || '' const numeric = Number(raw.trim()) diff --git a/frontend-vue/src/pages/home/DesktopHomePage.vue b/frontend-vue/src/pages/home/DesktopHomePage.vue index 4b1771b4..e1753129 100644 --- a/frontend-vue/src/pages/home/DesktopHomePage.vue +++ b/frontend-vue/src/pages/home/DesktopHomePage.vue @@ -4,6 +4,7 @@ 数富AI
{{ username || '未登录' }} - 退出 + 退出 @@ -78,6 +79,7 @@ import { computed, onMounted, ref } from 'vue' import { requestGetJson } from '@/shared/api/http' import { buildJavaUrl } from '@/shared/api/url' +import { isDesktopRuntime } from '@/shared/bridges/pywebview' import { resolvePageHref } from '@/shared/page-prefix' const username = ref('') @@ -88,6 +90,11 @@ const updateReady = ref(false) const updateFileUrl = ref('') const toastText = ref('') +// 桌面端走 Flask /logout(清 cookie 302 登录页);Web 独立部署 / dev 跳登录页并清本地 token(login.html?logout=1) +const logoutHref = computed(() => + isDesktopRuntime.value ? '/logout' : `${resolvePageHref('/new_web_source/login.html')}?logout=1`, +) + type PermissionState = { amazon: boolean; video: boolean; image: boolean } const permissionState = ref({ amazon: false, video: false, image: false }) const permissionUnavailable = ref(false) @@ -95,7 +102,7 @@ const permissionReady = ref(false) const amazonHref = computed(() => resolvePageHref('/new_web_source/amazon-console.html')) const videoHref = computed(() => resolvePageHref('/new_web_source/image-video.html')) -const imageHref = computed(() => resolvePageHref('/image')) +const imageHref = computed(() => resolvePageHref('/new_web_source/image.html')) const canShow = computed(() => permissionUnavailable.value ? { amazon: true, video: true, image: true } : permissionState.value, @@ -172,7 +179,11 @@ async function loadCurrentUser() { try { const localName = typeof window === 'undefined' ? '' : window.localStorage.getItem('username') || '' if (localName) username.value = localName - const resp = await window.fetch('/newApi/check_login', { credentials: 'include' }) + const token = typeof window === 'undefined' ? '' : window.localStorage.getItem('aiimage_auth_token') || '' + const resp = await window.fetch('/newApi/check_login', { + credentials: 'include', + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }) const body = await resp.json().catch(() => ({})) if (body?.success && body.data) { if (body.data.username) username.value = body.data.username @@ -257,7 +268,10 @@ async function doUpdate() { onMounted(() => { void loadCurrentUser() void loadPermissions() - void fetchVersion().catch(() => undefined) + // 版本检测/更新由桌面客户端 Flask 提供,Web 版不发起 + if (isDesktopRuntime.value) { + void fetchVersion().catch(() => undefined) + } }) diff --git a/frontend-vue/src/pages/image-video/ImageVideoPage.vue b/frontend-vue/src/pages/image-video/ImageVideoPage.vue index 4209a5ac..c2665364 100644 --- a/frontend-vue/src/pages/image-video/ImageVideoPage.vue +++ b/frontend-vue/src/pages/image-video/ImageVideoPage.vue @@ -2,7 +2,7 @@
@@ -21,9 +21,12 @@ - + 图片 +
@@ -75,10 +78,22 @@ import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue' import DeliveryVideoWorkspace from '@/pages/image-video/components/DeliveryVideoWorkspace.vue' import { getCurrentUserAppColumnKeys } from '@/shared/api/permission' import { getPywebviewApi } from '@/shared/bridges/pywebview' +import { isDesktopClientPage } from '@/shared/page-prefix' import DownloadProgressPanel from '@/shared/components/DownloadProgressPanel.vue' type ViewMode = 'menu' | 'delivery' +const isDesktop = isDesktopClientPage() + +function goHome() { + // 桌面端首页是 Flask /home(重定向到 Vue home);dev 下是 MPA 的 /home.html + return isDesktop ? '/home' : '/home.html' +} + +function showDesktopOnly(name: string) { + showStatus(`${name}为桌面客户端功能,请在本地客户端中打开`, 'error') +} + const currentView = ref('menu') const allowedColumnKeys = ref(new Set()) const permissionsLoading = ref(true) @@ -184,7 +199,7 @@ onBeforeUnmount(() => { async function launchDesktop() { const api = getPywebviewApi() if (!api?.launch_yaoayanui) { - showStatus('当前环境不支持拉起桌面端', 'error') + showStatus('数字人为桌面客户端程序:请在本机客户端中打开后再点击', 'error') return } @@ -246,7 +261,7 @@ async function launchDesktop() { font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif; background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)), - url('/static/bg.jpg') center/cover no-repeat, + url('/bg.jpg') center/cover no-repeat, #eef3f7; } diff --git a/frontend-vue/src/pages/login/DesktopLoginPage.vue b/frontend-vue/src/pages/login/DesktopLoginPage.vue index de871499..43af2443 100644 --- a/frontend-vue/src/pages/login/DesktopLoginPage.vue +++ b/frontend-vue/src/pages/login/DesktopLoginPage.vue @@ -53,6 +53,7 @@