实现商品发布/编辑表单中的价格库存表单区,包括价格输入框、库存输入框和商品编码输入框。
在 product-form.component.html 中添加了价格库存卡片:
<!-- 价格库存 -->
<mat-card class="form-section">
<mat-card-header>
<mat-card-title>价格库存</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="form-row">
<!-- 商品价格 -->
<mat-form-field appearance="outline" class="form-field-half">
<mat-label>商品价格(元)</mat-label>
<input
matInput
type="number"
formControlName="price"
placeholder="0.00"
min="0"
step="0.01"
/>
<mat-error *ngIf="productForm.get('price')?.invalid">
{{ getErrorMessage('price') }}
</mat-error>
</mat-form-field>
<!-- 商品库存 -->
<mat-form-field appearance="outline" class="form-field-half">
<mat-label>商品库存</mat-label>
<input
matInput
type="number"
formControlName="stock"
placeholder="0"
min="0"
step="1"
/>
<mat-error *ngIf="productForm.get('stock')?.invalid">
{{ getErrorMessage('stock') }}
</mat-error>
</mat-form-field>
</div>
<!-- 商品编码 -->
<mat-form-field appearance="outline" class="full-width">
<mat-label>商品编码(可选)</mat-label>
<input
matInput
formControlName="code"
placeholder="请输入商品编码"
/>
</mat-form-field>
</mat-card-content>
</mat-card>
在 product-form.component.ts 中的表单初始化方法中已包含:
private initializeForm(): void {
this.productForm = this.fb.group({
title: ['', [Validators.required, Validators.maxLength(60)]],
subtitle: ['', Validators.maxLength(100)],
category: ['', Validators.required],
categoryPath: [[]],
images: [[]],
description: ['', Validators.required],
price: [0, [Validators.required, Validators.min(0)]], // 价格字段
stock: [0, [Validators.required, Validators.min(0)]], // 库存字段
code: [''], // 商品编码字段
status: [ProductStatus.OffShelf]
});
}
在 product-form.component.scss 中添加了响应式布局样式:
.form-row {
display: flex;
gap: 16px;
margin-bottom: 16px;
.form-field-half {
flex: 1;
}
}
// 响应式适配
@media (max-width: 768px) {
.form-row {
flex-direction: column;
gap: 0;
}
}
价格字段:
Validators.required)Validators.min(0)),不允许负数step="0.01")库存字段:
Validators.required)Validators.min(0)),不允许负数step="1")商品编码字段:
通过 getErrorMessage() 方法提供友好的错误提示:
getErrorMessage(fieldName: string): string {
const field = this.productForm.get(fieldName);
if (!field) return '';
if (field.hasError('required')) {
return '此字段为必填项';
}
if (field.hasError('min')) {
return '值不能小于 0';
}
return '';
}
测试文件 product-form.component.spec.ts 中包含以下测试:
表单初始化测试:
it('应该初始化表单', () => {
fixture.detectChanges();
expect(component.productForm.get('price')).toBeDefined();
expect(component.productForm.get('stock')).toBeDefined();
});
表单验证测试:
it('表单验证应该正确工作', () => {
component.productForm.patchValue({
title: '测试商品',
category: '电子产品',
description: '测试描述',
price: 99.99,
stock: 100
});
expect(component.productForm.valid).toBe(true);
});
价格最小值验证测试:
it('价格字段应该有最小值验证', () => {
const priceControl = component.productForm.get('price');
priceControl?.setValue(-1);
expect(priceControl?.hasError('min')).toBe(true);
priceControl?.setValue(0);
expect(priceControl?.hasError('min')).toBe(false);
});
保存功能测试:
✅ Requirement 9.5: 实现价格、库存和商品编码输入字段
price: [0, [Validators.required, Validators.min(0)]]
stock: [0, [Validators.required, Validators.min(0)]]
code: ['']
type="number" + step="0.01"type="number" + step="1"type="text"mat-form-field 提供统一的输入框样式mat-error 显示验证错误appearance="outline" 提供轮廓样式✅ 所有子任务已完成:
任务 8.5 已完成。可以继续执行任务 8.6:实现底部固定操作栏。
完成时间: 2025-12-13 验证状态: ✅ 通过