mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-16 13:13:41 +08:00
✨ feat: add Midjourney (#138)
* 🚧 stash * ✨ feat: add Midjourney * 📝 doc: update readme
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
使用了以下开源项目作为我们项目的一部分:
|
||||
|
||||
- [Berry Free React Admin Template](https://github.com/codedthemes/berry-free-react-admin-template)
|
||||
- [minimal-ui-kit](minimal-ui-kit)
|
||||
- [minimal-ui-kit](https://github.com/minimal-ui-kit/material-kit-react)
|
||||
|
||||
## 许可证
|
||||
|
||||
|
||||
@@ -132,6 +132,13 @@ export const CHANNEL_OPTIONS = {
|
||||
color: 'primary',
|
||||
url: 'https://platform.lingyiwanwu.com/details'
|
||||
},
|
||||
34: {
|
||||
key: 34,
|
||||
text: 'Midjourney',
|
||||
value: 34,
|
||||
color: 'orange',
|
||||
url: ''
|
||||
},
|
||||
24: {
|
||||
key: 24,
|
||||
text: 'Azure Speech',
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
IconUserScan,
|
||||
IconActivity,
|
||||
IconBrandTelegram,
|
||||
IconReceipt2
|
||||
IconReceipt2,
|
||||
IconBrush
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
// constant
|
||||
@@ -27,7 +28,8 @@ const icons = {
|
||||
IconUserScan,
|
||||
IconActivity,
|
||||
IconBrandTelegram,
|
||||
IconReceipt2
|
||||
IconReceipt2,
|
||||
IconBrush
|
||||
};
|
||||
|
||||
// ==============================|| DASHBOARD MENU ITEMS ||============================== //
|
||||
@@ -96,6 +98,14 @@ const panel = {
|
||||
icon: icons.IconGardenCart,
|
||||
breadcrumbs: false
|
||||
},
|
||||
{
|
||||
id: 'midjourney',
|
||||
title: 'Midjourney',
|
||||
type: 'item',
|
||||
url: '/panel/midjourney',
|
||||
icon: icons.IconBrush,
|
||||
breadcrumbs: false
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
title: '用户',
|
||||
|
||||
@@ -16,6 +16,7 @@ const NotFoundView = Loadable(lazy(() => import('views/Error')));
|
||||
const Analytics = Loadable(lazy(() => import('views/Analytics')));
|
||||
const Telegram = Loadable(lazy(() => import('views/Telegram')));
|
||||
const Pricing = Loadable(lazy(() => import('views/Pricing')));
|
||||
const Midjourney = Loadable(lazy(() => import('views/Midjourney')));
|
||||
|
||||
// dashboard routing
|
||||
const Dashboard = Loadable(lazy(() => import('views/Dashboard')));
|
||||
@@ -81,6 +82,10 @@ const MainRoutes = {
|
||||
{
|
||||
path: 'pricing',
|
||||
element: <Pricing />
|
||||
},
|
||||
{
|
||||
path: 'midjourney',
|
||||
element: <Midjourney />
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -12,15 +12,7 @@ export default function componentStyleOverrides(theme) {
|
||||
}
|
||||
}
|
||||
},
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colors?.grey100
|
||||
}
|
||||
}
|
||||
}
|
||||
}, //MuiAutocomplete-popper MuiPopover-root
|
||||
//MuiAutocomplete-popper MuiPopover-root
|
||||
MuiAutocomplete: {
|
||||
styleOverrides: {
|
||||
popper: {
|
||||
@@ -247,7 +239,7 @@ export default function componentStyleOverrides(theme) {
|
||||
MuiTooltip: {
|
||||
styleOverrides: {
|
||||
tooltip: {
|
||||
color: theme.paper,
|
||||
color: theme.colors.paper,
|
||||
background: theme.colors?.grey700
|
||||
}
|
||||
}
|
||||
@@ -266,6 +258,9 @@ export default function componentStyleOverrides(theme) {
|
||||
.apexcharts-menu {
|
||||
background: ${theme.backgroundDefault} !important
|
||||
}
|
||||
.apexcharts-gridline, .apexcharts-xaxistooltip-background, .apexcharts-yaxistooltip-background {
|
||||
stroke: ${theme.divider} !important;
|
||||
}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,14 +19,14 @@ const Footer = () => {
|
||||
{siteInfo.system_name} {process.env.REACT_APP_VERSION}{' '}
|
||||
</Link>
|
||||
由{' '}
|
||||
<Link href="https://github.com/songquanpeng" target="_blank">
|
||||
JustSong
|
||||
</Link>{' '}
|
||||
构建,
|
||||
<Link href="https://github.com/MartialBE" target="_blank">
|
||||
MartialBE
|
||||
</Link>
|
||||
修改,源代码遵循
|
||||
开发,基于
|
||||
<Link href="https://github.com/songquanpeng" target="_blank">
|
||||
JustSong
|
||||
</Link>{' '}
|
||||
One API,源代码遵循
|
||||
<Link href="https://opensource.org/licenses/mit-license.php"> MIT 协议</Link>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -234,6 +234,33 @@ const typeConfig = {
|
||||
test_model: 'yi-34b-chat-0205'
|
||||
},
|
||||
modelGroup: 'Lingyiwanwu'
|
||||
},
|
||||
34: {
|
||||
input: {
|
||||
models: [
|
||||
'mj_imagine',
|
||||
'mj_variation',
|
||||
'mj_reroll',
|
||||
'mj_blend',
|
||||
'mj_modal',
|
||||
'mj_zoom',
|
||||
'mj_shorten',
|
||||
'mj_high_variation',
|
||||
'mj_low_variation',
|
||||
'mj_pan',
|
||||
'mj_inpaint',
|
||||
'mj_custom_zoom',
|
||||
'mj_describe',
|
||||
'mj_upscale',
|
||||
'swap_face'
|
||||
]
|
||||
},
|
||||
prompt: {
|
||||
key: '密钥填写midjourney-proxy的密钥,如果没有设置密钥,可以随便填',
|
||||
base_url: '地址填写midjourney-proxy部署的地址',
|
||||
test_model: ''
|
||||
},
|
||||
modelGroup: 'Midjourney'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
174
web/src/views/Midjourney/component/TableRow.js
Normal file
174
web/src/views/Midjourney/component/TableRow.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
TableRow,
|
||||
TableCell,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
ButtonGroup,
|
||||
Popover,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Tooltip
|
||||
} from '@mui/material';
|
||||
|
||||
import { timestamp2string, copy } from 'utils/common';
|
||||
import Label from 'ui-component/Label';
|
||||
import { ACTION_TYPE, CODE_TYPE, STATUS_TYPE } from '../type/Type';
|
||||
import { IconCaretDownFilled, IconCopy, IconDownload, IconExternalLink } from '@tabler/icons-react';
|
||||
|
||||
function renderType(types, type) {
|
||||
const typeOption = types[type];
|
||||
if (typeOption) {
|
||||
return (
|
||||
<Label variant="filled" color={typeOption.color}>
|
||||
{' '}
|
||||
{typeOption.text}{' '}
|
||||
</Label>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Label variant="filled" color="error">
|
||||
{' '}
|
||||
未知{' '}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
}
|
||||
async function downloadImage(url, filename) {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = blobUrl;
|
||||
link.download = filename;
|
||||
link.click();
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
}
|
||||
|
||||
function TruncatedText(text) {
|
||||
const truncatedText = text.length > 30 ? text.substring(0, 100) + '...' : text;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title={text}
|
||||
onClick={() => {
|
||||
copy(text, '');
|
||||
}}
|
||||
>
|
||||
<span>{truncatedText}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LogTableRow({ item, userIsAdmin }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(null);
|
||||
const handleClickOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleOpenMenu = (event) => {
|
||||
setMenuOpen(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseMenu = () => {
|
||||
setMenuOpen(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow tabIndex={item.id}>
|
||||
<TableCell>{item.mj_id}</TableCell>
|
||||
<TableCell>{timestamp2string(item.submit_time / 1000)}</TableCell>
|
||||
|
||||
{userIsAdmin && <TableCell>{item.channel_id || ''}</TableCell>}
|
||||
{userIsAdmin && <TableCell>{item.user_id || ''}</TableCell>}
|
||||
|
||||
<TableCell>{renderType(ACTION_TYPE, item.action)}</TableCell>
|
||||
{userIsAdmin && <TableCell>{renderType(CODE_TYPE, item.code)}</TableCell>}
|
||||
{userIsAdmin && <TableCell>{renderType(STATUS_TYPE, item.status)}</TableCell>}
|
||||
<TableCell>{item.progress}</TableCell>
|
||||
<TableCell>
|
||||
{item.image_url == '' ? (
|
||||
'无'
|
||||
) : (
|
||||
<ButtonGroup size="small" aria-label="split button">
|
||||
<Button color="primary" onClick={handleClickOpen}>
|
||||
显示
|
||||
</Button>
|
||||
<Button onClick={handleOpenMenu}>
|
||||
<IconCaretDownFilled size={'16px'} />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>{TruncatedText(item.prompt)}</TableCell>
|
||||
<TableCell>{TruncatedText(item.prompt_en)}</TableCell>
|
||||
<TableCell>{TruncatedText(item.fail_reason)}</TableCell>
|
||||
</TableRow>
|
||||
<Dialog open={open} onClose={handleClose}>
|
||||
<DialogContent>
|
||||
<img src={item.image_url} alt="item" style={{ maxWidth: '100%', maxHeight: '100%' }} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
关闭
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<Popover
|
||||
open={!!menuOpen}
|
||||
anchorEl={menuOpen}
|
||||
onClose={handleCloseMenu}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
PaperProps={{
|
||||
sx: { width: 140 }
|
||||
}}
|
||||
>
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseMenu();
|
||||
copy(item.image_url, '图片地址');
|
||||
}}
|
||||
>
|
||||
<IconCopy style={{ marginRight: '16px' }} />
|
||||
复制地址
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
handleCloseMenu();
|
||||
await downloadImage(item.image_url, item.mj_id + '.png');
|
||||
}}
|
||||
>
|
||||
<IconDownload style={{ marginRight: '16px' }} /> 下载图片{' '}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseMenu();
|
||||
}}
|
||||
>
|
||||
<IconExternalLink style={{ marginRight: '16px' }} /> 新窗口打开{' '}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
LogTableRow.propTypes = {
|
||||
item: PropTypes.object,
|
||||
userIsAdmin: PropTypes.bool
|
||||
};
|
||||
113
web/src/views/Midjourney/component/TableToolBar.js
Normal file
113
web/src/views/Midjourney/component/TableToolBar.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { IconBroadcast, IconCalendarEvent } from '@tabler/icons-react';
|
||||
import { InputAdornment, OutlinedInput, Stack, FormControl, InputLabel } from '@mui/material';
|
||||
import { LocalizationProvider, DateTimePicker } from '@mui/x-date-pickers';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import dayjs from 'dayjs';
|
||||
require('dayjs/locale/zh-cn');
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function TableToolBar({ filterName, handleFilterName, userIsAdmin }) {
|
||||
const theme = useTheme();
|
||||
const grey500 = theme.palette.grey[500];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={{ xs: 3, sm: 2, md: 4 }} padding={'24px'} paddingBottom={'0px'}>
|
||||
{userIsAdmin && (
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="channel-channel_id-label">渠道ID</InputLabel>
|
||||
<OutlinedInput
|
||||
id="channel_id"
|
||||
name="channel_id"
|
||||
sx={{
|
||||
minWidth: '100%'
|
||||
}}
|
||||
label="渠道ID"
|
||||
value={filterName.channel_id}
|
||||
onChange={handleFilterName}
|
||||
placeholder="渠道ID"
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<IconBroadcast stroke={1.5} size="20px" color={grey500} />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="channel-mj_id-label">任务ID</InputLabel>
|
||||
<OutlinedInput
|
||||
id="mj_id"
|
||||
name="mj_id"
|
||||
sx={{
|
||||
minWidth: '100%'
|
||||
}}
|
||||
label="任务ID"
|
||||
value={filterName.mj_id}
|
||||
onChange={handleFilterName}
|
||||
placeholder="任务ID"
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<IconCalendarEvent stroke={1.5} size="20px" color={grey500} />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={'zh-cn'}>
|
||||
<DateTimePicker
|
||||
label="起始时间"
|
||||
ampm={false}
|
||||
name="start_timestamp"
|
||||
value={filterName.start_timestamp === 0 ? null : dayjs.unix(filterName.start_timestamp / 1000)}
|
||||
onChange={(value) => {
|
||||
if (value === null) {
|
||||
handleFilterName({ target: { name: 'start_timestamp', value: 0 } });
|
||||
return;
|
||||
}
|
||||
handleFilterName({ target: { name: 'start_timestamp', value: value.unix() * 1000 } });
|
||||
}}
|
||||
slotProps={{
|
||||
actionBar: {
|
||||
actions: ['clear', 'today', 'accept']
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={'zh-cn'}>
|
||||
<DateTimePicker
|
||||
label="结束时间"
|
||||
name="end_timestamp"
|
||||
ampm={false}
|
||||
value={filterName.end_timestamp === 0 ? null : dayjs.unix(filterName.end_timestamp / 1000)}
|
||||
onChange={(value) => {
|
||||
if (value === null) {
|
||||
handleFilterName({ target: { name: 'end_timestamp', value: 0 } });
|
||||
return;
|
||||
}
|
||||
handleFilterName({ target: { name: 'end_timestamp', value: value.unix() * 1000 } });
|
||||
}}
|
||||
slotProps={{
|
||||
actionBar: {
|
||||
actions: ['clear', 'today', 'accept']
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
TableToolBar.propTypes = {
|
||||
filterName: PropTypes.object,
|
||||
handleFilterName: PropTypes.func,
|
||||
userIsAdmin: PropTypes.bool
|
||||
};
|
||||
247
web/src/views/Midjourney/index.js
Normal file
247
web/src/views/Midjourney/index.js
Normal file
@@ -0,0 +1,247 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { showError } from 'utils/common';
|
||||
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar';
|
||||
import TablePagination from '@mui/material/TablePagination';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
|
||||
import { Button, Card, Stack, Container, Typography, Box } from '@mui/material';
|
||||
import LogTableRow from './component/TableRow';
|
||||
import KeywordTableHead from 'ui-component/TableHead';
|
||||
import TableToolBar from './component/TableToolBar';
|
||||
import { API } from 'utils/api';
|
||||
import { isAdmin } from 'utils/common';
|
||||
import { ITEMS_PER_PAGE } from 'constants';
|
||||
import { IconRefresh, IconSearch } from '@tabler/icons-react';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export default function Log() {
|
||||
const originalKeyword = {
|
||||
p: 0,
|
||||
channel_id: '',
|
||||
mj_id: '',
|
||||
start_timestamp: 0,
|
||||
end_timestamp: dayjs().unix() * 1000 + 3600
|
||||
};
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const [order, setOrder] = useState('desc');
|
||||
const [orderBy, setOrderBy] = useState('id');
|
||||
const [rowsPerPage, setRowsPerPage] = useState(ITEMS_PER_PAGE);
|
||||
const [listCount, setListCount] = useState(0);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [toolBarValue, setToolBarValue] = useState(originalKeyword);
|
||||
const [searchKeyword, setSearchKeyword] = useState(originalKeyword);
|
||||
const [refreshFlag, setRefreshFlag] = useState(false);
|
||||
|
||||
const [logs, setLogs] = useState([]);
|
||||
const userIsAdmin = isAdmin();
|
||||
|
||||
const handleSort = (event, id) => {
|
||||
const isAsc = orderBy === id && order === 'asc';
|
||||
if (id !== '') {
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
setOrderBy(id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
};
|
||||
|
||||
const searchLogs = async () => {
|
||||
setPage(0);
|
||||
setSearchKeyword(toolBarValue);
|
||||
};
|
||||
|
||||
const handleToolBarValue = (event) => {
|
||||
setToolBarValue({ ...toolBarValue, [event.target.name]: event.target.value });
|
||||
};
|
||||
|
||||
const fetchData = useCallback(
|
||||
async (page, rowsPerPage, keyword, order, orderBy) => {
|
||||
setSearching(true);
|
||||
try {
|
||||
if (orderBy) {
|
||||
orderBy = order === 'desc' ? '-' + orderBy : orderBy;
|
||||
}
|
||||
const url = userIsAdmin ? '/api/mj/' : '/api/mj/self/';
|
||||
if (!userIsAdmin) {
|
||||
delete keyword.channel_id;
|
||||
}
|
||||
|
||||
const res = await API.get(url, {
|
||||
params: {
|
||||
page: page + 1,
|
||||
size: rowsPerPage,
|
||||
order: orderBy,
|
||||
...keyword
|
||||
}
|
||||
});
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
setListCount(data.total_count);
|
||||
setLogs(data.data);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setSearching(false);
|
||||
},
|
||||
[userIsAdmin]
|
||||
);
|
||||
|
||||
// 处理刷新
|
||||
const handleRefresh = async () => {
|
||||
setOrderBy('id');
|
||||
setOrder('desc');
|
||||
setToolBarValue(originalKeyword);
|
||||
setSearchKeyword(originalKeyword);
|
||||
setRefreshFlag(!refreshFlag);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(page, rowsPerPage, searchKeyword, order, orderBy);
|
||||
}, [page, rowsPerPage, searchKeyword, order, orderBy, fetchData, refreshFlag]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
||||
<Typography variant="h4">Midjourney</Typography>
|
||||
</Stack>
|
||||
<Card>
|
||||
<Box component="form" noValidate>
|
||||
<TableToolBar filterName={toolBarValue} handleFilterName={handleToolBarValue} userIsAdmin={userIsAdmin} />
|
||||
</Box>
|
||||
<Toolbar
|
||||
sx={{
|
||||
textAlign: 'right',
|
||||
height: 50,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
p: (theme) => theme.spacing(0, 1, 0, 3)
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||
刷新/清除搜索条件
|
||||
</Button>
|
||||
|
||||
<Button onClick={searchLogs} startIcon={<IconSearch width={'18px'} />}>
|
||||
搜索
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Container>
|
||||
</Toolbar>
|
||||
{searching && <LinearProgress />}
|
||||
<PerfectScrollbar component="div">
|
||||
<TableContainer sx={{ overflow: 'unset' }}>
|
||||
<Table sx={{ minWidth: 800 }}>
|
||||
<KeywordTableHead
|
||||
order={order}
|
||||
orderBy={orderBy}
|
||||
onRequestSort={handleSort}
|
||||
headLabel={[
|
||||
{
|
||||
id: 'mj_id',
|
||||
label: '任务ID',
|
||||
disableSort: false
|
||||
},
|
||||
{
|
||||
id: 'submit_time',
|
||||
label: '提交时间',
|
||||
disableSort: false
|
||||
},
|
||||
{
|
||||
id: 'channel_id',
|
||||
label: '渠道',
|
||||
disableSort: false,
|
||||
hide: !userIsAdmin
|
||||
},
|
||||
{
|
||||
id: 'user_id',
|
||||
label: '用户',
|
||||
disableSort: false,
|
||||
hide: !userIsAdmin
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
label: '类型',
|
||||
disableSort: false
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
label: '提交结果',
|
||||
disableSort: false,
|
||||
hide: !userIsAdmin
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
label: '任务状态',
|
||||
disableSort: false,
|
||||
hide: !userIsAdmin
|
||||
},
|
||||
{
|
||||
id: 'progress',
|
||||
label: '进度',
|
||||
disableSort: true
|
||||
},
|
||||
{
|
||||
id: 'image_url',
|
||||
label: '结果图片',
|
||||
disableSort: true,
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
id: 'prompt',
|
||||
label: 'Prompt',
|
||||
disableSort: true
|
||||
},
|
||||
{
|
||||
id: 'prompt_en',
|
||||
label: 'PromptEn',
|
||||
disableSort: true
|
||||
},
|
||||
{
|
||||
id: 'fail_reason',
|
||||
label: '失败原因',
|
||||
disableSort: true
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<TableBody>
|
||||
{logs.map((row, index) => (
|
||||
<LogTableRow item={row} key={`${row.id}_${index}`} userIsAdmin={userIsAdmin} />
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</PerfectScrollbar>
|
||||
<TablePagination
|
||||
page={page}
|
||||
component="div"
|
||||
count={listCount}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPageOptions={[10, 25, 30]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
33
web/src/views/Midjourney/type/Type.js
Normal file
33
web/src/views/Midjourney/type/Type.js
Normal file
@@ -0,0 +1,33 @@
|
||||
export const ACTION_TYPE = {
|
||||
IMAGINE: { value: 'IMAGINE', text: '绘图', color: 'primary' },
|
||||
UPSCALE: { value: 'UPSCALE', text: '放大', color: 'orange' },
|
||||
VARIATION: { value: 'VARIATION', text: '变换', color: 'default' },
|
||||
HIGH_VARIATION: { value: 'HIGH_VARIATION', text: '强变换', color: 'default' },
|
||||
LOW_VARIATION: { value: 'LOW_VARIATION', text: '弱变换', color: 'default' },
|
||||
PAN: { value: 'PAN', text: '平移', color: 'secondary' },
|
||||
DESCRIBE: { value: 'DESCRIBE', text: '图生文', color: 'secondary' },
|
||||
BLEND: { value: 'BLEND', text: '图混合', color: 'secondary' },
|
||||
SHORTEN: { value: 'SHORTEN', text: '缩词', color: 'secondary' },
|
||||
REROLL: { value: 'REROLL', text: '重绘', color: 'secondary' },
|
||||
INPAINT: { value: 'INPAINT', text: '局部重绘-提交', color: 'secondary' },
|
||||
ZOOM: { value: 'ZOOM', text: '变焦', color: 'secondary' },
|
||||
CUSTOM_ZOOM: { value: 'CUSTOM_ZOOM', text: '自定义变焦-提交', color: 'secondary' },
|
||||
MODAL: { value: 'MODAL', text: '窗口处理', color: 'secondary' },
|
||||
SWAP_FACE: { value: 'SWAP_FACE', text: '换脸', color: 'secondary' }
|
||||
};
|
||||
|
||||
export const CODE_TYPE = {
|
||||
1: { value: 1, text: '已提交', color: 'primary' },
|
||||
21: { value: 21, text: '等待中', color: 'orange' },
|
||||
22: { value: 22, text: '重复提交', color: 'default' },
|
||||
0: { value: 0, text: '未提交', color: 'default' }
|
||||
};
|
||||
|
||||
export const STATUS_TYPE = {
|
||||
SUCCESS: { value: 'SUCCESS', text: '成功', color: 'success' },
|
||||
NOT_START: { value: 'NOT_START', text: '未启动', color: 'default' },
|
||||
SUBMITTED: { value: 'SUBMITTED', text: '队列中', color: 'secondary' },
|
||||
IN_PROGRESS: { value: 'IN_PROGRESS', text: '执行中', color: 'primary' },
|
||||
FAILURE: { value: 'FAILURE', text: '失败', color: 'orange' },
|
||||
MODAL: { value: 'MODAL', text: '窗口等待', color: 'default' }
|
||||
};
|
||||
@@ -29,7 +29,8 @@ const OperationSetting = () => {
|
||||
DisplayTokenStatEnabled: '',
|
||||
ApproximateTokenEnabled: '',
|
||||
RetryTimes: 0,
|
||||
RetryCooldownSeconds: 0
|
||||
RetryCooldownSeconds: 0,
|
||||
MjNotifyEnabled: ''
|
||||
});
|
||||
const [originInputs, setOriginInputs] = useState({});
|
||||
let [loading, setLoading] = useState(false);
|
||||
@@ -278,6 +279,22 @@ const OperationSetting = () => {
|
||||
</Button>
|
||||
</Stack>
|
||||
</SubCard>
|
||||
<SubCard title="其他设置">
|
||||
<Stack justifyContent="flex-start" alignItems="flex-start" spacing={2}>
|
||||
<Stack
|
||||
direction={{ sm: 'column', md: 'row' }}
|
||||
spacing={{ xs: 3, sm: 2, md: 4 }}
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<FormControlLabel
|
||||
sx={{ marginLeft: '0px' }}
|
||||
label="Midjourney 允许回调(会泄露服务器ip地址)"
|
||||
control={<Checkbox checked={inputs.MjNotifyEnabled === 'true'} onChange={handleInputChange} name="MjNotifyEnabled" />}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</SubCard>
|
||||
<SubCard title="日志设置">
|
||||
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start" spacing={2}>
|
||||
<FormControlLabel
|
||||
|
||||
Reference in New Issue
Block a user