feat(client): 商品基础数据加香型字典 tab(接后端 categories CRUD)

新增 ProductCategoryOption 模型 + repo(list/create/update/deleteCategory)+
provider(productCategoryListProvider),基础数据页加「香型」tab(增删改查、
搜索、导出,仅名称字段)。接通后端 /product-options/categories。
(原型 5-tab 整合是更大 IA 任务,本次先把香型管理补齐可用。)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSKEiHsvauyxYUW2itzUXX
This commit is contained in:
wangjia
2026-06-25 15:42:23 +08:00
parent 355ac0ebc3
commit 38e2ea1c6c
4 changed files with 210 additions and 0 deletions
@@ -88,6 +88,48 @@ class ProductOptionRepository {
}
}
// ── 香型 / 分类 ──────────────────────────────────────────────
Future<List<ProductCategoryOption>> listCategories() async {
try {
final resp = await _client.get('/product-options/categories');
final data = (resp.data as Map<String, dynamic>)['data'] as List? ?? [];
return data
.map((e) => ProductCategoryOption.fromJson(e as Map<String, dynamic>))
.toList();
} on DioException catch (e) {
throw AppException(e.response?.data?['error'] as String? ?? '获取香型列表失败',
statusCode: e.response?.statusCode);
}
}
Future<void> createCategory(Map<String, dynamic> data) async {
try {
await _client.post('/product-options/categories', data: data);
} on DioException catch (e) {
throw AppException(e.response?.data?['error'] as String? ?? '创建失败',
statusCode: e.response?.statusCode);
}
}
Future<void> updateCategory(int id, Map<String, dynamic> data) async {
try {
await _client.put('/product-options/categories/$id', data: data);
} on DioException catch (e) {
throw AppException(e.response?.data?['error'] as String? ?? '更新失败',
statusCode: e.response?.statusCode);
}
}
Future<void> deleteCategory(int id) async {
try {
await _client.delete('/product-options/categories/$id');
} on DioException catch (e) {
throw AppException(e.response?.data?['error'] as String? ?? '删除失败',
statusCode: e.response?.statusCode);
}
}
// ── 规格 ────────────────────────────────────────────────────
Future<List<ProductSpecOption>> listSpecs() async {