Expose image studio flow and related admin controls
Bundle the current backend and web changes into one publishable snapshot from the server worktree so the remote mirror reflects the deployed source. Constraint: Push the existing mixed worktree from /docker/new-api/src without reshaping files Rejected: Split into feature-specific commits | current worktree is already a single undifferentiated snapshot Confidence: medium Scope-risk: moderate Directive: Split future feature work before deployment to preserve clearer history Tested: git status inspection; git diff --stat review Not-tested: build, lint, unit tests, integration tests
This commit is contained in:
11
web/src/App.jsx
vendored
11
web/src/App.jsx
vendored
@@ -47,6 +47,7 @@ import Playground from './pages/Playground';
|
||||
import Subscription from './pages/Subscription';
|
||||
import OAuth2Callback from './components/auth/OAuth2Callback';
|
||||
import PersonalSetting from './components/settings/PersonalSetting';
|
||||
import ImageStudio from './pages/ImageStudio';
|
||||
import Setup from './pages/Setup';
|
||||
import SetupCheck from './components/layout/SetupCheck';
|
||||
|
||||
@@ -277,6 +278,16 @@ function App() {
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/console/image-studio'
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<Suspense fallback={<Loading></Loading>} key={location.pathname}>
|
||||
<ImageStudio />
|
||||
</Suspense>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path='/console/log'
|
||||
element={
|
||||
|
||||
@@ -49,6 +49,7 @@ const routerMap = {
|
||||
deployment: '/console/deployment',
|
||||
playground: '/console/playground',
|
||||
personal: '/console/personal',
|
||||
image_studio: '/console/image-studio',
|
||||
};
|
||||
|
||||
const SiderBar = ({ onNavigate = () => {} }) => {
|
||||
@@ -145,6 +146,18 @@ const SiderBar = ({ onNavigate = () => {} }) => {
|
||||
return filteredItems;
|
||||
}, [t, isModuleVisible]);
|
||||
|
||||
const otherItems = useMemo(() => {
|
||||
const items = [
|
||||
{
|
||||
text: t('图片制作'),
|
||||
itemKey: 'image_studio',
|
||||
to: '/image-studio',
|
||||
},
|
||||
];
|
||||
|
||||
return items.filter((item) => isModuleVisible('other', item.itemKey));
|
||||
}, [t, isModuleVisible]);
|
||||
|
||||
const adminItems = useMemo(() => {
|
||||
const items = [
|
||||
{
|
||||
@@ -475,6 +488,19 @@ const SiderBar = ({ onNavigate = () => {} }) => {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 其他功能区域 */}
|
||||
{hasSectionVisibleModules('other') && (
|
||||
<>
|
||||
<Divider className='sidebar-divider' />
|
||||
<div>
|
||||
{!collapsed && (
|
||||
<div className='sidebar-group-label'>{t('其他功能')}</div>
|
||||
)}
|
||||
{otherItems.map((item) => renderNavItem(item))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 管理员区域 - 只在管理员时显示且配置允许时显示 */}
|
||||
{isAdmin() && hasSectionVisibleModules('admin') && (
|
||||
<>
|
||||
|
||||
@@ -63,36 +63,9 @@ const NotificationSettings = ({
|
||||
// 左侧边栏设置相关状态
|
||||
const [sidebarLoading, setSidebarLoading] = useState(false);
|
||||
const [activeTabKey, setActiveTabKey] = useState('notification');
|
||||
const [sidebarModulesUser, setSidebarModulesUser] = useState({
|
||||
chat: {
|
||||
enabled: true,
|
||||
playground: true,
|
||||
chat: true,
|
||||
},
|
||||
console: {
|
||||
enabled: true,
|
||||
detail: true,
|
||||
token: true,
|
||||
log: true,
|
||||
midjourney: true,
|
||||
task: true,
|
||||
},
|
||||
personal: {
|
||||
enabled: true,
|
||||
topup: true,
|
||||
personal: true,
|
||||
},
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
models: true,
|
||||
deployment: true,
|
||||
subscription: true,
|
||||
redemption: true,
|
||||
user: true,
|
||||
setting: true,
|
||||
},
|
||||
});
|
||||
const [sidebarModulesUser, setSidebarModulesUser] = useState(
|
||||
mergeAdminConfig(null),
|
||||
);
|
||||
const [adminConfig, setAdminConfig] = useState(null);
|
||||
|
||||
// 使用后端权限验证替代前端角色判断
|
||||
@@ -155,29 +128,7 @@ const NotificationSettings = ({
|
||||
};
|
||||
|
||||
const resetSidebarModules = () => {
|
||||
const defaultConfig = {
|
||||
chat: { enabled: true, playground: true, chat: true },
|
||||
console: {
|
||||
enabled: true,
|
||||
detail: true,
|
||||
token: true,
|
||||
log: true,
|
||||
midjourney: true,
|
||||
task: true,
|
||||
},
|
||||
personal: { enabled: true, topup: true, personal: true },
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
models: true,
|
||||
deployment: true,
|
||||
subscription: true,
|
||||
redemption: true,
|
||||
user: true,
|
||||
setting: true,
|
||||
},
|
||||
};
|
||||
setSidebarModulesUser(defaultConfig);
|
||||
setSidebarModulesUser(mergeAdminConfig(null));
|
||||
};
|
||||
|
||||
// 加载左侧边栏配置
|
||||
@@ -207,7 +158,7 @@ const NotificationSettings = ({
|
||||
} else {
|
||||
userConf = userRes.data.data.sidebar_modules;
|
||||
}
|
||||
setSidebarModulesUser(userConf);
|
||||
setSidebarModulesUser(mergeAdminConfig(userConf));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载边栏配置失败:', error);
|
||||
@@ -287,6 +238,18 @@ const NotificationSettings = ({
|
||||
],
|
||||
},
|
||||
// 管理员区域:根据后端权限控制显示
|
||||
{
|
||||
key: 'other',
|
||||
title: t('其他功能区域'),
|
||||
description: t('图片与扩展功能'),
|
||||
modules: [
|
||||
{
|
||||
key: 'image_studio',
|
||||
title: t('图片制作'),
|
||||
description: t('生成图片和编辑图片'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'admin',
|
||||
title: t('管理员区域'),
|
||||
|
||||
3
web/src/helpers/render.jsx
vendored
3
web/src/helpers/render.jsx
vendored
@@ -75,6 +75,7 @@ import {
|
||||
Package,
|
||||
Server,
|
||||
CalendarClock,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
SiAtlassian,
|
||||
@@ -146,6 +147,8 @@ export function getLucideIcon(key, selected = false) {
|
||||
return <CalendarClock {...commonProps} color={iconColor} />;
|
||||
case 'setting':
|
||||
return <Settings {...commonProps} color={iconColor} />;
|
||||
case 'image_studio':
|
||||
return <Sparkles {...commonProps} color={iconColor} />;
|
||||
default:
|
||||
return <CircleUser {...commonProps} color={iconColor} />;
|
||||
}
|
||||
|
||||
7
web/src/helpers/utils.jsx
vendored
7
web/src/helpers/utils.jsx
vendored
@@ -121,13 +121,12 @@ if (isMobileScreen) {
|
||||
|
||||
export function showError(error) {
|
||||
console.error(error);
|
||||
if (error.message) {
|
||||
if (error?.message) {
|
||||
if (error.name === 'AxiosError') {
|
||||
switch (error.response.status) {
|
||||
const status = error.response?.status;
|
||||
switch (status) {
|
||||
case 401:
|
||||
// 清除用户状态
|
||||
localStorage.removeItem('user');
|
||||
// toast.error('错误:未登录或登录已过期,请重新登录!', showErrorOptions);
|
||||
window.location.href = '/login?expired=true';
|
||||
break;
|
||||
case 429:
|
||||
|
||||
4
web/src/hooks/common/useSidebar.js
vendored
4
web/src/hooks/common/useSidebar.js
vendored
@@ -44,6 +44,10 @@ export const DEFAULT_ADMIN_CONFIG = {
|
||||
topup: true,
|
||||
personal: true,
|
||||
},
|
||||
other: {
|
||||
enabled: true,
|
||||
image_studio: true,
|
||||
},
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
|
||||
1310
web/src/pages/ImageStudio/index.jsx
Normal file
1310
web/src/pages/ImageStudio/index.jsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ export default function SettingsSensitiveWords(props) {
|
||||
CheckSensitiveEnabled: false,
|
||||
CheckSensitiveOnPromptEnabled: false,
|
||||
SensitiveWords: '',
|
||||
ImageStudioSafetyPrompt: '',
|
||||
});
|
||||
const refForm = useRef();
|
||||
const [inputsRow, setInputsRow] = useState(inputs);
|
||||
@@ -130,8 +131,8 @@ export default function SettingsSensitiveWords(props) {
|
||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||
<Form.TextArea
|
||||
label={t('屏蔽词列表')}
|
||||
extraText={t('一行一个屏蔽词,不需要符号分割')}
|
||||
placeholder={t('一行一个屏蔽词,不需要符号分割')}
|
||||
extraText={t('一行一个屏蔽词,不需要符号分割。该词库也会用于图片生图提示词审核')}
|
||||
placeholder={t('一行一个屏蔽词,不需要符号分割。该词库也会用于图片生图提示词审核')}
|
||||
field={'SensitiveWords'}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
@@ -144,6 +145,24 @@ export default function SettingsSensitiveWords(props) {
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||
<Form.TextArea
|
||||
label={t('图片生图安全提示词')}
|
||||
extraText={t('仅用于图片生成/编辑,请写入暴力、色情、赌博、毒品、中国政治敏感、恐怖、恶心等禁止内容约束')}
|
||||
placeholder={t('仅用于图片生成/编辑,请写入暴力、色情、赌博、毒品、中国政治敏感、恐怖、恶心等禁止内容约束')}
|
||||
field={'ImageStudioSafetyPrompt'}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
ImageStudioSafetyPrompt: value,
|
||||
})
|
||||
}
|
||||
style={{ fontFamily: 'JetBrains Mono, Consolas' }}
|
||||
autosize={{ minRows: 10, maxRows: 20 }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Button size='default' onClick={onSubmit}>
|
||||
{t('保存屏蔽词过滤设置')}
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from '@douyinfe/semi-ui';
|
||||
import { API, showSuccess, showError } from '../../../helpers';
|
||||
import { StatusContext } from '../../../context/Status';
|
||||
import { mergeAdminConfig } from '../../../hooks/common/useSidebar';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
@@ -39,36 +40,9 @@ export default function SettingsSidebarModulesAdmin(props) {
|
||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
||||
|
||||
// 左侧边栏模块管理状态(管理员全局控制)
|
||||
const [sidebarModulesAdmin, setSidebarModulesAdmin] = useState({
|
||||
chat: {
|
||||
enabled: true,
|
||||
playground: true,
|
||||
chat: true,
|
||||
},
|
||||
console: {
|
||||
enabled: true,
|
||||
detail: true,
|
||||
token: true,
|
||||
log: true,
|
||||
midjourney: true,
|
||||
task: true,
|
||||
},
|
||||
personal: {
|
||||
enabled: true,
|
||||
topup: true,
|
||||
personal: true,
|
||||
},
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
models: true,
|
||||
deployment: true,
|
||||
redemption: true,
|
||||
user: true,
|
||||
subscription: true,
|
||||
setting: true,
|
||||
},
|
||||
});
|
||||
const [sidebarModulesAdmin, setSidebarModulesAdmin] = useState(
|
||||
mergeAdminConfig(null),
|
||||
);
|
||||
|
||||
// 处理区域级别开关变更
|
||||
function handleSectionChange(sectionKey) {
|
||||
@@ -100,36 +74,7 @@ export default function SettingsSidebarModulesAdmin(props) {
|
||||
|
||||
// 重置为默认配置
|
||||
function resetSidebarModules() {
|
||||
const defaultModules = {
|
||||
chat: {
|
||||
enabled: true,
|
||||
playground: true,
|
||||
chat: true,
|
||||
},
|
||||
console: {
|
||||
enabled: true,
|
||||
detail: true,
|
||||
token: true,
|
||||
log: true,
|
||||
midjourney: true,
|
||||
task: true,
|
||||
},
|
||||
personal: {
|
||||
enabled: true,
|
||||
topup: true,
|
||||
personal: true,
|
||||
},
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
models: true,
|
||||
deployment: true,
|
||||
redemption: true,
|
||||
user: true,
|
||||
subscription: true,
|
||||
setting: true,
|
||||
},
|
||||
};
|
||||
const defaultModules = mergeAdminConfig(null);
|
||||
setSidebarModulesAdmin(defaultModules);
|
||||
showSuccess(t('已重置为默认配置'));
|
||||
}
|
||||
@@ -174,32 +119,9 @@ export default function SettingsSidebarModulesAdmin(props) {
|
||||
if (props.options && props.options.SidebarModulesAdmin) {
|
||||
try {
|
||||
const modules = JSON.parse(props.options.SidebarModulesAdmin);
|
||||
setSidebarModulesAdmin(modules);
|
||||
setSidebarModulesAdmin(mergeAdminConfig(modules));
|
||||
} catch (error) {
|
||||
// 使用默认配置
|
||||
const defaultModules = {
|
||||
chat: { enabled: true, playground: true, chat: true },
|
||||
console: {
|
||||
enabled: true,
|
||||
detail: true,
|
||||
token: true,
|
||||
log: true,
|
||||
midjourney: true,
|
||||
task: true,
|
||||
},
|
||||
personal: { enabled: true, topup: true, personal: true },
|
||||
admin: {
|
||||
enabled: true,
|
||||
channel: true,
|
||||
models: true,
|
||||
deployment: true,
|
||||
redemption: true,
|
||||
user: true,
|
||||
subscription: true,
|
||||
setting: true,
|
||||
},
|
||||
};
|
||||
setSidebarModulesAdmin(defaultModules);
|
||||
setSidebarModulesAdmin(mergeAdminConfig(null));
|
||||
}
|
||||
}
|
||||
}, [props.options]);
|
||||
@@ -248,6 +170,18 @@ export default function SettingsSidebarModulesAdmin(props) {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'other',
|
||||
title: t('其他功能区域'),
|
||||
description: t('图片与扩展功能'),
|
||||
modules: [
|
||||
{
|
||||
key: 'image_studio',
|
||||
title: t('图片制作'),
|
||||
description: t('生成图片和编辑图片'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'admin',
|
||||
title: t('管理员区域'),
|
||||
|
||||
Reference in New Issue
Block a user