11
This commit is contained in:
91
src/components/workflow/AddIpBrainDialog.vue
Normal file
91
src/components/workflow/AddIpBrainDialog.vue
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { useWorkflowStore } from "../../stores/workflow.js";
|
||||||
|
|
||||||
|
const workflow = useWorkflowStore();
|
||||||
|
const { ipBrainModalVisible, ipBrainProfileUrl, ipBrainCollecting } =
|
||||||
|
storeToRefs(workflow);
|
||||||
|
|
||||||
|
const feedback = ref({ severity: "", message: "" });
|
||||||
|
|
||||||
|
const tipItems = [
|
||||||
|
"输入账号主页或分享链接后点击「开始采集」按钮",
|
||||||
|
"系统会自动打开浏览器并抓取数据,请保持网络连接正常",
|
||||||
|
"无需任何手动操作,等待采集完成即可",
|
||||||
|
"采集完成后会自动将 5 个视频标题添加到选题库中",
|
||||||
|
];
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
feedback.value = { severity: "", message: "" };
|
||||||
|
workflow.closeIpBrainDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onStart() {
|
||||||
|
feedback.value = { severity: "", message: "" };
|
||||||
|
const result = await workflow.startIpBrainCollect();
|
||||||
|
if (!result.ok) {
|
||||||
|
feedback.value = { severity: "error", message: result.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model:visible="ipBrainModalVisible"
|
||||||
|
modal
|
||||||
|
header="添加IP大脑"
|
||||||
|
:style="{ width: '600px' }"
|
||||||
|
:draggable="false"
|
||||||
|
class="ip-brain-dialog"
|
||||||
|
>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Message
|
||||||
|
v-if="feedback.message"
|
||||||
|
:severity="feedback.severity"
|
||||||
|
:closable="false"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
{{ feedback.message }}
|
||||||
|
</Message>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="mb-2 block font-medium text-slate-200">
|
||||||
|
账号主页或分享链接
|
||||||
|
</label>
|
||||||
|
<InputText
|
||||||
|
v-model="ipBrainProfileUrl"
|
||||||
|
type="text"
|
||||||
|
size="large"
|
||||||
|
class="w-full"
|
||||||
|
placeholder="请输入抖音账号主页或用户分享链接,例如:https://www.douyin.com/user/..."
|
||||||
|
:disabled="ipBrainCollecting"
|
||||||
|
@keyup.enter="onStart"
|
||||||
|
/>
|
||||||
|
<p class="mt-2 text-xs text-slate-500">
|
||||||
|
当前 IP 学习支持抖音账号主页、用户分享链接和短链接,程序会自动识别并采集最新
|
||||||
|
5 个视频标题
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="elegant-tip-box rounded-lg p-3">
|
||||||
|
<div class="text-sm text-slate-300">
|
||||||
|
<div class="mb-1 font-medium">提示:</div>
|
||||||
|
<ul class="list-inside list-disc space-y-1">
|
||||||
|
<li v-for="(tip, i) in tipItems" :key="i">{{ tip }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<Button
|
||||||
|
label="取消"
|
||||||
|
severity="secondary"
|
||||||
|
:disabled="ipBrainCollecting"
|
||||||
|
@click="onCancel"
|
||||||
|
/>
|
||||||
|
<Button label="开始采集" :loading="ipBrainCollecting" @click="onStart" />
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useWorkflowStore } from "../../stores/workflow.js";
|
import { useWorkflowStore } from "../../stores/workflow.js";
|
||||||
|
import AddIpBrainDialog from "./AddIpBrainDialog.vue";
|
||||||
|
import VideoLinkDialog from "./VideoLinkDialog.vue";
|
||||||
|
|
||||||
const workflow = useWorkflowStore();
|
const workflow = useWorkflowStore();
|
||||||
const {
|
const {
|
||||||
@@ -13,7 +15,21 @@ const {
|
|||||||
otherRequirements,
|
otherRequirements,
|
||||||
targetWords,
|
targetWords,
|
||||||
recognizedContent,
|
recognizedContent,
|
||||||
|
ipBenchmarks,
|
||||||
|
ipBrainSelectedBenchmarkId,
|
||||||
} = storeToRefs(workflow);
|
} = storeToRefs(workflow);
|
||||||
|
|
||||||
|
function onSelectBenchmark(id) {
|
||||||
|
workflow.selectBenchmark(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenVideoLink() {
|
||||||
|
workflow.videoLinkModalVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenIpBrain() {
|
||||||
|
workflow.ipBrainModalVisible = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -28,22 +44,63 @@ const {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-if="ipMode === 'ipBrain'">
|
<template v-if="ipMode === 'ipBrain'">
|
||||||
<Button label="添加IP大脑" class="w-full" size="small" />
|
<Button
|
||||||
|
label="添加IP大脑"
|
||||||
|
class="w-full"
|
||||||
|
size="small"
|
||||||
|
@click="onOpenIpBrain"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div class="dashboard-field-label">
|
<div class="dashboard-field-label">
|
||||||
已学习到的对标
|
已学习到的对标
|
||||||
<span class="dashboard-field-label--muted text-xs">(0/5)</span>
|
<span class="dashboard-field-label--muted text-xs">
|
||||||
|
({{ workflow.ipBenchmarkCount }}/5)
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="dashboard-muted py-2">暂无对标</p>
|
<p v-if="ipBenchmarks.length === 0" class="dashboard-muted py-2">暂无对标</p>
|
||||||
|
<div v-else class="py-1">
|
||||||
|
<button
|
||||||
|
v-for="item in ipBenchmarks"
|
||||||
|
:key="item.id"
|
||||||
|
type="button"
|
||||||
|
class="benchmark-item"
|
||||||
|
:class="{ 'benchmark-item--active': ipBrainSelectedBenchmarkId === item.id }"
|
||||||
|
@click="onSelectBenchmark(item.id)"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div class="dashboard-field-label mb-2">选题库</div>
|
<div class="dashboard-field-label mb-2">选题库</div>
|
||||||
<p class="dashboard-muted py-2 text-center text-sm">请先选择一个对标</p>
|
<p
|
||||||
|
v-if="!ipBrainSelectedBenchmarkId"
|
||||||
|
class="dashboard-muted py-2 text-center text-sm"
|
||||||
|
>
|
||||||
|
请先选择一个对标
|
||||||
|
</p>
|
||||||
|
<p v-else-if="workflow.ipTopics.length === 0" class="dashboard-muted py-2 text-sm">
|
||||||
|
采集完成后将显示视频标题
|
||||||
|
</p>
|
||||||
|
<ul v-else class="space-y-1 py-1 text-sm text-slate-300">
|
||||||
|
<li v-for="(topic, index) in workflow.ipTopics" :key="topic.id">
|
||||||
|
{{ index + 1 }}. {{ topic.title }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AddIpBrainDialog />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else-if="ipMode === 'videoWrite'">
|
<template v-else-if="ipMode === 'videoWrite'">
|
||||||
<Button label="打开视频链接" class="w-full" size="small" />
|
<Button
|
||||||
|
label="打开视频链接"
|
||||||
|
class="w-full"
|
||||||
|
size="small"
|
||||||
|
@click="onOpenVideoLink"
|
||||||
|
/>
|
||||||
<div class="mt-3 space-y-1">
|
<div class="mt-3 space-y-1">
|
||||||
<div class="dashboard-field-label flex items-center gap-1">
|
<div class="dashboard-field-label flex items-center gap-1">
|
||||||
<span>识别的原始内容</span>
|
<span>识别的原始内容</span>
|
||||||
@@ -57,6 +114,7 @@ const {
|
|||||||
rows="10"
|
rows="10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<VideoLinkDialog />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -126,7 +184,7 @@ const {
|
|||||||
:min="50"
|
:min="50"
|
||||||
:max="1500"
|
:max="1500"
|
||||||
size="small"
|
size="small"
|
||||||
class="w-24"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,20 +16,22 @@ const { scriptContent, editLanguage, editWordCount } = storeToRefs(workflow);
|
|||||||
class="dashboard-textarea-mono w-full"
|
class="dashboard-textarea-mono w-full"
|
||||||
rows="12"
|
rows="12"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-wrap items-center gap-2 pt-1">
|
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 pt-2">
|
||||||
|
<div class="flex shrink-0 items-center gap-2">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
v-model="editWordCount"
|
v-model="editWordCount"
|
||||||
:min="100"
|
:min="30"
|
||||||
:max="2000"
|
:max="2000"
|
||||||
size="small"
|
size="small"
|
||||||
class="w-28"
|
class="w-28 shrink-0"
|
||||||
/>
|
/>
|
||||||
<span class="text-xs text-slate-400">字</span>
|
<span class="shrink-0 text-xs text-slate-400">字</span>
|
||||||
|
</div>
|
||||||
<Select
|
<Select
|
||||||
v-model="editLanguage"
|
v-model="editLanguage"
|
||||||
:options="workflow.languages"
|
:options="workflow.languages"
|
||||||
size="small"
|
size="small"
|
||||||
class="w-36"
|
class="min-w-36 shrink-0 ml-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 pt-1">
|
<div class="flex gap-2 pt-1">
|
||||||
|
|||||||
90
src/components/workflow/VideoLinkDialog.vue
Normal file
90
src/components/workflow/VideoLinkDialog.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { useWorkflowStore, executeVideoRewrite } from "../../stores/workflow.js";
|
||||||
|
|
||||||
|
const workflow = useWorkflowStore();
|
||||||
|
const { videoLinkModalVisible, videoShareText, videoRewriting } = storeToRefs(workflow);
|
||||||
|
|
||||||
|
const feedback = ref({ severity: "", message: "" });
|
||||||
|
|
||||||
|
const tipItems = [
|
||||||
|
"已支持抖音分享文本、抖音视频链接、抖音短链接",
|
||||||
|
"支持直接视频文件 URL(如 .mp4、.mov 等)",
|
||||||
|
"快手、小红书、B站、视频号分享链接目前会提示暂不支持",
|
||||||
|
"使用 FUNASR 进行语音识别",
|
||||||
|
"根据设置的仿写提示词生成仿写文案",
|
||||||
|
];
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
feedback.value = { severity: "", message: "" };
|
||||||
|
workflow.videoLinkModalVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onRewrite() {
|
||||||
|
feedback.value = { severity: "", message: "" };
|
||||||
|
const result = await executeVideoRewrite(workflow);
|
||||||
|
if (!result.ok) {
|
||||||
|
feedback.value = { severity: "error", message: result.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model:visible="videoLinkModalVisible"
|
||||||
|
modal
|
||||||
|
header="粘贴视频分享文本 - 快速仿写"
|
||||||
|
:style="{ width: '600px' }"
|
||||||
|
:draggable="false"
|
||||||
|
class="ip-brain-dialog"
|
||||||
|
>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Message
|
||||||
|
v-if="feedback.message"
|
||||||
|
:severity="feedback.severity"
|
||||||
|
:closable="false"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
{{ feedback.message }}
|
||||||
|
</Message>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="mb-2 block font-medium text-slate-200">
|
||||||
|
请粘贴视频分享文本或视频链接
|
||||||
|
</label>
|
||||||
|
<p class="mb-2 text-xs text-slate-500">
|
||||||
|
支持抖音分享文本、抖音视频链接、抖音短链接,以及直接视频文件链接
|
||||||
|
</p>
|
||||||
|
<InputText
|
||||||
|
v-model="videoShareText"
|
||||||
|
type="text"
|
||||||
|
size="large"
|
||||||
|
class="w-full"
|
||||||
|
placeholder="请输入分享文本、视频链接,或 mp4/mov/webm 等直链..."
|
||||||
|
:disabled="videoRewriting"
|
||||||
|
@keyup.enter="onRewrite"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="elegant-tip-box rounded-lg p-3">
|
||||||
|
<div class="text-sm text-slate-300">
|
||||||
|
<div class="mb-1 font-medium">提示:</div>
|
||||||
|
<ul class="list-inside list-disc space-y-1">
|
||||||
|
<li v-for="(tip, i) in tipItems" :key="i">{{ tip }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<Button
|
||||||
|
label="取消"
|
||||||
|
severity="secondary"
|
||||||
|
:disabled="videoRewriting"
|
||||||
|
@click="onCancel"
|
||||||
|
/>
|
||||||
|
<Button label="一键仿写" :loading="videoRewriting" @click="onRewrite" />
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -18,6 +18,7 @@ import TabList from "primevue/tablist";
|
|||||||
import Tab from "primevue/tab";
|
import Tab from "primevue/tab";
|
||||||
import TabPanels from "primevue/tabpanels";
|
import TabPanels from "primevue/tabpanels";
|
||||||
import TabPanel from "primevue/tabpanel";
|
import TabPanel from "primevue/tabpanel";
|
||||||
|
import Dialog from "primevue/dialog";
|
||||||
|
|
||||||
import DashboardCard from "./components/dashboard/DashboardCard.vue";
|
import DashboardCard from "./components/dashboard/DashboardCard.vue";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
@@ -55,5 +56,6 @@ app.component("Tab", Tab);
|
|||||||
app.component("TabPanels", TabPanels);
|
app.component("TabPanels", TabPanels);
|
||||||
app.component("TabPanel", TabPanel);
|
app.component("TabPanel", TabPanel);
|
||||||
app.component("DashboardCard", DashboardCard);
|
app.component("DashboardCard", DashboardCard);
|
||||||
|
app.component("Dialog", Dialog);
|
||||||
|
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore, acceptHMRUpdate } from "pinia";
|
||||||
|
|
||||||
const defaultPublishPlatforms = () => [
|
const defaultPublishPlatforms = () => [
|
||||||
{ label: "*音", checked: false, account: null },
|
{ label: "*音", checked: false, account: null },
|
||||||
@@ -11,6 +11,15 @@ export const useWorkflowStore = defineStore("workflow", {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
// 01 IP 深度学习
|
// 01 IP 深度学习
|
||||||
ipMode: "ipBrain",
|
ipMode: "ipBrain",
|
||||||
|
ipBrainModalVisible: false,
|
||||||
|
ipBrainProfileUrl: "",
|
||||||
|
ipBrainCollecting: false,
|
||||||
|
ipBenchmarks: [],
|
||||||
|
ipTopics: [],
|
||||||
|
ipBrainSelectedBenchmarkId: null,
|
||||||
|
videoLinkModalVisible: false,
|
||||||
|
videoShareText: "",
|
||||||
|
videoRewriting: false,
|
||||||
recognizedContent: "",
|
recognizedContent: "",
|
||||||
videoType: "口播文案",
|
videoType: "口播文案",
|
||||||
copyType: "人设型",
|
copyType: "人设型",
|
||||||
@@ -71,9 +80,69 @@ export const useWorkflowStore = defineStore("workflow", {
|
|||||||
keywordCountDescribe: (state) => countLines(state.keywordsDescribe),
|
keywordCountDescribe: (state) => countLines(state.keywordsDescribe),
|
||||||
keywordCountAction: (state) => countLines(state.keywordsAction),
|
keywordCountAction: (state) => countLines(state.keywordsAction),
|
||||||
keywordCountEmotion: (state) => countLines(state.keywordsEmotion),
|
keywordCountEmotion: (state) => countLines(state.keywordsEmotion),
|
||||||
|
ipBenchmarkCount: (state) => state.ipBenchmarks.length,
|
||||||
|
selectedBenchmark: (state) =>
|
||||||
|
state.ipBenchmarks.find((b) => b.id === state.ipBrainSelectedBenchmarkId) ?? null,
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
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() {
|
resetAll() {
|
||||||
this.$reset();
|
this.$reset();
|
||||||
this.publishPlatforms = defaultPublishPlatforms();
|
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) {
|
function countLines(text) {
|
||||||
if (!text?.trim()) return 0;
|
if (!text?.trim()) return 0;
|
||||||
return text.split("\n").filter((line) => line.trim()).length;
|
return text.split("\n").filter((line) => line.trim()).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept(acceptHMRUpdate(useWorkflowStore, import.meta.hot));
|
||||||
|
}
|
||||||
|
|||||||
@@ -347,6 +347,47 @@ body {
|
|||||||
box-shadow: 0 0 20px rgba(0, 229, 255, 0.15);
|
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) {
|
.dashboard-textarea-mono :deep(textarea) {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
Reference in New Issue
Block a user