diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b174b12 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,37 @@ +# Repository Guidelines + +## Project Structure & Module Organization +This repository is a `pnpm` workspace with three packages under `packages/`: + +- `packages/app`: Vue 3 + `uni-app` WeChat mini-program. Pages live in `src/pages`, shared UI in `src/components`, state in `src/stores`, and utilities in `src/utils`. +- `packages/server`: NestJS API with Prisma. Feature modules live in `src/`, unit tests are colocated in `__tests__`, and the database schema and seed script are in `prisma/`. +- `packages/shared`: shared TypeScript enums, constants, and types used by both app and server. + +Reference docs and implementation notes are under `docs/`. + +## Build, Test, and Development Commands +- `pnpm dev:server`: start the NestJS API with watch mode. +- `pnpm dev:app`: build and serve the `uni-app` target for WeChat mini-program development. +- `pnpm build:shared`: compile shared types before consuming package changes. +- `pnpm build:server`: build the backend into `packages/server/dist`. +- `pnpm build:app`: produce the WeChat mini-program build. +- `pnpm test`: run workspace tests; today this mainly executes server Jest tests. +- `pnpm lint`: run workspace linting; currently defined for the server. + +For Prisma tasks, work in `packages/server`: `pnpm prisma:generate`, `pnpm prisma:migrate`, `pnpm prisma:seed`. + +## Coding Style & Naming Conventions +Use TypeScript throughout. Follow the existing style: 2-space indentation in JSON/Markdown, `camelCase` for variables/functions, `PascalCase` for Vue components and NestJS classes, and `kebab-case` for page/component filenames such as `flash-sales.vue`. Keep modules feature-oriented and prefer colocating DTOs and tests with their domain module. + +Linting is configured in the server via ESLint. The app relies on `vue-tsc` for type checks: run `pnpm --filter @mp-pilates/app type-check` before shipping UI changes. + +## Testing Guidelines +Server tests use Jest with `*.spec.ts` naming. Place tests in `packages/server/src/**/__tests__/` and focus on service-level behavior and edge cases around booking, membership, payment, and scheduling logic. Run `pnpm test` for the full suite or `pnpm --filter @mp-pilates/server test:cov` when touching business-critical paths. + +## Commit & Pull Request Guidelines +Recent history uses Conventional Commit prefixes such as `feat:`, `fix:`, `fix(app):`, and `perf:`. Keep subjects short and specific, preferably describing the user-visible effect. + +Pull requests should include a concise summary, linked issue or task reference, test notes, and screenshots or recordings for mini-program UI changes. Call out Prisma schema changes, new environment variables, or deployment steps explicitly. + +## Security & Configuration Tips +Do not commit real secrets. Review `packages/server/certs/` and environment-specific payment or WeChat credentials carefully before pushing. When changing shared types or enums, update both consumers and rebuild `@mp-pilates/shared` to avoid runtime drift. diff --git a/packages/app/src/pages/admin/orders.vue b/packages/app/src/pages/admin/orders.vue index b0853cc..17bfe35 100644 --- a/packages/app/src/pages/admin/orders.vue +++ b/packages/app/src/pages/admin/orders.vue @@ -44,7 +44,9 @@ class="list-scroll" :refresher-enabled="true" :refresher-triggered="refreshing" + :lower-threshold="120" @refresherrefresh="onRefresh" + @scrolltolower="loadMore" > @@ -120,7 +122,7 @@ - + {{ loading ? '加载中...' : '加载更多' }} diff --git a/packages/app/src/stores/admin.ts b/packages/app/src/stores/admin.ts index 1fa9b6e..1a6c8c6 100644 --- a/packages/app/src/stores/admin.ts +++ b/packages/app/src/stores/admin.ts @@ -20,6 +20,33 @@ import type { UpdateFlashSaleDto, } from '@mp-pilates/shared' +interface LegacyPaginatedData { + readonly data: readonly T[] + readonly total: number + readonly page: number + readonly limit: number +} + +function normalizePaginatedData( + result: PaginatedData | LegacyPaginatedData, +): PaginatedData { + if ('items' in result) { + return { + items: [...result.items], + total: result.total, + page: result.page, + limit: result.limit, + } + } + + return { + items: [...result.data], + total: result.total, + page: result.page, + limit: result.limit, + } +} + export interface AdminStats { todayBookings: number totalOrders: number @@ -82,13 +109,13 @@ export const useAdminStore = defineStore('admin', () => { } async function createCardType(dto: CreateCardTypeDto): Promise { - const data = await post('/admin/card-types', dto) + const data = await post('/admin/card-types', dto as unknown as Record) await fetchCardTypes() return data } async function updateCardType(id: string, dto: UpdateCardTypeDto): Promise { - const data = await put(`/admin/card-types/${id}`, dto) + const data = await put(`/admin/card-types/${id}`, dto as unknown as Record) await fetchCardTypes() return data } @@ -109,7 +136,7 @@ export const useAdminStore = defineStore('admin', () => { } async function saveStudioConfig(dto: UpdateStudioConfigDto): Promise { - const data = await put('/admin/studio/info', dto) + const data = await put('/admin/studio/info', dto as unknown as Record) studioConfig.value = data return data } @@ -120,10 +147,11 @@ export const useAdminStore = defineStore('admin', () => { limit?: number status?: string }): Promise> { - console.log('[admin] fetchAdminOrders params:', params) - const result = await get>('/admin/orders', params) - console.log('[admin] fetchAdminOrders result:', result) - return result + const result = await get | LegacyPaginatedData>( + '/admin/orders', + params, + ) + return normalizePaginatedData(result) } // ── Bookings ───────────────────────────────────────────────────── @@ -176,7 +204,7 @@ export const useAdminStore = defineStore('admin', () => { } async function createManualSlot(dto: CreateManualSlotDto): Promise { - return post('/admin/time-slot/manual', dto) + return post('/admin/time-slot/manual', dto as unknown as Record) } async function closeSlot(id: string): Promise { diff --git a/packages/app/src/utils/request.ts b/packages/app/src/utils/request.ts index 496e6f8..893a352 100644 --- a/packages/app/src/utils/request.ts +++ b/packages/app/src/utils/request.ts @@ -1,16 +1,7 @@ import type { ApiResponse, PaginatedData } from '@mp-pilates/shared' -const BASE_URL = (() => { - try { - const { miniProgram } = uni.getAccountInfoSync() - if (miniProgram.envVersion !== 'develop') { - return 'https://focus.richarjiang.com/api' - } - } catch { - // 非小程序环境,使用开发地址 - } - return 'http://localhost:3000/api' -})() +// 统一使用线上服务地址 +const BASE_URL = 'https://focus.richarjiang.com/api' interface RequestOptions { readonly url: string