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
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
// screens/auth/auth_shared.dart — 登录/注册两屏共用的原型 1:1 骨架。
|
||||
// 对应 login.html / register.html 的公共部分:
|
||||
// body:--page 底、居中、pad 24;
|
||||
// 主题切换:主界面同款小衣服 ThemePickerPill(onSurface 浅色变体),
|
||||
// 放表单面板右上角(2026-07-03 用户拍板,替代原型的 A/B/C 圆钮);
|
||||
// .auth:两栏卡片(surface / 1px border / r-xl / sh-3,overflow 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user