/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import React from 'react';
import { Progress, Tag, Typography } from '@douyinfe/semi-ui';
import {
Music,
FileText,
HelpCircle,
CheckCircle,
Pause,
Clock,
Play,
XCircle,
Loader,
List,
Hash,
Video,
Sparkles,
} from 'lucide-react';
import {
TASK_ACTION_FIRST_TAIL_GENERATE,
TASK_ACTION_GENERATE,
TASK_ACTION_REFERENCE_GENERATE,
TASK_ACTION_TEXT_GENERATE,
} from '../../../constants/common.constant';
import { CHANNEL_OPTIONS } from '../../../constants/channel.constants';
const colors = [
'amber',
'blue',
'cyan',
'green',
'grey',
'indigo',
'light-blue',
'lime',
'orange',
'pink',
'purple',
'red',
'teal',
'violet',
'yellow',
];
// Render functions
const renderTimestamp = (timestampInSeconds) => {
const date = new Date(timestampInSeconds * 1000); // 从秒转换为毫秒
const year = date.getFullYear(); // 获取年份
const month = ('0' + (date.getMonth() + 1)).slice(-2); // 获取月份,从0开始需要+1,并保证两位数
const day = ('0' + date.getDate()).slice(-2); // 获取日期,并保证两位数
const hours = ('0' + date.getHours()).slice(-2); // 获取小时,并保证两位数
const minutes = ('0' + date.getMinutes()).slice(-2); // 获取分钟,并保证两位数
const seconds = ('0' + date.getSeconds()).slice(-2); // 获取秒钟,并保证两位数
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; // 格式化输出
};
function renderDuration(submit_time, finishTime) {
if (!submit_time || !finishTime) return 'N/A';
const durationSec = finishTime - submit_time;
const color = durationSec > 60 ? 'red' : 'green';
// 返回带有样式的颜色标签
return (
}>
{durationSec} 秒
);
}
const renderType = (type, t) => {
switch (type) {
case 'MUSIC':
return (
}>
{t('生成音乐')}
);
case 'LYRICS':
return (
}>
{t('生成歌词')}
);
case TASK_ACTION_GENERATE:
return (
}>
{t('图生视频')}
);
case TASK_ACTION_TEXT_GENERATE:
return (
}>
{t('文生视频')}
);
case TASK_ACTION_FIRST_TAIL_GENERATE:
return (
}>
{t('首尾生视频')}
);
case TASK_ACTION_REFERENCE_GENERATE:
return (
}>
{t('参照生视频')}
);
default:
return (
}>
{t('未知')}
);
}
};
const renderPlatform = (platform, t) => {
let option = CHANNEL_OPTIONS.find(
(opt) => String(opt.value) === String(platform),
);
if (option) {
return (
}>
{option.label}
);
}
switch (platform) {
case 'suno':
return (
}>
Suno
);
default:
return (
}>
{t('未知')}
);
}
};
const renderStatus = (type, t) => {
switch (type) {
case 'SUCCESS':
return (
}
>
{t('成功')}
);
case 'NOT_START':
return (
}>
{t('未启动')}
);
case 'SUBMITTED':
return (
}>
{t('队列中')}
);
case 'IN_PROGRESS':
return (
}>
{t('执行中')}
);
case 'FAILURE':
return (
}>
{t('失败')}
);
case 'QUEUED':
return (
}>
{t('排队中')}
);
case 'UNKNOWN':
return (
}>
{t('未知')}
);
case '':
return (
}>
{t('正在提交')}
);
default:
return (
}>
{t('未知')}
);
}
};
export const getTaskLogsColumns = ({
t,
COLUMN_KEYS,
copyText,
openContentModal,
isAdminUser,
openVideoModal,
}) => {
return [
{
key: COLUMN_KEYS.SUBMIT_TIME,
title: t('提交时间'),
dataIndex: 'submit_time',
render: (text, record, index) => {
return
{text ? renderTimestamp(text) : '-'}
;
},
},
{
key: COLUMN_KEYS.FINISH_TIME,
title: t('结束时间'),
dataIndex: 'finish_time',
render: (text, record, index) => {
return {text ? renderTimestamp(text) : '-'}
;
},
},
{
key: COLUMN_KEYS.DURATION,
title: t('花费时间'),
dataIndex: 'finish_time',
render: (finish, record) => {
return <>{finish ? renderDuration(record.submit_time, finish) : '-'}>;
},
},
{
key: COLUMN_KEYS.CHANNEL,
title: t('渠道'),
dataIndex: 'channel_id',
render: (text, record, index) => {
return isAdminUser ? (
}
onClick={() => {
copyText(text);
}}
>
{text}
) : (
<>>
);
},
},
{
key: COLUMN_KEYS.PLATFORM,
title: t('平台'),
dataIndex: 'platform',
render: (text, record, index) => {
return {renderPlatform(text, t)}
;
},
},
{
key: COLUMN_KEYS.TYPE,
title: t('类型'),
dataIndex: 'action',
render: (text, record, index) => {
return {renderType(text, t)}
;
},
},
{
key: COLUMN_KEYS.TASK_ID,
title: t('任务ID'),
dataIndex: 'task_id',
render: (text, record, index) => {
return (
{
openContentModal(JSON.stringify(record, null, 2));
}}
>
{text}
);
},
},
{
key: COLUMN_KEYS.TASK_STATUS,
title: t('任务状态'),
dataIndex: 'status',
render: (text, record, index) => {
return {renderStatus(text, t)}
;
},
},
{
key: COLUMN_KEYS.PROGRESS,
title: t('进度'),
dataIndex: 'progress',
render: (text, record, index) => {
return (
{isNaN(text?.replace('%', '')) ? (
text || '-'
) : (
)}
);
},
},
{
key: COLUMN_KEYS.FAIL_REASON,
title: t('详情'),
dataIndex: 'fail_reason',
fixed: 'right',
render: (text, record, index) => {
// 仅当为视频生成任务且成功,且 fail_reason 是 URL 时显示可点击链接
const isVideoTask =
record.action === TASK_ACTION_GENERATE ||
record.action === TASK_ACTION_TEXT_GENERATE ||
record.action === TASK_ACTION_FIRST_TAIL_GENERATE ||
record.action === TASK_ACTION_REFERENCE_GENERATE;
const isSuccess = record.status === 'SUCCESS';
const isUrl = typeof text === 'string' && /^https?:\/\//.test(text);
if (isSuccess && isVideoTask && isUrl) {
return (
{
e.preventDefault();
openVideoModal(text);
}}
>
{t('点击预览视频')}
);
}
if (!text) {
return t('无');
}
return (
{
openContentModal(text);
}}
>
{text}
);
},
},
];
};