diff --git a/src/components/workflow/VideoLinkDialog.vue b/src/components/workflow/VideoLinkDialog.vue
new file mode 100644
index 0000000..a4d691a
--- /dev/null
+++ b/src/components/workflow/VideoLinkDialog.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
diff --git a/src/main.js b/src/main.js
index e6b1896..26d7142 100644
--- a/src/main.js
+++ b/src/main.js
@@ -18,6 +18,7 @@ import TabList from "primevue/tablist";
import Tab from "primevue/tab";
import TabPanels from "primevue/tabpanels";
import TabPanel from "primevue/tabpanel";
+import Dialog from "primevue/dialog";
import DashboardCard from "./components/dashboard/DashboardCard.vue";
import App from "./App.vue";
@@ -55,5 +56,6 @@ app.component("Tab", Tab);
app.component("TabPanels", TabPanels);
app.component("TabPanel", TabPanel);
app.component("DashboardCard", DashboardCard);
+app.component("Dialog", Dialog);
app.mount("#app");
diff --git a/src/stores/workflow.js b/src/stores/workflow.js
index 7490ad0..fcc99a4 100644
--- a/src/stores/workflow.js
+++ b/src/stores/workflow.js
@@ -1,4 +1,4 @@
-import { defineStore } from "pinia";
+import { defineStore, acceptHMRUpdate } from "pinia";
const defaultPublishPlatforms = () => [
{ label: "*音", checked: false, account: null },
@@ -11,6 +11,15 @@ export const useWorkflowStore = defineStore("workflow", {
state: () => ({
// 01 IP 深度学习
ipMode: "ipBrain",
+ ipBrainModalVisible: false,
+ ipBrainProfileUrl: "",
+ ipBrainCollecting: false,
+ ipBenchmarks: [],
+ ipTopics: [],
+ ipBrainSelectedBenchmarkId: null,
+ videoLinkModalVisible: false,
+ videoShareText: "",
+ videoRewriting: false,
recognizedContent: "",
videoType: "口播文案",
copyType: "人设型",
@@ -71,9 +80,69 @@ export const useWorkflowStore = defineStore("workflow", {
keywordCountDescribe: (state) => countLines(state.keywordsDescribe),
keywordCountAction: (state) => countLines(state.keywordsAction),
keywordCountEmotion: (state) => countLines(state.keywordsEmotion),
+ ipBenchmarkCount: (state) => state.ipBenchmarks.length,
+ selectedBenchmark: (state) =>
+ state.ipBenchmarks.find((b) => b.id === state.ipBrainSelectedBenchmarkId) ?? null,
},
actions: {
+ openIpBrainDialog() {
+ this.ipBrainModalVisible = true;
+ },
+
+ closeIpBrainDialog() {
+ this.ipBrainModalVisible = false;
+ },
+
+ async startIpBrainCollect() {
+ const url = this.ipBrainProfileUrl?.trim();
+ if (!url) {
+ return { ok: false, message: "请输入账号主页或分享链接" };
+ }
+ if (this.ipBenchmarks.length >= 5) {
+ return { ok: false, message: "最多添加 5 个对标账号" };
+ }
+
+ this.ipBrainCollecting = true;
+ try {
+ // TODO: 对接后端采集 API / Tauri 浏览器自动化
+ await new Promise((r) => setTimeout(r, 800));
+
+ const benchmark = {
+ id: `bm_${Date.now()}`,
+ url,
+ label: truncateUrl(url),
+ };
+ this.ipBenchmarks.push(benchmark);
+ this.ipBrainSelectedBenchmarkId = benchmark.id;
+ this.ipTopics = Array.from({ length: 5 }, (_, i) => ({
+ id: `topic_${Date.now()}_${i}`,
+ title: `示例视频标题 ${i + 1}(待对接真实采集)`,
+ }));
+ this.ipBrainProfileUrl = "";
+ this.ipBrainModalVisible = false;
+ return { ok: true, message: "采集完成" };
+ } finally {
+ this.ipBrainCollecting = false;
+ }
+ },
+
+ selectBenchmark(id) {
+ this.ipBrainSelectedBenchmarkId = id;
+ this.ipTopics = [];
+ },
+
+ openVideoLinkDialog() {
+ this.videoLinkModalVisible = true;
+ },
+
+ closeVideoLinkDialog() {
+ this.videoLinkModalVisible = false;
+ },
+
+ async startVideoRewrite() {
+ return executeVideoRewrite(this);
+ },
resetAll() {
this.$reset();
this.publishPlatforms = defaultPublishPlatforms();
@@ -85,7 +154,59 @@ export const useWorkflowStore = defineStore("workflow", {
},
});
+export async function executeVideoRewrite(store) {
+ const text = store.videoShareText?.trim();
+ if (!text) {
+ return { ok: false, message: "请粘贴视频分享文本或视频链接" };
+ }
+
+ const unsupported = detectUnsupportedPlatform(text);
+ if (unsupported) {
+ return { ok: false, message: unsupported };
+ }
+
+ store.videoRewriting = true;
+ try {
+ // TODO: 对接 FUNASR 语音识别与仿写 API
+ await new Promise((r) => setTimeout(r, 1000));
+
+ store.recognizedContent =
+ "【示例识别结果】这里是 FUNASR 语音识别后的原始文案内容,待对接后端后将显示真实结果。\n\n" +
+ `来源:${truncateUrl(text, 48)}`;
+ store.videoShareText = "";
+ store.videoLinkModalVisible = false;
+ return { ok: true, message: "仿写完成" };
+ } finally {
+ store.videoRewriting = false;
+ }
+}
+
+const UNSUPPORTED_PLATFORM_RULES = [
+ { pattern: /kuaishou\.com|ksurl\.cn|gifshow\.com/i, name: "快手" },
+ { pattern: /xiaohongshu\.com|xhslink\.com/i, name: "小红书" },
+ { pattern: /bilibili\.com|b23\.tv/i, name: "B站" },
+ { pattern: /channels\.weixin\.qq\.com|finder\.video\.qq\.com/i, name: "视频号" },
+];
+
+function detectUnsupportedPlatform(text) {
+ for (const { pattern, name } of UNSUPPORTED_PLATFORM_RULES) {
+ if (pattern.test(text)) {
+ return `${name}分享链接暂不支持,请使用抖音分享文本或视频链接`;
+ }
+ }
+ return null;
+}
+
+function truncateUrl(url, max = 36) {
+ if (url.length <= max) return url;
+ return `${url.slice(0, max)}…`;
+}
+
function countLines(text) {
if (!text?.trim()) return 0;
return text.split("\n").filter((line) => line.trim()).length;
}
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useWorkflowStore, import.meta.hot));
+}
diff --git a/src/styles/main.css b/src/styles/main.css
index 33d218d..b81d032 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -347,6 +347,47 @@ body {
box-shadow: 0 0 20px rgba(0, 229, 255, 0.15);
}
+.elegant-tip-box {
+ background: rgba(0, 229, 255, 0.06);
+ border: 1px solid rgba(0, 229, 255, 0.12);
+}
+
+.ip-brain-dialog :deep(.p-dialog-header) {
+ justify-content: center;
+}
+
+.ip-brain-dialog :deep(.p-dialog-title) {
+ flex: 1;
+ text-align: center;
+ font-weight: 600;
+}
+
+.benchmark-item {
+ display: block;
+ width: 100%;
+ padding: 8px 10px;
+ margin-bottom: 6px;
+ text-align: left;
+ font-size: 13px;
+ color: #cbd5e1;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid transparent;
+ border-radius: 8px;
+ cursor: pointer;
+ transition:
+ border-color 0.2s,
+ background 0.2s;
+}
+
+.benchmark-item:hover {
+ background: rgba(255, 255, 255, 0.06);
+}
+
+.benchmark-item--active {
+ border-color: rgba(0, 229, 255, 0.4);
+ background: rgba(0, 229, 255, 0.08);
+}
+
.dashboard-textarea-mono :deep(textarea) {
font-family: var(--font-mono);
font-size: 14px;