Compare commits

...

5 Commits

Author SHA1 Message Date
Coltea d8c869198e
Merge pull request #1542 from coltea/feat-copyright
Feat 前台问答版权配置 && 支持超级管理员修改普通用户密码
2025-11-20 18:19:44 +08:00
Coltea f04f96d894
Merge pull request #1544 from guanweiwang/feature/pref
feat: 添加智能问答版权信息, 修改超级管理员权限, 优化 banner 移动端样式, 优化 header 移动端样式
2025-11-20 18:19:28 +08:00
Gavan 74e87540e0 feat: 添加智能问答版权信息, 修改超级管理员权限, 优化 banner 移动端样式, 优化 header 移动端样式 2025-11-20 18:15:30 +08:00
coltea 4a011aa1d2 feat conversation setting 2025-11-20 16:12:03 +08:00
coltea acf17e94b2 feat admin reset user password 2025-11-20 15:46:44 +08:00
20 changed files with 375 additions and 76 deletions

View File

@ -4533,6 +4533,9 @@ const docTemplate = `{
"contribute_settings": {
"$ref": "#/definitions/domain.ContributeSettings"
},
"conversation_setting": {
"$ref": "#/definitions/domain.ConversationSetting"
},
"copy_setting": {
"enum": [
"",
@ -4812,6 +4815,9 @@ const docTemplate = `{
"contribute_settings": {
"$ref": "#/definitions/domain.ContributeSettings"
},
"conversation_setting": {
"$ref": "#/definitions/domain.ConversationSetting"
},
"copy_setting": {
"$ref": "#/definitions/consts.CopySetting"
},
@ -5752,6 +5758,17 @@ const docTemplate = `{
}
}
},
"domain.ConversationSetting": {
"type": "object",
"properties": {
"copyright_hide_enabled": {
"type": "boolean"
},
"copyright_info": {
"type": "string"
}
}
},
"domain.CreateKBReleaseReq": {
"type": "object",
"required": [

View File

@ -4526,6 +4526,9 @@
"contribute_settings": {
"$ref": "#/definitions/domain.ContributeSettings"
},
"conversation_setting": {
"$ref": "#/definitions/domain.ConversationSetting"
},
"copy_setting": {
"enum": [
"",
@ -4805,6 +4808,9 @@
"contribute_settings": {
"$ref": "#/definitions/domain.ContributeSettings"
},
"conversation_setting": {
"$ref": "#/definitions/domain.ConversationSetting"
},
"copy_setting": {
"$ref": "#/definitions/consts.CopySetting"
},
@ -5745,6 +5751,17 @@
}
}
},
"domain.ConversationSetting": {
"type": "object",
"properties": {
"copyright_hide_enabled": {
"type": "boolean"
},
"copyright_info": {
"type": "string"
}
}
},
"domain.CreateKBReleaseReq": {
"type": "object",
"required": [

View File

@ -431,6 +431,8 @@ definitions:
description: catalog settings
contribute_settings:
$ref: '#/definitions/domain.ContributeSettings'
conversation_setting:
$ref: '#/definitions/domain.ConversationSetting'
copy_setting:
allOf:
- $ref: '#/definitions/consts.CopySetting'
@ -606,6 +608,8 @@ definitions:
description: catalog settings
contribute_settings:
$ref: '#/definitions/domain.ContributeSettings'
conversation_setting:
$ref: '#/definitions/domain.ConversationSetting'
copy_setting:
$ref: '#/definitions/consts.CopySetting'
desc:
@ -1218,6 +1222,13 @@ definitions:
url:
type: string
type: object
domain.ConversationSetting:
properties:
copyright_hide_enabled:
type: boolean
copyright_info:
type: string
type: object
domain.CreateKBReleaseReq:
properties:
kb_id:

View File

@ -161,11 +161,17 @@ type AppSettings struct {
WebAppLandingConfigs []WebAppLandingConfig `json:"web_app_landing_configs,omitempty"`
WebAppLandingTheme WebAppLandingTheme `json:"web_app_landing_theme"`
WatermarkContent string `json:"watermark_content"`
WatermarkSetting consts.WatermarkSetting `json:"watermark_setting" validate:"omitempty,oneof='' hidden visible"`
CopySetting consts.CopySetting `json:"copy_setting" validate:"omitempty,oneof='' append disabled"`
ContributeSettings ContributeSettings `json:"contribute_settings"`
HomePageSetting consts.HomePageSetting `json:"home_page_setting"`
WatermarkContent string `json:"watermark_content"`
WatermarkSetting consts.WatermarkSetting `json:"watermark_setting" validate:"omitempty,oneof='' hidden visible"`
CopySetting consts.CopySetting `json:"copy_setting" validate:"omitempty,oneof='' append disabled"`
ContributeSettings ContributeSettings `json:"contribute_settings"`
HomePageSetting consts.HomePageSetting `json:"home_page_setting"`
ConversationSetting ConversationSetting `json:"conversation_setting"`
}
type ConversationSetting struct {
CopyrightInfo string `json:"copyright_info"`
CopyrightHideEnabled bool `json:"copyright_hide_enabled"`
}
type WebAppLandingTheme struct {
@ -537,6 +543,7 @@ type AppSettingsResp struct {
WebAppLandingConfigs []WebAppLandingConfigResp `json:"web_app_landing_configs,omitempty"`
WebAppLandingTheme WebAppLandingTheme `json:"web_app_landing_theme"`
HomePageSetting consts.HomePageSetting `json:"home_page_setting"`
ConversationSetting ConversationSetting `json:"conversation_setting"`
}
type WebAppLandingConfigResp struct {

View File

@ -192,12 +192,29 @@ func (h *UserHandler) ResetPassword(c echo.Context) error {
if err != nil {
return h.NewResponseWithError(c, "failed to get user", err)
}
if user.Account == "admin" && authInfo.UserId == req.ID {
return h.NewResponseWithError(c, "请修改安装目录下 .env 文件中的 ADMIN_PASSWORD并重启 panda-wiki-api 容器使更改生效。", nil)
// 非超级管理员没有改密码权限
if user.Role != consts.UserRoleAdmin {
return h.NewResponseWithErrCode(c, domain.ErrCodePermissionDenied)
}
if user.Account != "admin" && authInfo.UserId != req.ID {
return h.NewResponseWithError(c, "只有管理员可以重置其他用户密码", nil)
if user.Account == "admin" {
// admin 改不了自己的密码
if authInfo.UserId == req.ID {
return h.NewResponseWithError(c, "请修改安装目录下 .env 文件中的 ADMIN_PASSWORD并重启 panda-wiki-api 容器使更改生效。", nil)
}
} else {
targetUser, err := h.usecase.GetUser(ctx, req.ID)
if err != nil {
return h.NewResponseWithError(c, "failed to get target user", err)
}
// 超级管理员不能改其他超级管理员密码
if targetUser.Role == consts.UserRoleAdmin && targetUser.ID != authInfo.UserId {
return h.NewResponseWithError(c, "无法修改其他超级管理员密码", nil)
}
}
err = h.usecase.ResetPassword(c.Request().Context(), &req)
if err != nil {
return h.NewResponseWithError(c, "failed to reset password", err)

View File

@ -125,6 +125,9 @@ func (u *AppUsecase) ValidateUpdateApp(ctx context.Context, id string, req *doma
if app.Settings.WidgetBotSettings.CopyrightHideEnabled != req.Settings.WidgetBotSettings.CopyrightHideEnabled || app.Settings.WidgetBotSettings.CopyrightInfo != req.Settings.WidgetBotSettings.CopyrightInfo {
return domain.ErrPermissionDenied
}
if app.Settings.ConversationSetting.CopyrightHideEnabled != req.Settings.ConversationSetting.CopyrightHideEnabled || app.Settings.ConversationSetting.CopyrightInfo != req.Settings.ConversationSetting.CopyrightInfo {
return domain.ErrPermissionDenied
}
}
return nil
@ -513,14 +516,21 @@ func (u *AppUsecase) GetAppDetailByKBIDAndAppType(ctx context.Context, kbID stri
WebAppLandingConfigs: webAppLandingConfigs,
WebAppLandingTheme: app.Settings.WebAppLandingTheme,
WatermarkContent: app.Settings.WatermarkContent,
WatermarkSetting: app.Settings.WatermarkSetting,
CopySetting: app.Settings.CopySetting,
ContributeSettings: app.Settings.ContributeSettings,
HomePageSetting: app.Settings.HomePageSetting,
WatermarkContent: app.Settings.WatermarkContent,
WatermarkSetting: app.Settings.WatermarkSetting,
CopySetting: app.Settings.CopySetting,
ContributeSettings: app.Settings.ContributeSettings,
HomePageSetting: app.Settings.HomePageSetting,
ConversationSetting: app.Settings.ConversationSetting,
WecomAIBotSettings: app.Settings.WecomAIBotSettings,
}
if !domain.GetBaseEditionLimitation(ctx).AllowCustomCopyright {
appDetailResp.Settings.ConversationSetting.CopyrightHideEnabled = false
appDetailResp.Settings.ConversationSetting.CopyrightInfo = domain.SettingCopyrightInfo
}
// init ai feedback string
if app.Settings.AIFeedbackSettings.AIFeedbackType == nil {
appDetailResp.Settings.AIFeedbackSettings.AIFeedbackType = []string{"内容不准确", "没有帮助", "其他"}
@ -613,11 +623,12 @@ func (u *AppUsecase) ShareGetWebAppInfo(ctx context.Context, kbID string, authId
WebAppLandingConfigs: webAppLandingConfigs,
WebAppLandingTheme: app.Settings.WebAppLandingTheme,
WatermarkContent: app.Settings.WatermarkContent,
WatermarkSetting: app.Settings.WatermarkSetting,
CopySetting: app.Settings.CopySetting,
ContributeSettings: app.Settings.ContributeSettings,
HomePageSetting: app.Settings.HomePageSetting,
WatermarkContent: app.Settings.WatermarkContent,
WatermarkSetting: app.Settings.WatermarkSetting,
CopySetting: app.Settings.CopySetting,
ContributeSettings: app.Settings.ContributeSettings,
HomePageSetting: app.Settings.HomePageSetting,
ConversationSetting: app.Settings.ConversationSetting,
},
}
// init ai feedback string
@ -633,6 +644,8 @@ func (u *AppUsecase) ShareGetWebAppInfo(ctx context.Context, kbID string, authId
if !domain.GetBaseEditionLimitation(ctx).AllowCustomCopyright {
appInfo.Settings.WebAppCustomSettings.ShowBrandInfo = &showBrand
appInfo.Settings.DisclaimerSettings.Content = &defaultDisclaimer
appInfo.Settings.ConversationSetting.CopyrightHideEnabled = false
appInfo.Settings.ConversationSetting.CopyrightInfo = domain.SettingCopyrightInfo
} else {
if appInfo.Settings.DisclaimerSettings.Content == nil {
appInfo.Settings.DisclaimerSettings.Content = &defaultDisclaimer

View File

@ -534,6 +534,7 @@ const FooterConfig = ({ data, setIsEdit, isEdit }: FooterConfigProps) => {
</Box>
<VersionMask
permission={PROFESSION_VERSION_PERMISSION}
wrapperSx={{ px: 2 }}
sx={{ inset: '-8px 0' }}
>
<Controller

View File

@ -137,7 +137,7 @@ const Member = () => {
<Button
size='small'
sx={{
color: 'text.tertiary',
color: 'var(--mui-palette-action-disabled)',
cursor: 'not-allowed',
p: 0,
minWidth: 'auto',
@ -151,6 +151,11 @@ const Member = () => {
size='small'
sx={{ p: 0, minWidth: 'auto' }}
color='primary'
disabled={
record.role === 'admin' &&
user.account !== 'admin' &&
user.id !== record.id
}
onClick={() => {
setCurUser(record);
setCurType('reset-password');
@ -159,8 +164,9 @@ const Member = () => {
{user?.id === record.id ? '修改密码' : '重置密码'}
</Button>
)}
{record.account !== 'admin' &&
(user?.id === record.id || user.account === 'admin') && (
{user?.id !== record.id &&
(user.account === 'admin' ||
(user.role === 'admin' && record.role !== 'admin')) && (
<Button
size='small'
color='error'

View File

@ -108,7 +108,6 @@ const System = () => {
aria-label='system tabs'
sx={{
mb: 2,
ml: -2,
borderBottom: 1,
borderColor: 'divider',
'& .MuiTabs-indicator': {
@ -144,12 +143,12 @@ const System = () => {
))}
</Tabs>
{activeTab === 'user-management' && (
<Box sx={{ ml: -2 }}>
<Box>
<Member />
</Box>
)}
{activeTab === 'model-config' && (
<Box sx={{ ml: -2 }}>
<Box>
<ModelConfig
ref={modelConfigRef}
onCloseModal={() => setOpen(false)}

View File

@ -55,10 +55,12 @@ const VersionMask = ({
ConstsLicenseEdition.LicenseEditionEnterprise,
],
children,
wrapperSx,
sx,
}: {
permission?: ConstsLicenseEdition[];
children?: React.ReactNode;
wrapperSx?: SxProps;
sx?: SxProps;
}) => {
const versionInfo = useVersionInfo();
@ -67,7 +69,7 @@ const VersionMask = ({
const nextVersionInfo = VersionInfoMap[permission[0]];
return (
<StyledMaskWrapper>
<StyledMaskWrapper sx={wrapperSx}>
{children}
<StyledMask sx={sx}>
<StyledMaskContent>

View File

@ -0,0 +1,139 @@
import { putApiV1App } from '@/request/App';
import { FormItem, SettingCardItem } from './Common';
import {
DomainAppDetailResp,
DomainConversationSetting,
} from '@/request/types';
import { PROFESSION_VERSION_PERMISSION } from '@/constant/version';
import {
FormControlLabel,
Radio,
RadioGroup,
TextField,
Box,
} from '@mui/material';
import { message } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import VersionMask from '@/components/VersionMask';
import { Controller, useForm } from 'react-hook-form';
import { useAppSelector } from '@/store';
const CardQaCopyright = ({
data,
refresh,
}: {
data: DomainAppDetailResp;
refresh: (value: DomainConversationSetting) => void;
}) => {
const [isEdit, setIsEdit] = useState<boolean>(false);
const { kb_id } = useAppSelector(state => state.config);
const {
control,
handleSubmit,
reset,
watch,
setValue,
formState: { errors },
} = useForm({
defaultValues: {
copyright_hide_enabled: false,
copyright_info: '',
},
});
const copyright_hide_enabled = watch('copyright_hide_enabled');
const onSubmit = handleSubmit(value => {
putApiV1App(
{ id: data.id! },
{ settings: { ...data.settings, conversation_setting: value }, kb_id },
).then(() => {
refresh(value);
message.success('保存成功');
setIsEdit(false);
});
});
useEffect(() => {
setValue(
'copyright_hide_enabled',
data.settings?.conversation_setting?.copyright_hide_enabled ?? false,
);
setValue(
'copyright_info',
data.settings?.conversation_setting?.copyright_info ?? '',
);
}, [data]);
return (
<SettingCardItem
title='智能问答版权信息'
isEdit={isEdit}
onSubmit={onSubmit}
>
<VersionMask permission={PROFESSION_VERSION_PERMISSION}>
<FormItem
label='版权信息'
sx={{ alignItems: 'flex-start' }}
labelSx={{ mt: 1 }}
>
<Controller
control={control}
name='copyright_hide_enabled'
render={({ field }) => {
return (
<RadioGroup
row
{...field}
onChange={e => {
field.onChange(e.target.value === 'true');
setIsEdit(true);
}}
>
<FormControlLabel
value={false}
control={<Radio size='small' />}
label={<Box sx={{ width: 100 }}></Box>}
/>
<FormControlLabel
value={true}
control={<Radio size='small' />}
label={<Box sx={{ width: 100 }}></Box>}
/>
</RadioGroup>
);
}}
/>
</FormItem>
{!copyright_hide_enabled && (
<FormItem
label='版权文字'
sx={{ alignItems: 'flex-start' }}
labelSx={{ mt: 1 }}
>
<Controller
control={control}
name='copyright_info'
render={({ field }) => (
<TextField
fullWidth
{...field}
placeholder='本网站由 PandaWiki 提供技术支持'
error={!!errors.copyright_info}
helperText={errors.copyright_info?.message}
onChange={event => {
setIsEdit(true);
field.onChange(event);
}}
/>
)}
/>
</FormItem>
)}
</VersionMask>
</SettingCardItem>
);
};
export default CardQaCopyright;

View File

@ -14,6 +14,7 @@ import CardProxy from './CardProxy';
import CardStyle from './CardStyle';
import CardWebCustomCode from './CardWebCustomCode';
import CardWebSEO from './CardWebSEO';
import CardQaCopyright from './CardQaCopyright';
interface CardWebProps {
kb: DomainKnowledgeBaseDetail;
@ -76,6 +77,18 @@ const CardWeb = ({ kb, refresh }: CardWebProps) => {
<CardListen kb={kb} refresh={refresh} />
<CardProxy kb={kb} refresh={refresh} />
<CardBasicInfo kb={kb} refresh={refresh} />
<CardQaCopyright
data={info}
refresh={value => {
setInfo({
...info,
settings: {
...info.settings,
conversation_setting: value,
},
});
}}
/>
<CardAuth kb={kb} refresh={refresh} />
<CardCatalog
id={info.id}

View File

@ -304,6 +304,7 @@ export interface DomainAppSettings {
/** catalog settings */
catalog_settings?: DomainCatalogSettings;
contribute_settings?: DomainContributeSettings;
conversation_setting?: DomainConversationSetting;
copy_setting?: "" | "append" | "disabled";
/** seo */
desc?: string;
@ -389,6 +390,7 @@ export interface DomainAppSettingsResp {
/** catalog settings */
catalog_settings?: DomainCatalogSettings;
contribute_settings?: DomainContributeSettings;
conversation_setting?: DomainConversationSetting;
copy_setting?: ConstsCopySetting;
/** seo */
desc?: string;
@ -705,6 +707,11 @@ export interface DomainConversationReference {
url?: string;
}
export interface DomainConversationSetting {
copyright_hide_enabled?: boolean;
copyright_info?: string;
}
export interface DomainCreateKBReleaseReq {
kb_id: string;
message: string;
@ -982,11 +989,11 @@ export interface DomainNodeListItemResp {
editor_id?: string;
emoji?: string;
id?: string;
publisher_id?: string;
name?: string;
parent_id?: string;
permissions?: DomainNodePermissions;
position?: number;
publisher_id?: string;
rag_info?: DomainRagInfo;
status?: DomainNodeStatus;
summary?: string;
@ -1773,23 +1780,23 @@ export interface DeleteApiV1AuthDeleteParams {
export interface GetApiV1AuthGetParams {
kb_id?: string;
source_type:
| "dingtalk"
| "feishu"
| "wecom"
| "oauth"
| "github"
| "cas"
| "ldap"
| "widget"
| "dingtalk_bot"
| "feishu_bot"
| "lark_bot"
| "wechat_bot"
| "wecom_ai_bot"
| "wechat_service_bot"
| "discord_bot"
| "wechat_official_account"
| "openai_api";
| "dingtalk"
| "feishu"
| "wecom"
| "oauth"
| "github"
| "cas"
| "ldap"
| "widget"
| "dingtalk_bot"
| "feishu_bot"
| "lark_bot"
| "wechat_bot"
| "wecom_ai_bot"
| "wechat_service_bot"
| "discord_bot"
| "wechat_official_account"
| "openai_api";
}
export interface GetApiV1CommentParams {

View File

@ -2,6 +2,7 @@ import {
ConstsCopySetting,
ConstsWatermarkSetting,
DomainDisclaimerSettings,
DomainConversationSetting,
DomainWebAppLandingConfig,
} from '@/request/types';
@ -58,6 +59,7 @@ export interface ThemeAndStyleSetting {
export interface KBDetail {
name: string;
settings: {
conversation_setting: DomainConversationSetting;
title: string;
btns: NavBtn[];
icon: string;

View File

@ -247,7 +247,8 @@ const QaModal: React.FC<QaModalProps> = () => {
<Box
sx={{
px: 3,
pt: kbDetail?.settings?.web_app_custom_style?.show_brand_info
pt: !kbDetail?.settings?.conversation_setting
?.copyright_hide_enabled
? 2
: 0,
display: 'flex',
@ -266,8 +267,10 @@ const QaModal: React.FC<QaModalProps> = () => {
}}
>
<Box>
{kbDetail?.settings?.web_app_custom_style?.show_brand_info &&
'本网站由 PandaWiki 提供技术支持'}
{!kbDetail?.settings?.conversation_setting
?.copyright_hide_enabled &&
(kbDetail?.settings?.conversation_setting?.copyright_info ||
'本网站由 PandaWiki 提供技术支持')}
</Box>
</Typography>
</Box>

View File

@ -152,7 +152,11 @@ export const WelcomeHeader = () => {
onSearch={handleSearch}
onQaClick={() => setQaModalOpen?.(true)}
>
<Box sx={{ ml: 2 }}>{!!authInfo && <LogoutButton />}</Box>
{!!authInfo && (
<Box sx={{ ml: 2 }}>
<LogoutButton />
</Box>
)}
<QaModal />
</WelcomeHeaderComponent>
);

View File

@ -304,6 +304,7 @@ export interface DomainAppSettings {
/** catalog settings */
catalog_settings?: DomainCatalogSettings;
contribute_settings?: DomainContributeSettings;
conversation_setting?: DomainConversationSetting;
copy_setting?: "" | "append" | "disabled";
/** seo */
desc?: string;
@ -389,6 +390,7 @@ export interface DomainAppSettingsResp {
/** catalog settings */
catalog_settings?: DomainCatalogSettings;
contribute_settings?: DomainContributeSettings;
conversation_setting?: DomainConversationSetting;
copy_setting?: ConstsCopySetting;
/** seo */
desc?: string;
@ -705,6 +707,11 @@ export interface DomainConversationReference {
url?: string;
}
export interface DomainConversationSetting {
copyright_hide_enabled?: boolean;
copyright_info?: string;
}
export interface DomainCreateKBReleaseReq {
kb_id: string;
message: string;
@ -986,6 +993,7 @@ export interface DomainNodeListItemResp {
parent_id?: string;
permissions?: DomainNodePermissions;
position?: number;
publisher_id?: string;
rag_info?: DomainRagInfo;
status?: DomainNodeStatus;
summary?: string;

View File

@ -23,11 +23,17 @@ const StyledBanner = styled('div')(({ theme }) => ({
}));
const StyledTitle = styled('h1')(({ theme }) => ({
fontSize: 36,
fontSize: 60,
fontWeight: 700,
wordBreak: 'break-all',
color: theme.palette.primary.main,
marginBottom: theme.spacing(3),
[theme.breakpoints.down('md')]: {
fontSize: 50,
},
[theme.breakpoints.down('sm')]: {
fontSize: 40,
},
}));
const StyledSubTitle = styled('h2')(({ theme }) => ({
@ -328,16 +334,15 @@ const Banner = React.memo(
: {}),
}}
>
<StyledTopicBox sx={{ alignItems: 'flex-start', gap: 0, py: '200px' }}>
<StyledTitle
ref={titleRef}
sx={{
fontSize: `${title.fontSize || 60}px`,
// color: title.color || '#5F58FE',
}}
>
{title.text}
</StyledTitle>
<StyledTopicBox
sx={{
alignItems: 'flex-start',
gap: 0,
py: { xs: 8, md: '200px' },
pt: { xs: 16 },
}}
>
<StyledTitle ref={titleRef}>{title.text}</StyledTitle>
{/* {subtitle.text && ( */}
<StyledSubTitle
ref={subtitleRef}

View File

@ -130,7 +130,7 @@ const Header = React.memo(
// }),
}}
>
<Link href={'/'}>
<Link href={'/'} sx={{ flex: 1, minWidth: 0 }}>
<Stack
direction='row'
alignItems='center'
@ -143,7 +143,16 @@ const Header = React.memo(
}}
>
<img src={logo} alt='logo' width={36} />
<Box sx={{ fontSize: 20 }}>{title}</Box>
<Box
sx={{
fontSize: 20,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
}}
>
{title}
</Box>
</Stack>
</Link>
{showSearch &&
@ -153,7 +162,6 @@ const Header = React.memo(
direction='row'
alignItems='center'
justifyContent='flex-end'
sx={{ flex: 1 }}
>
<IconButton
size='small'
@ -171,11 +179,9 @@ const Header = React.memo(
focused={false}
onClick={() => onQaClick?.()}
sx={{
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
flex: 1,
maxWidth: '500px',
minWidth: '220px',
bgcolor: 'background.paper3',
borderRadius: '10px',
overflow: 'hidden',
@ -273,7 +279,13 @@ const Header = React.memo(
))}
{!mobile && btns && btns.length > 0 && (
<Stack direction='row' gap={2} alignItems='center'>
<Stack
direction='row'
gap={2}
alignItems='center'
justifyContent='flex-end'
sx={{ flex: 1 }}
>
{btns.slice(0, Math.min(2, btns.length)).map((item, index) => (
<Link key={index} href={item.url} target={item.target}>
<Button
@ -290,6 +302,7 @@ const Header = React.memo(
}
sx={theme => ({
px: 3.5,
whiteSpace: 'nowrap',
textTransform: 'none',
boxSizing: 'border-box',
height: 40,

View File

@ -155,7 +155,7 @@ const Header = React.memo(
// }),
}}
>
<Link href={'/'}>
<Link href={'/'} sx={{ flex: 1, minWidth: 0 }}>
<Stack
direction='row'
alignItems='center'
@ -167,7 +167,16 @@ const Header = React.memo(
}}
>
<img src={logo} alt='logo' height={36} />
<Box sx={{ fontSize: 20 }}>{title}</Box>
<Box
sx={{
fontSize: 20,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
}}
>
{title}
</Box>
</Stack>
</Link>
{showSearch &&
@ -177,7 +186,6 @@ const Header = React.memo(
direction='row'
alignItems='center'
justifyContent='flex-end'
sx={{ flex: 1 }}
>
<IconButton
size='small'
@ -195,11 +203,9 @@ const Header = React.memo(
focused={false}
onClick={() => onQaClick?.()}
sx={theme => ({
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
flex: 1,
maxWidth: '500px',
minWidth: '220px',
borderRadius: '10px',
overflow: 'hidden',
cursor: 'pointer',
@ -237,7 +243,7 @@ const Header = React.memo(
direction='row'
alignItems='center'
gap={1.5}
sx={{ flexShrink: 0 }}
sx={{ flexShrink: 0, ml: 1 }}
>
<Box
sx={{
@ -258,13 +264,22 @@ const Header = React.memo(
))}
{!mobile && btns && btns.length > 0 && (
<Stack direction='row' gap={2} alignItems='center'>
<Stack
direction='row'
gap={2}
alignItems='center'
justifyContent='flex-end'
sx={{
flex: 1,
}}
>
{btns.slice(0, Math.min(2, btns.length)).map((item, index) => (
<Link key={index} href={item.url} target={item.target}>
<Button
variant={item.variant}
sx={theme => ({
px: 3.5,
whiteSpace: 'nowrap',
textTransform: 'none',
boxSizing: 'border-box',
height: 40,