refine multi-code boxing: inline finish button with P3 shortcut

- Move 完成本箱 button to same row as quantity input, each taking half width
- Add P3 (BUTTON_L2, key code 104) hardware key support for finishing box
- Display P3 shortcut label on the finish button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-05-18 17:27:12 +08:00
parent f60745607f
commit d9f8e066b3

View File

@@ -85,6 +85,7 @@ class BoxingPage extends StatefulWidget {
class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
static const _androidKeyCodeButtonR1 = 103;
static const _androidKeyCodeButtonL2 = 104;
final _scannerService = ScannerService();
final _apiService = ApiService();
@@ -194,6 +195,12 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
if (_isModeSwitchKey(event)) {
_cycleMode();
}
if (_isFinishBoxKey(event)) {
final hasItems = _visibleManyToOnePackedItems.isNotEmpty;
if (hasItems && _mode == BoxingMode.multiCode) {
_finishManyToOneBox();
}
}
}
bool _isModeSwitchKey(KeyEvent event) {
@@ -203,6 +210,12 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
_androidKeyCodeFromLogicalKey(key) == _androidKeyCodeButtonR1;
}
bool _isFinishBoxKey(KeyEvent event) {
final key = event.logicalKey;
return key == LogicalKeyboardKey.gameButtonLeft2 ||
_androidKeyCodeFromLogicalKey(key) == _androidKeyCodeButtonL2;
}
int? _androidKeyCodeFromLogicalKey(LogicalKeyboardKey key) {
final keyId = key.keyId;
const androidPlane = LogicalKeyboardKey.androidPlane;
@@ -2504,35 +2517,7 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
mainAxisSize: MainAxisSize.min,
children: [
_buildManyToOneScanBar(hasScan),
_buildManyToOneSubmitRow(hasScan),
Padding(
padding: const EdgeInsets.fromLTRB(12, 6, 12, 8),
child: SizedBox(
width: double.infinity,
height: 36,
child: OutlinedButton(
onPressed: hasItems ? _finishManyToOneBox : null,
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
side: BorderSide(
color: hasItems
? Colors.grey.shade600
: Colors.grey.shade300,
),
),
child: Text(
'完成本箱',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: hasItems ? Colors.black87 : Colors.grey.shade400,
),
),
),
),
),
_buildManyToOneSubmitRow(hasScan, hasItems),
],
),
);
@@ -2586,93 +2571,154 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
);
}
Widget _buildManyToOneSubmitRow(bool hasScan) {
Widget _buildManyToOneSubmitRow(bool hasScan, bool hasItems) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
padding: const EdgeInsets.fromLTRB(12, 7, 12, 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'数量',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: hasScan ? Colors.black54 : Colors.grey.shade400,
),
),
const SizedBox(width: 6),
// Left half: quantity label + input + confirm
Expanded(
child: SizedBox(
height: 34,
child: TextField(
controller: _quantityController,
focusNode: _quantityFocusNode,
enabled: hasScan && !_isSubmitting,
keyboardType: TextInputType.none,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (_) => setState(() {}),
onEditingComplete: () {},
onSubmitted: (_) => _submitFromKeyboard(),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
flex: 1,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'数量',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: hasScan ? Colors.black54 : Colors.grey.shade400,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(
color: _quantityTooHigh
? Colors.red
: Colors.grey.shade300,
),
const SizedBox(width: 6),
Expanded(
child: SizedBox(
height: 34,
child: TextField(
controller: _quantityController,
focusNode: _quantityFocusNode,
enabled: hasScan && !_isSubmitting,
keyboardType: TextInputType.none,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (_) => setState(() {}),
onEditingComplete: () {},
onSubmitted: (_) => _submitFromKeyboard(),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(
color: _quantityTooHigh
? Colors.red
: Colors.grey.shade300,
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(color: Colors.grey.shade200),
),
filled: !hasScan,
fillColor: Colors.grey.shade100,
),
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(color: Colors.grey.shade200),
),
const SizedBox(width: 8),
SizedBox(
height: 34,
child: ElevatedButton(
onPressed: _canSubmit ? _submit : null,
style: ElevatedButton.styleFrom(
backgroundColor: _canSubmit
? Theme.of(context).colorScheme.primary
: Colors.grey.shade200,
foregroundColor: _canSubmit
? Colors.white
: Colors.grey.shade400,
padding: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
child: _isSubmitting
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'确认',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
),
),
),
filled: !hasScan,
fillColor: Colors.grey.shade100,
),
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(width: 8),
SizedBox(
height: 34,
child: ElevatedButton(
onPressed: _canSubmit ? _submit : null,
style: ElevatedButton.styleFrom(
backgroundColor: _canSubmit
? Theme.of(context).colorScheme.primary
: Colors.grey.shade200,
foregroundColor: _canSubmit
? Colors.white
: Colors.grey.shade400,
padding: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
// Right half: 完成本箱
Expanded(
flex: 1,
child: SizedBox(
height: 34,
child: OutlinedButton(
onPressed: hasItems ? _finishManyToOneBox : null,
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
side: BorderSide(
color: hasItems
? Colors.grey.shade600
: Colors.grey.shade300,
),
),
),
child: _isSubmitting
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'确认',
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'完成本箱',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: hasItems ? Colors.black87 : Colors.grey.shade400,
),
),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: hasItems
? Colors.grey.shade200
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(3),
),
child: Text(
'P3',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
color: hasItems ? Colors.black54 : Colors.grey.shade400,
),
),
),
],
),
),
),
),
],