mirror of
https://github.com/dataelement/bisheng.git
synced 2026-09-17 17:18:35 +08:00
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
95 lines
4.5 KiB
TypeScript
95 lines
4.5 KiB
TypeScript
// F035: 应用 tab — app-center welcome message and description, moved out of
|
|
// the home tab. Both fields still persist into the daily workstation config:
|
|
// the full config is loaded on mount and round-tripped on save with only
|
|
// these two fields replaced (tab content remounts on switch, so the loaded
|
|
// snapshot stays fresh).
|
|
import { Button } from "@/components/bs-ui/button";
|
|
import { CardContent } from "@/components/bs-ui/card";
|
|
import { useToast } from "@/components/bs-ui/toast/use-toast";
|
|
import { locationContext } from "@/contexts/locationContext";
|
|
import { getDailyConfigApi, setDailyConfigApi } from "@/controllers/API";
|
|
import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
|
|
import { useContext, useEffect, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import ConfigInheritanceBanner, { resolveConfigEnvelope } from "./ConfigInheritanceBanner";
|
|
import { FormInput } from "./FormInput";
|
|
|
|
const MAX_LEN = 1000;
|
|
|
|
export function AppCenter({ scopeVersion = 0 }: { scopeVersion?: number }) {
|
|
const { t } = useTranslation();
|
|
const { toast } = useToast();
|
|
const { reloadConfig } = useContext(locationContext);
|
|
const [welcome, setWelcome] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
const [errors, setErrors] = useState({ welcome: '', description: '' });
|
|
const [configMeta, setConfigMeta] = useState<any>(null);
|
|
// Full loaded config — round-tripped on save so home-tab fields survive.
|
|
const loadedCfgRef = useRef<any>(null);
|
|
|
|
useEffect(() => {
|
|
setConfigMeta(null);
|
|
getDailyConfigApi().then((res) => {
|
|
const { data: cfg, meta } = resolveConfigEnvelope<any>(res);
|
|
setConfigMeta(meta);
|
|
loadedCfgRef.current = cfg || {};
|
|
setWelcome(cfg?.applicationCenterWelcomeMessage ?? '');
|
|
setDescription(cfg?.applicationCenterDescription ?? '');
|
|
});
|
|
}, [scopeVersion]);
|
|
|
|
const handleChange = (field: 'welcome' | 'description', value: string) => {
|
|
(field === 'welcome' ? setWelcome : setDescription)(value);
|
|
setErrors(prev => ({
|
|
...prev,
|
|
[field]: value.length >= MAX_LEN ? t('chatConfig.errors.maxCharacters', { count: MAX_LEN }) : '',
|
|
}));
|
|
};
|
|
|
|
const handleSave = () => {
|
|
const dataToSave = {
|
|
...(loadedCfgRef.current || {}),
|
|
applicationCenterWelcomeMessage: welcome.trim() || t('chatConfig.appCenterWelcomePlaceholder'),
|
|
applicationCenterDescription: description.trim() || t('chatConfig.appCenterDescriptionPlaceholder'),
|
|
};
|
|
captureAndAlertRequestErrorHoc(setDailyConfigApi(dataToSave)).then((res) => {
|
|
if (res) {
|
|
setConfigMeta({ inherited_from_root: false, has_override: true });
|
|
toast({ variant: 'success', description: t('chatConfig.saveSuccess') });
|
|
reloadConfig();
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<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-var(--license-banner-h,0px))] overflow-y-scroll scrollbar-hide pb-10">
|
|
<ConfigInheritanceBanner meta={configMeta} />
|
|
<FormInput
|
|
label={t('chatConfig.appCenterWelcome')}
|
|
value={welcome}
|
|
error={errors.welcome}
|
|
placeholder={t('chatConfig.appCenterWelcomePlaceholder')}
|
|
maxLength={MAX_LEN}
|
|
onChange={(v) => handleChange('welcome', v)}
|
|
/>
|
|
<FormInput
|
|
label={t('chatConfig.appCenterDescription')}
|
|
value={description}
|
|
error={errors.description}
|
|
placeholder={t('chatConfig.appCenterDescriptionPlaceholder')}
|
|
maxLength={MAX_LEN}
|
|
onChange={(v) => handleChange('description', v)}
|
|
/>
|
|
</div>
|
|
<div className="flex justify-end gap-4 absolute bottom-1 right-4">
|
|
<Button onClick={handleSave}>{t('save')}</Button>
|
|
</div>
|
|
</CardContent>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|