refactor(client): widgets 颜色迁移到 context.tokens
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -288,7 +288,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
||||||
: '-'));
|
: '-'));
|
||||||
case 'status':
|
case 'status':
|
||||||
final rb = returnStateBadge(o.returnState);
|
final rb = returnStateBadge(context, o.returnState);
|
||||||
return DataCell(Row(mainAxisSize: MainAxisSize.min, children: [
|
return DataCell(Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
StatusBadge(_apiStatusToEnum(o.status)),
|
StatusBadge(_apiStatusToEnum(o.status)),
|
||||||
if (rb != null) ...[const SizedBox(width: 4), rb],
|
if (rb != null) ...[const SizedBox(width: 4), rb],
|
||||||
@@ -520,6 +520,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
/// 入库特有的「打标签」通过 afterPrint 注入。
|
/// 入库特有的「打标签」通过 afterPrint 注入。
|
||||||
List<Widget> _orderActions(BuildContext context, StockInOrder o) {
|
List<Widget> _orderActions(BuildContext context, StockInOrder o) {
|
||||||
return buildOrderRowActions(
|
return buildOrderRowActions(
|
||||||
|
context: context,
|
||||||
readonly: WriteGuard.isReadonly(ref),
|
readonly: WriteGuard.isReadonly(ref),
|
||||||
canWithdraw: ref.watch(isAdminProvider) ||
|
canWithdraw: ref.watch(isAdminProvider) ||
|
||||||
(o.operatorId != null &&
|
(o.operatorId != null &&
|
||||||
@@ -586,9 +587,9 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
style: const TextStyle(fontFamily: 'monospace', fontSize: 14)),
|
style: const TextStyle(fontFamily: 'monospace', fontSize: 14)),
|
||||||
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
StatusBadge(_apiStatusToEnum(o.status)),
|
StatusBadge(_apiStatusToEnum(o.status)),
|
||||||
if (returnStateBadge(o.returnState) != null) ...[
|
if (returnStateBadge(context, o.returnState) != null) ...[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
returnStateBadge(o.returnState)!,
|
returnStateBadge(context, o.returnState)!,
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
fields: [
|
fields: [
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
||||||
: '-'));
|
: '-'));
|
||||||
case 'status':
|
case 'status':
|
||||||
final rb = returnStateBadge(o.returnState);
|
final rb = returnStateBadge(context, o.returnState);
|
||||||
return DataCell(Row(mainAxisSize: MainAxisSize.min, children: [
|
return DataCell(Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
StatusBadge(_apiStatusToEnum(o.status)),
|
StatusBadge(_apiStatusToEnum(o.status)),
|
||||||
if (rb != null) ...[const SizedBox(width: 4), rb],
|
if (rb != null) ...[const SizedBox(width: 4), rb],
|
||||||
@@ -548,6 +548,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
/// 操作按钮列表,表格与移动端卡片共用。
|
/// 操作按钮列表,表格与移动端卡片共用。
|
||||||
List<Widget> _orderActions(BuildContext context, StockOutOrder o) {
|
List<Widget> _orderActions(BuildContext context, StockOutOrder o) {
|
||||||
return buildOrderRowActions(
|
return buildOrderRowActions(
|
||||||
|
context: context,
|
||||||
readonly: WriteGuard.isReadonly(ref),
|
readonly: WriteGuard.isReadonly(ref),
|
||||||
canWithdraw: ref.watch(isAdminProvider) ||
|
canWithdraw: ref.watch(isAdminProvider) ||
|
||||||
(o.operatorId != null &&
|
(o.operatorId != null &&
|
||||||
@@ -584,9 +585,9 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
style: const TextStyle(fontFamily: 'monospace', fontSize: 14)),
|
style: const TextStyle(fontFamily: 'monospace', fontSize: 14)),
|
||||||
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
StatusBadge(_apiStatusToEnum(o.status)),
|
StatusBadge(_apiStatusToEnum(o.status)),
|
||||||
if (returnStateBadge(o.returnState) != null) ...[
|
if (returnStateBadge(context, o.returnState) != null) ...[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
returnStateBadge(o.returnState)!,
|
returnStateBadge(context, o.returnState)!,
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
fields: [
|
fields: [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/responsive/responsive.dart';
|
import '../core/responsive/responsive.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
class DataTableCard extends StatefulWidget {
|
class DataTableCard extends StatefulWidget {
|
||||||
final List<DataColumn> columns;
|
final List<DataColumn> columns;
|
||||||
@@ -53,7 +53,7 @@ class _DataTableCardState extends State<DataTableCard> {
|
|||||||
children: [
|
children: [
|
||||||
if (widget.toolbar != null)
|
if (widget.toolbar != null)
|
||||||
Container(
|
Container(
|
||||||
color: AppTheme.surface,
|
color: context.tokens.surface,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
child: widget.toolbar!,
|
child: widget.toolbar!,
|
||||||
),
|
),
|
||||||
@@ -67,14 +67,14 @@ class _DataTableCardState extends State<DataTableCard> {
|
|||||||
if (widget.totalCount != null)
|
if (widget.totalCount != null)
|
||||||
Container(
|
Container(
|
||||||
height: 48,
|
height: 48,
|
||||||
color: AppTheme.surface,
|
color: context.tokens.surface,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'共 ${widget.totalCount} 条记录',
|
'共 ${widget.totalCount} 条记录',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13, color: AppTheme.textSecondary),
|
fontSize: 13, color: context.tokens.muted),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
_PaginationBar(
|
_PaginationBar(
|
||||||
@@ -132,11 +132,11 @@ class _DataTableCardState extends State<DataTableCard> {
|
|||||||
Widget _buildMobileList() {
|
Widget _buildMobileList() {
|
||||||
final cards = widget.mobileCards!;
|
final cards = widget.mobileCards!;
|
||||||
if (cards.isEmpty) {
|
if (cards.isEmpty) {
|
||||||
return const Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(32),
|
padding: const EdgeInsets.all(32),
|
||||||
child: Text('暂无数据',
|
child: Text('暂无数据',
|
||||||
style: TextStyle(color: AppTheme.textSecondary, fontSize: 14)),
|
style: TextStyle(color: context.tokens.muted, fontSize: 14)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,10 +155,10 @@ class _DataTableCardState extends State<DataTableCard> {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
child: DefaultTextStyle(
|
child: DefaultTextStyle(
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: AppTheme.primaryDark,
|
color: context.tokens.primaryDark,
|
||||||
),
|
),
|
||||||
child: col.numeric
|
child: col.numeric
|
||||||
? Align(alignment: Alignment.centerRight, child: col.label)
|
? Align(alignment: Alignment.centerRight, child: col.label)
|
||||||
@@ -189,8 +189,8 @@ class _DataTableCardState extends State<DataTableCard> {
|
|||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
child: DefaultTextStyle(
|
child: DefaultTextStyle(
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13, color: AppTheme.textPrimary),
|
fontSize: 13, color: context.tokens.text),
|
||||||
child: cell.child,
|
child: cell.child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -227,13 +227,13 @@ class _PaginationBar extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
// 每页条数选择器
|
// 每页条数选择器
|
||||||
if (onPageSizeChanged != null) ...[
|
if (onPageSizeChanged != null) ...[
|
||||||
const Text('每页', style: TextStyle(fontSize: 13, color: AppTheme.textSecondary)),
|
Text('每页', style: TextStyle(fontSize: 13, color: context.tokens.muted)),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
DropdownButtonHideUnderline(
|
DropdownButtonHideUnderline(
|
||||||
child: DropdownButton<int>(
|
child: DropdownButton<int>(
|
||||||
value: effectiveSize,
|
value: effectiveSize,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
style: const TextStyle(fontSize: 13, color: AppTheme.textPrimary),
|
style: TextStyle(fontSize: 13, color: context.tokens.text),
|
||||||
items: _pageSizeOptions
|
items: _pageSizeOptions
|
||||||
.map((n) => DropdownMenuItem(value: n, child: Text('$n 条')))
|
.map((n) => DropdownMenuItem(value: n, child: Text('$n 条')))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
import '../core/utils/date_util.dart';
|
import '../core/utils/date_util.dart';
|
||||||
|
|
||||||
/// 可键入日期框 + 日历图标(替代旧的「年/月/日」三下拉)。
|
/// 可键入日期框 + 日历图标(替代旧的「年/月/日」三下拉)。
|
||||||
@@ -138,8 +138,8 @@ class _DatePickerFieldState extends State<DatePickerField> {
|
|||||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||||
errorText: field.errorText,
|
errorText: field.errorText,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: const Icon(Icons.calendar_today,
|
icon: Icon(Icons.calendar_today,
|
||||||
size: 16, color: AppTheme.textSecondary),
|
size: 16, color: context.tokens.muted),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(minWidth: 34, minHeight: 34),
|
constraints: const BoxConstraints(minWidth: 34, minHeight: 34),
|
||||||
tooltip: '选择日期',
|
tooltip: '选择日期',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import '../core/utils/dialog_util.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
/// Generic dialog wrapper for forms
|
/// Generic dialog wrapper for forms
|
||||||
class FormDialog extends StatelessWidget {
|
class FormDialog extends StatelessWidget {
|
||||||
@@ -35,9 +34,9 @@ class FormDialog extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
height: 48,
|
height: 48,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppTheme.primary,
|
color: context.tokens.primary,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(4),
|
topLeft: Radius.circular(4),
|
||||||
topRight: Radius.circular(4),
|
topRight: Radius.circular(4),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../core/errors/error_reporter.dart';
|
import '../core/errors/error_reporter.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
import '../core/responsive/responsive.dart';
|
import '../core/responsive/responsive.dart';
|
||||||
import '../core/utils/print_util.dart';
|
import '../core/utils/print_util.dart';
|
||||||
import '../core/utils/label_data.dart';
|
import '../core/utils/label_data.dart';
|
||||||
@@ -164,13 +164,13 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? AppTheme.primary : AppTheme.border,
|
color: isSelected ? context.tokens.primary : context.tokens.border,
|
||||||
width: isSelected ? 2 : 1,
|
width: isSelected ? 2 : 1,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? AppTheme.primary.withOpacity(0.04)
|
? context.tokens.primary.withOpacity(0.04)
|
||||||
: AppTheme.surface,
|
: context.tokens.surface,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -204,8 +204,8 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? AppTheme.primary
|
? context.tokens.primary
|
||||||
: AppTheme.textSecondary,
|
: context.tokens.muted,
|
||||||
fontWeight:
|
fontWeight:
|
||||||
isSelected ? FontWeight.w600 : FontWeight.normal,
|
isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||||
),
|
),
|
||||||
@@ -228,7 +228,7 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 420, maxHeight: 230),
|
constraints: const BoxConstraints(maxWidth: 420, maxHeight: 230),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: AppTheme.border),
|
border: Border.all(color: context.tokens.border),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
@@ -297,9 +297,9 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
Container(
|
Container(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppTheme.primary,
|
color: context.tokens.primary,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(12),
|
topLeft: Radius.circular(12),
|
||||||
topRight: Radius.circular(12),
|
topRight: Radius.circular(12),
|
||||||
),
|
),
|
||||||
@@ -334,24 +334,24 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
child: _printers.isEmpty
|
child: _printers.isEmpty
|
||||||
? Text(
|
? Text(
|
||||||
_printersChecked ? '无打印机可用' : '正在检测打印机...',
|
_printersChecked ? '无打印机可用' : '正在检测打印机...',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: AppTheme.textSecondary))
|
color: context.tokens.muted))
|
||||||
: DropdownButtonHideUnderline(
|
: DropdownButtonHideUnderline(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 10, vertical: 4),
|
horizontal: 10, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: AppTheme.border),
|
border: Border.all(color: context.tokens.border),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: DropdownButton<String>(
|
child: DropdownButton<String>(
|
||||||
value: _printer,
|
value: _printer,
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: AppTheme.textPrimary),
|
color: context.tokens.text),
|
||||||
items: _printers
|
items: _printers
|
||||||
.map((p) => DropdownMenuItem(
|
.map((p) => DropdownMenuItem(
|
||||||
value: p,
|
value: p,
|
||||||
@@ -410,8 +410,8 @@ class _LabelPreviewDialogState extends State<LabelPreviewDialog> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
_status,
|
_status,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12, color: AppTheme.textSecondary),
|
fontSize: 12, color: context.tokens.muted),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
/// 移动端列表卡片的单个字段(label: value)。
|
/// 移动端列表卡片的单个字段(label: value)。
|
||||||
class MobileCardField {
|
class MobileCardField {
|
||||||
@@ -56,19 +56,19 @@ class MobileListCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
DefaultTextStyle(
|
DefaultTextStyle(
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: AppTheme.textPrimary,
|
color: context.tokens.text,
|
||||||
),
|
),
|
||||||
child: title,
|
child: title,
|
||||||
),
|
),
|
||||||
if (subtitle != null) ...[
|
if (subtitle != null) ...[
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
DefaultTextStyle(
|
DefaultTextStyle(
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: AppTheme.textSecondary),
|
color: context.tokens.muted),
|
||||||
child: subtitle!,
|
child: subtitle!,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -83,7 +83,7 @@ class MobileListCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (fields.isNotEmpty) ...[
|
if (fields.isNotEmpty) ...[
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
...fields.map(_buildField),
|
...fields.map((f) => _buildField(context, f)),
|
||||||
],
|
],
|
||||||
if (actions != null && actions!.isNotEmpty) ...[
|
if (actions != null && actions!.isNotEmpty) ...[
|
||||||
const Divider(height: 18),
|
const Divider(height: 18),
|
||||||
@@ -102,7 +102,7 @@ class MobileListCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildField(MobileCardField f) {
|
Widget _buildField(BuildContext context, MobileCardField f) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -112,16 +112,16 @@ class MobileListCard extends StatelessWidget {
|
|||||||
width: 64,
|
width: 64,
|
||||||
child: Text(
|
child: Text(
|
||||||
f.label,
|
f.label,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13, color: AppTheme.textSecondary),
|
fontSize: 13, color: context.tokens.muted),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: f.valueWidget ??
|
child: f.valueWidget ??
|
||||||
Text(
|
Text(
|
||||||
f.value ?? '—',
|
f.value ?? '—',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13, color: AppTheme.textPrimary),
|
fontSize: 13, color: context.tokens.text),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import '../core/utils/dialog_util.dart';
|
import '../core/utils/dialog_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
/// Compact button → dialog with checkboxes for multi-select filtering
|
/// Compact button → dialog with checkboxes for multi-select filtering
|
||||||
class MultiSelectDropdown extends StatelessWidget {
|
class MultiSelectDropdown extends StatelessWidget {
|
||||||
@@ -23,8 +23,8 @@ class MultiSelectDropdown extends StatelessWidget {
|
|||||||
return OutlinedButton(
|
return OutlinedButton(
|
||||||
onPressed: options.isEmpty ? null : () => _show(context),
|
onPressed: options.isEmpty ? null : () => _show(context),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: active ? AppTheme.primary : AppTheme.textSecondary,
|
foregroundColor: active ? context.tokens.primary : context.tokens.muted,
|
||||||
side: BorderSide(color: active ? AppTheme.primary : AppTheme.border),
|
side: BorderSide(color: active ? context.tokens.primary : context.tokens.border),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||||
minimumSize: Size.zero,
|
minimumSize: Size.zero,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
@@ -143,8 +143,8 @@ class _MultiSelectDialogState extends State<_MultiSelectDialog> {
|
|||||||
onDeleted: () => setState(() => _selected.remove(s)),
|
onDeleted: () => setState(() => _selected.remove(s)),
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
backgroundColor: AppTheme.primary.withOpacity(0.08),
|
backgroundColor: context.tokens.primary.withOpacity(0.08),
|
||||||
side: BorderSide(color: AppTheme.primary.withOpacity(0.3), width: 0.5),
|
side: BorderSide(color: context.tokens.primary.withOpacity(0.3), width: 0.5),
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 2),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
|
||||||
)).toList(),
|
)).toList(),
|
||||||
@@ -168,12 +168,12 @@ class _MultiSelectDialogState extends State<_MultiSelectDialog> {
|
|||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxHeight: 280),
|
constraints: const BoxConstraints(maxHeight: 280),
|
||||||
child: filtered.isEmpty
|
child: filtered.isEmpty
|
||||||
? const Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text('无匹配项',
|
child: Text('无匹配项',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13, color: AppTheme.textSecondary)),
|
fontSize: 13, color: context.tokens.muted)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListView(
|
: ListView(
|
||||||
@@ -256,7 +256,7 @@ class _FilterableColumnHeaderState extends State<FilterableColumnHeader> {
|
|||||||
child: Icon(
|
child: Icon(
|
||||||
active ? Icons.filter_alt : Icons.filter_alt_outlined,
|
active ? Icons.filter_alt : Icons.filter_alt_outlined,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: active ? AppTheme.primary : AppTheme.textSecondary,
|
color: active ? context.tokens.primary : context.tokens.muted,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -264,8 +264,8 @@ class _FilterableColumnHeaderState extends State<FilterableColumnHeader> {
|
|||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => widget.onChanged({}),
|
onTap: () => widget.onChanged({}),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
child: const Icon(Icons.cancel, size: 14,
|
child: Icon(Icons.cancel, size: 14,
|
||||||
color: AppTheme.primary),
|
color: context.tokens.primary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -327,8 +327,8 @@ class ColumnToggleButton extends StatelessWidget {
|
|||||||
return OutlinedButton(
|
return OutlinedButton(
|
||||||
onPressed: () => _show(context),
|
onPressed: () => _show(context),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: AppTheme.textSecondary,
|
foregroundColor: context.tokens.muted,
|
||||||
side: const BorderSide(color: AppTheme.border),
|
side: BorderSide(color: context.tokens.border),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||||
minimumSize: Size.zero,
|
minimumSize: Size.zero,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
import '../providers/connectivity_provider.dart';
|
import '../providers/connectivity_provider.dart';
|
||||||
|
|
||||||
/// 离线提示里的「重试」按钮:
|
/// 离线提示里的「重试」按钮:
|
||||||
@@ -27,7 +27,7 @@ class NetworkRetryButton extends ConsumerWidget {
|
|||||||
..hideCurrentSnackBar()
|
..hideCurrentSnackBar()
|
||||||
..showSnackBar(SnackBar(
|
..showSnackBar(SnackBar(
|
||||||
content: Text(ok ? '已重新连接服务器' : '仍无法连接服务器,请稍后重试'),
|
content: Text(ok ? '已重新连接服务器' : '仍无法连接服务器,请稍后重试'),
|
||||||
backgroundColor: ok ? AppTheme.success : AppTheme.danger,
|
backgroundColor: ok ? context.tokens.success : context.tokens.danger,
|
||||||
duration: const Duration(seconds: 2),
|
duration: const Duration(seconds: 2),
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../core/responsive/responsive.dart';
|
import '../core/responsive/responsive.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
import '../core/utils/dialog_util.dart';
|
import '../core/utils/dialog_util.dart';
|
||||||
|
|
||||||
/// 退单状态小徽章(none 返回 null 不显示)。partial=部分退单(amber),full=已退单(红)。
|
/// 退单状态小徽章(none 返回 null 不显示)。partial=部分退单(amber),full=已退单(红)。
|
||||||
Widget? returnStateBadge(String state) {
|
Widget? returnStateBadge(BuildContext context, String state) {
|
||||||
Color? c;
|
Color? c;
|
||||||
String? t;
|
String? t;
|
||||||
if (state == 'partial') {
|
if (state == 'partial') {
|
||||||
c = AppTheme.warning;
|
c = context.tokens.warn;
|
||||||
t = '部分退单';
|
t = '部分退单';
|
||||||
} else if (state == 'full') {
|
} else if (state == 'full') {
|
||||||
c = AppTheme.danger;
|
c = context.tokens.danger;
|
||||||
t = '已退单';
|
t = '已退单';
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -109,7 +109,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
title: const Text('退单确认', style: TextStyle(fontSize: 16)),
|
title: const Text('退单确认', style: TextStyle(fontSize: 16)),
|
||||||
content: RichText(
|
content: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: const TextStyle(fontSize: 13, color: AppTheme.textPrimary, height: 1.6),
|
style: TextStyle(fontSize: 13, color: context.tokens.text, height: 1.6),
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: '确认退掉以下明细?\n'),
|
const TextSpan(text: '确认退掉以下明细?\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@@ -118,7 +118,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
TextSpan(text: ' · ${l.series} · ${l.spec}\n'),
|
TextSpan(text: ' · ${l.series} · ${l.spec}\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '编码 ${l.code} · 数量 ${_q(l.quantity)}',
|
text: '编码 ${l.code} · 数量 ${_q(l.quantity)}',
|
||||||
style: const TextStyle(color: AppTheme.textSecondary, fontSize: 12)),
|
style: TextStyle(color: context.tokens.muted, fontSize: 12)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -129,7 +129,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.pop(context, true),
|
onPressed: () => Navigator.pop(context, true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppTheme.danger, foregroundColor: Colors.white),
|
backgroundColor: context.tokens.danger, foregroundColor: Colors.white),
|
||||||
child: const Text('退单'),
|
child: const Text('退单'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -154,7 +154,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.pop(context, true),
|
onPressed: () => Navigator.pop(context, true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppTheme.danger, foregroundColor: Colors.white),
|
backgroundColor: context.tokens.danger, foregroundColor: Colors.white),
|
||||||
child: const Text('全部退单'),
|
child: const Text('全部退单'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -181,7 +181,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.pop(context, true),
|
onPressed: () => Navigator.pop(context, true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppTheme.danger, foregroundColor: Colors.white),
|
backgroundColor: context.tokens.danger, foregroundColor: Colors.white),
|
||||||
child: const Text('提交退单'),
|
child: const Text('提交退单'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -192,15 +192,15 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
try {
|
try {
|
||||||
await widget.onSubmit(_staged.toList());
|
await widget.onSubmit(_staged.toList());
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text('退单成功'), backgroundColor: AppTheme.success));
|
content: const Text('退单成功'), backgroundColor: context.tokens.success));
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _submitting = false);
|
setState(() => _submitting = false);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text('退单失败:$e'), backgroundColor: AppTheme.danger));
|
content: Text('退单失败:$e'), backgroundColor: context.tokens.danger));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,8 +232,8 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onPressed: _submitting ? null : _confirmAll,
|
onPressed: _submitting ? null : _confirmAll,
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: AppTheme.danger,
|
foregroundColor: context.tokens.danger,
|
||||||
side: const BorderSide(color: AppTheme.danger),
|
side: BorderSide(color: context.tokens.danger),
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
),
|
),
|
||||||
child: const Text('全部退单', style: TextStyle(fontSize: 12)),
|
child: const Text('全部退单', style: TextStyle(fontSize: 12)),
|
||||||
@@ -255,10 +255,10 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
children: [
|
children: [
|
||||||
for (final (k, v) in widget.meta)
|
for (final (k, v) in widget.meta)
|
||||||
Text.rich(TextSpan(
|
Text.rich(TextSpan(
|
||||||
style: const TextStyle(fontSize: 12, color: AppTheme.textSecondary),
|
style: TextStyle(fontSize: 12, color: context.tokens.muted),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: '$k:'),
|
TextSpan(text: '$k:'),
|
||||||
TextSpan(text: v, style: const TextStyle(color: AppTheme.textPrimary)),
|
TextSpan(text: v, style: TextStyle(color: context.tokens.text)),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
@@ -271,14 +271,14 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF0F6FF),
|
color: const Color(0xFFF0F6FF),
|
||||||
border: const Border(left: BorderSide(color: AppTheme.primary, width: 3)),
|
border: Border(left: BorderSide(color: context.tokens.primary, width: 3)),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.isOut
|
widget.isOut
|
||||||
? '提交后,已退单明细的数量将「加回库存」。退单的明细不会删除,以红色「已退单」标注。'
|
? '提交后,已退单明细的数量将「加回库存」。退单的明细不会删除,以红色「已退单」标注。'
|
||||||
: '提交后,已退单明细对应的库存将「从库存删除」。退单的明细不会删除,以红色「已退单」标注。',
|
: '提交后,已退单明细对应的库存将「从库存删除」。退单的明细不会删除,以红色「已退单」标注。',
|
||||||
style: const TextStyle(fontSize: 12, color: AppTheme.primaryDark),
|
style: TextStyle(fontSize: 12, color: context.tokens.primaryDark),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 明细列表
|
// 明细列表
|
||||||
@@ -304,8 +304,8 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: _staged.isEmpty
|
color: _staged.isEmpty
|
||||||
? AppTheme.textSecondary
|
? context.tokens.muted
|
||||||
: AppTheme.danger,
|
: context.tokens.danger,
|
||||||
fontWeight: FontWeight.w600),
|
fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -317,7 +317,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: (_staged.isEmpty || _submitting) ? null : _submit,
|
onPressed: (_staged.isEmpty || _submitting) ? null : _submit,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppTheme.danger,
|
backgroundColor: context.tokens.danger,
|
||||||
foregroundColor: Colors.white),
|
foregroundColor: Colors.white),
|
||||||
child: _submitting
|
child: _submitting
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
@@ -341,12 +341,12 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
final nameStyle = TextStyle(
|
final nameStyle = TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: returned ? AppTheme.danger : AppTheme.textPrimary,
|
color: returned ? context.tokens.danger : context.tokens.text,
|
||||||
decoration: returned ? TextDecoration.lineThrough : null,
|
decoration: returned ? TextDecoration.lineThrough : null,
|
||||||
);
|
);
|
||||||
final subStyle = TextStyle(
|
final subStyle = TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: returned ? AppTheme.danger : AppTheme.textSecondary,
|
color: returned ? context.tokens.danger : context.tokens.muted,
|
||||||
decoration: returned ? TextDecoration.lineThrough : null,
|
decoration: returned ? TextDecoration.lineThrough : null,
|
||||||
);
|
);
|
||||||
return Container(
|
return Container(
|
||||||
@@ -357,8 +357,8 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
child: Text('${idx + 1}',
|
child: Text('${idx + 1}',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12, color: AppTheme.textSecondary))),
|
fontSize: 12, color: context.tokens.muted))),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -370,7 +370,7 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppTheme.danger,
|
color: context.tokens.danger,
|
||||||
borderRadius: BorderRadius.circular(8)),
|
borderRadius: BorderRadius.circular(8)),
|
||||||
child: const Text('已退单',
|
child: const Text('已退单',
|
||||||
style: TextStyle(fontSize: 10, color: Colors.white)),
|
style: TextStyle(fontSize: 10, color: Colors.white)),
|
||||||
@@ -396,14 +396,14 @@ class _OrderReturnDialogState extends State<_OrderReturnDialog> {
|
|||||||
onPressed: _submitting
|
onPressed: _submitting
|
||||||
? null
|
? null
|
||||||
: () => setState(() => _staged.remove(l.itemId)),
|
: () => setState(() => _staged.remove(l.itemId)),
|
||||||
child: const Text('撤销',
|
child: Text('撤销',
|
||||||
style: TextStyle(fontSize: 12, color: AppTheme.primary)),
|
style: TextStyle(fontSize: 12, color: context.tokens.primary)),
|
||||||
)
|
)
|
||||||
: OutlinedButton(
|
: OutlinedButton(
|
||||||
onPressed: _submitting ? null : () => _confirmLine(l),
|
onPressed: _submitting ? null : () => _confirmLine(l),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: AppTheme.danger,
|
foregroundColor: context.tokens.danger,
|
||||||
side: const BorderSide(color: AppTheme.danger),
|
side: BorderSide(color: context.tokens.danger),
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
import 'write_guard.dart';
|
import 'write_guard.dart';
|
||||||
|
|
||||||
/// 入/出库单行内操作按钮,列表表格与移动端卡片、入库与出库两屏共用。
|
/// 入/出库单行内操作按钮,列表表格与移动端卡片、入库与出库两屏共用。
|
||||||
@@ -12,6 +12,7 @@ import 'write_guard.dart';
|
|||||||
/// - [afterPrint]:紧随「打印」之后的附加按钮(入库的「打标签」;出库传空)。
|
/// - [afterPrint]:紧随「打印」之后的附加按钮(入库的「打标签」;出库传空)。
|
||||||
/// - 各 `on*` 回调由调用方绑定到对应单据的确认/跳转逻辑。
|
/// - 各 `on*` 回调由调用方绑定到对应单据的确认/跳转逻辑。
|
||||||
List<Widget> buildOrderRowActions({
|
List<Widget> buildOrderRowActions({
|
||||||
|
required BuildContext context,
|
||||||
required bool readonly,
|
required bool readonly,
|
||||||
required String status,
|
required String status,
|
||||||
required int orderId,
|
required int orderId,
|
||||||
@@ -56,32 +57,32 @@ List<Widget> buildOrderRowActions({
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return [
|
return [
|
||||||
btn('详情', AppTheme.primary, onDetail),
|
btn('详情', context.tokens.primary, onDetail),
|
||||||
btn('打印', AppTheme.primary, onPrint),
|
btn('打印', context.tokens.primary, onPrint),
|
||||||
...afterPrint,
|
...afterPrint,
|
||||||
if (!readonly && status == 'approved')
|
if (!readonly && status == 'approved')
|
||||||
WriteGuard(child: btn('结清', AppTheme.accent, onSettle)),
|
WriteGuard(child: btn('结清', context.tokens.accent, onSettle)),
|
||||||
// 退单(已审核单退货):红色实心按钮突出(与原型一致),按权限显示
|
// 退单(已审核单退货):红色实心按钮突出(与原型一致),按权限显示
|
||||||
if (!readonly && status == 'approved' && canReturn)
|
if (!readonly && status == 'approved' && canReturn)
|
||||||
WriteGuard(
|
WriteGuard(
|
||||||
child: filledBtn('退单', AppTheme.danger, onReturn,
|
child: filledBtn('退单', context.tokens.danger, onReturn,
|
||||||
key: Key('btn_return_$orderId'))),
|
key: Key('btn_return_$orderId'))),
|
||||||
if (!readonly && status == 'draft') ...[
|
if (!readonly && status == 'draft') ...[
|
||||||
WriteGuard(child: btn('修改', AppTheme.primary, onEdit)),
|
WriteGuard(child: btn('修改', context.tokens.primary, onEdit)),
|
||||||
WriteGuard(child: btn('删除', AppTheme.danger, onDelete)),
|
WriteGuard(child: btn('删除', context.tokens.danger, onDelete)),
|
||||||
WriteGuard(child: btn('提交', AppTheme.primary, onSubmit)),
|
WriteGuard(child: btn('提交', context.tokens.primary, onSubmit)),
|
||||||
],
|
],
|
||||||
if (!readonly && status == 'pending') ...[
|
if (!readonly && status == 'pending') ...[
|
||||||
WriteGuard(
|
WriteGuard(
|
||||||
child: btn('通过', AppTheme.success, onApprove,
|
child: btn('通过', context.tokens.success, onApprove,
|
||||||
key: Key('btn_approve_$orderId'))),
|
key: Key('btn_approve_$orderId'))),
|
||||||
WriteGuard(
|
WriteGuard(
|
||||||
child: btn('拒绝', AppTheme.danger, onReject,
|
child: btn('拒绝', context.tokens.danger, onReject,
|
||||||
key: Key('btn_reject_$orderId'))),
|
key: Key('btn_reject_$orderId'))),
|
||||||
// 撤回(审核中→草稿):管理员/超管任意单、操作员本人单可见,用 warn 样式
|
// 撤回(审核中→草稿):管理员/超管任意单、操作员本人单可见,用 warn 样式
|
||||||
if (canWithdraw)
|
if (canWithdraw)
|
||||||
WriteGuard(
|
WriteGuard(
|
||||||
child: btn('撤回', AppTheme.warning, onWithdraw,
|
child: btn('撤回', context.tokens.warn, onWithdraw,
|
||||||
key: Key('btn_withdraw_$orderId'))),
|
key: Key('btn_withdraw_$orderId'))),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
class PageScaffold extends StatefulWidget {
|
class PageScaffold extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
@@ -54,14 +54,14 @@ class _PageScaffoldState extends State<PageScaffold>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
color: AppTheme.surface,
|
color: context.tokens.surface,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabAlignment: TabAlignment.start,
|
tabAlignment: TabAlignment.start,
|
||||||
labelColor: AppTheme.primary,
|
labelColor: context.tokens.primary,
|
||||||
unselectedLabelColor: AppTheme.textSecondary,
|
unselectedLabelColor: context.tokens.muted,
|
||||||
indicatorColor: AppTheme.primary,
|
indicatorColor: context.tokens.primary,
|
||||||
indicatorWeight: 2,
|
indicatorWeight: 2,
|
||||||
labelStyle:
|
labelStyle:
|
||||||
const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import '../core/utils/dialog_util.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/responsive/responsive.dart';
|
import '../core/responsive/responsive.dart';
|
||||||
import 'package:lpinyin/lpinyin.dart';
|
import 'package:lpinyin/lpinyin.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
class OptionItem {
|
class OptionItem {
|
||||||
final int id;
|
final int id;
|
||||||
@@ -100,18 +100,18 @@ class SearchableOptionField extends StatelessWidget {
|
|||||||
suffixIcon: selected
|
suffixIcon: selected
|
||||||
? GestureDetector(
|
? GestureDetector(
|
||||||
onTap: () => onChanged(null),
|
onTap: () => onChanged(null),
|
||||||
child: const Icon(Icons.close,
|
child: Icon(Icons.close,
|
||||||
size: 14, color: AppTheme.textSecondary),
|
size: 14, color: context.tokens.muted),
|
||||||
)
|
)
|
||||||
: const Icon(Icons.arrow_drop_down,
|
: Icon(Icons.arrow_drop_down,
|
||||||
size: 16, color: AppTheme.textSecondary),
|
size: 16, color: context.tokens.muted),
|
||||||
),
|
),
|
||||||
isEmpty: !selected,
|
isEmpty: !selected,
|
||||||
child: Text(
|
child: Text(
|
||||||
selected ? _displayText : hint,
|
selected ? _displayText : hint,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: selected ? Colors.black87 : AppTheme.textSecondary,
|
color: selected ? Colors.black87 : context.tokens.muted,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -269,7 +269,7 @@ class _SearchDialogState extends State<_SearchDialog> {
|
|||||||
? Center(
|
? Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
canCreate ? '无匹配结果,可新增到基础数据' : '无匹配结果',
|
canCreate ? '无匹配结果,可新增到基础数据' : '无匹配结果',
|
||||||
style: const TextStyle(color: AppTheme.textSecondary),
|
style: TextStyle(color: context.tokens.muted),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
@@ -287,7 +287,7 @@ class _SearchDialogState extends State<_SearchDialog> {
|
|||||||
: null,
|
: null,
|
||||||
selected: isSelected,
|
selected: isSelected,
|
||||||
selectedTileColor:
|
selectedTileColor:
|
||||||
AppTheme.primary.withValues(alpha: 0.08),
|
context.tokens.primary.withValues(alpha: 0.08),
|
||||||
onTap: () => Navigator.of(context).pop(opt.id),
|
onTap: () => Navigator.of(context).pop(opt.id),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -316,8 +316,8 @@ class _SearchDialogState extends State<_SearchDialog> {
|
|||||||
if (widget.selectedId != null)
|
if (widget.selectedId != null)
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(-1),
|
onPressed: () => Navigator.of(context).pop(-1),
|
||||||
child: const Text('清除选择',
|
child: Text('清除选择',
|
||||||
style: TextStyle(color: AppTheme.textSecondary)),
|
style: TextStyle(color: context.tokens.muted)),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(null),
|
onPressed: () => Navigator.of(context).pop(null),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../core/theme/app_theme.dart';
|
import '../core/theme/context_tokens.dart';
|
||||||
|
|
||||||
enum OrderStatus { draft, pending, approved, rejected }
|
enum OrderStatus { draft, pending, approved, rejected }
|
||||||
|
|
||||||
@@ -29,19 +29,19 @@ class StatusBadge extends StatelessWidget {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case OrderStatus.draft:
|
case OrderStatus.draft:
|
||||||
bg = const Color(0xFFF5F5F5);
|
bg = const Color(0xFFF5F5F5);
|
||||||
fg = AppTheme.textSecondary;
|
fg = context.tokens.muted;
|
||||||
break;
|
break;
|
||||||
case OrderStatus.pending:
|
case OrderStatus.pending:
|
||||||
bg = const Color(0xFFFFF3E0);
|
bg = const Color(0xFFFFF3E0);
|
||||||
fg = AppTheme.accent;
|
fg = context.tokens.accent;
|
||||||
break;
|
break;
|
||||||
case OrderStatus.approved:
|
case OrderStatus.approved:
|
||||||
bg = const Color(0xFFE8F5E9);
|
bg = const Color(0xFFE8F5E9);
|
||||||
fg = AppTheme.success;
|
fg = context.tokens.success;
|
||||||
break;
|
break;
|
||||||
case OrderStatus.rejected:
|
case OrderStatus.rejected:
|
||||||
bg = const Color(0xFFFFEBEE);
|
bg = const Color(0xFFFFEBEE);
|
||||||
fg = AppTheme.danger;
|
fg = context.tokens.danger;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/core/utils/date_util.dart';
|
import 'package:jiu_client/core/utils/date_util.dart';
|
||||||
import 'package:jiu_client/widgets/date_picker_field.dart';
|
import 'package:jiu_client/widgets/date_picker_field.dart';
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ void main() {
|
|||||||
group('DatePickerField', () {
|
group('DatePickerField', () {
|
||||||
testWidgets('渲染可键入框 + 日历图标', (tester) async {
|
testWidgets('渲染可键入框 + 日历图标', (tester) async {
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: DatePickerField(value: '2026-06-19', onChanged: (_) {}),
|
body: DatePickerField(value: '2026-06-19', onChanged: (_) {}),
|
||||||
),
|
),
|
||||||
@@ -44,6 +46,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('初始值回显到输入框', (tester) async {
|
testWidgets('初始值回显到输入框', (tester) async {
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: DatePickerField(value: '2026-06-19', onChanged: (_) {}),
|
body: DatePickerField(value: '2026-06-19', onChanged: (_) {}),
|
||||||
),
|
),
|
||||||
@@ -55,6 +58,7 @@ void main() {
|
|||||||
testWidgets('只输年份归一为 yyyy-01-01', (tester) async {
|
testWidgets('只输年份归一为 yyyy-01-01', (tester) async {
|
||||||
String? out;
|
String? out;
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: DatePickerField(onChanged: (v) => out = v),
|
body: DatePickerField(onChanged: (v) => out = v),
|
||||||
),
|
),
|
||||||
@@ -66,6 +70,7 @@ void main() {
|
|||||||
testWidgets('年月归一为 yyyy-MM-01', (tester) async {
|
testWidgets('年月归一为 yyyy-MM-01', (tester) async {
|
||||||
String? out;
|
String? out;
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: DatePickerField(onChanged: (v) => out = v),
|
body: DatePickerField(onChanged: (v) => out = v),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:jiu_client/core/models/page_result.dart';
|
import 'package:jiu_client/core/models/page_result.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/models/license.dart';
|
import 'package:jiu_client/models/license.dart';
|
||||||
import 'package:jiu_client/models/partner.dart';
|
import 'package:jiu_client/models/partner.dart';
|
||||||
import 'package:jiu_client/providers/license_provider.dart';
|
import 'package:jiu_client/providers/license_provider.dart';
|
||||||
@@ -103,7 +104,7 @@ Widget _buildApp({
|
|||||||
() => _FakePartnerNotifier('customer', customerState),
|
() => _FakePartnerNotifier('customer', customerState),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(home: Scaffold(body: PartnersScreen())),
|
child: MaterialApp(theme: buildTheme('a'), home: const Scaffold(body: PartnersScreen())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ Widget _buildLoadingApp() {
|
|||||||
() => _FakeLoadingPartnerNotifier('customer'),
|
() => _FakeLoadingPartnerNotifier('customer'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(home: Scaffold(body: PartnersScreen())),
|
child: MaterialApp(theme: buildTheme('a'), home: const Scaffold(body: PartnersScreen())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:jiu_client/core/api/api_client.dart';
|
|||||||
import 'package:jiu_client/core/auth/auth_state.dart';
|
import 'package:jiu_client/core/auth/auth_state.dart';
|
||||||
import 'package:jiu_client/core/config/license_copy.dart';
|
import 'package:jiu_client/core/config/license_copy.dart';
|
||||||
import 'package:jiu_client/core/models/page_result.dart';
|
import 'package:jiu_client/core/models/page_result.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/models/license.dart';
|
import 'package:jiu_client/models/license.dart';
|
||||||
import 'package:jiu_client/models/partner.dart';
|
import 'package:jiu_client/models/partner.dart';
|
||||||
import 'package:jiu_client/providers/license_provider.dart';
|
import 'package:jiu_client/providers/license_provider.dart';
|
||||||
@@ -76,7 +77,7 @@ Widget _buildApp({required bool readonly}) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(home: Scaffold(body: PartnersScreen())),
|
child: MaterialApp(theme: buildTheme('a'), home: const Scaffold(body: PartnersScreen())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/widgets/searchable_option_field.dart';
|
import 'package:jiu_client/widgets/searchable_option_field.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -8,6 +9,7 @@ void main() {
|
|||||||
Future<int?> Function(String)? onCreate,
|
Future<int?> Function(String)? onCreate,
|
||||||
}) {
|
}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: SearchableOptionField(
|
body: SearchableOptionField(
|
||||||
options: [OptionItem(id: 1, name: '53度')],
|
options: [OptionItem(id: 1, name: '53度')],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:jiu_client/core/models/page_result.dart';
|
import 'package:jiu_client/core/models/page_result.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/models/license.dart';
|
import 'package:jiu_client/models/license.dart';
|
||||||
import 'package:jiu_client/models/stock_in.dart';
|
import 'package:jiu_client/models/stock_in.dart';
|
||||||
import 'package:jiu_client/providers/license_provider.dart';
|
import 'package:jiu_client/providers/license_provider.dart';
|
||||||
@@ -119,7 +120,7 @@ Widget _buildApp(AsyncValue<PageResult<StockInOrder>> state) {
|
|||||||
licenseProvider.overrideWith(() => _FakeLicenseNotifier()),
|
licenseProvider.overrideWith(() => _FakeLicenseNotifier()),
|
||||||
stockInListProvider.overrideWith(() => _FakeStockInNotifier(state)),
|
stockInListProvider.overrideWith(() => _FakeStockInNotifier(state)),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(routerConfig: router),
|
child: MaterialApp.router(theme: buildTheme('a'), routerConfig: router),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +144,7 @@ Widget _buildLoadingApp() {
|
|||||||
licenseProvider.overrideWith(() => _FakeLicenseNotifier()),
|
licenseProvider.overrideWith(() => _FakeLicenseNotifier()),
|
||||||
stockInListProvider.overrideWith(() => _FakeLoadingStockInNotifier()),
|
stockInListProvider.overrideWith(() => _FakeLoadingStockInNotifier()),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(routerConfig: router),
|
child: MaterialApp.router(theme: buildTheme('a'), routerConfig: router),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:jiu_client/core/auth/auth_state.dart';
|
import 'package:jiu_client/core/auth/auth_state.dart';
|
||||||
import 'package:jiu_client/core/config/license_copy.dart';
|
import 'package:jiu_client/core/config/license_copy.dart';
|
||||||
|
import 'package:jiu_client/core/theme/themes.dart';
|
||||||
import 'package:jiu_client/models/license.dart';
|
import 'package:jiu_client/models/license.dart';
|
||||||
import 'package:jiu_client/providers/license_provider.dart';
|
import 'package:jiu_client/providers/license_provider.dart';
|
||||||
import 'package:jiu_client/widgets/write_guard.dart';
|
import 'package:jiu_client/widgets/write_guard.dart';
|
||||||
@@ -37,6 +38,7 @@ Widget _harness({
|
|||||||
licenseProvider.overrideWith(() => _FakeLicenseNotifier(lic)),
|
licenseProvider.overrideWith(() => _FakeLicenseNotifier(lic)),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
theme: buildTheme('a'),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: WriteGuard(
|
body: WriteGuard(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
|
|||||||
Reference in New Issue
Block a user