For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Implement 7 production management modules (耗材管理、试剂管理、SOP文档、工艺工序分类、工艺产出物类型、耗材包管理、工艺库管理) with backend API + frontend UI, following existing codebase patterns exactly. 工艺路线配置 deferred to next iteration.
Architecture: FastAPI backend with flat-function CRUD (no service layer), Vue 3 + Element Plus frontend with standard list page template. Batch 1 (5 simple CRUD modules) has no dependencies. Batch 2 (耗材包+工艺库) depends on Batch 1. All modules share a new API route file app/api/production.py and frontend route group under /production.
Tech Stack: Python 3.9 / FastAPI / SQLAlchemy / Pydantic v2 (backend); Vue 3 / TypeScript / Element Plus / Tailwind CSS 4 (frontend)
Design Spec: docs/superpowers/specs/2026-05-17-production-management-design.md
Key Reference Files (READ FIRST):
backend/app/crud/item_unit.py — canonical simple CRUDbackend/app/api/admin.py (item-unit section, lines 968-1059)backend/app/models/models.py (class BizItemUnit)web-admin/src/views/system/unit/index.vue — canonical list pageweb-admin/src/api/admin.ts (fetchXxxList/fetchCreateXxx/fetchUpdateXxx/fetchDeleteXxx)web-admin/src/router/modules/sqxp.tsweb-admin/src/api/business.ts (fetchUploadAttachment)| File | Action | Purpose |
|---|---|---|
docs/sql/production.sql |
CREATE | All 11 tables DDL + menu INSERT statements |
backend/app/models/models.py |
MODIFY | Add 11 model classes at end of file |
backend/app/crud/consumable.py |
CREATE | 耗材 CRUD |
backend/app/crud/reagent.py |
CREATE | 试剂 CRUD |
backend/app/crud/sop_document.py |
CREATE | SOP文档 CRUD |
backend/app/crud/process_category.py |
CREATE | 工艺工序分类 CRUD |
backend/app/crud/output_type.py |
CREATE | 工艺产出物类型 CRUD |
backend/app/crud/consumable_pack.py |
CREATE | 耗材包 CRUD (主表+明细) |
backend/app/crud/process.py |
CREATE | 工艺库 CRUD (主表+3子表) |
backend/app/api/production.py |
CREATE | All 7 modules API routes |
backend/app/api/__init__.py |
MODIFY | Import + export production_router |
backend/app/main.py |
MODIFY | Import + register production_router |
| File | Action | Purpose |
|---|---|---|
web-admin/src/api/production.ts |
CREATE | All 7 modules API functions |
web-admin/src/router/modules/sqxp.ts |
MODIFY | Add 生产管理 route group |
web-admin/src/views/production/consumable/index.vue |
CREATE | 耗材管理标准列表页 |
web-admin/src/views/production/reagent/index.vue |
CREATE | 试剂管理标准列表页 |
web-admin/src/views/production/sop/index.vue |
CREATE | SOP文档列表页(含附件上传) |
web-admin/src/views/production/process-category/index.vue |
CREATE | 工艺工序分类标准列表页 |
web-admin/src/views/production/output-type/index.vue |
CREATE | 工艺产出物类型标准列表页 |
web-admin/src/views/production/consumable-pack/index.vue |
CREATE | 耗材包管理列表页(含明细子表) |
web-admin/src/views/production/process/index.vue |
CREATE | 工艺库管理列表页(Tab式编辑) |
Files:
Create: docs/sql/production.sql
[ ] Step 1: Create the SQL DDL file with all 11 tables and menu entries
Create docs/sql/production.sql with the complete DDL. All tables follow project conventions: id VARCHAR(64) UUID, business fields first, then public fields (tenant_no through del_flag), ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci.
Tables to create:
biz_consumable — consumable_code, consumable_name, spec_model, standard_usage, descriptionbiz_reagent — reagent_code, reagent_name, spec, standard_usage, unit, descriptionbiz_sop_document — sop_code, sop_name, version_no, sop_attachmentsbiz_process_category — category_code, category_namebiz_output_type — type_code, type_namebiz_consumable_pack — pack_code, pack_name, usage_scene, pack_type, statusbiz_consumable_pack_item — pack_id, consumable_code, consumable_name, spec_model, standard_usage, remarkbiz_process — process_code, category_id, process_name, description, equipment_name, estimated_duration, sop_idsbiz_process_reagent — process_id, reagent_code, reagent_name, spec, standard_usage, unit, is_key_materialbiz_process_consumable — process_id, consumable_code, consumable_name, spec_model, standard_usage, is_required, remarkbiz_process_output — process_id, output_type_id, output_name, output_barcodeAlso include INSERT statements for sys_menu:
menu_code (e.g., production:consumable:list, production:reagent:list, etc.)Verification: File contains valid SQL, all tables have public fields, all tables use VARCHAR(64) for id, charset utf8mb4.
git add docs/sql/production.sql
git commit -m "feat: add production management SQL DDL and menu entries"
Files:
backend/app/models/models.py (append at end)Modify: backend/app/models/__init__.py (re-export new models)
[ ] Step 1: Add 11 model classes to models.py
Append after the last class in backend/app/models/models.py. Follow the exact pattern of BizItemUnit:
Base (from app.core.database)Column(...) with explicit typescreate_time uses default=datetime.now, update_time uses default=datetime.now, onupdate=datetime.nowdel_flag uses SmallInteger with default=0Models:
BizConsumable → biz_consumableBizReagent → biz_reagentBizSopDocument → biz_sop_documentBizProcessCategory → biz_process_categoryBizOutputType → biz_output_typeBizConsumablePack → biz_consumable_packBizConsumablePackItem → biz_consumable_pack_itemBizProcess → biz_processBizProcessReagent → biz_process_reagentBizProcessConsumable → biz_process_consumableBizProcessOutput → biz_process_output
[ ] Step 2: Re-export in backend/app/models/__init__.py
Add all 11 new classes to the imports and __all__ list.
Run: cd backend && source venv/bin/activate && python -c "from app.models.models import BizConsumable, BizReagent, BizSopDocument, BizProcessCategory, BizOutputType, BizConsumablePack, BizConsumablePackItem, BizProcess, BizProcessReagent, BizProcessConsumable, BizProcessOutput; print('All models imported OK')"
git add backend/app/models/
git commit -m "feat: add 11 production management SQLAlchemy models"
Files:
backend/app/crud/consumable.pybackend/app/crud/reagent.pybackend/app/crud/sop_document.pybackend/app/crud/process_category.pybackend/app/crud/output_type.pyEach CRUD file follows the EXACT pattern from backend/app/crud/item_unit.py:
_row_to_dict(obj) — returns dict with camelCase keyslist_xxx(db, *, page, page_size, keyword, tenant_no) — paginated list with keyword search + tenant filter + soft-delete filterget_xxx(db, id) — single record by id with del_flag=0 checkxxx_code_exists(db, code, tenant_no, exclude_id=None) — uniqueness check on code fieldcreate_xxx(db, *, ...fields..., tenant_no, user_id) — validate + create with generate_uuid()update_xxx(db, id, *, ...fields..., user_id) — validate + updatesoft_delete_xxx(db, id) — set del_flag=1total_pages(total, page_size) — pagination helperbackend/app/crud/consumable.pyFields: consumable_code, consumable_name, spec_model, standard_usage, description Keyword search on: consumable_code, consumable_name
backend/app/crud/reagent.pyFields: reagent_code, reagent_name, spec, standard_usage, unit, description Keyword search on: reagent_code, reagent_name
backend/app/crud/sop_document.pyFields: sop_code, sop_name, version_no, sop_attachments
Keyword search on: sop_code, sop_name
NOTE: sop_attachments stores comma-separated URLs, returned as-is in _row_to_dict
backend/app/crud/process_category.pyFields: category_code, category_name Keyword search on: category_code, category_name
backend/app/crud/output_type.pyFields: type_code, type_name Keyword search on: type_code, type_name
Run: cd backend && source venv/bin/activate && python -c "from app.crud import consumable, reagent, sop_document, process_category, output_type; print('All CRUD modules OK')"
git add backend/app/crud/consumable.py backend/app/crud/reagent.py backend/app/crud/sop_document.py backend/app/crud/process_category.py backend/app/crud/output_type.py
git commit -m "feat: add batch 1 CRUD modules (consumable, reagent, sop, process-category, output-type)"
Files:
backend/app/crud/consumable_pack.pybackend/app/crud/process.pybackend/app/crud/consumable_pack.pyThis module needs:
BizConsumablePack main table CRUD (list, get, create, update, soft_delete)BizConsumablePackItem detail table CRUDpack_id._row_to_dict for main includes items: [...])_row_to_dict for main includes: pack items as nested list_row_to_dict for pack item:
{
"id": item.id,
"packId": item.pack_id,
"consumableCode": item.consumable_code,
"consumableName": item.consumable_name,
"specModel": item.spec_model,
"standardUsage": item.standard_usage,
"remark": item.remark or "",
}
backend/app/crud/process.pyThis module needs:
BizProcess main table CRUDBizProcessReagent sub-table CRUDBizProcessConsumable sub-table CRUDBizProcessOutput sub-table CRUDprocess_id._row_to_dict for process includes:
{
"id": ...,
"processCode": ...,
"categoryId": ...,
"processName": ...,
"description": ...,
"equipmentName": ...,
"estimatedDuration": ...,
"sopIds": ...,
# ... public fields ...
"reagents": [reagent_row_to_dict(r) for r in reagents],
"consumables": [consumable_row_to_dict(c) for c in consumables],
"outputs": [output_row_to_dict(o) for o in outputs],
}
Run: cd backend && source venv/bin/activate && python -c "from app.crud import consumable_pack, process; print('Batch 2 CRUD OK')"
git add backend/app/crud/consumable_pack.py backend/app/crud/process.py
git commit -m "feat: add batch 2 CRUD modules (consumable-pack, process)"
Files:
backend/app/api/production.pybackend/app/api/__init__.pyModify: backend/app/main.py
[ ] Step 1: Create backend/app/api/production.py
Follow the exact pattern from backend/app/api/admin.py (item-units section).
Structure:
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy.orm import Session
from typing import Any, Optional, List
from app.core.database import get_db
from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
from app.core.response import success_response, error_response
from app.crud import consumable as consumable_svc
from app.crud import reagent as reagent_svc
from app.crud import sop_document as sop_document_svc
from app.crud import process_category as process_category_svc
from app.crud import output_type as output_type_svc
from app.crud import consumable_pack as consumable_pack_svc
from app.crud import process as process_svc
from app.models.models import SysUser
router = APIRouter(prefix="/web/admin", tags=["生产管理"])
def _get_tenant_no(db: Session, user: dict) -> str:
# Same pattern as admin.py
...
Endpoints to create (7 modules × 4 CRUD operations = 28 endpoints):
| Module | List (GET) | Create (POST) | Update (PUT) | Delete (DELETE) |
|---|---|---|---|---|
| 耗材 | /consumables |
/consumables |
/consumables/{id} |
/consumables/{id} |
| 试剂 | /reagents |
/reagents |
/reagents/{id} |
/reagents/{id} |
| SOP | /sop-documents |
/sop-documents |
/sop-documents/{id} |
/sop-documents/{id} |
| 工序分类 | /process-categories |
/process-categories |
/process-categories/{id} |
/process-categories/{id} |
| 产出物类型 | /output-types |
/output-types |
/output-types/{id} |
/output-types/{id} |
| 耗材包 | /consumable-packs |
/consumable-packs |
/consumable-packs/{id} |
/consumable-packs/{id} |
| 工艺库 | /processes |
/processes |
/processes/{id} |
/processes/{id} |
Additional list-only endpoints needed by dropdowns:
/reagers/all — all reagents (no pagination, for dropdown in process form)/consumables/all — all consumables (for dropdown in pack/process form)/sop-documents/all — all SOP docs (for multi-select in process form)/process-categories/all — all categories (for dropdown)/output-types/all — all output types (for dropdown)/consumable-packs/{id}/items — pack items (for importing into process)Request body patterns:
Simple CRUD (耗材 as example):
class ConsumableCreateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
consumable_code: str = Field(..., alias="consumableCode")
consumable_name: str = Field(..., alias="consumableName")
spec_model: Optional[str] = Field(None, alias="specModel")
standard_usage: Optional[str] = Field(None, alias="standardUsage")
description: Optional[str] = None
order_num: Optional[int] = Field(0, alias="orderNum")
remark: Optional[str] = None
耗材包 Create/Update (includes items):
class PackItemInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
consumable_code: Optional[str] = Field(None, alias="consumableCode")
consumable_name: Optional[str] = Field(None, alias="consumableName")
spec_model: Optional[str] = Field(None, alias="specModel")
standard_usage: Optional[str] = Field(None, alias="standardUsage")
remark: Optional[str] = None
class ConsumablePackCreateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
pack_code: str = Field(..., alias="packCode")
pack_name: str = Field(..., alias="packName")
usage_scene: Optional[str] = Field(None, alias="usageScene")
pack_type: Optional[str] = Field(None, alias="packType")
status: Optional[str] = "1"
order_num: Optional[int] = Field(0, alias="orderNum")
remark: Optional[str] = None
items: List[PackItemInput] = []
工艺库 Create/Update (includes 3 sub-lists):
class ProcessReagentInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
reagent_code: Optional[str] = Field(None, alias="reagentCode")
reagent_name: Optional[str] = Field(None, alias="reagentName")
spec: Optional[str] = None
standard_usage: Optional[str] = Field(None, alias="standardUsage")
unit: Optional[str] = None
is_key_material: Optional[str] = Field("0", alias="isKeyMaterial")
class ProcessConsumableInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
consumable_code: Optional[str] = Field(None, alias="consumableCode")
consumable_name: Optional[str] = Field(None, alias="consumableName")
spec_model: Optional[str] = Field(None, alias="specModel")
standard_usage: Optional[str] = Field(None, alias="standardUsage")
is_required: Optional[str] = Field("1", alias="isRequired")
remark: Optional[str] = None
class ProcessOutputInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
output_type_id: Optional[str] = Field(None, alias="outputTypeId")
output_name: Optional[str] = Field(None, alias="outputName")
output_barcode: Optional[str] = Field(None, alias="outputBarcode")
class ProcessCreateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
process_code: str = Field(..., alias="processCode")
category_id: Optional[str] = Field(None, alias="categoryId")
process_name: str = Field(..., alias="processName")
description: Optional[str] = None
equipment_name: Optional[str] = Field(None, alias="equipmentName")
estimated_duration: Optional[str] = Field(None, alias="estimatedDuration")
sop_ids: Optional[str] = Field(None, alias="sopIds")
order_num: Optional[int] = Field(0, alias="orderNum")
remark: Optional[str] = None
reagents: List[ProcessReagentInput] = []
consumables: List[ProcessConsumableInput] = []
outputs: List[ProcessOutputInput] = []
Auth patterns:
Depends(require_menu_code("production:xxx:list"))Depends(require_admin_or_super_admin)"All" dropdown endpoints: Depends(require_admin_or_super_admin) (lightweight, no pagination)
[ ] Step 2: Register router in backend/app/api/__init__.py
Add:
from app.api.production import router as production_router
Add production_router to __all__.
backend/app/main.pyAdd import and:
app.include_router(production_router, prefix=settings.API_V1_PREFIX)
Run: cd backend && source venv/bin/activate && python -c "from app.main import app; routes = [r.path for r in app.routes]; prod = [r for r in routes if 'consumable' in r or 'reagent' in r or 'sop-document' in r or 'process-categor' in r or 'output-type' in r or 'consumable-pack' in r or 'processes' in r]; print(f'Production routes: {len(prod)}'); print(prod[:10])"
Expected: 28+ production routes registered.
git add backend/app/api/production.py backend/app/api/__init__.py backend/app/main.py
git commit -m "feat: add production management API routes (7 modules, 28 endpoints)"
Files:
Create: web-admin/src/api/production.ts
[ ] Step 1: Create web-admin/src/api/production.ts
Follow the pattern from web-admin/src/api/admin.ts. Use request.get/post/put/del with params for JSON body and data for FormData.
For each of the 7 modules, define 4 API functions (list, create, update, delete). Also define "all" functions for dropdown data.
import request from '@/utils/http'
// ==================== 耗材管理 ====================
export function fetchConsumableList(params?: { page?: number; pageSize?: number; keyword?: string }) {
return request.get<{ list: any[]; total: number; page: number; pageSize: number; totalPages: number }>({
url: '/web/admin/consumables', params
})
}
export function fetchAllConsumables() {
return request.get<any[]>({ url: '/web/admin/consumables/all' })
}
export function fetchCreateConsumable(data: Record<string, any>) {
return request.post({ url: '/web/admin/consumables', params: data })
}
export function fetchUpdateConsumable(id: string, data: Record<string, any>) {
return request.put({ url: `/web/admin/consumables/${id}`, params: data })
}
export function fetchDeleteConsumable(id: string) {
return request.del({ url: `/web/admin/consumables/${id}` })
}
// ==================== 试剂管理 ====================
// (same pattern: fetchReagentList, fetchAllReagents, fetchCreateReagent, fetchUpdateReagent, fetchDeleteReagent)
// ==================== SOP文档 ====================
// (same pattern: fetchSopList, fetchAllSopDocuments, fetchCreateSop, fetchUpdateSop, fetchDeleteSop)
// ==================== 工艺工序分类 ====================
// (same pattern: fetchProcessCategoryList, fetchAllProcessCategories, fetchCreateProcessCategory, ...)
// ==================== 工艺产出物类型 ====================
// (same pattern: fetchOutputTypeList, fetchAllOutputTypes, fetchCreateOutputType, ...)
// ==================== 耗材包管理 ====================
// (same pattern, but create/update body includes items array)
// ==================== 工艺库管理 ====================
// (same pattern, but create/update body includes reagents, consumables, outputs arrays)
Important convention from codebase: POST/PUT use params for JSON body, showSuccessMessage: true is NOT needed (messages shown in page component).
git add web-admin/src/api/production.ts
git commit -m "feat: add production management frontend API functions"
Files:
Modify: web-admin/src/router/modules/sqxp.ts
[ ] Step 1: Add production route group to sqxp.ts
Add a new top-level route entry. Use a unique ID (e.g., '90000000000000000000000000000001'). Follow the existing pattern of other top-level groups.
{
id: '90000000000000000000000000000001',
name: 'ProductionManagement',
path: '/production',
component: '/index/index',
meta: { title: '生产管理', icon: 'ri:factory-line', keepAlive: false, roles: ['admin'] },
children: [
{
id: '90000000000000000000000000000010',
name: 'ProductionConsumable',
path: 'consumable',
component: '/production/consumable/index',
meta: { title: '耗材管理', keepAlive: true, menuCode: 'production:consumable:list' }
},
{
id: '90000000000000000000000000000020',
name: 'ProductionReagent',
path: 'reagent',
component: '/production/reagent/index',
meta: { title: '试剂管理', keepAlive: true, menuCode: 'production:reagent:list' }
},
{
id: '90000000000000000000000000000030',
name: 'ProductionSop',
path: 'sop',
component: '/production/sop/index',
meta: { title: 'SOP文档', keepAlive: true, menuCode: 'production:sop:list' }
},
{
id: '90000000000000000000000000000040',
name: 'ProductionProcessCategory',
path: 'process-category',
component: '/production/process-category/index',
meta: { title: '工艺工序分类', keepAlive: true, menuCode: 'production:process-category:list' }
},
{
id: '90000000000000000000000000000050',
name: 'ProductionOutputType',
path: 'output-type',
component: '/production/output-type/index',
meta: { title: '工艺产出物类型', keepAlive: true, menuCode: 'production:output-type:list' }
},
{
id: '90000000000000000000000000000060',
name: 'ProductionConsumablePack',
path: 'consumable-pack',
component: '/production/consumable-pack/index',
meta: { title: '耗材包管理', keepAlive: true, menuCode: 'production:consumable-pack:list' }
},
{
id: '90000000000000000000000000000070',
name: 'ProductionProcess',
path: 'process',
component: '/production/process/index',
meta: { title: '工艺库管理', keepAlive: true, menuCode: 'production:process:list' }
}
]
}
NOTE: Add this to sqxpAdminRoutes array in sqxp.ts. Icon only on top-level (sidebar convention).
git add web-admin/src/router/modules/sqxp.ts
git commit -m "feat: add production management frontend routes"
Files:
web-admin/src/views/production/consumable/index.vueweb-admin/src/views/production/reagent/index.vueweb-admin/src/views/production/sop/index.vueweb-admin/src/views/production/process-category/index.vueweb-admin/src/views/production/output-type/index.vueAll 5 pages follow the exact same pattern as web-admin/src/views/system/unit/index.vue (327 lines). Read that file first and replicate its structure.
page-container
page-head (h1 title + description)
panel
panel-top (title-bar-heading with green vertical bar)
filter-block (keyword search + search/reset buttons)
list-toolbar (selected count + add button)
list-body/list-wrap (el-table with columns + pagination)
el-dialog (add/edit form, label-position="top", form-grid-2)
production/consumable/index.vue)defineOptions({ name: 'ProductionConsumable' })
API: fetchConsumableList, fetchCreateConsumable, fetchUpdateConsumable, fetchDeleteConsumable from @/api/production
[ ] Step 2: Create 试剂管理 page (production/reagent/index.vue)
defineOptions({ name: 'ProductionReagent' })
fetchAllItemUnits() (import from @/api/admin)API: fetchReagentList, fetchCreateReagent, fetchUpdateReagent, fetchDeleteReagent from @/api/production
[ ] Step 3: Create SOP文档 page (production/sop/index.vue)
defineOptions({ name: 'ProductionSop' })
ProtocolFormDialog.vue:
el-upload with :auto-upload="false", @change="handleFileChange"fetchUploadAttachment(formData) from @/api/businesssopAttachments fieldAPI: fetchSopList, fetchCreateSop, fetchUpdateSop, fetchDeleteSop from @/api/production
[ ] Step 4: Create 工艺工序分类 page (production/process-category/index.vue)
defineOptions({ name: 'ProductionProcessCategory' })
API: fetchProcessCategoryList, fetchCreateProcessCategory, fetchUpdateProcessCategory, fetchDeleteProcessCategory from @/api/production
[ ] Step 5: Create 工艺产出物类型 page (production/output-type/index.vue)
defineOptions({ name: 'ProductionOutputType' })
API: fetchOutputTypeList, fetchCreateOutputType, fetchUpdateOutputType, fetchDeleteOutputType from @/api/production
[ ] Step 6: Verify all 5 pages load
Run: cd web-admin && pnpm build 2>&1 | tail -5
Expected: Build succeeds with no errors.
git add web-admin/src/views/production/
git commit -m "feat: add batch 1 frontend pages (consumable, reagent, sop, process-category, output-type)"
Files:
Create: web-admin/src/views/production/consumable-pack/index.vue
[ ] Step 1: Create 耗材包管理 page
defineOptions({ name: 'ProductionConsumablePack' })
List page: Standard template with additional columns:
Add/Edit dialog: Larger dialog with 2 sections:
fetchAllConsumables() dropdown or table select)Data flow:
items arrayOn save: Submit main fields + items array together to create/update API
[ ] Step 2: Verify page loads
Run: cd web-admin && pnpm build 2>&1 | tail -5
git add web-admin/src/views/production/consumable-pack/
git commit -m "feat: add consumable pack management frontend page"
Files:
Create: web-admin/src/views/production/process/index.vue
[ ] Step 1: Create 工艺库管理 page
defineOptions({ name: 'ProductionProcess' })
List page: Standard template:
Add/Edit dialog: Wide dialog (width="900px") with el-tabs:
Data flow:
Key considerations:
onMounted or dialog open handler to pre-fetch reference dataDisplay category name in table by maintaining a category map {id: name}
[ ] Step 2: Verify page loads
Run: cd web-admin && pnpm build 2>&1 | tail -5
git add web-admin/src/views/production/process/
git commit -m "feat: add process library management frontend page"
Run: cd web-admin && pnpm build
Expected: Exit code 0, no errors.
Run: cd backend && source venv/bin/activate && python -c "from app.main import app; print('Backend OK')"
cd backend && ./start-local.shcd web-admin && pnpm devTask 1 (SQL)
↓
Task 2 (Models)
↓
Task 3 (CRUD Batch 1) ← independent from Task 6/7
↓
Task 4 (CRUD Batch 2)
↓
Task 5 (API Routes) ← depends on Task 3 + 4
↓
Task 6 (Frontend API) ← independent, can parallel with Task 5
Task 7 (Frontend Routes) ← independent, can parallel with Task 5
↓
Task 8 (Frontend Batch 1) ← depends on Task 5 + 6 + 7
↓
Task 9 (Frontend 耗材包) ← depends on Task 5 + 6 + 7
Task 10 (Frontend 工艺库) ← depends on Task 5 + 6 + 7 + 8 (needs dropdowns from batch 1)
↓
Task 11 (Final Verification) ← depends on all above
Parallelization opportunities: