diff --git a/controller/midjourney.go b/controller/midjourney.go
index a5ef8e4..cf5ce64 100644
--- a/controller/midjourney.go
+++ b/controller/midjourney.go
@@ -148,7 +148,16 @@ func GetAllMidjourney(c *gin.Context) {
if p < 0 {
p = 0
}
- logs := model.GetAllTasks(p*common.ItemsPerPage, common.ItemsPerPage)
+
+ // 解析其他查询参数
+ queryParams := model.TaskQueryParams{
+ ChannelID: c.Query("channel_id"),
+ MjID: c.Query("mj_id"),
+ StartTimestamp: c.Query("start_timestamp"),
+ EndTimestamp: c.Query("end_timestamp"),
+ }
+
+ logs := model.GetAllTasks(p*common.ItemsPerPage, common.ItemsPerPage, queryParams)
if logs == nil {
logs = make([]*model.Midjourney, 0)
}
@@ -164,9 +173,17 @@ func GetUserMidjourney(c *gin.Context) {
if p < 0 {
p = 0
}
+
userId := c.GetInt("id")
log.Printf("userId = %d \n", userId)
- logs := model.GetAllUserTask(userId, p*common.ItemsPerPage, common.ItemsPerPage)
+
+ queryParams := model.TaskQueryParams{
+ MjID: c.Query("mj_id"),
+ StartTimestamp: c.Query("start_timestamp"),
+ EndTimestamp: c.Query("end_timestamp"),
+ }
+
+ logs := model.GetAllUserTask(userId, p*common.ItemsPerPage, common.ItemsPerPage, queryParams)
if logs == nil {
logs = make([]*model.Midjourney, 0)
}
diff --git a/model/midjourney.go b/model/midjourney.go
index c6e5c9a..6cc1b21 100644
--- a/model/midjourney.go
+++ b/model/midjourney.go
@@ -22,29 +22,68 @@ type Midjourney struct {
ChannelId int `json:"channel_id"`
}
-func GetAllUserTask(userId int, startIdx int, num int) []*Midjourney {
+// 用于包含所有搜索条件的结构体,可以根据需求添加更多字段
+type TaskQueryParams struct {
+ ChannelID string
+ MjID string
+ StartTimestamp string
+ EndTimestamp string
+}
+
+func GetAllUserTask(userId int, startIdx int, num int, queryParams TaskQueryParams) []*Midjourney {
var tasks []*Midjourney
var err error
- err = DB.Where("user_id = ?", userId).Order("id desc").Limit(num).Offset(startIdx).Find(&tasks).Error
+
+ // 初始化查询构建器
+ query := DB.Where("user_id = ?", userId)
+
+ if queryParams.MjID != "" {
+ query = query.Where("mj_id = ?", queryParams.MjID)
+ }
+ if queryParams.StartTimestamp != "" {
+ // 假设您已将前端传来的时间戳转换为数据库所需的时间格式,并处理了时间戳的验证和解析
+ query = query.Where("submit_time >= ?", queryParams.StartTimestamp)
+ }
+ if queryParams.EndTimestamp != "" {
+ query = query.Where("submit_time <= ?", queryParams.EndTimestamp)
+ }
+
+ // 获取数据
+ err = query.Order("id desc").Limit(num).Offset(startIdx).Find(&tasks).Error
if err != nil {
return nil
}
- for _, task := range tasks {
- task.ImageUrl = common.ServerAddress + "/mj/image/" + task.MjId
- }
+
return tasks
}
-func GetAllTasks(startIdx int, num int) []*Midjourney {
+func GetAllTasks(startIdx int, num int, queryParams TaskQueryParams) []*Midjourney {
var tasks []*Midjourney
var err error
- err = DB.Order("id desc").Limit(num).Offset(startIdx).Find(&tasks).Error
+
+ // 初始化查询构建器
+ query := DB
+
+ // 添加过滤条件
+ if queryParams.ChannelID != "" {
+ query = query.Where("channel_id = ?", queryParams.ChannelID)
+ }
+ if queryParams.MjID != "" {
+ query = query.Where("mj_id = ?", queryParams.MjID)
+ }
+ if queryParams.StartTimestamp != "" {
+ query = query.Where("submit_time >= ?", queryParams.StartTimestamp)
+ }
+ if queryParams.EndTimestamp != "" {
+ query = query.Where("submit_time <= ?", queryParams.EndTimestamp)
+ }
+
+ // 获取数据
+ err = query.Order("id desc").Limit(num).Offset(startIdx).Find(&tasks).Error
if err != nil {
return nil
}
- for _, task := range tasks {
- task.ImageUrl = common.ServerAddress + "/mj/image/" + task.MjId
- }
+
return tasks
}
diff --git a/web/src/components/MjLogsTable.js b/web/src/components/MjLogsTable.js
index 4d070b0..e6b13c2 100644
--- a/web/src/components/MjLogsTable.js
+++ b/web/src/components/MjLogsTable.js
@@ -1,409 +1,432 @@
-import React, { useEffect, useState } from 'react';
-import { Button, Form, Header, Label, Pagination, Segment, Select, Table,Modal } from 'semantic-ui-react';
-import { API, isAdmin, showError, timestamp2string } from '../helpers';
+import React, {useEffect, useState} from 'react';
+import {Label} from 'semantic-ui-react';
+import {API, copy, isAdmin, showError, showSuccess, timestamp2string} from '../helpers';
-import { ITEMS_PER_PAGE } from '../constants';
-import { renderQuota } from '../helpers/render';
-import {Link} from "react-router-dom";
+import {Table, Avatar, Tag, Form, Button, Layout, Select, Popover, Modal } from '@douyinfe/semi-ui';
+import {ITEMS_PER_PAGE} from '../constants';
+import {renderNumber, renderQuota, stringToColor} from '../helpers/render';
-function renderTimestamp(timestamp) {
- return (
- <>
- {timestamp2string(timestamp)}
- >
- );
-}
-const MODE_OPTIONS = [
- { key: 'all', text: '全部用户', value: 'all' },
- { key: 'self', text: '当前用户', value: 'self' }
-];
-
-const LOG_OPTIONS = [
- { key: '0', text: '全部', value: 0 },
- // { key: '1', text: '绘图', value: 1 },
- // { key: '2', text: '放大', value: 2 },
- // { key: '3', text: '变换', value: 3 },
- // { key: '4', text: '图生文', value: 4 },
- // { key: '5', text: '图片混合', value: 5 }
-];
+const colors = ['amber', 'blue', 'cyan', 'green', 'grey', 'indigo',
+ 'light-blue', 'lime', 'orange', 'pink',
+ 'purple', 'red', 'teal', 'violet', 'yellow'
+]
function renderType(type) {
switch (type) {
case 'IMAGINE':
- return ;
+ return 绘图;
case 'UPSCALE':
- return ;
+ return 放大;
case 'VARIATION':
- return ;
+ return 变换;
case 'DESCRIBE':
- return ;
+ return 图生文;
case 'BLEAND':
- return ;
+ return 图混合;
default:
- return ;
+ return 未知;
}
}
-function renderCode(type) {
- switch (type) {
+
+function renderCode(code) {
+ switch (code) {
case 1:
- return ;
+ return 已提交;
case 21:
- return ;
+ return 排队中;
case 22:
- return ;
+ return 重复提交;
default:
- return ;
+ return 未知;
}
}
+
function renderStatus(type) {
+ // Ensure all cases are string literals by adding quotes.
switch (type) {
case 'SUCCESS':
- return ;
+ return 成功;
case 'NOT_START':
- return ;
+ return 未启动;
case 'SUBMITTED':
- return ;
+ return 队列中;
case 'IN_PROGRESS':
- return ;
+ return 执行中;
case 'FAILURE':
- return ;
+ return 失败;
default:
- return ;
+ return 未知;
}
}
+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}`; // 格式化输出
+};
+
+
+
const LogsTable = () => {
- const [logs, setLogs] = useState([
-
- ]);
- const [loading, setLoading] = useState(true);
- const [activePage, setActivePage] = useState(1);
- const [searchKeyword, setSearchKeyword] = useState('');
- const [searching, setSearching] = useState(false);
- const [logType, setLogType] = useState(0);
- const isAdminUser = isAdmin();
-
-
- let now = new Date();
- const [inputs, setInputs] = useState({
- username: '',
- token_name: '',
- model_name: '',
- start_timestamp: timestamp2string(0),
- end_timestamp: timestamp2string(now.getTime() / 1000 + 3600)
- });
- const { username, token_name, model_name, start_timestamp, end_timestamp } = inputs;
-
- const [stat, setStat] = useState({
- quota: 0,
- token: 0
- });
-
- const [modalContent, setModalContent] = useState('');
- const [showModal, setShowModal] = useState(false);
-
- const showFullContent = (content) => {
- setModalContent(content);
- setShowModal(true);
- };
-
-
- const loadLogs = async (startIdx) => {
- let url = '';
- let localStartTimestamp = Date.parse(start_timestamp) / 1000;
- let localEndTimestamp = Date.parse(end_timestamp) / 1000;
- if (isAdminUser) {
- url = `/api/mj/?p=${startIdx}&username=${username}&token_name=${token_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
- } else {
- url = `/api/mj/self/?p=${startIdx}&token_name=${token_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
- }
- const res = await API.get(url);
- const { success, message, data } = res.data;
- if (success) {
- if (startIdx === 0) {
- setLogs(data);
- } else {
- let newLogs = [...logs];
- newLogs.splice(startIdx * ITEMS_PER_PAGE, data.length, ...data);
- setLogs(newLogs);
- }
- } else {
- showError(message);
- }
- setLoading(false);
- };
-
- const onPaginationChange = (e, { activePage }) => {
- (async () => {
- if (activePage === Math.ceil(logs.length / ITEMS_PER_PAGE) + 1) {
- // In this case we have to load more data and then append them.
- await loadLogs(activePage - 1);
- }
- setActivePage(activePage);
- })();
- };
-
- const refresh = async () => {
- setLoading(true);
- setActivePage(1)
- await loadLogs(0);
- // if (isAdminUser) {
- // getLogStat().then();
- // } else {
- // getLogSelfStat().then();
- // }
- };
-
- useEffect(() => {
- refresh().then();
- }, [logType]);
-
- const searchLogs = async () => {
- if (searchKeyword === '') {
- // if keyword is blank, load files instead.
- await loadLogs(0);
- setActivePage(1);
- return;
- }
- setSearching(true);
- const res = await API.get(`/api/log/self/search?keyword=${searchKeyword}`);
- const { success, message, data } = res.data;
- if (success) {
- setLogs(data);
- setActivePage(1);
- } else {
- showError(message);
- }
- setSearching(false);
- };
-
- const handleKeywordChange = async (e, { value }) => {
- setSearchKeyword(value.trim());
- };
-
- const sortLog = (key) => {
- if (logs.length === 0) return;
- setLoading(true);
- let sortedLogs = [...logs];
- if (typeof sortedLogs[0][key] === 'string'){
- sortedLogs.sort((a, b) => {
- return ('' + a[key]).localeCompare(b[key]);
- });
- } else {
- sortedLogs.sort((a, b) => {
- if (a[key] === b[key]) return 0;
- if (a[key] > b[key]) return -1;
- if (a[key] < b[key]) return 1;
- });
- }
- if (sortedLogs[0].id === logs[0].id) {
- sortedLogs.reverse();
- }
- setLogs(sortedLogs);
- setLoading(false);
- };
-
- return (
- <>
-
-
-
-
- {
- sortLog('submit_time');
- }}
- width={2}
- >
- 提交时间
-
- {
- sortLog('action');
- }}
- width={1}
- >
- 类型
-
- {
- sortLog('mj_id');
- }}
- width={2}
- >
- 任务ID
-
- {
- sortLog('code');
- }}
- width={1}
- >
- 提交结果
-
- {
- sortLog('status');
- }}
- width={1}
- >
- 任务状态
-
- {
- sortLog('progress');
- }}
- width={1}
- >
- 进度
-
- {
- sortLog('image_url');
- }}
- width={1}
- >
- 结果图片
-
- {
- sortLog('prompt');
- }}
- width={3}
- >
- Prompt
-
- {
- sortLog('prompt_en');
- }}
- width={3}
- >
- PromptEn
-
- {
- sortLog('fail_reason');
- }}
- width={1}
- >
- 失败原因
-
-
-
-
-
- {logs
- .slice(
- (activePage - 1) * ITEMS_PER_PAGE,
- activePage * ITEMS_PER_PAGE
- )
- .map((log, idx) => {
- if (log.deleted) return <>>;
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [modalContent, setModalContent] = useState('');
+ const columns = [
+ {
+ title: '提交时间',
+ dataIndex: 'submit_time',
+ render: (text, record, index) => {
+ return (
+
+ {renderTimestamp(text / 1000)}
+
+ );
+ },
+ },
+ {
+ title: '渠道',
+ dataIndex: 'channel_id',
+ className: isAdmin() ? 'tableShow' : 'tableHiddle',
+ render: (text, record, index) => {
return (
-
- {renderTimestamp(log.submit_time/1000)}
- {/*{*/}
- {/* isAdminUser && (*/}
- {/* {log.username ? : ''}*/}
- {/* )*/}
- {/*}*/}
- {renderType(log.action)}
- {log.mj_id}
- {renderCode(log.code)}
- {renderStatus(log.status)}
- {log.progress ? : ''}
-
- {
- log.image_url ? (
- // 点击查看
- 点击查看
- ) : '暂未生成图片'
- }
-
-
- {log.prompt.length > 10
- ?
- : log.prompt
- }
-
-
- {log.prompt_en.length > 10
- ?
- : log.prompt_en
- }
-
-
- {log.fail_reason && log.fail_reason.length > 10
- ?
- : log.fail_reason || '无'
- }
-
-
- );
- })}
-
-
-
-
-
-
-
-
-
- {/*Modal component goes here*/}
- setShowModal(false)} centered>
-
- {modalContent}
-
-
- >
- );
+
+ {
+ copyText(text); // 假设copyText是用于文本复制的函数
+ }}> {text}
+
+
+ );
+ },
+ },
+ {
+ title: '类型',
+ dataIndex: 'action',
+ render: (text, record, index) => {
+ return (
+
+ {renderType(text)}
+
+ );
+ },
+ },
+ {
+ title: '任务ID',
+ dataIndex: 'mj_id',
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: '提交结果',
+ dataIndex: 'code',
+ className: isAdmin() ? 'tableShow' : 'tableHiddle',
+ render: (text, record, index) => {
+ return (
+
+ {renderCode(text)}
+
+ );
+ },
+ },
+ {
+ title: '任务状态',
+ dataIndex: 'status',
+ className: isAdmin() ? 'tableShow' : 'tableHiddle',
+ render: (text, record, index) => {
+ return (
+
+ {renderStatus(text)}
+
+ );
+ },
+ },
+ {
+ title: '进度',
+ dataIndex: 'progress',
+ render: (text, record, index) => {
+ return (
+
+ { {text} }
+
+ );
+ },
+ },
+ {
+ title: '结果图片',
+ dataIndex: 'image_url',
+ render: (text, record, index) => {
+ if (!text) {
+ return '无';
+ }
+ return (
+
+ );
+ }
+ },
+ {
+ title: 'Prompt',
+ dataIndex: 'prompt',
+ render: (text, record, index) => {
+ // 如果text未定义,返回替代文本,例如空字符串''或其他
+ if (!text) {
+ return '无';
+ }
+
+ return (
+ text.length > 10 ?
+ <>
+ {text.slice(0, 10)}
+
+ >
+ : text
+ );
+ }
+ },
+ {
+ title: 'PromptEn',
+ dataIndex: 'prompt_en',
+ render: (text, record, index) => {
+ // 如果text未定义,返回替代文本,例如空字符串''或其他
+ if (!text) {
+ return '无';
+ }
+
+ return (
+ text.length > 10 ?
+ <>
+ {text.slice(0, 10)}
+
+ >
+ : text
+ );
+ }
+ },
+ {
+ title: '失败原因',
+ dataIndex: 'fail_reason',
+ render: (text, record, index) => {
+ // 如果text未定义,返回替代文本,例如空字符串''或其他
+ if (!text) {
+ return '无';
+ }
+
+ return (
+ text.length > 10 ?
+ <>
+ {text.slice(0, 10)}
+
+ >
+ : text
+ );
+ }
+ }
+
+ ];
+
+ const [logs, setLogs] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [activePage, setActivePage] = useState(1);
+ const [logCount, setLogCount] = useState(ITEMS_PER_PAGE);
+ const [logType, setLogType] = useState(0);
+ const isAdminUser = isAdmin();
+ const [isModalOpenurl, setIsModalOpenurl] = useState(false);
+
+ // 定义模态框图片URL的状态和更新函数
+ const [modalImageUrl, setModalImageUrl] = useState('');
+ let now = new Date();
+ // 初始化start_timestamp为前一天
+ const [inputs, setInputs] = useState({
+ channel_id: '',
+ mj_id: '',
+ start_timestamp: timestamp2string(now.getTime() / 1000 - 2592000),
+ end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
+ });
+ const {channel_id, mj_id, start_timestamp, end_timestamp} = inputs;
+
+ const [stat, setStat] = useState({
+ quota: 0,
+ token: 0
+ });
+
+ const handleInputChange = (value, name) => {
+ setInputs((inputs) => ({...inputs, [name]: value}));
+ };
+
+
+
+ const setLogsFormat = (logs) => {
+ for (let i = 0; i < logs.length; i++) {
+ logs[i].timestamp2string = timestamp2string(logs[i].created_at);
+ logs[i].key = '' + logs[i].id;
+ }
+ // data.key = '' + data.id
+ setLogs(logs);
+ setLogCount(logs.length + ITEMS_PER_PAGE);
+ console.log(logCount);
+ }
+
+ const loadLogs = async (startIdx) => {
+ setLoading(true);
+
+ let url = '';
+ let localStartTimestamp = Date.parse(start_timestamp);
+ let localEndTimestamp = Date.parse(end_timestamp);
+ if (isAdminUser) {
+ url = `/api/mj/?p=${startIdx}&channel_id=${channel_id}&mj_id=${mj_id}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
+ } else {
+ url = `/api/mj/self/?p=${startIdx}&mj_id=${mj_id}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
+ }
+ const res = await API.get(url);
+ const {success, message, data} = res.data;
+ if (success) {
+ if (startIdx === 0) {
+ setLogsFormat(data);
+ } else {
+ let newLogs = [...logs];
+ newLogs.splice(startIdx * ITEMS_PER_PAGE, data.length, ...data);
+ setLogsFormat(newLogs);
+ }
+ } else {
+ showError(message);
+ }
+ setLoading(false);
+ };
+
+ const pageData = logs.slice((activePage - 1) * ITEMS_PER_PAGE, activePage * ITEMS_PER_PAGE);
+
+ const handlePageChange = page => {
+ setActivePage(page);
+ if (page === Math.ceil(logs.length / ITEMS_PER_PAGE) + 1) {
+ // In this case we have to load more data and then append them.
+ loadLogs(page - 1).then(r => {
+ });
+ }
+ };
+
+ const refresh = async () => {
+ // setLoading(true);
+ setActivePage(1);
+ await loadLogs(0);
+ };
+
+ const copyText = async (text) => {
+ if (await copy(text)) {
+ showSuccess('已复制:' + text);
+ } else {
+ // setSearchKeyword(text);
+ Modal.error({title: '无法复制到剪贴板,请手动复制', content: text});
+ }
+ }
+
+ useEffect(() => {
+ refresh().then();
+ }, [logType]);
+
+
+
+
+ return (
+ <>
+
+
+ handleInputChange(value, 'channel_id')}/>
+ handleInputChange(value, 'mj_id')}/>
+ handleInputChange(value, 'start_timestamp')}/>
+ handleInputChange(value, 'end_timestamp')}/>
+
+
+
+
+ >
+
+
+ setIsModalOpen(false)}
+ onCancel={() => setIsModalOpen(false)}
+ closable={null}
+ bodyStyle={{ height: '400px', overflow: 'auto' }} // 设置模态框内容区域样式
+ width={800} // 设置模态框宽度
+ >
+ {modalContent}
+
+ {/* 模态框组件,用于展示图片 */}
+ setIsModalOpenurl(false)}
+ footer={null} // 模态框不显示底部按钮
+ >
+
+
+
+
+ >
+ );
};
export default LogsTable;