|
|
@@ -37,9 +37,17 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column label="目标账号" width="120">
|
|
|
+ <el-table-column label="目标账号" min-width="180">
|
|
|
<template #default="{ row }">
|
|
|
- {{ row.targetAccounts?.length || 0 }} 个
|
|
|
+ <span>{{ row.targetAccounts?.length || 0 }} 个</span>
|
|
|
+ <el-tag
|
|
|
+ v-for="platform in getTaskPlatforms(row)"
|
|
|
+ :key="platform"
|
|
|
+ size="small"
|
|
|
+ style="margin-left: 4px"
|
|
|
+ >
|
|
|
+ {{ getPlatformName(platform) }}
|
|
|
+ </el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
@@ -113,7 +121,17 @@
|
|
|
<!-- 创建发布对话框 -->
|
|
|
<el-dialog v-model="showCreateDialog" title="新建发布" width="600px">
|
|
|
<el-form :model="createForm" label-width="100px">
|
|
|
- <el-form-item label="视频文件">
|
|
|
+ <!-- 平台提示:根据选择的目标平台动态显示必填要求 -->
|
|
|
+ <el-alert
|
|
|
+ v-if="createSelectedPlatforms.length > 0"
|
|
|
+ :title="createPlatformHint"
|
|
|
+ type="info"
|
|
|
+ :closable="false"
|
|
|
+ show-icon
|
|
|
+ style="margin-bottom: 16px"
|
|
|
+ />
|
|
|
+
|
|
|
+ <el-form-item label="视频文件" :required="createRequireVideo">
|
|
|
<el-upload
|
|
|
action=""
|
|
|
:auto-upload="false"
|
|
|
@@ -125,17 +143,26 @@
|
|
|
<span v-if="createForm.videoFile" style="margin-left: 12px">
|
|
|
{{ createForm.videoFile.name }}
|
|
|
</span>
|
|
|
+ <span v-if="createRequireImage && !createForm.videoFile" class="form-tip">
|
|
|
+ 也可上传图片发布
|
|
|
+ </span>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="标题">
|
|
|
- <el-input v-model="createForm.title" placeholder="视频标题" />
|
|
|
+ <el-form-item v-if="createRequireTitle" label="标题" required>
|
|
|
+ <el-input v-model="createForm.title" placeholder="视频标题" show-word-limit :maxlength="createTitleMaxLength" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-else label="标题">
|
|
|
+ <el-input v-model="createForm.title" placeholder="视频标题(可选)" />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="描述">
|
|
|
- <el-input v-model="createForm.description" type="textarea" :rows="3" placeholder="视频描述" />
|
|
|
+ <el-form-item v-if="createRequireDescription" label="描述" required>
|
|
|
+ <el-input v-model="createForm.description" type="textarea" :rows="3" placeholder="视频描述" show-word-limit :maxlength="createDescMaxLength" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-else label="描述">
|
|
|
+ <el-input v-model="createForm.description" type="textarea" :rows="3" placeholder="视频描述(可选)" />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <el-form-item label="标签">
|
|
|
+ <el-form-item v-if="createShowTags" label="标签">
|
|
|
<el-select v-model="createForm.tags" multiple filterable allow-create placeholder="添加标签" style="width: 100%">
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
@@ -217,7 +244,7 @@
|
|
|
</el-tag>
|
|
|
<span v-if="!currentTask.tags?.length">-</span>
|
|
|
</el-descriptions-item>
|
|
|
- <el-descriptions-item label="目标账号">{{ currentTask.targetAccounts?.length || 0 }} 个</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="目标账号">{{ taskDetail?.results?.length || currentTask?.targetAccounts?.length || 0 }} 个</el-descriptions-item>
|
|
|
<el-descriptions-item label="定时发布">
|
|
|
{{ currentTask.scheduledAt ? formatDate(currentTask.scheduledAt) : '立即发布' }}
|
|
|
</el-descriptions-item>
|
|
|
@@ -451,6 +478,74 @@ const filteredTasks = computed(() => {
|
|
|
);
|
|
|
});
|
|
|
|
|
|
+// ===== Bug #6069: 创建表单平台感知字段 =====
|
|
|
+
|
|
|
+// 当前选中的目标平台列表
|
|
|
+const createSelectedPlatforms = computed<PlatformType[]>(() => {
|
|
|
+ const ids = new Set(createForm.targetAccounts);
|
|
|
+ const platforms = new Set<PlatformType>();
|
|
|
+ for (const account of accounts.value) {
|
|
|
+ if (ids.has(Number(account.id))) {
|
|
|
+ platforms.add(account.platform);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Array.from(platforms);
|
|
|
+});
|
|
|
+
|
|
|
+// 各平台发布要求
|
|
|
+const PLATFORM_PUBLISH_REQUIREMENTS: Record<string, {
|
|
|
+ requireTitle: boolean;
|
|
|
+ requireDescription: boolean;
|
|
|
+ requireVideo: boolean;
|
|
|
+ requireImage: boolean;
|
|
|
+ showTags: boolean;
|
|
|
+}> = {
|
|
|
+ douyin: { requireTitle: true, requireDescription: false, requireVideo: true, requireImage: false, showTags: true },
|
|
|
+ xiaohongshu: { requireTitle: true, requireDescription: true, requireVideo: false, requireImage: true, showTags: true },
|
|
|
+ weixin_video: { requireTitle: true, requireDescription: false, requireVideo: true, requireImage: false, showTags: true },
|
|
|
+ baijiahao: { requireTitle: true, requireDescription: true, requireVideo: false, requireImage: false, showTags: true },
|
|
|
+ kuaishou: { requireTitle: true, requireDescription: false, requireVideo: true, requireImage: false, showTags: true },
|
|
|
+ bilibili: { requireTitle: true, requireDescription: false, requireVideo: true, requireImage: false, showTags: true },
|
|
|
+};
|
|
|
+
|
|
|
+// 只要任一选中平台要求某字段,就显示必填
|
|
|
+const createRequireTitle = computed(() => createSelectedPlatforms.value.some(p => PLATFORM_PUBLISH_REQUIREMENTS[p]?.requireTitle));
|
|
|
+const createRequireDescription = computed(() => createSelectedPlatforms.value.some(p => PLATFORM_PUBLISH_REQUIREMENTS[p]?.requireDescription));
|
|
|
+const createRequireVideo = computed(() => createSelectedPlatforms.value.some(p => PLATFORM_PUBLISH_REQUIREMENTS[p]?.requireVideo));
|
|
|
+const createRequireImage = computed(() => createSelectedPlatforms.value.some(p => PLATFORM_PUBLISH_REQUIREMENTS[p]?.requireImage));
|
|
|
+const createShowTags = computed(() => createSelectedPlatforms.value.some(p => PLATFORM_PUBLISH_REQUIREMENTS[p]?.showTags));
|
|
|
+
|
|
|
+// 取最严格的标题/描述长度限制
|
|
|
+const createTitleMaxLength = computed(() => {
|
|
|
+ let max = 100;
|
|
|
+ for (const p of createSelectedPlatforms.value) {
|
|
|
+ const info = PLATFORMS[p];
|
|
|
+ if (info?.maxTitleLength && info.maxTitleLength < max) max = info.maxTitleLength;
|
|
|
+ }
|
|
|
+ return max;
|
|
|
+});
|
|
|
+const createDescMaxLength = computed(() => {
|
|
|
+ let max = 2000;
|
|
|
+ for (const p of createSelectedPlatforms.value) {
|
|
|
+ const info = PLATFORMS[p];
|
|
|
+ if (info?.maxDescriptionLength && info.maxDescriptionLength < max) max = info.maxDescriptionLength;
|
|
|
+ }
|
|
|
+ return max;
|
|
|
+});
|
|
|
+
|
|
|
+// 平台提示文案
|
|
|
+const createPlatformHint = computed(() => {
|
|
|
+ const platforms = createSelectedPlatforms.value;
|
|
|
+ if (!platforms.length) return '';
|
|
|
+ const names = platforms.map(p => PLATFORMS[p]?.name || p).join('、');
|
|
|
+ const tips: string[] = [];
|
|
|
+ if (createRequireTitle.value) tips.push('标题必填');
|
|
|
+ if (createRequireDescription.value) tips.push('正文必填');
|
|
|
+ if (createRequireVideo.value) tips.push('视频必填');
|
|
|
+ if (createRequireImage.value) tips.push('图片或视频必填');
|
|
|
+ return `已选平台:${names}。要求:${tips.join('、')}`;
|
|
|
+});
|
|
|
+
|
|
|
const pagination = reactive({
|
|
|
page: 1,
|
|
|
pageSize: 20,
|
|
|
@@ -617,6 +712,18 @@ function getPlatformName(platform: PlatformType) {
|
|
|
return PLATFORMS[platform]?.name || platform;
|
|
|
}
|
|
|
|
|
|
+// 根据 targetAccounts 获取关联的平台列表(Bug #6066: 显示渠道)
|
|
|
+function getTaskPlatforms(task: PublishTask): PlatformType[] {
|
|
|
+ const ids = new Set(task.targetAccounts || []);
|
|
|
+ const platforms = new Set<PlatformType>();
|
|
|
+ for (const account of accounts.value) {
|
|
|
+ if (ids.has(Number(account.id))) {
|
|
|
+ platforms.add(account.platform);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Array.from(platforms);
|
|
|
+}
|
|
|
+
|
|
|
function getStatusType(status: string) {
|
|
|
const types: Record<string, string> = {
|
|
|
pending: 'info',
|
|
|
@@ -653,13 +760,13 @@ function isScreenshotPendingError(errorMessage: string | null | undefined): bool
|
|
|
return !!errorMessage && errorMessage.includes('请查看截图');
|
|
|
}
|
|
|
|
|
|
-// 是否可以使用有头浏览器重新发布(目前主要用于小红书发布失败场景)
|
|
|
+// 是否可以使用有头浏览器重新发布
|
|
|
function canRetryWithHeadful(row: { platform: string; status: string | null | undefined }): boolean {
|
|
|
if (row.status !== 'failed') return false;
|
|
|
- return row.platform === 'xiaohongshu' || row.platform === 'baijiahao';
|
|
|
+ return row.platform === 'xiaohongshu' || row.platform === 'baijiahao' || row.platform === 'douyin' || row.platform === 'weixin_video';
|
|
|
}
|
|
|
|
|
|
-// 打开查看截图(小红书等平台暂打开创作者中心,用户可自行查看发布状态)
|
|
|
+// 打开查看截图(平台暂打开创作者中心,用户可自行查看发布状态)
|
|
|
function openScreenshotView(row: { platform: string; accountId: number }) {
|
|
|
if (row.platform === 'xiaohongshu') {
|
|
|
window.open('https://creator.xiaohongshu.com/publish/publish', '_blank', 'noopener,noreferrer');
|
|
|
@@ -669,6 +776,14 @@ function openScreenshotView(row: { platform: string; accountId: number }) {
|
|
|
window.open('https://baijiahao.baidu.com/builder/rc/content', '_blank', 'noopener,noreferrer');
|
|
|
return;
|
|
|
}
|
|
|
+ if (row.platform === 'douyin') {
|
|
|
+ window.open('https://creator.douyin.com/creator-micro/content/upload', '_blank', 'noopener,noreferrer');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (row.platform === 'weixin_video') {
|
|
|
+ window.open('https://channels.weixin.qq.com/platform', '_blank', 'noopener,noreferrer');
|
|
|
+ return;
|
|
|
+ }
|
|
|
ElMessage.info('请前往对应平台查看发布状态');
|
|
|
}
|
|
|
|
|
|
@@ -786,8 +901,20 @@ function handleFileChange(file: UploadFile) {
|
|
|
}
|
|
|
|
|
|
async function handleCreate() {
|
|
|
- if (!createForm.videoFile || !createForm.title || createForm.targetAccounts.length === 0) {
|
|
|
- ElMessage.warning('请填写完整信息');
|
|
|
+ if (createForm.targetAccounts.length === 0) {
|
|
|
+ ElMessage.warning('请至少选择一个目标账号');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (createRequireTitle.value && !createForm.title) {
|
|
|
+ ElMessage.warning('所选平台要求标题必填');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (createRequireDescription.value && !createForm.description) {
|
|
|
+ ElMessage.warning('所选平台要求正文必填');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (createRequireVideo.value && !createForm.videoFile) {
|
|
|
+ ElMessage.warning('所选平台要求视频必填');
|
|
|
return;
|
|
|
}
|
|
|
|