fix: 优化NapCat WebUI页面全屏布局

This commit is contained in:
sunlei 2026-06-24 20:16:33 +08:00
parent 8e8358a6a1
commit 9c0720f183
3 changed files with 129 additions and 106 deletions

View File

@ -1,72 +1,26 @@
.qqbot-napcat-webui { .qqbot-napcat-webui-page {
display: flex; width: 100%;
flex-direction: column;
gap: 12px;
height: var(--vben-content-height, 100%); height: var(--vben-content-height, 100%);
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
color: hsl(var(--foreground)); color: hsl(var(--foreground));
background: hsl(var(--background)); background: hsl(var(--background));
}
&__header { .qqbot-napcat-webui {
display: flex; position: relative;
flex: 0 0 auto; width: 100%;
flex-wrap: wrap; height: 100%;
gap: 12px; min-height: 0;
align-items: center; overflow: hidden;
justify-content: space-between; color: inherit;
min-height: 44px; background: inherit;
padding-bottom: 12px;
border-bottom: 1px solid hsl(var(--border));
}
&__identity {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
min-width: 0;
}
&__back {
display: inline-flex;
gap: 6px;
align-items: center;
padding-inline: 0;
}
&__back-icon {
width: 16px;
height: 16px;
}
&__title {
display: inline-flex;
gap: 8px;
align-items: center;
min-width: 0;
font-size: 16px;
font-weight: 600;
}
&__actions {
flex: 0 0 auto;
}
&__meta {
display: flex;
flex: 0 0 auto;
flex-wrap: wrap;
gap: 12px;
min-width: 0;
font-size: 12px;
color: hsl(var(--muted-foreground));
}
&__content { &__content {
position: absolute;
inset: 0;
display: flex; display: flex;
flex: 1 1 0; min-width: 0;
flex-direction: column;
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
} }
@ -80,6 +34,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-height: 0; min-height: 0;
padding: 24px;
} }
&__message { &__message {
@ -90,11 +45,10 @@
&__iframe-shell { &__iframe-shell {
flex: 1 1 0; flex: 1 1 0;
min-width: 0;
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
background: hsl(var(--background)); background: hsl(var(--background));
border: 1px solid hsl(var(--border));
border-radius: 8px;
} }
&__iframe { &__iframe {
@ -104,4 +58,64 @@
background: hsl(var(--background)); background: hsl(var(--background));
border: 0; border: 0;
} }
&__floating-card {
position: absolute;
right: 16px;
bottom: 16px;
z-index: 2;
display: flex;
flex-direction: column;
gap: 8px;
max-width: min(360px, calc(100% - 32px));
padding: 10px 12px;
color: hsl(var(--foreground));
pointer-events: auto;
background: hsl(var(--background) / 92%);
border: 1px solid hsl(var(--border));
border-radius: 8px;
box-shadow: 0 10px 30px hsl(var(--foreground) / 12%);
backdrop-filter: blur(10px);
}
&__floating-head {
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
min-width: 0;
}
&__floating-title {
min-width: 0;
overflow: hidden;
font-size: 13px;
font-weight: 600;
line-height: 18px;
text-overflow: ellipsis;
white-space: nowrap;
}
&__floating-meta {
display: flex;
flex-wrap: wrap;
gap: 6px 10px;
min-width: 0;
font-size: 12px;
line-height: 16px;
color: hsl(var(--muted-foreground));
}
&__floating-actions {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
&__back-icon {
width: 14px;
height: 14px;
margin-right: 4px;
vertical-align: -2px;
}
} }

View File

@ -3,7 +3,6 @@ import type { NapcatWebuiGatewaySessionState } from './useNapcatWebuiGatewaySess
import { computed, defineComponent, watch } from 'vue'; import { computed, defineComponent, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { ArrowLeft } from '@vben/icons'; import { ArrowLeft } from '@vben/icons';
import { Alert, Button, Space, Spin, Tag } from 'antdv-next'; import { Alert, Button, Space, Spin, Tag } from 'antdv-next';
@ -70,6 +69,51 @@ export default defineComponent({
void session.revoke(); void session.revoke();
} }
/**
* Renders the floating metadata panel without taking layout space from the iframe.
*
* @returns Overlay card content for the current session metadata and actions.
*/
const renderFloatingCard = () => {
return (
<div class="qqbot-napcat-webui__floating-card">
<div class="qqbot-napcat-webui__floating-head">
<span class="qqbot-napcat-webui__floating-title">
{accountTitle.value}
</span>
<ATag color={statusMeta.value.color}>{statusMeta.value.label}</ATag>
</div>
<div class="qqbot-napcat-webui__floating-meta">
<span>NapCat WebUI</span>
{expiresAtText.value ? (
<span>{expiresAtText.value}</span>
) : null}
</div>
<ASpace class="qqbot-napcat-webui__floating-actions" size={6}>
<AButton onClick={goBack} size="small" type="text">
<ArrowLeft class="qqbot-napcat-webui__back-icon" />
</AButton>
<AButton
disabled={session.state.value === 'loading'}
onClick={reopen}
size="small"
>
</AButton>
<AButton
danger
disabled={session.state.value === 'loading'}
onClick={closeSession}
size="small"
>
</AButton>
</ASpace>
</div>
);
};
/** /**
* Renders the main state area for loading, error, revoked, and ready states. * Renders the main state area for loading, error, revoked, and ready states.
* *
@ -124,52 +168,12 @@ export default defineComponent({
*/ */
const renderPage = () => { const renderPage = () => {
return ( return (
<Page autoContentHeight> <div class="qqbot-napcat-webui-page">
<div class="qqbot-napcat-webui"> <div class="qqbot-napcat-webui">
<div class="qqbot-napcat-webui__header">
<div class="qqbot-napcat-webui__identity">
<AButton
class="qqbot-napcat-webui__back"
onClick={goBack}
type="text"
>
<ArrowLeft class="qqbot-napcat-webui__back-icon" />
</AButton>
<div class="qqbot-napcat-webui__title">
<span>{accountTitle.value}</span>
<ATag color={statusMeta.value.color}>
{statusMeta.value.label}
</ATag>
</div>
</div>
<ASpace class="qqbot-napcat-webui__actions">
<AButton
disabled={session.state.value === 'loading'}
onClick={reopen}
>
</AButton>
<AButton
danger
disabled={session.state.value === 'loading'}
onClick={closeSession}
>
</AButton>
</ASpace>
</div>
<div class="qqbot-napcat-webui__meta">
<span>NapCat WebUI</span>
{expiresAtText.value ? (
<span>{expiresAtText.value}</span>
) : null}
</div>
<div class="qqbot-napcat-webui__content">{renderBody()}</div> <div class="qqbot-napcat-webui__content">{renderBody()}</div>
{renderFloatingCard()}
</div> </div>
</Page> </div>
); );
}; };

View File

@ -190,6 +190,11 @@ describe('qqbot account NapCat WebUI page', () => {
expect(wrapper.find('iframe').attributes('src')).toBe( expect(wrapper.find('iframe').attributes('src')).toBe(
'/qqbot/napcat/webui/session/session-1/', '/qqbot/napcat/webui/session/session-1/',
); );
expect(wrapper.find('.qqbot-napcat-webui__header').exists()).toBe(false);
expect(wrapper.find('.qqbot-napcat-webui__meta').exists()).toBe(false);
expect(wrapper.find('.qqbot-napcat-webui__floating-card').exists()).toBe(
true,
);
expect(wrapper.text()).toContain('主账号10001'); expect(wrapper.text()).toContain('主账号10001');
expect(wrapper.text()).toContain('NapCat WebUI'); expect(wrapper.text()).toContain('NapCat WebUI');
expect(wrapper.text()).not.toContain('kt-qqbot-napcat'); expect(wrapper.text()).not.toContain('kt-qqbot-napcat');