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:
wangjia
2026-07-03 09:58:14 +08:00
parent ca7595b113
commit 6238b86dcb
286 changed files with 16831 additions and 11136 deletions
+125
View File
@@ -0,0 +1,125 @@
// 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,
);
}
}