From 2b4fd6fff835d1ec678e12917f04989676eb1f4a Mon Sep 17 00:00:00 2001 From: luo Date: Thu, 8 Oct 2026 11:00:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E9=94=80=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yaml | 8 +- .../add-agent-distribution-code/design.md | 21 +++ .../add-agent-distribution-code/proposal.md | 16 +++ .../specs/agent-distribution-code/spec.md | 23 +++ .../add-agent-distribution-code/tasks.md | 12 ++ package.json | 1 + pnpm-lock.yaml | 8 ++ src/api/shop.ts | 25 +++- src/pages.json | 8 ++ .../agent-system/distribution-code/index.vue | 136 ++++++++++++++++++ src/pages/agent-system/home/index.vue | 8 ++ src/static/icons/分销码.png | Bin 0 -> 3202 bytes 12 files changed, 255 insertions(+), 11 deletions(-) create mode 100644 openspec/changes/add-agent-distribution-code/design.md create mode 100644 openspec/changes/add-agent-distribution-code/proposal.md create mode 100644 openspec/changes/add-agent-distribution-code/specs/agent-distribution-code/spec.md create mode 100644 openspec/changes/add-agent-distribution-code/tasks.md create mode 100644 src/pages/agent-system/distribution-code/index.vue create mode 100644 src/static/icons/分销码.png diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index fb52fb7..4414ea4 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -3,7 +3,7 @@ name: 构建并部署前端到生产环境 on: push: branches: - - develop + - iteration/august - dev - test @@ -27,7 +27,7 @@ jobs: - name: 设置镜像标签 id: tag run: | - if [ "${{ github.ref }}" = "refs/heads/develop" ]; then + if [ "${{ github.ref }}" = "refs/heads/iteration/august" ]; then echo "tag=latest" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then echo "tag=dev" >> $GITHUB_OUTPUT @@ -51,8 +51,8 @@ jobs: docker push ${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }} docker push ${{ env.IMAGE_NAME }}:${{ github.sha }} - - name: 部署到本地(仅 develop 分支) - if: github.ref == 'refs/heads/develop' + - name: 部署到本地(仅 iteration/august 分支) + if: github.ref == 'refs/heads/iteration/august' run: | mkdir -p ${{ env.DEPLOY_DIR }} diff --git a/openspec/changes/add-agent-distribution-code/design.md b/openspec/changes/add-agent-distribution-code/design.md new file mode 100644 index 0000000..c3a9842 --- /dev/null +++ b/openspec/changes/add-agent-distribution-code/design.md @@ -0,0 +1,21 @@ +## Context + +The Uni-app frontend already obtains the authenticated user through `GET /api/auth/me`. That user includes `user_type` and `shop_id`; `GET /api/admin/shops/{shop_id}` returns the shop's `distribution_code`. The home page currently has an agent-only “提现管理” shortcut, and `src/static/icons/分销码.png` is present as an untracked user-supplied asset. + +## Goals / Non-Goals + +- Goals: expose a role-restricted shortcut after withdrawal management and show the current shop's distribution code as a scannable QR code. +- Non-Goals: change backend contracts, generate or rotate distribution codes, or expose another shop's code. + +## Decisions + +- Use the current authenticated user's `shop_id` from `/api/auth/me`; do not hardcode a shop ID or accept one from the page URL. +- Fetch the shop record through a typed shop-detail API method and encode the returned `distribution_code` value itself in the QR code. +- Generate/render the QR code locally with a Uni-app-compatible solution. Do not send distribution codes to a remote QR-generation service. +- Add a dedicated page, use the supplied icon for the home shortcut, and re-check the current user type on the page so direct navigation does not bypass the agent-only UI. +- Show a clear unavailable/error state when the account has no `shop_id`, the shop response has no distribution code, or the request fails. + +## Risks / Trade-offs + +- QR rendering APIs differ across Uni-app targets. Select a local renderer compatible with the project's supported H5 and mini-program builds and verify it in the target runtime. +- The supplied icon is currently untracked; implementation must preserve and use it without replacing or deleting it. diff --git a/openspec/changes/add-agent-distribution-code/proposal.md b/openspec/changes/add-agent-distribution-code/proposal.md new file mode 100644 index 0000000..3a35bd3 --- /dev/null +++ b/openspec/changes/add-agent-distribution-code/proposal.md @@ -0,0 +1,16 @@ +# Change: Add agent distribution code viewing + +## Why + +Agent accounts need a quick way to share their shop's distribution code. The authenticated user response already provides `shop_id`, and the shop detail endpoint returns `distribution_code`, but the agent home page does not expose or display it. + +## What Changes + +- Add a “查看我的分销码” shortcut immediately after “提现管理” on the agent home page, visible only when `user_type` is `3`. +- Add a distribution-code page that uses the current authenticated user's `shop_id` to fetch `/api/admin/shops/{shop_id}` and renders the returned `distribution_code` as a QR code. +- Reuse the existing `src/static/icons/分销码.png` shortcut icon. + +## Impact + +- Affected specs: `agent-distribution-code` (new capability) +- Affected code: `src/pages/agent-system/home/index.vue`, `src/pages/agent-system/distribution-code/index.vue`, `src/pages.json`, and the shop API module; a Uni-app-compatible local QR renderer may require a dependency. diff --git a/openspec/changes/add-agent-distribution-code/specs/agent-distribution-code/spec.md b/openspec/changes/add-agent-distribution-code/specs/agent-distribution-code/spec.md new file mode 100644 index 0000000..8bcd434 --- /dev/null +++ b/openspec/changes/add-agent-distribution-code/specs/agent-distribution-code/spec.md @@ -0,0 +1,23 @@ +## ADDED Requirements + +### Requirement: Agent users can view their shop distribution code + +The authenticated home page SHALL show a “查看我的分销码” shortcut immediately after “提现管理” only when the current user's `user_type` is `3`. The shortcut SHALL use `src/static/icons/分销码.png`. Opening it SHALL use the current user's `shop_id` to retrieve that shop's `distribution_code` from `/api/admin/shops/{shop_id}` and display a QR code encoding that exact value. The code SHALL be generated locally and SHALL NOT be sent to a third-party QR service. + +#### Scenario: Agent sees and opens the shortcut +- **GIVEN** `/api/auth/me` returns a user with `user_type` `3` and a valid `shop_id` +- **WHEN** the home page displays its quick actions +- **THEN** “查看我的分销码” appears immediately after “提现管理” with the supplied distribution-code icon +- **AND** opening it displays the current shop's distribution code as a QR code + +#### Scenario: Non-agent does not see the shortcut +- **GIVEN** `/api/auth/me` returns a user whose `user_type` is not `3` +- **WHEN** the home page displays its quick actions +- **THEN** the distribution-code shortcut is not shown +- **AND** direct navigation to the distribution-code page does not reveal a code + +#### Scenario: Shop or distribution code is unavailable +- **GIVEN** an agent opens the distribution-code page +- **WHEN** the current account has no `shop_id`, the shop request fails, or the shop has no `distribution_code` +- **THEN** no QR code is rendered +- **AND** the page explains that the distribution code is currently unavailable diff --git a/openspec/changes/add-agent-distribution-code/tasks.md b/openspec/changes/add-agent-distribution-code/tasks.md new file mode 100644 index 0000000..35bb4b8 --- /dev/null +++ b/openspec/changes/add-agent-distribution-code/tasks.md @@ -0,0 +1,12 @@ +## 1. Proposal Review +- [x] 1.1 Review and approve this proposal before implementation + +## 2. Implementation +- [x] 2.1 Add a typed shop-detail API method for `/api/admin/shops/{shop_id}` and its `distribution_code` field +- [x] 2.2 Add a distribution-code page that checks the current user type, fetches the current shop, and renders the code locally as a QR +- [x] 2.3 Register the page and add the agent-only home shortcut immediately after withdrawal management using the supplied icon +- [x] 2.4 Handle missing shop/code and request failure states + +## 3. Verification +- [x] 3.1 Run targeted ESLint and type-check; record unrelated existing type-check failures +- [x] 3.2 Verify role gating, `shop_id` lookup, and QR content mapping, and build H5 and WeChat mini-program targets diff --git a/package.json b/package.json index 8a4f072..c3d47c0 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "destr": "^2.0.5", "pinia": "2.2.4", "pinia-plugin-persistedstate": "4.1.3", + "qrcode-generator": "^2.0.4", "uview-plus": "^3.7.13", "vue": "3.4.21", "vue-i18n": "9.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e47306..0f524cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: pinia-plugin-persistedstate: specifier: 4.1.3 version: 4.1.3(pinia@2.2.4(typescript@5.8.3)(vue@3.4.21(typescript@5.8.3))) + qrcode-generator: + specifier: ^2.0.4 + version: 2.0.4 uview-plus: specifier: ^3.7.13 version: 3.7.13 @@ -7304,6 +7307,9 @@ packages: resolution: {integrity: sha512-kXuQdQTB6oN3KhI6V4acnBSZx8D2I4xzZvn9+wFLLFCoBNQY/sFnCW6c43OL7pOQ2HvGV4lnWIXNmgfp7cTWhQ==} engines: {node: '>=20'} + qrcode-generator@2.0.4: + resolution: {integrity: sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g==} + qrcode-reader@1.0.4: resolution: {integrity: sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==} @@ -18535,6 +18541,8 @@ snapshots: dependencies: hookified: 1.15.0 + qrcode-generator@2.0.4: {} + qrcode-reader@1.0.4: {} qrcode-terminal@0.12.0: {} diff --git a/src/api/shop.ts b/src/api/shop.ts index 668f5ec..5eddfb8 100644 --- a/src/api/shop.ts +++ b/src/api/shop.ts @@ -1,16 +1,27 @@ -import { get } from '@/utils/request' +import { get } from '@/utils/request'; export interface ShopCascadeParams { - parent_id?: number - shop_name?: string + parent_id?: number; + shop_name?: string; } export interface ShopCascadeItem { - id: number - shop_name: string - has_children: boolean + id: number; + shop_name: string; + has_children: boolean; +} + +export interface ShopDetail { + id: number; + shop_name: string; + shop_code: string; + distribution_code?: string | null; } export function getShopCascade(params?: ShopCascadeParams) { - return get('/api/admin/shops/cascade', { params }) + return get('/api/admin/shops/cascade', { params }); +} + +export function getShopDetail(shopId: number) { + return get(`/api/admin/shops/${shopId}`); } diff --git a/src/pages.json b/src/pages.json index 0312ca1..0fc1516 100644 --- a/src/pages.json +++ b/src/pages.json @@ -55,6 +55,14 @@ "navigationBarTextStyle": "black" } }, + { + "path": "pages/agent-system/distribution-code/index", + "style": { + "navigationBarTitleText": "我的分销码", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, { "path": "pages/agent-system/asset-wallet/index", "style": { diff --git a/src/pages/agent-system/distribution-code/index.vue b/src/pages/agent-system/distribution-code/index.vue new file mode 100644 index 0000000..4951877 --- /dev/null +++ b/src/pages/agent-system/distribution-code/index.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/pages/agent-system/home/index.vue b/src/pages/agent-system/home/index.vue index 1c24077..9260412 100644 --- a/src/pages/agent-system/home/index.vue +++ b/src/pages/agent-system/home/index.vue @@ -142,6 +142,14 @@ + + + + + 查看我的分销码 + + + diff --git a/src/static/icons/分销码.png b/src/static/icons/分销码.png new file mode 100644 index 0000000000000000000000000000000000000000..ea26cda0fbd3b90c491d1d939b16dba49be294c0 GIT binary patch literal 3202 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 H?IZpLyf((i literal 0 HcmV?d00001