Two-package monorepo: Python backend + Vue 3 admin frontend. Named "sanqiang" internally.
backend/ FastAPI backend (Python 3.9, venv)
web-admin/ Vue 3 admin panel (pnpm, Vite 7, Element Plus, Tailwind CSS 4)
docs/ UI specs, SQL schemas, requirements docs
openspec/ OpenSpec: `config.yaml`; active changes under `openspec/changes/*/`
.cursorrules Detailed UI conventions and constraints (Chinese)
Full specs: openspec/changes/zkxw-engineering-standards/specs/*/spec.md. Below are the key SHALL items every coding session MUST follow.
web-admin SHALL follow docs/web-admin-ui-spec-fresh-mint.md (Fresh Mint palette, --primary #10b981). miniprogram SHALL follow docs/miniprogram-ui-spec-sanqiang.md (三墙 palette, brand green #0a9f6d).web-admin SHALL NOT use #0a9f6d as primary; miniprogram SHALL NOT use #10b981 as primary./ (space-slash-space). See web-admin/src/utils/tree-path.ts.label-position="top". No backdrop blur (backdrop-filter: none). Use .dialog-form-layout with max-height: calc(100vh - 100px), only body scrolls..form-grid-2). Max 5 columns. Full-width fields use .form-col-span-2 (grid-column: 1 / -1). Narrow viewport SHALL fallback to fewer columns.panel-top → h2.title-bar-heading SHALL show primary-color vertical bar via ::before. Ambiguous titles (e.g. "租户" containing "户") SHALL be registered in App.vue panelTitleBarTextRules.aspectFill; carousel 4:3 aspectFill; detail images widthFix. Min tap target 44×44px.level === 0 check in SidebarSubmenu.vue.meta.icon is for sidebar only.<template #label>字段名<span class="form-label-tip">注释</span></template>, NOT beside the input control. Style class: .form-label-tip (12px, --mint-text-muted).#a7f3d0) inline as the first item in the same flex row as uploaded attachment cards; select file → immediate upload via fetchUploadAttachment; preview/download/delete on cards; hint below the row. Authority: ProtocolFormDialog.vue «协议附件». See openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md §「附件上传卡片化布局」for full Vue template + SCSS + interaction rules.web-admin el-button with visible text labels SHALL include a leading <el-icon> from @element-plus/icons-vue (explicit import). See openspec/.../ui-conventions/spec.md §「按钮须携带图标」.<el-tag> with semantic colors. Tables: use <el-tag :type="..." size="small"> in column templates. SystemViewDialog: use tagType property on ViewField. Semantic colors: success=正常/启用/已完成/通过, warning=进行中/待确认/已过期, danger=异常/已作废/拒绝/不通过/质检待执行/质检待结论, info=待生效/停用/默认, primary=特殊分类/来源类型. SHALL NOT use plain text or custom <span class="tag">. See openspec/.../ui-conventions/spec.md "状态、结论与类型字段使用 Tag 标签展示".src/views/system/unit/index.vuesrc/views/system/order/index.vuesrc/views/system/product/index.vuesrc/views/system/category/index.vuesrc/views/system/user/index.vuedocs/web-admin-ui-pattern-index.md and OpenSpec ui-conventions §「界面模式索引与参考页选型」; PRs SHALL cite pattern code and reference page path.biz_protocol_sample to downstream rows (sample reception, QC work orders, etc.) SHALL use tenant_no + sample_code only—not collection_protocol_id or protocol_sample_id (protocol may change; sample code does not). See openspec/changes/zkxw-engineering-standards/specs/api-conventions/spec.md §「细胞存储样本跨表关联键」.tenant_no. All write/update endpoints SHALL set tenant_no to the current user's tenant. MUST NOT trust client-submitted tenant overrides (unless multi-tenant admin API with security review).create_time DESC when no custom sort is specified.app/api/__init__.py AND app/main.py. SHALL NOT bypass auth/crypto/audit middleware.id VARCHAR(64) UUID, tenant_no, order_num, remark, create_by, create_dept_id, create_time, update_by, update_time, del_flag (default 0).id → business fields → public fields (tenant_no through del_flag).del_flag=1. Queries SHALL exclude del_flag=1 by default.id SHALL be VARCHAR(64) NOT NULL, UUID value.backend/) + Vue 3 web-admin (web-admin/) + uni-app miniprogram (miniprogram/). Shared backend and tenant model.docs/中科兴皖需求V0.3.md (requirements), docs/web-admin-ui-spec-fresh-mint.md (web UI), docs/miniprogram-ui-spec-sanqiang.md (miniprogram UI).app/services/ and app/crud/. Config via pydantic-settings + .env files.docs/sql/. Schema changes SHALL align with database-conventions public field rules.feat|fix|docs|…)._row_to_dict function is the single source of truth for all readable fields. The CreateBody/UpdateBody Pydantic schemas and the frontend formData initialization SHALL include every writable field present in _row_to_dict (excluding computed/read-only fields like id, createTime, updateTime, stockQty). Run python scripts/check_field_consistency.py after implementation to verify. See docs/dev-field-consistency.md for details.id,字段变化时该行必须保留原 id。请求未带 id 时,先用稳定业务键(样本编码、物料编码、条码、组分 id 等)匹配一条尚未占用的有效行,匹配不到再插入新 UUID。属于其他父记录或其他租户的 id SHALL 拒绝。SHALL NOT 在保存时把子表全部软删除再整批插入新行——其他表会拿这些 id 做外键(production_step_output_id、protocol_sample_id、donation_sample_id),整批换 id 会把关联打断。删除父记录时可以级联软删除其子行。复制或克隆父记录时 SHALL 为子行分配新 id。实现入口:backend/app/crud/child_row_sync.py 的 sync_child_rows。sys_role_data_scope_detail with scope_type (dept/user) + scope_value.parent_id=0). New menus SHALL be placed under the correct root.super_admin. All other system management runs within tenant context.backend/)FastAPI · SQLAlchemy · Pydantic v2 · MySQL (pymysql) · Redis · MinIO · JWT auth · Alembic migrations
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env.local # fill in DB, Redis, MinIO, etc.
./start-local.sh # uvicorn --reload on 127.0.0.1:8000
./start-prod.sh start # production on :8111 (reads .env.production, PID-managed)
./start-prod.sh status|stop|restart|logs
./start-test.sh start # test on :8112 (reads .env.test, PID-managed)
./start-test.sh activate # source .venv/bin/activate (interactive shell)
./start-test.sh status|stop|restart|logs
app/main.py → app.main:appapp/api/ — Route files: public.py (no auth), menu.py, admin.py. Register new routers in app/api/__init__.py and app/main.py.app/core/ — Config (config.py), DB (database.py), auth (auth.py, security.py), middleware (api_crypto_middleware.py, api_request_log_middleware.py), RSA login crypto (login_crypto.py)app/models/ — SQLAlchemy models (models.py, base.py)app/schemas/ — Pydantic schemas per domain (auth, order, product, user, etc.)app/crud/ — Database CRUD operations (base.py has generic base)app/services/ — Business logic layerapp/tasks/ — APScheduler scheduled tasksscripts/ — SQL migrations and one-off scripts/api (set via API_V1_PREFIX in config). Routes: /api/public/*, /api/admin/*, /api/supplier/*, /api/client/*, /api/deliver/*LOGIN_RSA_PRIVATE_KEY_PEM env var.API_ENCRYPT_* env vars. Excluded paths listed in API_ENCRYPT_EXCLUDE_PATHS.app/core/config.py), reads from .env / .env.local / .env.production / .env.test (via ENV_FILE).docs/sql/zkxw.sql(从当前库导出的全量 SQL)。Default admin: admin / 111111, role super_admin./docs, ReDoc at /redoc.tests/test_api_crypto.py. Run with pytest.web-admin/)Vue 3 · TypeScript (strict: false, noImplicitAny: false) · Vite 7 · Element Plus · Tailwind CSS 4 · Pinia · vue-router (hash mode) · SCSS · Axios · pnpm
pnpm install # if fails: pnpm install --ignore-scripts
pnpm dev # Vite dev server (port from .env.development)
pnpm build # production build
pnpm build:typecheck # typecheck + build (vue-tsc --noEmit)
pnpm lint # ESLint check
pnpm fix # ESLint --fix
pnpm lint:prettier # Prettier format all
pnpm lint:stylelint # Stylelint fix SCSS/Vue
pnpm commit # cz-git interactive commit
pnpm clean:dev # strip demo data for fresh start
src/main.tssrc/views/system/ — Main business pages: category/, order/, product/, tenant/, unit/, user/src/api/ — Axios API modules: auth.ts, admin.ts, business.ts, system.ts, chinaRegion.tssrc/store/modules/ — Pinia stores: user.ts, menu.ts, setting.ts, table.ts, worktab.tssrc/router/modules/ — Route modules: index.ts, sqxp.ts, exception.tssrc/utils/tree-path.ts — Tree path utilities (buildTreePathLabelFromFlat, etc.). Separator: / (spaced slash).@/* → src/*, @views/*, @imgs/*, @icons/*, @utils/*, @stores/*, @plugins/*, @styles/*
unplugin-auto-import + unplugin-vue-components — Vue, vue-router, pinia, @vueuse/core APIs and Element Plus components are auto-imported. Don't manually import them.@tailwindcss/vite plugin.vite.config.ts additionalData (uses @styles/core/el-light.scss and @styles/core/mixin.scss)./api → VITE_API_PROXY_URL (localhost:8000). Also /web and /mini proxies configured..env.development (local), .env.production (prod). Uses VITE_* prefix.VITE_API_ENCRYPT_ENABLED toggles request/response crypto in dev. Production has it on./#/ format.VITE_ACCESS_MODE=frontend (fixed frontend menu mode).vueIndentScriptAndStyle: true@typescript-eslint/no-explicit-any is OFF.cz-git + commitlint. Types: feat|fix|docs|style|refactor|perf|test|build|ci|revert|chore|wip.The .cursorrules file and docs/ are the authoritative source for all UI decisions.
docs/web-admin-ui-spec-fresh-mint.md + docs/fresh-mint-green.htmlsrc/views/system/unit/index.vue (/#/demo/unit)src/views/system/order/index.vue (/#/demo/order)src/views/system/product/index.vuesrc/views/system/category/index.vuesrc/views/system/user/index.vuesrc/views/dashboard/console/index.vue#10b981, dark #059669, bg #ecfdf5. Fresh Mint palette. Do NOT use miniprogram colors in web-admin.项目已生成代码知识图谱,用于理解模块间依赖关系、追踪修改影响范围。
| 项目 | 报告 | 可视化 | 数据 |
|---|---|---|---|
| 后端 | backend/graphify-out/GRAPH_REPORT.md |
backend/graphify-out/graph.html |
backend/graphify-out/graph.json |
| 前端 | web-admin/graphify-out/GRAPH_REPORT.md |
web-admin/graphify-out/graph.html |
web-admin/graphify-out/graph.json |
*/graphify-out/graph.html 查看交互式图谱./scripts/update-graph.sh [backend|frontend|all]完成代码修改后(新建文件、修改函数签名、新增/删除 import、重构模块),Agent SHALL 执行 ./scripts/update-graph.sh 增量更新图谱,确保图谱与代码保持同步。如果只修改了单个项目(backend 或 web-admin),只需更新对应项目即可。
#10b981), miniprogram uses 三墙 (#0a9f6d). Don't mix.backdrop-filter: none.label-position="top".fixed="right", min-width ≥ 260px.el-table-column SHALL use min-width (not width) for proportional scaling when viewport resizes.type="primary" per toolbar row), no space-between push.docs/sql/ and evaluate API/TypeScript type impact.web-admin 开发服务与 backend 服务。