From bf22177e806e5fc95eeda07e1fdc3ffa0b5cfe69 Mon Sep 17 00:00:00 2001 From: "949036910@qq.com" <> Date: Thu, 28 May 2026 15:24:21 +0800 Subject: [PATCH] 11 --- src-tauri/src/commands/publish.rs | 27 +- src-tauri/src/publish_db.rs | 47 +++- src-tauri/src/publish_store.rs | 7 + src/components/workflow/Step07Publish.vue | 304 +++++++++++++++++++++- src/services/videoPublish.js | 38 +-- src/stores/workflow.js | 4 +- 6 files changed, 387 insertions(+), 40 deletions(-) diff --git a/src-tauri/src/commands/publish.rs b/src-tauri/src/commands/publish.rs index 267e0b9..7cbcde1 100644 --- a/src-tauri/src/commands/publish.rs +++ b/src-tauri/src/commands/publish.rs @@ -96,13 +96,23 @@ pub async fn publish_login( params: PublishLoginParams, ) -> Result { let _ = app_config; + let platform = params.platform.trim().to_string(); + if platform.is_empty() { + return Err("platform 不能为空".to_string()); + } + // 对齐 Electron:先创建账号槽位,再走扫码登录,保证每个账号独立 userDataDir。 + let (effective_account_id, created_slot) = if let Some(id) = params.account_id { + (id, false) + } else { + (store.create_account_slot(platform.clone()).await?.id, true) + }; let result = run_publish_script( app, window, "publish_login.js", json!({ - "platform": params.platform, - "accountId": params.account_id, + "platform": platform, + "accountId": effective_account_id, }), ) .await?; @@ -118,17 +128,23 @@ pub async fn publish_login( .and_then(|v| v.as_str()) .unwrap_or("") .to_string(); + let avatar = result + .get("avatar") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); let cookies = result .get("cookies") .map(|v| serde_json::to_string(v).unwrap_or_else(|_| "[]".to_string())) .unwrap_or_else(|| "[]".to_string()); let rec = store .upsert_login( - params.platform.clone(), + platform, nickname, uid, + avatar, cookies, - params.account_id, + Some(effective_account_id), ) .await?; return Ok(json!({ @@ -138,6 +154,9 @@ pub async fn publish_login( })); } + if created_slot { + let _ = store.delete(effective_account_id).await; + } Ok(result) } diff --git a/src-tauri/src/publish_db.rs b/src-tauri/src/publish_db.rs index 8464d84..3f16c8a 100644 --- a/src-tauri/src/publish_db.rs +++ b/src-tauri/src/publish_db.rs @@ -138,6 +138,7 @@ impl PublishDb { platform: &str, nickname: &str, uid: &str, + avatar: &str, cookies_json: &str, account_id: Option, ) -> Result { @@ -149,9 +150,9 @@ impl PublishDb { if let Some(id) = account_id { conn.execute( - "UPDATE platform_accounts SET nickname = ?1, uid = ?2, cookies = ?3, - login_status = 'active', expires_at = ?4, updated_at = ?5 WHERE id = ?6", - params![nickname, uid, cookies_json, expires, now, id], + "UPDATE platform_accounts SET nickname = ?1, uid = ?2, avatar = ?3, cookies = ?4, + login_status = 'active', expires_at = ?5, updated_at = ?6 WHERE id = ?7", + params![nickname, uid, avatar, cookies_json, expires, now, id], )?; return self .get(id)? @@ -159,11 +160,30 @@ impl PublishDb { .ok_or_else(|| PublishDbError::Message("更新账号失败".into())); } + if !uid.trim().is_empty() { + let mut stmt = conn.prepare( + "SELECT id FROM platform_accounts WHERE platform = ?1 AND uid = ?2 LIMIT 1", + )?; + let mut rows = stmt.query(params![platform, uid])?; + if let Some(row) = rows.next()? { + let existing_id: i64 = row.get(0)?; + conn.execute( + "UPDATE platform_accounts SET nickname = ?1, avatar = ?2, cookies = ?3, + login_status = 'active', expires_at = ?4, updated_at = ?5 WHERE id = ?6", + params![nickname, avatar, cookies_json, expires, now, existing_id], + )?; + return self + .get(existing_id)? + .map(|(r, _)| r) + .ok_or_else(|| PublishDbError::Message("更新账号失败".into())); + } + } + conn.execute( "INSERT INTO platform_accounts (platform, nickname, uid, avatar, cookies, tokens, login_status, expires_at, created_at, updated_at) - VALUES (?1, ?2, ?3, '', ?4, '', 'active', ?5, ?6, ?6)", - params![platform, nickname, uid, cookies_json, expires, now], + VALUES (?1, ?2, ?3, ?4, ?5, '', 'active', ?6, ?7, ?7)", + params![platform, nickname, uid, avatar, cookies_json, expires, now], )?; let id = conn.last_insert_rowid(); self.get(id)? @@ -171,6 +191,23 @@ impl PublishDb { .ok_or_else(|| PublishDbError::Message("插入账号失败".into())) } + pub fn create_account_slot(&self, platform: &str) -> Result { + let conn = self.conn.lock().map_err(|_| { + PublishDbError::Message("数据库锁异常".into()) + })?; + let now = chrono::Utc::now().timestamp_millis(); + conn.execute( + "INSERT INTO platform_accounts (platform, nickname, uid, avatar, cookies, tokens, + login_status, expires_at, created_at, updated_at) + VALUES (?1, '', '', '', '[]', '', 'inactive', NULL, ?2, ?2)", + params![platform, now], + )?; + let id = conn.last_insert_rowid(); + self.get(id)? + .map(|(r, _)| r) + .ok_or_else(|| PublishDbError::Message("创建账号槽位失败".into())) + } + pub fn set_login_status(&self, id: i64, status: &str) -> Result<(), PublishDbError> { let conn = self.conn.lock().map_err(|_| { PublishDbError::Message("数据库锁异常".into()) diff --git a/src-tauri/src/publish_store.rs b/src-tauri/src/publish_store.rs index 2087619..4140264 100644 --- a/src-tauri/src/publish_store.rs +++ b/src-tauri/src/publish_store.rs @@ -64,6 +64,7 @@ impl PublishStore { platform: String, nickname: String, uid: String, + avatar: String, cookies_json: String, account_id: Option, ) -> Result { @@ -72,6 +73,7 @@ impl PublishStore { &platform, &nickname, &uid, + &avatar, &cookies_json, account_id, ) @@ -80,6 +82,11 @@ impl PublishStore { .await } + pub async fn create_account_slot(&self, platform: String) -> Result { + self.with_db(move |db| db.create_account_slot(&platform).map_err(|e| e.to_string())) + .await + } + pub async fn set_login_status(&self, id: i64, status: String) -> Result<(), String> { self.with_db(move |db| db.set_login_status(id, &status).map_err(|e| e.to_string())) .await diff --git a/src/components/workflow/Step07Publish.vue b/src/components/workflow/Step07Publish.vue index 3fc6c1c..8afb078 100644 --- a/src/components/workflow/Step07Publish.vue +++ b/src/components/workflow/Step07Publish.vue @@ -18,6 +18,8 @@ const { const feedback = ref({ severity: "", message: "" }); const scheduleDialogVisible = ref(false); const scheduleDate = ref(null); +const accountDialogVisible = ref(false); +const activePlatformKey = ref("douyin"); const hasVideo = computed( () => Boolean(processedVideoPath.value || generatedVideoPath.value), @@ -40,6 +42,20 @@ const accountOptions = (platform) => value: a.id, })); +const platformIcons = { + douyin: "📱", + kuaishou: "🎬", + shipin: "📺", + xiaohongshu: "📕", +}; + +const activePlatform = computed( + () => + publishPlatforms.value.find((p) => p.key === activePlatformKey.value) || + publishPlatforms.value[0] || + null, +); + function showFeedback(result) { if (!result?.message) return; feedback.value = { @@ -52,6 +68,17 @@ onMounted(async () => { await workflow.refreshPublishAccounts(); }); +function openAccountManager() { + if (publishPlatforms.value.length && !activePlatform.value) { + activePlatformKey.value = publishPlatforms.value[0].key; + } + accountDialogVisible.value = true; +} + +function selectPlatform(platformKey) { + activePlatformKey.value = platformKey; +} + async function onPublish() { feedback.value = { severity: "", message: "" }; showFeedback(await workflow.publishVideo({ autoPublish: true })); @@ -85,22 +112,77 @@ function cancelSchedule() { feedback.value = { severity: "success", message: "已取消定时发布" }; } -async function onLogin(platform) { +async function onLogin(platform, accountId = undefined) { feedback.value = { severity: "", message: "" }; - showFeedback(await workflow.loginPublishPlatform(platform.key)); + showFeedback(await workflow.loginPublishPlatform(platform.key, accountId)); +} + +async function onLoginAccount(platform, account) { + await onLogin(platform, account.id); +} + +async function onAddAccount(platform) { + await onLogin(platform, null); +} + +function onDeleteAccount(platform, account) { + platform.accounts = (platform.accounts || []).filter((a) => a.id !== account.id); + if (platform.accountId === account.id) { + platform.accountId = null; + } + if (!platform.accounts.length) { + platform.loggedIn = false; + platform.nickname = ""; + } + feedback.value = { severity: "success", message: "已从本地列表移除账号,刷新后可重新获取" }; +} + +function formatAccountStatus(account) { + if (account?.loginStatus === "active" && account?.hasCookies) { + return { dot: "🟢", text: "已登录", color: "#4ade80" }; + } + if (account?.loginStatus === "expired") { + return { dot: "🟠", text: "登录过期", color: "#f59e0b" }; + } + return { dot: "⚪", text: "未登录", color: "#999999" }; +} + +function formatUpdateTime(account) { + const raw = account?.updatedAt || account?.lastLoginAt || account?.createdAt; + if (!raw) return ""; + const d = new Date(raw); + if (Number.isNaN(d.getTime())) return ""; + return d.toLocaleString("zh-CN", { + month: "numeric", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); } + + diff --git a/src/services/videoPublish.js b/src/services/videoPublish.js index 77110ac..9ed2bca 100644 --- a/src/services/videoPublish.js +++ b/src/services/videoPublish.js @@ -54,15 +54,17 @@ export async function refreshPublishAccounts(store) { * @param {ReturnType} store * @param {string} platformKey */ -export async function loginPublishPlatform(store, platformKey) { +export async function loginPublishPlatform(store, platformKey, accountId = undefined) { const row = store.publishPlatforms.find((p) => p.key === platformKey); if (!row) return { ok: false, message: "未知平台" }; store.publishLoggingIn = platformKey; try { const result = await invoke("publish_login", { - platform: platformKey, - accountId: row.accountId || null, + params: { + platform: platformKey, + accountId: accountId === undefined ? row.accountId || null : accountId, + }, }); if (!result?.success) { return { @@ -116,9 +118,11 @@ export async function executePublishVideo(store, { autoPublish = true } = {}) { for (const row of selected) { const check = await invoke("publish_check_login", { - platform: row.key, - accountId: row.accountId || null, - checkBrowser: false, + params: { + platform: row.key, + accountId: row.accountId || null, + checkBrowser: false, + }, }); if (!check?.isLoggedIn) { const label = row.label || row.key; @@ -142,16 +146,18 @@ export async function executePublishVideo(store, { autoPublish = true } = {}) { try { const result = await invoke("publish_execute", { - platforms: selected.map((p) => ({ - platform: p.key, - accountId: p.accountId || null, - })), - videoPath, - coverPath, - title: title.split(/\r?\n/)[0].slice(0, 30), - description, - tags, - autoPublish, + params: { + platforms: selected.map((p) => ({ + platform: p.key, + accountId: p.accountId || null, + })), + videoPath, + coverPath, + title: title.split(/\r?\n/)[0].slice(0, 30), + description, + tags, + autoPublish, + }, }); store.publishResults = Array.isArray(result?.results) ? result.results : []; diff --git a/src/stores/workflow.js b/src/stores/workflow.js index 5f64337..b183ca5 100644 --- a/src/stores/workflow.js +++ b/src/stores/workflow.js @@ -685,8 +685,8 @@ export const useWorkflowStore = defineStore("workflow", { return refreshPublishAccounts(this); }, - async loginPublishPlatform(platformKey) { - return loginPublishPlatform(this, platformKey); + async loginPublishPlatform(platformKey, accountId = undefined) { + return loginPublishPlatform(this, platformKey, accountId); }, async publishVideo(options) {