Files
jiu/client/lib/screens/auth/auth_shared.dart
T
wangjia 6238b86dcb feat(client): 登录/注册页照原型重建,ds 真相源组件族统一全部屏
- 登录/注册(login.html/register.html 1:1):两栏卡片+品牌渐变面板+主题小衣服
  pill(onSurface 变体)+记住我(记录并预填最近账号)+原型式 toast 校验;
  登录 fidelity 1.4–2.1% 三主题全绿;注册暂不入闸(少 门店编号/兑换券 字段,
  已记 CONTRACT,screens.mjs 留存根)
- ds 原子补齐:DsToast(.toast 单例)/DsCheck(.check/.agree)/DsButton lg 档/
  DsSelect 替换全部旧 DropdownButton/DsField label 在上/DsInput 后缀与密码形态
- 全屏统一:对话框按钮全 DsButton、盒式输入主题钉死(visualDensity.standard、
  h38、InputDecorationTheme 渗漏修复)、图标全 lucide、JetBrains Mono 三端同源、
  BrandMark 真相源 logo、只读模式写操作全量守卫(WriteGuard+DsToast)
- 出入库列表:版式对齐原型(卡片对齐+搜索框居中)、KPI 近30天滚动、结清后
  失效财务应收应付表;商品编辑抽屉介绍库改搜索下拉、图片双击全屏预览
- 删除旧 UI 死代码:DataTableCard/FormDialog/PageScaffold/SearchChip/
  SelectProductDialog/tabStateProvider
- golden/fidelity:stock-in/out 补注册(9%)、login 入册(8%)、goldens 全量重打;
  修复 pubspec flutter_web_plugins 非法声明(CI pub get 阻断)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-03 09:58:14 +08:00

126 lines
4.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// screens/auth/auth_shared.dart — 登录/注册两屏共用的原型 1:1 骨架。
// 对应 login.html / register.html 的公共部分:
// body--page 底、居中、pad 24
// 主题切换:主界面同款小衣服 ThemePickerPillonSurface 浅色变体),
// 放表单面板右上角(2026-07-03 用户拍板,替代原型的 A/B/C 圆钮);
// .auth:两栏卡片(surface / 1px border / r-xl / sh-3overflow hidden);
// .brand:左侧品牌渐变面板 linear-gradient(150deg, side-bg, side-active-bg)。
// 窄屏(≤ context.isMobile)折叠为单列,隐藏 pts/steps(原型 @media 760px 同构,
// 断点遵项目规则统一走 isMobile)。
import 'package:flutter/material.dart';
import '../../core/responsive/responsive.dart';
import '../../core/theme/app_dims.g.dart';
import '../../core/theme/context_tokens.dart';
import '../../widgets/theme_picker_pill.dart';
/// 整页骨架:page 底 + 居中滚动 + 两栏卡片 + 右上角主题切换器。
/// [brand]/[form] 为两栏内容;宽屏按 [brandWidth]/[formWidth] 定宽
/// (其一为 null 表示该栏弹性 1fr),窄屏纵向堆叠。
class AuthPageScaffold extends StatelessWidget {
final Widget brand;
final Widget form;
final double maxWidth;
final double narrowMaxWidth;
final double? brandWidth; // null = 弹性栏
final double? formWidth; // null = 弹性栏
const AuthPageScaffold({
super.key,
required this.brand,
required this.form,
required this.maxWidth,
required this.narrowMaxWidth,
this.brandWidth,
this.formWidth,
});
@override
Widget build(BuildContext context) {
final t = context.tokens;
final narrow = context.isMobile;
// 表单面板右上角叠主题小衣服(宽/窄屏都落在白底表单区上)
final formWithPill = Stack(
children: [
form,
const Positioned(
top: 14, right: 16, child: ThemePickerPill(onSurface: true)),
],
);
final card = Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: t.surface,
border: Border.all(color: t.border),
borderRadius: BorderRadius.circular(AppDims.rXl),
boxShadow: [
BoxShadow(
color: t.shadow, offset: const Offset(0, 12), blurRadius: 34),
],
),
child: narrow
? Column(
mainAxisSize: MainAxisSize.min, children: [brand, formWithPill])
: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (brandWidth != null)
SizedBox(width: brandWidth, child: brand)
else
Expanded(child: brand),
if (formWidth != null)
SizedBox(width: formWidth, child: formWithPill)
else
Expanded(child: formWithPill),
],
),
),
);
return Scaffold(
backgroundColor: t.page,
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height - 48),
child: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: narrow ? narrowMaxWidth : maxWidth),
child: card,
),
),
),
),
);
}
}
/// 品牌渐变面板容器(.brand):linear-gradient(150deg, side-bg, side-active-bg)。
class AuthBrandPanel extends StatelessWidget {
final EdgeInsets padding;
final double? minHeight;
final Widget child;
const AuthBrandPanel(
{super.key, required this.padding, this.minHeight, required this.child});
@override
Widget build(BuildContext context) {
final t = context.tokens;
return Container(
constraints:
minHeight != null ? BoxConstraints(minHeight: minHeight!) : null,
padding: padding,
decoration: BoxDecoration(
// 150deg ≈ 从左上偏上往右下(CSS 角度顺时针自 12 点起)
gradient: LinearGradient(
begin: const Alignment(-0.5, -1),
end: const Alignment(0.5, 1),
colors: [t.sideBg, t.sideActiveBg],
),
),
child: child,
);
}
}