fix(platform): make page heights license-banner-aware; fix form file-type image var backfill

F037 license banner shifts all admin content down by its height, but page
scroll containers hardcode viewport-relative heights (calc(100vh - Npx)) that
ignore it, so their bottoms fell off-screen and could not be scrolled to when
the banner was shown.

- LicenseBanner publishes its rendered height as the CSS var --license-banner-h
  on :root (0px when hidden, so all subtractions are no-ops without the banner)
- sweep every calc(100vh - Npx) page/scroll height to subtract the var
- MainLayout sidebar nav max-height subtracts the var too
- SystemPage 组织同步/角色管理: root lacked a fill+scroll container (or used a
  viewport calc miscalibrated for the nested TabsList) -> switch to h-full +
  internal overflow-y-auto so they fill their flex parent, banner-agnostic
- InputFormItem: in edit mode, switching the upload file type to an image-capable
  type no longer backfilled the image variable name (the new-item auto-fill effect
  is skipped when editing); backfill image_file / file_path on file-type change
This commit is contained in:
dolphin
2026-07-09 19:01:31 +08:00
parent 4b52b32b03
commit fce368f177
42 changed files with 108 additions and 51 deletions
@@ -1,6 +1,6 @@
import { getLicenseStatus, LicenseStatus } from "@/controllers/API/license";
import { AlertTriangle } from "lucide-react";
import { useEffect, useState } from "react";
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
// Severity → styling. Only warning/critical/expired render a banner; normal/unknown render nothing.
@@ -13,6 +13,10 @@ const SEVERITY_STYLE: Record<string, string> = {
const VISIBLE_SEVERITIES = ["warning", "critical", "expired"];
// TODO(debug): TEMPORARY — force the banner visible so the license is not required
// to be expired while debugging the page-height offset. Revert to `false` before shipping.
const FORCE_DEBUG_VISIBLE = false;
/**
* Persistent top banner that surfaces the gateway license expiry state.
*
@@ -20,10 +24,15 @@ const VISIBLE_SEVERITIES = ["warning", "critical", "expired"];
* unless the severity is warning/critical/expired (so normal/unknown/unavailable stay silent).
* Status comes from the gateway via getLicenseStatus(); open-source deployments have no gateway,
* so it resolves to null and the banner stays hidden.
*
* When visible it publishes its rendered height as the CSS custom property `--license-banner-h`
* on :root, so viewport-based page heights (`calc(100vh - Npx)`) can subtract it. The property
* defaults to `0px` whenever the banner is hidden, making that subtraction a no-op.
*/
export function LicenseBanner() {
const { t } = useTranslation();
const [status, setStatus] = useState<LicenseStatus | null>(null);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
let active = true;
@@ -35,16 +44,32 @@ export function LicenseBanner() {
};
}, []);
if (!status || !VISIBLE_SEVERITIES.includes(status.severity)) return null;
const realVisible = Boolean(status && VISIBLE_SEVERITIES.includes(status.severity));
const visible = FORCE_DEBUG_VISIBLE || realVisible;
const { severity, days_remaining } = status;
const message =
severity === "expired"
// Publish / clear the banner height as a global CSS var for page-height calcs.
useLayoutEffect(() => {
const root = document.documentElement;
if (visible && ref.current) {
root.style.setProperty("--license-banner-h", `${ref.current.offsetHeight}px`);
} else {
root.style.setProperty("--license-banner-h", "0px");
}
return () => root.style.setProperty("--license-banner-h", "0px");
}, [visible, status]);
if (!visible) return null;
const severity = realVisible ? status!.severity : "expired";
const message = realVisible
? severity === "expired"
? t("license.expired")
: t(`license.${severity}`, { days: days_remaining ?? 0 });
: t(`license.${severity}`, { days: status!.days_remaining ?? 0 })
: t("license.expired"); // debug placeholder when the real license is not expired
return (
<div
ref={ref}
role="alert"
className={`flex shrink-0 items-center justify-center gap-2 border-b px-4 py-2 text-sm font-medium ${SEVERITY_STYLE[severity]}`}
>
@@ -178,7 +178,7 @@ export default function MainLayout() {
</div>
<div className="flex flex-1 min-h-0">
<div className="relative z-10 bg-background-main h-full w-[184px] min-w-[184px] px-3 shadow-x1 flex justify-between text-center ">
<nav className="overflow-y-auto overflow-x-hidden" style={{ maxHeight: "calc(100vh - 64px - 90px)" }}>
<nav className="overflow-y-auto overflow-x-hidden" style={{ maxHeight: "calc(100vh - 64px - 90px - var(--license-banner-h, 0px))" }}>
{/* <NavLink to='/' className={`navlink inline-flex rounded-lg w-full px-6 hover:bg-nav-hover h-12 mb-[3.5px]`}>
<ApplicationIcon className="h-6 w-6 my-[12px]" /><span className="mx-[14px] max-w-[48px] text-[14px] leading-[48px]">{t('menu.app')}</span>
</NavLink> */}
@@ -161,11 +161,11 @@ export default function editAssistant() {
return <div className="bg-background-main">
<Header loca={loca} canEdit={canEdit} onSave={() => handleSave(true)} onLine={handleOnline} onTabChange={(t) => setShowApiPage(t === 'api')}></Header>
<div className="h-[calc(100vh-70px)]">
<div className="h-[calc(100vh-70px-var(--license-banner-h,0px))]">
<div className={`flex h-full ${showApiPage ? 'hidden' : ''}`}>
<div className="w-[60%]">
<div className="text-md font-medium leading-none p-4 shadow-sm">{t('build.assistantConfiguration')}</div>
<div className="flex h-[calc(100vh-120px)]">
<div className="flex h-[calc(100vh-120px-var(--license-banner-h,0px))]">
<Prompt canEdit={canEdit}></Prompt>
<Setting></Setting>
</div>
@@ -65,7 +65,7 @@ export function AppCenter({ scopeVersion = 0 }: { scopeVersion?: number }) {
<div className="h-full overflow-y-scroll scrollbar-hide relative border-t">
<div className="pt-4 relative">
<CardContent className="pt-4 relative">
<div className="w-full max-h-[calc(100vh-180px)] overflow-y-scroll scrollbar-hide pb-10">
<div className="w-full max-h-[calc(100vh-180px-var(--license-banner-h,0px))] overflow-y-scroll scrollbar-hide pb-10">
<ConfigInheritanceBanner meta={configMeta} />
<FormInput
label={t('chatConfig.appCenterWelcome')}
@@ -91,7 +91,7 @@ export default function KnowledgeSpace({ scopeVersion = 0 }: { scopeVersion?: nu
<div className="h-full overflow-y-scroll scrollbar-hide relative border-t">
<div className="pt-4 relative">
<CardContent className="p-0 pt-4 relative">
<div className="w-full max-h-[calc(100vh-180px)] overflow-y-scroll scrollbar-hide">
<div className="w-full max-h-[calc(100vh-180px-var(--license-banner-h,0px))] overflow-y-scroll scrollbar-hide">
<ConfigInheritanceBanner meta={configMeta} />
<div className="mb-6">
<div className="p-5 bg-gray-50 rounded-lg">
@@ -103,7 +103,7 @@ export default function Subscribe({ scopeVersion = 0 }: { scopeVersion?: number
<div className=" h-full overflow-y-scroll scrollbar-hide relative border-t">
<div className="pt-4 relative">
<CardContent className="pt-4 relative">
<div className="w-full max-h-[calc(100vh-180px)] overflow-y-scroll scrollbar-hide">
<div className="w-full max-h-[calc(100vh-180px-var(--license-banner-h,0px))] overflow-y-scroll scrollbar-hide">
<ConfigInheritanceBanner meta={configMeta} />
<div className="mb-6">
<div className="flex items-center mb-2">
@@ -172,7 +172,7 @@ export default function index({ scopeVersion = 0 }: { scopeVersion?: number }) {
<div className="daily-page h-full overflow-y-scroll scrollbar-hide relative border-t">
<div className="pt-4 relative">
<CardContent className="pt-4 relative ">
<div className="w-full max-h-[calc(100vh-180px)] overflow-y-scroll scrollbar-hide pb-10">
<div className="w-full max-h-[calc(100vh-180px-var(--license-banner-h,0px))] overflow-y-scroll scrollbar-hide pb-10">
<ConfigInheritanceBanner meta={configMeta} />
{/* <ToggleSection
title={t('chatConfig.workstationEntry')}
@@ -98,7 +98,7 @@ export const ChatTest = forwardRef((props, ref) => {
><X /></Button>
</div>
</div>
<div className={`h-[calc(100vh-28px)] relative overflow-y-auto ${small ? 'hidden' : ''}`} onKeyDown={(e) => e.stopPropagation()}>
<div className={`h-[calc(100vh-28px-var(--license-banner-h,0px))] relative overflow-y-auto ${small ? 'hidden' : ''}`} onKeyDown={(e) => e.stopPropagation()}>
<ChatPane autoRun chatId={chatId} flow={flow} wsUrl={`${host}${__APP_ENV__.BASE_URL}/api/v1/workflow/chat/${flow?.id}`} />
</div>
{!small && <div
@@ -241,7 +241,7 @@ export const RunTest = forwardRef((props, ref) => {
{t('singleNodeRun')}
</SheetTitle>
</SheetHeader>
<div className="px-2 pt-2 pb-10 h-[calc(100vh-40px)] overflow-y-auto bg-[#fff] dark:bg-[#303134]">
<div className="px-2 pt-2 pb-10 h-[calc(100vh-40px-var(--license-banner-h,0px))] overflow-y-auto bg-[#fff] dark:bg-[#303134]">
{inputs.map((input) => (
input.autoFill ? null : <div className="mb-2" key={input.key}>
<Label className="flex items-center bisheng-label mb-2">
@@ -376,9 +376,41 @@ function Form({ nodeId, nodeData, initialData, onSubmit, onCancel, existingOptio
// 处理文件类型变化
const handleFileTypeChange = (fileType) => {
setFormData({ ...formData, fileType });
// 清空相关错误
setErrors({});
setFormData(prev => {
const updates: any = { fileType };
const fileOptions = existingOptions?.filter(opt => opt.type === FormType.File) || [];
const isImageCapable = fileType === 'all' || fileType === 'image';
// Image variable only exists for image-capable types. Backfill a unique name
// when switching into such a type with no value yet — e.g. editing an item
// that was saved as document-only, where image_file was cleared on submit and
// the new-item auto-fill effect is skipped in edit mode (initialData present).
if (isImageCapable && (!prev.imageFile || prev.imageFile.trim() === '')) {
let name = 'image_file';
let counter = 1;
while (fileOptions.some(opt => opt.image_file === name)) {
counter += 1;
name = `image_file${counter}`;
}
updates.imageFile = name;
}
// File path is always exposed now; backfill if somehow empty (legacy items).
if (!prev.filepath || prev.filepath.trim() === '') {
let name = 'file_path';
let counter = 1;
while (fileOptions.some(opt => opt.file_path === name)) {
counter += 1;
name = `file_path${counter}`;
}
updates.filepath = name;
}
return { ...prev, ...updates };
});
}
// 处理文件处理策略变化(单选 3 项)
@@ -346,7 +346,7 @@ const McpServerEditorDialog = forwardRef(({ existingNames = [], onReload }, ref)
<SheetTitle>{isEditMode ? t('edit') : t('add')} {t('mcpServer')}</SheetTitle>
</SheetHeader>
<div className="mt-4 space-y-6 px-6 overflow-y-auto h-[calc(100vh-200px)]">
<div className="mt-4 space-y-6 px-6 overflow-y-auto h-[calc(100vh-200px-var(--license-banner-h,0px))]">
{/* Name input */}
<div>
<label className="">{t('tools.name')}</label>
@@ -193,7 +193,7 @@ export default function HomePage({ onSelect }) {
</div>
</div>
<div className="relative overflow-y-auto h-[calc(100vh-308px)]">
<div className="relative overflow-y-auto h-[calc(100vh-308px-var(--license-banner-h,0px))]">
<div className="flex flex-wrap gap-2 px-12 scrollbar-hide pt-4 pb-20">
{renderChatOptions()}
{hasMoreData && <LoadMore onScrollLoad={handleLoadMore} />}
@@ -217,7 +217,7 @@ export function DashboardSidebar({
/>
</div>
<div className="overflow-y-auto space-y-2 h-[calc(100vh-174px)]">
<div className="overflow-y-auto space-y-2 h-[calc(100vh-174px-var(--license-banner-h,0px))]">
{filteredDashboards.length === 0 ? (
<div className="text-center text-muted-foreground text-sm py-8">
{searchQuery ? t('noMatchingDashboards') : t('noDashboards')}
@@ -46,7 +46,7 @@ export default function EditorPage() {
dashboard={currentDashboard}
dashboardId={dashboardId}
/>
<div className="h-[calc(100vh-64px)]">
<div className="h-[calc(100vh-64px-var(--license-banner-h,0px))]">
<EditorCanvas isLoading={isLoading} />
</div>
</div>
@@ -51,7 +51,7 @@ export default function index() {
<LoadingIcon />
</div>
)}
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-10">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-10">
<div className="flex justify-end gap-4 items-center">
<SearchInput placeholder={t('dataset.name')} onChange={(e) => search(e.target.value)} />
<Button className="px-8 text-[#FFFFFF]" onClick={() => modelRef.current.open()}>{t('dataset.create')}</Button>
@@ -513,7 +513,7 @@ export default function KnowledgeFile() {
{loading && <div className="absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-[rgba(255,255,255,0.6)] dark:bg-blur-shared">
<LoadingIcon />
</div>}
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-20">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-20">
<div className="flex justify-end gap-4 items-center absolute right-0 top-[-44px]">
<SearchInput placeholder={t('lib.searchPlaceholder', { ns: 'bs' })} onChange={(e) => search(e.target.value)} />
{canCreateLibrary && <Button className="px-8 text-[#FFFFFF]" onClick={() => setOpen(true)}>{t('create', { ns: 'bs' })}</Button>}
@@ -399,7 +399,7 @@ export default function KnowledgeQa(params) {
<LoadingIcon />
</div>
)}
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-20">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-20">
<div className="flex justify-end gap-4 items-center absolute right-0 top-[-44px]">
<SearchInput placeholder={t('lib.searchPlaceholder', { ns: 'bs' })} onChange={(e) => search(e.target.value)} />
{canCreateLibrary && <Button className="px-8 text-[#FFFFFF]" onClick={() => setOpen(true)}>{t('create', { ns: 'bs' })}</Button>}
@@ -203,7 +203,7 @@ export default function FileUploadStep4({ data, kId, hasRepeat }) {
<div className="flex-1">
<h1 className="text-3xl text-primary mt-2">{finish ? t('documentDataParsingCompleted') : t('documentDataBeingPrepared')}</h1>
<p className="text-base text-gray-500 mt-2">{t('youCanReturn')}</p>
<div className="overflow-y-auto mt-4 space-y-2 pb-10 max-h-[calc(100vh-400px)]">
<div className="overflow-y-auto mt-4 space-y-2 pb-10 max-h-[calc(100vh-400px-var(--license-banner-h,0px))]">
{files.map(item => <ProgressItem analysis key={item.id} item={item} />)}
</div>
<div className="flex justify-end gap-4">
@@ -536,7 +536,7 @@ export default function Files({ onPreview, canEditKb = false, canDeleteKb = fals
</div>
</div>
<div className="h-[calc(100vh-180px)] overflow-y-auto pb-20">
<div className="h-[calc(100vh-180px-var(--license-banner-h,0px))] overflow-y-auto pb-20">
<Table>
<TableHeader>
<TableRow>
@@ -138,7 +138,7 @@ export default forwardRef(function Markdown({ edit, isUns, title, q, value }, re
<Switch checked={!isAce} onCheckedChange={hangleCheckChagne} />
</div>}
</div>
<div className="border mb-2 h-[calc(100vh-104px)]">
<div className="border mb-2 h-[calc(100vh-104px-var(--license-banner-h,0px))]">
{/* 编辑器 */}
<AceEditorCom hidden={!isAce} markdown={val} onChange={setValue} />
<VditorEditor ref={vditorRef} edit={edit} hidden={isAce} markdown={val} />
@@ -298,7 +298,7 @@ const ParagraphEdit = ({
{/* file view */}
<div className="bg-gray-100 relative">
{showPos && value && Object.keys(labels).length !== 0 && <Button className="absolute top-2 right-2 z-10 bg-background" variant="outline" onClick={() => setRandom(Math.random() / 10000)}><Crosshair className="mr-1" size={16} />{t('backToPosition')}</Button>}
<div className="h-[calc(100vh-104px)] overflow-auto"
<div className="h-[calc(100vh-104px-var(--license-banner-h,0px))] overflow-auto"
style={{
width: 'calc(100vh - 104px)',
minWidth: '100%',
@@ -811,7 +811,7 @@ export default function Paragraphs({ fileId, onBack, canEditKb = false, canDelet
}, [canEditKb, mainMetadataList, selectedFileId, t]);
return (
<div className="relative flex flex-col h-[calc(100vh-64px)]">
<div className="relative flex flex-col h-[calc(100vh-64px-var(--license-banner-h,0px))]">
{load && <div className="absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-[rgba(255,255,255,1)] dark:bg-blur-shared">
<LoadingIcon />
</div>}
@@ -900,7 +900,7 @@ export default function Paragraphs({ fileId, onBack, canEditKb = false, canDelet
edit={canEditKb}
canDelete={canDeleteKb}
page={page}
className="h-[calc(100vh-206px)] pb-6"
className="h-[calc(100vh-206px-var(--license-banner-h,0px))] pb-6"
fileSuffix={currentFile?.suffix || ''}
loading={loading}
chunks={chunks}
@@ -361,7 +361,7 @@ export default function PreviewFile({
<span className="text-primary cursor-pointer" onClick={handleOvergap}>{t('overwriteSegment')}</span>
</div>
</div>
<div className={`relative ${previewScrollClass} ${edit ? 'h-[calc(100vh-206px)]' : 'h-[calc(100vh-284px)]'}`}>
<div className={`relative ${previewScrollClass} ${edit ? 'h-[calc(100vh-206px-var(--license-banner-h,0px))]' : 'h-[calc(100vh-284px-var(--license-banner-h,0px))]'}`}>
{render(file.suffix)}
</div>
</div>
@@ -260,7 +260,7 @@ export default function PreviewResult({
fileId={syncChunksSelectId}
fileSuffix={currentFile?.suffix}
previewCount={previewCount}
className="h-[calc(100vh-284px)]"
className="h-[calc(100vh-284px-var(--license-banner-h,0px))]"
edit={step === 3 || (step === 2 && !showPreview)}
loading={loading}
chunks={chunks}
@@ -529,7 +529,7 @@ export default function QasPage() {
</div>
</div>
</div>
<div className="overflow-y-auto h-[calc(100vh-132px)] pb-20">
<div className="overflow-y-auto h-[calc(100vh-132px-var(--license-banner-h,0px))] pb-20">
<Table>
<TableHeader>
<TableRow>
@@ -159,7 +159,7 @@ export default function index() {
</RadioGroup>
<PageChange />
</div>
<div className="h-[calc(100vh-132px)]">
<div className="h-[calc(100vh-132px-var(--license-banner-h,0px))]">
{type === AppNumType.FLOW
? <ChatMessages mark={mark} logo='' useName='' guideWord='' loadMore={() => loadMoreFlowHistoryMsg(fid, true)} onMarkClick={handleMarkClick}></ChatMessages>
: <MessagePanne mark={mark} logo='' useName='' guideWord=''
@@ -115,7 +115,7 @@ export default function SystemLog() {
<LoadingIcon />
</div>
)}
<div className="h-[calc(100vh-128px)] overflow-y-auto px-2 py-4 pb-10">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto px-2 py-4 pb-10">
<div className="flex flex-wrap gap-4">
<div className="w-[200px] relative">
<MultiSelect contentClassName="overflow-y-auto max-w-[200px]" multiple
@@ -68,7 +68,7 @@ export default function AppChatDetail() {
<span className=" text-gray-700 text-sm font-black pl-4">{title}</span>
</div>
</div>
<div className="h-[calc(100vh-132px)]">
<div className="h-[calc(100vh-132px-var(--license-banner-h,0px))]">
{type === AppNumType.FLOW
? <ChatMessages logo={logo} debug useName={''} guideWord={''} loadMore={() => loadMoreFlowHistoryMsg(fid, true)} onMarkClick={null}></ChatMessages>
: <MessagePanne logo={logo} debug useName='' guideWord=''
@@ -122,7 +122,7 @@ export default function DailyChatDetail() {
</div>
{/* messages */}
<div className="h-[calc(100vh-132px)] overflow-y-auto">
<div className="h-[calc(100vh-132px-var(--license-banner-h,0px))] overflow-y-auto">
<div className="max-w-4xl mx-auto px-4 py-8">
{messages.map((msg) => (
<div key={msg.messageId} className="mb-8 flex items-start gap-4">
@@ -267,7 +267,7 @@ export default function AppUseLog() {
{loading && <div className="absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-[rgba(255,255,255,0.6)] dark:bg-blur-shared">
<LoadingIcon />
</div>}
<div className="h-[calc(100vh-128px)] overflow-y-auto px-2 py-4 pb-20">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto px-2 py-4 pb-20">
<div className="flex flex-wrap gap-4">
<FilterByApp value={filters.appName} placeholder={t('log.appName')} onChange={(value) => dispatch({ type: 'SET_FILTER', payload: { ['appName']: value } })} />
<FilterByUser value={filters.userName} placeholder={t('log.userName')} onChange={(value) => dispatch({ type: 'SET_FILTER', payload: { ['userName']: value } })} />
@@ -77,7 +77,7 @@ export default function CreateTask({ rtClick, gpuClick, onCancel, onCreate }) {
onCreate(res.id)
}
return <div className="pt-2 h-[calc(100vh-162px)] px-2 overflow-y-auto">
return <div className="pt-2 h-[calc(100vh-162px-var(--license-banner-h,0px))] px-2 overflow-y-auto">
<div className="border-b pb-2 flex justify-between items-center">
<h1 className="">{t('finetune.createTrainingTask')}</h1>
{/* <Button variant="black" onClick={rtClick}>FT服务管理</Button> */}
@@ -67,7 +67,7 @@ export const Finetune = () => {
<div className="mt-6 text-center text-gray-400">{t('finetune.noData')}</div>
: <div className="flex gap-4 mt-4">
<div className="w-[40%] relative">
<div className="border-r overflow-y-auto max-h-[calc(100vh-150px)] pb-20">
<div className="border-r overflow-y-auto max-h-[calc(100vh-150px-var(--license-banner-h,0px))] pb-20">
<Table className="px-2">
<TableHeader>
<TableRow>
@@ -99,7 +99,7 @@ export const Finetune = () => {
/>
</div>
</div>
<div className="flex-1 overflow-hidden overflow-y-auto max-h-[calc(100vh-150px)]">
<div className="flex-1 overflow-hidden overflow-y-auto max-h-[calc(100vh-150px-var(--license-banner-h,0px))]">
{taskId ?
<FinetuneDetail id={taskId} onDelete={handleDeleteTask} onStatusChange={reload}></FinetuneDetail> :
<div className="flex justify-center items-center h-full">
@@ -629,7 +629,7 @@ export default function ModelConfig({ id, onGetName, onBack, onReload, onBerforS
</ShadTooltip>
<span>{id === -1 ? t('model.addModel') : t('model.modelConfiguration')}</span>
</div>
<div className="w-[50%] min-w-64 px-4 pb-10 mx-auto mt-6 h-[calc(100vh-220px)] overflow-y-auto">
<div className="w-[50%] min-w-64 px-4 pb-10 mx-auto mt-6 h-[calc(100vh-220px-var(--license-banner-h,0px))] overflow-y-auto">
<div className="mb-2">
<Label className="bisheng-label"> {t('model.interModelFormat')}</Label>
<Select value={formData.type} disabled={id !== -1} onValueChange={handleTypeChange}>
@@ -160,7 +160,7 @@ export default function Departments() {
}, [])
return (
<div ref={containerRef} className="flex h-[calc(100vh-140px)]">
<div ref={containerRef} className="flex h-[calc(100vh-140px-var(--license-banner-h,0px))]">
{/* Left tree panel */}
<div className="flex min-w-[240px] flex-col border-r pr-4 pt-2" style={{ width: leftPaneWidth }}>
<LazyDepartmentTree
@@ -586,7 +586,7 @@ export default function EditRole({ id, name, groupId, knowledgeSpaceFileLimit, o
onChange(true);
};
return (
<div className="max-w-[600px] mx-auto pt-4 h-[calc(100vh-128px)] overflow-y-auto pb-40 scrollbar-hide">
<div className="max-w-[600px] mx-auto pt-4 h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-40 scrollbar-hide">
{/* 角色名称输入 */}
<div className="font-bold mt-4">
<p className="text-xl mb-4">{t('system.roleName')}</p>
@@ -191,7 +191,7 @@ export default function EditUserGroup({ data, onBeforeChange, onChange }: EditPr
}
return (
<div className="mx-auto flex h-[calc(100vh-128px)] max-w-[800px] flex-col">
<div className="mx-auto flex h-[calc(100vh-128px-var(--license-banner-h,0px))] max-w-[800px] flex-col">
<div className="min-h-0 flex-1 overflow-y-auto px-1 pt-4 pb-4">
<div className="font-bold mt-4">
<p className="text-xl mb-4">{t('system.groupName')}</p>
@@ -67,7 +67,7 @@ export default function OrgSync() {
}
return (
<div className="flex flex-col gap-4 p-4">
<div className="flex h-full flex-col gap-4 overflow-y-auto p-4">
<h2 className="text-lg font-semibold">{t("title")}</h2>
<div className="rounded-lg border">
@@ -578,8 +578,8 @@ export default function Roles() {
}
return (
<div className="relative">
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-10 pt-2">
<div className="relative h-full">
<div className="h-full overflow-y-auto pb-10 pt-2">
<div className="mb-3 flex items-center justify-between">
<div className="w-[220px]">
<SearchInput
@@ -157,7 +157,7 @@ export default function UserGroups() {
}
return <div className="relative">
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-10">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-10">
<div className="flex gap-6 items-center justify-end">
<div className="w-[180px] relative">
<SearchInput placeholder={t('system.groupName')} onChange={handleSearch}></SearchInput>
@@ -190,7 +190,7 @@ export default function Users(params) {
}
return <div className="relative">
<div className="h-[calc(100vh-128px)] overflow-y-auto pb-10">
<div className="h-[calc(100vh-128px-var(--license-banner-h,0px))] overflow-y-auto pb-10">
<div className="flex justify-end gap-6">
<div className="w-[180px] relative">
<SearchInput placeholder={t('system.username')} onChange={(e) => search(e.target.value)}></SearchInput>
@@ -38,7 +38,7 @@ const invoices = [
export default function Example(params) {
const { t } = useTranslation(); // Initialize translation hook
return <div className="h-[calc(100vh-220px)] overflow-y-auto py-10 pl-2 pr-10">
return <div className="h-[calc(100vh-220px-var(--license-banner-h,0px))] overflow-y-auto py-10 pl-2 pr-10">
<Label className="mt-10">{t('example.buttons')}</Label>
<div className="flex gap-2 mb-6">
<Button variant="default">{t('example.button')}</Button>
@@ -148,7 +148,7 @@ function TenantPageInner() {
</div>
</div>
<div className="h-[calc(100vh-200px)] overflow-y-auto">
<div className="h-[calc(100vh-200px-var(--license-banner-h,0px))] overflow-y-auto">
<Table>
<TableHeader>
<TableRow>