From f9fe76ff997d28e7255532da72a03a18d4f758e1 Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Thu, 14 May 2026 09:50:34 +0800 Subject: [PATCH] fix:bug --- check-tags.cjs | 2 +- docs/权限按钮.md | 21 + docs/资产详情流量-spec.md | 6 +- env/.env.production | 2 +- env/.env.test | 2 +- .../proposal.md | 25 + .../specs/agent-asset-list/spec.md | 46 + .../tasks.md | 16 + .../proposal.md | 1 - openspec/project.md | 3 +- src/api/auth.ts | 10 +- src/pages.json | 40 - src/pages/agent-system/asset-detail/index.vue | 1881 +++++++++++------ src/pages/agent-system/asset-search/index.vue | 1391 ------------ src/pages/agent-system/assets/index.vue | 87 +- .../agent-system/commission-center/index.vue | 1 - src/pages/agent-system/commission/index.vue | 16 - .../agent-system/enterprise-cards/index.vue | 4 +- .../agent-system/enterprise-devices/index.vue | 2 +- src/pages/agent-system/home/index.vue | 2 - src/pages/agent-system/my-packages/index.vue | 23 - src/pages/agent-system/packages/index.vue | 245 --- src/pages/agent-system/tags/index.vue | 23 - src/pages/agent-system/user-info/index.vue | 1 - src/pages/agent-system/utils.ts | 77 - src/pages/common/login/index.vue | 15 +- src/store/modules/user/index.ts | 14 +- src/utils/request/index.ts | 2 +- 28 files changed, 1440 insertions(+), 2518 deletions(-) create mode 100644 docs/权限按钮.md create mode 100644 openspec/changes/update-agent-asset-action-button-permissions/proposal.md create mode 100644 openspec/changes/update-agent-asset-action-button-permissions/specs/agent-asset-list/spec.md create mode 100644 openspec/changes/update-agent-asset-action-button-permissions/tasks.md delete mode 100644 src/pages/agent-system/asset-search/index.vue delete mode 100644 src/pages/agent-system/commission/index.vue delete mode 100644 src/pages/agent-system/my-packages/index.vue delete mode 100644 src/pages/agent-system/packages/index.vue delete mode 100644 src/pages/agent-system/tags/index.vue delete mode 100644 src/pages/agent-system/utils.ts diff --git a/check-tags.cjs b/check-tags.cjs index 6716c0f..0f4189f 100644 --- a/check-tags.cjs +++ b/check-tags.cjs @@ -1,5 +1,5 @@ const fs = require('fs') -const content = fs.readFileSync('src/pages/agent-system/asset-search/index.vue', 'utf8') +const content = fs.readFileSync('src/pages/agent-system/asset-detail/index.vue', 'utf8') const lines = content.split('\n') let depth = 0 diff --git a/docs/权限按钮.md b/docs/权限按钮.md new file mode 100644 index 0000000..38eda99 --- /dev/null +++ b/docs/权限按钮.md @@ -0,0 +1,21 @@ +/api/auth/login 登录接口返回了权限按钮 +user:{ + "id": 144, + "username": "MLLxp", + "phone": "15571055000", + "user_type": 3, + "user_type_name": "代理账号", + "shop_id": 1 +}, + +"buttons": [ + "device:clear_series", +] + +这个地方有返回按钮的权限 + + + + + +资产列表里面要加这个权限编码,然后判断按钮是否显示 \ No newline at end of file diff --git a/docs/资产详情流量-spec.md b/docs/资产详情流量-spec.md index 4157b1b..722d353 100644 --- a/docs/资产详情流量-spec.md +++ b/docs/资产详情流量-spec.md @@ -18,7 +18,7 @@ ### 联动范围 -- 资产搜索页中的同类套餐展示:`src/pages/agent-system/asset-search/index.vue` +- 资产详情页中的同类套餐展示:`src/pages/agent-system/asset-detail/index.vue` ### 本次覆盖的 UI 区块 @@ -140,7 +140,7 @@ ### 关键实现要求 - `getPackageRemainMb` 必须改为基于“当前展示的已使用值”计算 -- `asset-detail` 与 `asset-search` 不允许维护不同口径的同名逻辑 +- `asset-detail` 页面内不同区块不允许维护不同口径的同名逻辑 - 格式化继续沿用现有 `formatDataSize`,本次不单独引入新的单位换算规则 --- @@ -213,7 +213,7 @@ ### 验收用例 4:跨页面一致性 -- 同一套餐数据在 `asset-detail` 与 `asset-search` 中展示结果一致 +- 同一套餐数据在 `asset-detail` 页面各区块中的展示结果一致 - 当前生效套餐卡片与套餐列表摘要使用同一套字段口径 --- diff --git a/env/.env.production b/env/.env.production index 7329d43..0d916b3 100644 --- a/env/.env.production +++ b/env/.env.production @@ -2,7 +2,7 @@ VITE_APP_ENV=production # 接口地址 -VITE_API_BASE_URL=https://cmp-api.boss160.cn +VITE_API_BASE_URL=https://cmp-api.xm-iot.cn # 是否使用代理 (生产环境不使用代理) VITE_APP_PROXY=false diff --git a/env/.env.test b/env/.env.test index 14407bd..9faf8cd 100644 --- a/env/.env.test +++ b/env/.env.test @@ -2,7 +2,7 @@ VITE_APP_ENV=staging # 接口地址 -VITE_API_BASE_URL=https://cmp-api.boss160.cn +VITE_API_BASE_URL=https://cmp-api.xm-iot.cn # 是否使用代理 (测试环境不使用代理) VITE_APP_PROXY=false diff --git a/openspec/changes/update-agent-asset-action-button-permissions/proposal.md b/openspec/changes/update-agent-asset-action-button-permissions/proposal.md new file mode 100644 index 0000000..7403f75 --- /dev/null +++ b/openspec/changes/update-agent-asset-action-button-permissions/proposal.md @@ -0,0 +1,25 @@ +# Change: Update agent asset action button permissions + +## Why + +The login response already returns button-level permission codes, but the agent asset list still renders IoT card and device action buttons without using those codes. This leaves action visibility disconnected from backend authorization and makes button exposure inconsistent with the authenticated session. + +## What Changes + +- Define agent asset-list behavior for consuming button permission codes returned from `POST /api/auth/login` +- Require each IoT card action button and device action button in the agent asset list to be bound to an explicit H5 permission code: + - IoT卡停机:`asset:card_stop_h5` + - IoT卡复机:`asset:card_start_h5` + - 设备停机:`asset:device_stop_h5` + - 设备复机:`asset:device_start_h5` +- Hide action buttons when the current session does not contain the corresponding permission code +- Keep asset status text and detail-page entry visible even when action buttons are hidden + +## Impact + +- Affected specs: + - `agent-asset-list` +- Affected code: + - `src/api/auth.ts` + - `src/pages/common/login/index.vue` + - `src/pages/agent-system/assets/index.vue` diff --git a/openspec/changes/update-agent-asset-action-button-permissions/specs/agent-asset-list/spec.md b/openspec/changes/update-agent-asset-action-button-permissions/specs/agent-asset-list/spec.md new file mode 100644 index 0000000..e5a05b9 --- /dev/null +++ b/openspec/changes/update-agent-asset-action-button-permissions/specs/agent-asset-list/spec.md @@ -0,0 +1,46 @@ +## ADDED Requirements + +### Requirement: Agent Asset Action Buttons SHALL Respect Session Button Permissions + +The agent asset list SHALL use the backend-provided button permission codes from the authenticated session to determine whether IoT card and device action buttons are rendered. The page SHALL use the following H5-specific permission-code mapping: + +- IoT卡停机:`asset:card_stop_h5` +- IoT卡复机:`asset:card_start_h5` +- 设备停机:`asset:device_stop_h5` +- 设备复机:`asset:device_start_h5` + +#### Scenario: Login response returns button permission codes + +- **WHEN** `POST /api/auth/login` succeeds +- **THEN** the frontend SHALL treat the response `buttons` array as the source of button-level permissions for the authenticated session +- **AND** the session data used by the agent asset list SHALL preserve those button permission codes + +#### Scenario: Asset action has its mapped H5 permission code and the code is present + +- **WHEN** an IoT card or device action button in the agent asset list has one of the mapped H5 permission codes +- **AND** that permission code exists in the current session `buttons` array +- **THEN** the corresponding action button SHALL be rendered + +#### Scenario: Asset action has its mapped H5 permission code and the code is absent + +- **WHEN** an IoT card or device action button in the agent asset list has one of the mapped H5 permission codes +- **AND** that permission code does not exist in the current session `buttons` array +- **THEN** the corresponding action button SHALL NOT be rendered + +#### Scenario: Session includes only a subset of asset action permissions + +- **WHEN** the current session `buttons` array contains permission codes for only some of the asset actions +- **THEN** only the matching asset action buttons SHALL be shown +- **AND** non-matching asset action buttons SHALL remain hidden + +#### Scenario: Session includes unrelated button codes + +- **WHEN** the current session `buttons` array includes unrelated button codes such as `device:clear_series` +- **THEN** no asset action button SHALL become visible unless its own mapped H5 permission code is also present + +#### Scenario: Action permissions are absent but the list item remains accessible + +- **WHEN** the current session does not grant any asset action button permissions for a list item +- **THEN** the asset status text SHALL still be shown +- **AND** the detail-page entry affordance for the list item SHALL remain available +- **AND** the page SHALL NOT render placeholder or disabled action buttons in place of the hidden ones diff --git a/openspec/changes/update-agent-asset-action-button-permissions/tasks.md b/openspec/changes/update-agent-asset-action-button-permissions/tasks.md new file mode 100644 index 0000000..a888691 --- /dev/null +++ b/openspec/changes/update-agent-asset-action-button-permissions/tasks.md @@ -0,0 +1,16 @@ +## 1. Spec + +- [x] 1.1 Review `docs/权限按钮.md` and confirm the login response is the source of button permission codes +- [x] 1.2 Add OpenSpec proposal, tasks, and `agent-asset-list` delta spec for action-button permissions +- [x] 1.3 Validate the new change with strict OpenSpec validation + +## 2. Implementation + +- [x] 2.1 Extend login response typing and session persistence for the backend-provided `buttons` array +- [x] 2.2 Define explicit H5 permission-code mapping for agent asset-list IoT card and device action buttons: + - `asset:card_stop_h5` + - `asset:card_start_h5` + - `asset:device_stop_h5` + - `asset:device_start_h5` +- [x] 2.3 Gate asset-list action-button rendering by the current session button permissions +- [x] 2.4 Verify card and device action visibility for sessions with and without matching button permission codes diff --git a/openspec/changes/update-agent-asset-list-and-detail-display/proposal.md b/openspec/changes/update-agent-asset-list-and-detail-display/proposal.md index 4fd4bc8..4da45e4 100644 --- a/openspec/changes/update-agent-asset-list-and-detail-display/proposal.md +++ b/openspec/changes/update-agent-asset-list-and-detail-display/proposal.md @@ -29,6 +29,5 @@ The current agent-side asset experience exposes too much low-value information, - `agent-asset-detail` - Affected code: - `src/pages/agent-system/assets/index.vue` - - `src/pages/agent-system/asset-search/index.vue` - `src/pages/agent-system/asset-detail/index.vue` - `src/api/assets.ts` diff --git a/openspec/project.md b/openspec/project.md index 0837ee2..0edb18c 100644 --- a/openspec/project.md +++ b/openspec/project.md @@ -49,8 +49,7 @@ This project is a Uni-app based agent and enterprise management frontend for IoT - Asset identifiers may be ICCID or VirtualNo depending on the resource and entry point. - Agent-side pages currently include: - asset list: `src/pages/agent-system/assets/index.vue` - - asset search/detail: `src/pages/agent-system/asset-search/index.vue` - - secondary asset detail page: `src/pages/agent-system/asset-detail/index.vue` + - asset detail: `src/pages/agent-system/asset-detail/index.vue` - Package display and realtime status are important parts of the asset detail experience. ## Important Constraints diff --git a/src/api/auth.ts b/src/api/auth.ts index 20c7b33..0135600 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -9,6 +9,7 @@ import { get, post, put } from '@/utils/request' export interface LoginParams { username: string password: string + device?: 'h5' } /** @@ -18,6 +19,7 @@ export interface LoginResponse { access_token: string refresh_token: string expires_in: number + buttons?: string[] user: { id: number username: string @@ -47,6 +49,7 @@ export interface UserInfo { status?: 'active' | 'inactive' create_time?: string last_login_time?: string + buttons?: string[] } /** @@ -62,7 +65,12 @@ export interface ChangePasswordParams { * POST /api/auth/login */ export function login(data: LoginParams) { - return post('/api/auth/login', { data }) + return post('/api/auth/login', { + data: { + ...data, + device: data.device ?? 'h5', + }, + }) } /** diff --git a/src/pages.json b/src/pages.json index 03fb232..0dcf042 100644 --- a/src/pages.json +++ b/src/pages.json @@ -15,30 +15,6 @@ "navigationStyle": "custom" } }, - { - "path": "pages/agent-system/my-packages/index", - "style": { - "navigationBarTitleText": "我的套餐", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" - } - }, - { - "path": "pages/agent-system/commission/index", - "style": { - "navigationBarTitleText": "我的佣金", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" - } - }, - { - "path": "pages/agent-system/packages/index", - "style": { - "navigationBarTitleText": "套餐列表", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" - } - }, { "path": "pages/agent-system/assets/index", "style": { @@ -47,22 +23,6 @@ "navigationBarTextStyle": "black" } }, - { - "path": "pages/agent-system/tags/index", - "style": { - "navigationBarTitleText": "标签管理", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" - } - }, - { - "path": "pages/agent-system/asset-search/index", - "style": { - "navigationBarTitleText": "资产详情", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" - } - }, { "path": "pages/agent-system/asset-detail/index", "style": { diff --git a/src/pages/agent-system/asset-detail/index.vue b/src/pages/agent-system/asset-detail/index.vue index 3970094..9554d21 100644 --- a/src/pages/agent-system/asset-detail/index.vue +++ b/src/pages/agent-system/asset-detail/index.vue @@ -1,514 +1,486 @@ diff --git a/src/pages/agent-system/asset-search/index.vue b/src/pages/agent-system/asset-search/index.vue deleted file mode 100644 index 5df940f..0000000 --- a/src/pages/agent-system/asset-search/index.vue +++ /dev/null @@ -1,1391 +0,0 @@ - - - - - diff --git a/src/pages/agent-system/assets/index.vue b/src/pages/agent-system/assets/index.vue index 4606d27..c292076 100644 --- a/src/pages/agent-system/assets/index.vue +++ b/src/pages/agent-system/assets/index.vue @@ -71,6 +71,14 @@ // 用户信息 const userInfo = ref(null) + const sessionButtonCodes = ref([]) + + const ASSET_ACTION_BUTTON_CODES = { + cardStop: 'asset:card_stop_h5', + cardStart: 'asset:card_start_h5', + deviceStop: 'asset:device_stop_h5', + deviceStart: 'asset:device_start_h5', + } as const // 获取单卡列表(代理端) function getStandaloneCards(params : any) { @@ -130,10 +138,36 @@ const carrierList = ref([]) const isAgent = computed(() => userInfo.value?.user_type === 3) const searchPlaceholder = computed(() => activeTab.value === 'card' ? '搜索ICCID' : '搜索设备号') + const canShowDeviceStopButton = computed(() => hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.deviceStop)) + const canShowDeviceStartButton = computed(() => hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.deviceStart)) function hasStatusCode(error : unknown) { return typeof error === 'object' && error !== null && 'statusCode' in error } + + function getSessionButtonCodes() { + const cachedUserInfo = uni.getStorageSync('user_info') + + if (!cachedUserInfo || typeof cachedUserInfo !== 'object' || !Array.isArray((cachedUserInfo as UserInfo).buttons)) { + return [] + } + + return (cachedUserInfo as UserInfo).buttons!.filter((code) => { + return typeof code === 'string' && code.trim().length > 0 + }) + } + + function hasActionButtonPermission(code : string) { + return sessionButtonCodes.value.includes(code) + } + + function canShowCardStopButton(card : CardInfo) { + return card.network_status !== 0 && hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.cardStop) + } + + function canShowCardStartButton(card : CardInfo) { + return card.network_status !== 1 && hasActionButtonPermission(ASSET_ACTION_BUTTON_CODES.cardStart) + } // 运营商选择器选项 const carrierOptions = computed(() => [ { label: '全部运营商', value: undefined }, @@ -410,12 +444,12 @@ if (asset.type === 'card') { const card = asset.raw as CardInfo uni.navigateTo({ - url: `/pages/agent-system/asset-search/index?iccid=${card.iccid}`, + url: `/pages/agent-system/asset-detail/index?iccid=${card.iccid}`, }) } else { const device = asset.raw as DeviceInfo uni.navigateTo({ - url: `/pages/agent-system/asset-search/index?virtual_no=${device.virtual_no}`, + url: `/pages/agent-system/asset-detail/index?virtual_no=${device.virtual_no}`, }) } } @@ -575,6 +609,7 @@ onMounted(async () => { // 1. 获取状态栏高度 statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight || 20 + sessionButtonCodes.value = getSessionButtonCodes() // 2. 加载用户信息(判断是企业端还是代理端) try { @@ -646,8 +681,7 @@ - + {{ selectedShopName }} @@ -716,42 +750,30 @@