fix(external-admin): refine authenticated branding
This commit is contained in:
parent
7bc1b6a423
commit
c67d2e2d4b
@ -22,7 +22,7 @@ const displayScopeOptions = [
|
||||
];
|
||||
|
||||
export function BannerCreateDialog({ loading, onClose, onSubmit, open }) {
|
||||
const { locale, t } = useExternalI18n();
|
||||
const { direction, locale, t } = useExternalI18n();
|
||||
const [form, setForm] = useState(initialForm);
|
||||
useEffect(() => setForm(initialForm), [open]);
|
||||
|
||||
@ -79,6 +79,7 @@ export function BannerCreateDialog({ loading, onClose, onSubmit, open }) {
|
||||
<TextField disabled={loading} label={t("banners.sort")} slotProps={{ htmlInput: { min: 0 } }} type="number" value={form.sortOrder} onChange={(event) => setForm({ ...form, sortOrder: event.target.value })} />
|
||||
</Stack>
|
||||
<TimeRangeFilter
|
||||
direction={direction}
|
||||
disabled={loading}
|
||||
label={t("banners.schedule")}
|
||||
locale={locale}
|
||||
|
||||
@ -138,7 +138,18 @@ export function ExternalAdminLayout() {
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<Typography className="external-header-title" component="h2" noWrap>{mobile && !current ? t("app.shortTitle") : t(current?.labelKey || "app.shortTitle")}</Typography>
|
||||
{mobile ? (
|
||||
<AppIdentity
|
||||
appCode={session?.appCode}
|
||||
appName={session?.appName || session?.appCode}
|
||||
className="external-mobile-brand"
|
||||
logoUrl={session?.logoUrl}
|
||||
showLogoFallback={false}
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<Typography className="external-header-title" component="h2" noWrap>{t(current?.labelKey || "app.shortTitle")}</Typography>
|
||||
)}
|
||||
<Box flex={1} />
|
||||
<Stack direction="row" spacing={1.5} sx={{ alignItems: "center" }}>
|
||||
<LanguageSwitcher compact={mobile} />
|
||||
@ -193,7 +204,7 @@ export function ExternalAdminLayout() {
|
||||
})}
|
||||
</Box>
|
||||
<Divider sx={{ my: 1.5 }} />
|
||||
<Stack direction="row" spacing={1.5}>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={1.5}>
|
||||
<Button color="inherit" fullWidth startIcon={<KeyOutlined />} onClick={() => { setSheetOpen(false); navigate("/change-password"); }}>{t("auth.changePassword")}</Button>
|
||||
<Button fullWidth startIcon={<LogoutOutlined />} variant="outlined" onClick={handleLogout}>{t("auth.logout")}</Button>
|
||||
</Stack>
|
||||
|
||||
@ -55,20 +55,9 @@ const CAPABILITY_ICONS = {
|
||||
export default function OverviewPage() {
|
||||
const { session } = useExternalAuth();
|
||||
const { t } = useExternalI18n();
|
||||
const enabledCount = EXTERNAL_CAPABILITIES.filter((item) => hasExternalCapability(session, item.code)).length;
|
||||
|
||||
return (
|
||||
<ExternalPage title={t("nav.overview")}>
|
||||
<Box className="external-hero">
|
||||
<Typography className="external-hero-eyebrow">{t("overview.welcome")}</Typography>
|
||||
<Typography className="external-hero-title" component="h2">{session.appName || session.appCode}</Typography>
|
||||
<Box className="external-hero-meta">
|
||||
<Box><Typography color="text.secondary" variant="caption">{t("overview.currentApp")}</Typography><Typography fontWeight={700}>{session.appName || session.appCode} ({session.appCode})</Typography></Box>
|
||||
<Box><Typography color="text.secondary" variant="caption">{t("overview.account")}</Typography><Typography fontWeight={700}>{session.account}</Typography></Box>
|
||||
<Box><Typography color="text.secondary" variant="caption">{t("overview.permissions")}</Typography><Typography fontWeight={700}>{enabledCount} / {EXTERNAL_CAPABILITIES.length}</Typography></Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography className="external-section-title" component="h3">{t("overview.features")}</Typography>
|
||||
<Box className="external-capability-grid">
|
||||
{EXTERNAL_CAPABILITIES.map((item) => {
|
||||
const enabled = hasExternalCapability(session, item.code);
|
||||
|
||||
@ -224,6 +224,22 @@ body {
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.external-mobile-brand {
|
||||
max-width: min(48vw, 180px);
|
||||
color: var(--primary-strong);
|
||||
font-family: var(--font-display);
|
||||
font-size: 15px;
|
||||
font-weight: 750;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.external-mobile-brand img,
|
||||
.external-sidebar-brand img,
|
||||
.external-change-password-brand img {
|
||||
border-color: var(--primary-border);
|
||||
box-shadow: 0 0 0 1px rgba(214, 181, 110, 0.08), 0 8px 24px rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
.external-user-avatar {
|
||||
width: 34px !important;
|
||||
height: 34px !important;
|
||||
|
||||
@ -49,7 +49,7 @@ const defaultMessages = {
|
||||
|
||||
// withTime=false 时是纯日期区间选择器:隐藏时/分与小时级快捷区间,
|
||||
// 选中的开始/结束日分别取当天 00:00:00.000 与 23:59:59.999 的本地毫秒。
|
||||
export function TimeRangeFilter({ className = "", disabled = false, label = "时间", locale = "zh-CN", messages, onChange, value, withTime = true }) {
|
||||
export function TimeRangeFilter({ className = "", direction = "ltr", disabled = false, label = "时间", locale = "zh-CN", messages, onChange, value, withTime = true }) {
|
||||
const copy = useMemo(() => ({ ...defaultMessages, ...messages }), [messages]);
|
||||
const range = useMemo(() => normalizeRange(value), [value]);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@ -69,6 +69,12 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
|
||||
const rangeDistance = formatLocalizedRangeDistance(draft.startMs, draft.endMs, withTime, copy);
|
||||
const weekLabels = useMemo(() => localizedWeekLabels(locale), [locale]);
|
||||
const hasRange = Boolean(range.startMs || range.endMs);
|
||||
// CSS Modules do not pass through the external-admin Emotion RTL cache. Keep
|
||||
// popover anchoring and directional glyphs explicit so Arabic mirrors as a unit.
|
||||
const rtl = direction === "rtl";
|
||||
const horizontalOrigin = rtl ? "right" : "left";
|
||||
const PreviousMonthIcon = rtl ? ChevronRightOutlined : ChevronLeftOutlined;
|
||||
const NextMonthIcon = rtl ? ChevronLeftOutlined : ChevronRightOutlined;
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
@ -183,9 +189,9 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
|
||||
<Popover
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
anchorOrigin={{ horizontal: "left", vertical: "bottom" }}
|
||||
slotProps={{ paper: { className: styles.paper } }}
|
||||
transformOrigin={{ horizontal: "left", vertical: "top" }}
|
||||
anchorOrigin={{ horizontal: horizontalOrigin, vertical: "bottom" }}
|
||||
slotProps={{ paper: { className: styles.paper, dir: direction } }}
|
||||
transformOrigin={{ horizontal: horizontalOrigin, vertical: "top" }}
|
||||
onClose={closePopover}
|
||||
>
|
||||
<form className={styles.panel} onSubmit={submitDraft}>
|
||||
@ -233,11 +239,11 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
|
||||
</div>
|
||||
<div className={styles.calendarHeader}>
|
||||
<IconButton aria-label={copy.previousMonth} size="small" onClick={() => changeMonth(-1)}>
|
||||
<ChevronLeftOutlined fontSize="small" />
|
||||
<PreviousMonthIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<span className={styles.calendarTitle}>{monthTitle(viewDate, locale)}</span>
|
||||
<IconButton aria-label={copy.nextMonth} size="small" onClick={() => changeMonth(1)}>
|
||||
<ChevronRightOutlined fontSize="small" />
|
||||
<NextMonthIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className={`${styles.calendarGrid} ${styles.rangeCalendarGrid}`}>
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
width: auto;
|
||||
min-width: 132px !important;
|
||||
max-width: min(420px, 100%);
|
||||
padding: 7px 28px 3px var(--control-padding-x) !important;
|
||||
padding-block: 7px 3px !important;
|
||||
padding-inline: var(--control-padding-x) 28px !important;
|
||||
border-color: var(--border) !important;
|
||||
background: var(--bg-input-strong) !important;
|
||||
box-shadow: none !important;
|
||||
@ -29,8 +30,8 @@
|
||||
|
||||
.triggerMeta {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: var(--space-2);
|
||||
inset-block-start: -7px;
|
||||
inset-inline-start: var(--space-2);
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - var(--space-4));
|
||||
align-items: center;
|
||||
@ -58,15 +59,15 @@
|
||||
color: var(--text-primary);
|
||||
font-size: var(--control-font-size);
|
||||
line-height: var(--control-line-height);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.triggerChevron {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: var(--space-2);
|
||||
inset-block-start: 50%;
|
||||
inset-inline-end: var(--space-2);
|
||||
color: var(--text-tertiary);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
@ -94,8 +95,7 @@
|
||||
|
||||
.quickButton {
|
||||
min-width: 0 !important;
|
||||
padding-right: var(--space-2) !important;
|
||||
padding-left: var(--space-2) !important;
|
||||
padding-inline: var(--space-2) !important;
|
||||
}
|
||||
|
||||
.rangeFields {
|
||||
@ -115,7 +115,7 @@
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--bg-input-strong);
|
||||
color: var(--text-primary);
|
||||
text-align: left;
|
||||
text-align: start;
|
||||
transition:
|
||||
border-color var(--motion-fast) var(--ease-standard),
|
||||
box-shadow var(--motion-fast) var(--ease-standard),
|
||||
@ -275,11 +275,15 @@
|
||||
}
|
||||
|
||||
.dayRangeStart {
|
||||
border-radius: var(--radius-control) 0 0 var(--radius-control);
|
||||
border-radius: 0;
|
||||
border-start-start-radius: var(--radius-control);
|
||||
border-end-start-radius: var(--radius-control);
|
||||
}
|
||||
|
||||
.dayRangeEnd {
|
||||
border-radius: 0 var(--radius-control) var(--radius-control) 0;
|
||||
border-radius: 0;
|
||||
border-start-end-radius: var(--radius-control);
|
||||
border-end-end-radius: var(--radius-control);
|
||||
}
|
||||
|
||||
.dayRangeStart.dayRangeEnd {
|
||||
@ -304,8 +308,8 @@
|
||||
|
||||
.daySelected::after {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 3px;
|
||||
inset-block-start: 1px;
|
||||
inset-inline-end: 3px;
|
||||
color: currentcolor;
|
||||
content: attr(data-range-edge);
|
||||
font-size: 8px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user