fix: 优化体验
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Failing after 3h10m20s

This commit is contained in:
sexygoat
2026-05-09 16:13:39 +08:00
parent b54d8f5b32
commit 28246f5345
17 changed files with 1684 additions and 255 deletions

456
openspec/AGENTS.md Normal file
View File

@@ -0,0 +1,456 @@
# OpenSpec Instructions
Instructions for AI coding assistants using OpenSpec for spec-driven development.
## TL;DR Quick Checklist
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
- Validate: `openspec validate [change-id] --strict` and fix issues
- Request approval: Do not start implementation until proposal is approved
## Three-Stage Workflow
### Stage 1: Creating Changes
Create proposal when you need to:
- Add features or functionality
- Make breaking changes (API, schema)
- Change architecture or patterns
- Optimize performance (changes behavior)
- Update security patterns
Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"
Loose matching guidance:
- Contains one of: `proposal`, `change`, `spec`
- With one of: `create`, `plan`, `make`, `start`, `help`
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior
**Workflow**
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
4. Run `openspec validate <id> --strict` and resolve any issues before sharing the proposal.
### Stage 2: Implementing Changes
Track these steps as TODOs and complete them one by one.
1. **Read proposal.md** - Understand what's being built
2. **Read design.md** (if exists) - Review technical decisions
3. **Read tasks.md** - Get implementation checklist
4. **Implement tasks sequentially** - Complete in order
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
### Stage 3: Archiving Changes
After deployment, create separate PR to:
- Move `changes/[name]/``changes/archive/YYYY-MM-DD-[name]/`
- Update `specs/` if capabilities changed
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
- Run `openspec validate --strict` to confirm the archived change passes checks
## Before Any Task
**Context Checklist:**
- [ ] Read relevant specs in `specs/[capability]/spec.md`
- [ ] Check pending changes in `changes/` for conflicts
- [ ] Read `openspec/project.md` for conventions
- [ ] Run `openspec list` to see active changes
- [ ] Run `openspec list --specs` to see existing capabilities
**Before Creating Specs:**
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use `openspec show [spec]` to review current state
- If request is ambiguous, ask 12 clarifying questions before scaffolding
### Search Guidance
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
- Show details:
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
- Change: `openspec show <change-id> --json --deltas-only`
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
## Quick Start
### CLI Commands
```bash
# Essential commands
openspec list # List active changes
openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec validate [item] # Validate changes or specs
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
openspec update [path] # Update instruction files
# Interactive mode
openspec show # Prompts for selection
openspec validate # Bulk validation mode
# Debugging
openspec show [change] --json --deltas-only
openspec validate [change] --strict
```
### Command Flags
- `--json` - Machine-readable output
- `--type change|spec` - Disambiguate items
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
## Directory Structure
```
openspec/
├── project.md # Project conventions
├── specs/ # Current truth - what IS built
│ └── [capability]/ # Single focused capability
│ ├── spec.md # Requirements and scenarios
│ └── design.md # Technical patterns
├── changes/ # Proposals - what SHOULD change
│ ├── [change-name]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional; see criteria)
│ │ └── specs/ # Delta changes
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
│ └── archive/ # Completed changes
```
## Creating Change Proposals
### Decision Tree
```
New request?
├─ Bug fix restoring spec behavior? → Fix directly
├─ Typo/format/comment? → Fix directly
├─ New feature/capability? → Create proposal
├─ Breaking change? → Create proposal
├─ Architecture change? → Create proposal
└─ Unclear? → Create proposal (safer)
```
### Proposal Structure
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
2. **Write proposal.md:**
```markdown
# Change: [Brief description of change]
## Why
[1-2 sentences on problem/opportunity]
## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]
## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]
```
3. **Create spec deltas:** `specs/[capability]/spec.md`
```markdown
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...
#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result
## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]
## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]
```
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
4. **Create tasks.md:**
```markdown
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests
```
5. **Create design.md when needed:**
Create `design.md` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding
Minimal `design.md` skeleton:
```markdown
## Context
[Background, constraints, stakeholders]
## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]
## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]
## Risks / Trade-offs
- [Risk] → Mitigation
## Migration Plan
[Steps, rollback]
## Open Questions
- [...]
```
## Spec File Format
### Critical: Scenario Formatting
**CORRECT** (use #### headers):
```markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token
```
**WRONG** (don't use bullets or bold):
```markdown
- **Scenario: User login** ❌
**Scenario**: User login ❌
### Scenario: User login ❌
```
Every requirement MUST have at least one scenario.
### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
### Delta Operations
- `## ADDED Requirements` - New capabilities
- `## MODIFIED Requirements` - Changed behavior
- `## REMOVED Requirements` - Deprecated features
- `## RENAMED Requirements` - Name changes
Headers matched with `trim(header)` - whitespace ignored.
#### When to use ADDED vs MODIFIED
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you arent explicitly changing the existing requirement, add a new requirement under ADDED instead.
Authoring a MODIFIED requirement correctly:
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
Example for RENAMED:
```markdown
## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`
```
## Troubleshooting
### Common Errors
**"Change must have at least one delta"**
- Check `changes/[name]/specs/` exists with .md files
- Verify files have operation prefixes (## ADDED Requirements)
**"Requirement must have at least one scenario"**
- Check scenarios use `#### Scenario:` format (4 hashtags)
- Don't use bullet points or bold for scenario headers
**Silent scenario parsing failures**
- Exact format required: `#### Scenario: Name`
- Debug with: `openspec show [change] --json --deltas-only`
### Validation Tips
```bash
# Always use strict mode for comprehensive checks
openspec validate [change] --strict
# Debug delta parsing
openspec show [change] --json | jq '.deltas'
# Check specific requirement
openspec show [spec] --json -r 1
```
## Happy Path Script
```bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes
# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.
#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF
# 4) Validate
openspec validate $CHANGE --strict
```
## Multi-Capability Example
```
openspec/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # ADDED: Two-Factor Authentication
└── notifications/
└── spec.md # ADDED: OTP email notification
```
auth/spec.md
```markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...
```
notifications/spec.md
```markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...
```
## Best Practices
### Simplicity First
- Default to <100 lines of new code
- Single-file implementations until proven insufficient
- Avoid frameworks without clear justification
- Choose boring, proven patterns
### Complexity Triggers
Only add complexity with:
- Performance data showing current solution too slow
- Concrete scale requirements (>1000 users, >100MB data)
- Multiple proven use cases requiring abstraction
### Clear References
- Use `file.ts:42` format for code locations
- Reference specs as `specs/auth/spec.md`
- Link related changes and PRs
### Capability Naming
- Use verb-noun: `user-auth`, `payment-capture`
- Single purpose per capability
- 10-minute understandability rule
- Split if description needs "AND"
### Change ID Naming
- Use kebab-case, short and descriptive: `add-two-factor-auth`
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
## Tool Selection Guide
| Task | Tool | Why |
|------|------|-----|
| Find files by pattern | Glob | Fast pattern matching |
| Search code content | Grep | Optimized regex search |
| Read specific files | Read | Direct file access |
| Explore unknown scope | Task | Multi-step investigation |
## Error Recovery
### Change Conflicts
1. Run `openspec list` to see active changes
2. Check for overlapping specs
3. Coordinate with change owners
4. Consider combining proposals
### Validation Failures
1. Run with `--strict` flag
2. Check JSON output for details
3. Verify spec file format
4. Ensure scenarios properly formatted
### Missing Context
1. Read project.md first
2. Check related specs
3. Review recent archives
4. Ask for clarification
## Quick Reference
### Stage Indicators
- `changes/` - Proposed, not yet built
- `specs/` - Built and deployed
- `archive/` - Completed changes
### File Purposes
- `proposal.md` - Why and what
- `tasks.md` - Implementation steps
- `design.md` - Technical decisions
- `spec.md` - Requirements and behavior
### CLI Essentials
```bash
openspec list # What's in progress?
openspec show [item] # View details
openspec validate --strict # Is it correct?
openspec archive <change-id> [--yes|-y] # Mark complete (add --yes for automation)
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.

View File

@@ -0,0 +1,34 @@
# Change: Update agent asset list and detail display
## Why
The current agent-side asset experience exposes too much low-value information, hides some high-value state, and requires unnecessary search-type switching. The latest requests also require shop-based filtering, clearer detail presentation, explicit pagination, and a fixed top control area so search and filters remain available while the user browses long lists.
## What Changes
- Add IoT card device-binding status to the agent asset list
- Add cascaded shop filtering for the agent IoT card and device lists
- Remove the manual search-type selector and fix search semantics by active tab
- Keep the search input and filter controls fixed at the top while the asset list scrolls underneath
- Replace bottom-reached auto pagination with an explicit load-more button for both asset tabs
- Simplify asset detail basic fields and strengthen bound-card status presentation
- Remove `保护状态`
- Remove `切卡模式`
- Move the bound-card real-name status badge into the slot-title row
- Remove low-value device realtime traffic items and move "last online" into the realtime block as `最后上线时间`
- Make package effective-period text more prominent in asset detail package lists
- Only active packages should show the effective-period line
- Adjust the current active package header content
- Remove the order number line below the package name
- Show the active period there as `生效期:开始时间 - 到期时间`
## Impact
- Affected specs:
- `agent-asset-list`
- `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`

View File

@@ -0,0 +1,108 @@
## ADDED Requirements
### Requirement: Agent Asset Detail SHALL Remove Redundant Basic Fields
The agent asset detail experience SHALL remove fields that are no longer required from the basic information area.
#### Scenario: Viewing an IoT card detail
- **WHEN** the current asset is an IoT card
- **THEN** the basic information section SHALL NOT show `批次号`
#### Scenario: Viewing a device detail
- **WHEN** the current asset is a device
- **THEN** the basic information section SHALL NOT show `制造商`
- **AND** the basic information section SHALL NOT show `批次号`
- **AND** the basic information section SHALL NOT show `保护状态`
- **AND** the basic information section SHALL NOT show `切卡模式`
### Requirement: Agent Asset Detail SHALL Emphasize Bound IoT Card Identity And Status
The agent device detail view SHALL make bound IoT card identity and real-name status easier to scan.
#### Scenario: Rendering a bound IoT card item
- **WHEN** a device detail contains a bound IoT card entry
- **THEN** the label `电话` SHALL be renamed to `MSISDN`
- **AND** the item SHALL display `carrier_name` as `运营商`
- **AND** missing `msisdn` or `carrier_name` values SHALL render as `-`
#### Scenario: Rendering real-name status
- **WHEN** a bound IoT card has a `real_name_status`
- **THEN** the UI SHALL render that status with stronger visual emphasis than ordinary low-contrast metadata text
- **AND** the meaning of existing status values SHALL remain unchanged
#### Scenario: Rendering the bound-card header row
- **WHEN** a bound IoT card item is rendered in the device detail page
- **THEN** the real-name status badge SHALL be placed in the same row as the slot title
- **AND** the badge SHALL appear after the slot title text
- **AND** the lower metadata area SHALL NOT render a second standalone real-name field for the same card
### Requirement: Agent Device Realtime Status SHALL Prioritize High-Value Realtime Information
The agent device detail realtime block SHALL remove low-value traffic rows and absorb the "last online" field as a realtime-oriented field.
#### Scenario: Rendering device realtime traffic statistics
- **WHEN** the device realtime block is shown
- **THEN** it SHALL continue to show `今日用量`
- **AND** it SHALL continue to show `限速`
- **AND** it SHALL NOT show `下载流量`
- **AND** it SHALL NOT show `上传流量`
#### Scenario: Rendering last online time
- **WHEN** a device detail page has a realtime section
- **THEN** the page SHALL show `最后上线时间` inside the realtime section
- **AND** it SHALL prefer `realtimeStatus.device_realtime.last_online_time`
- **AND** it SHALL fall back to `deviceInfo.last_online_time` when the realtime value is absent
#### Scenario: Rendering the legacy time-information block
- **WHEN** the device detail page previously showed `创建时间`, `最后在线`, or `网关同步` in a separate time-information block
- **THEN** that block SHALL no longer show `创建时间`
- **AND** that block SHALL no longer show `最后在线`
- **AND** that block SHALL no longer show `网关同步`
### Requirement: Agent Asset Detail Package List SHALL Emphasize Effective Period
The package list in agent asset detail SHALL make the effective period line visually prominent.
#### Scenario: Package has both start and end time
- **WHEN** a package list item has both `activated_at` and `expires_at`
- **AND** the package is in active status
- **THEN** the page SHALL render a dedicated effective-period line with the meaning `生效期:开始时间 至 结束时间`
- **AND** that line SHALL have stronger visual prominence than the current weak auxiliary timestamp style
#### Scenario: Package is missing part of the effective period
- **WHEN** either `activated_at` or `expires_at` is missing
- **AND** the package is in active status
- **THEN** the effective-period line SHALL still be rendered
- **AND** the missing value SHALL display as `-`
#### Scenario: Package is not in active status
- **WHEN** a package list item is not in active status
- **THEN** the page SHALL NOT render the dedicated `生效期` line for that package
### Requirement: Agent Asset Detail Current Package SHALL Prioritize Effective Period Over Order Number
The current active package card in agent asset detail SHALL remove the order number from the package header area and use that space to surface the package active period.
#### Scenario: Rendering the current active package card
- **WHEN** the current active package card is rendered
- **THEN** the package name SHALL remain visible
- **AND** the order number line below the package name SHALL NOT be shown
- **AND** the card SHALL show a labeled `生效期:开始时间 - 到期时间` line in that position instead
#### Scenario: Current package is missing part of the active period
- **WHEN** either the current package `activated_at` or `expires_at` is missing
- **THEN** the labeled `生效期:开始时间 - 到期时间` line SHALL still be rendered
- **AND** the missing value SHALL display as `-`

View File

@@ -0,0 +1,123 @@
## ADDED Requirements
### Requirement: Agent IoT Card List SHALL Surface Device Binding Status
The agent-side IoT card list SHALL derive a device-binding status from the `device_virtual_no` field returned by `GET /api/admin/iot-cards/standalone` and show that status in each IoT card list item.
#### Scenario: Card is not bound to a device
- **WHEN** an IoT card item has `device_virtual_no` equal to `null`, `undefined`, an empty string, or a whitespace-only string
- **THEN** the list item SHALL show a status tag with `未绑定设备`
- **AND** the UI SHALL NOT display a fabricated device identifier
- **AND** the UI SHALL NOT prepend an extra `设备绑定` label before the tag
#### Scenario: Card is bound to a device
- **WHEN** an IoT card item has a non-empty `device_virtual_no`
- **THEN** the list item SHALL show a status tag with `已绑定设备`
- **AND** the UI MAY keep the raw `device_virtual_no` hidden
### Requirement: Agent Asset Lists SHALL Support Cascaded Shop Filtering
The agent-side asset lists SHALL provide a cascaded shop filter that is backed by `GET /api/admin/shops/cascade` and applies the selected shop ID to the active list request.
#### Scenario: User chooses a shop node under the IoT card tab
- **WHEN** the user opens the shop filter under the `IoT卡` tab and confirms a shop node
- **THEN** the selected node `id` SHALL be sent as `shop_id` in the subsequent IoT card list request
- **AND** the list SHALL refresh using that shop filter
#### Scenario: User chooses a shop node under the device tab
- **WHEN** the user opens the shop filter under the `设备` tab and confirms a shop node
- **THEN** the selected node `id` SHALL be sent as `shop_id` in the subsequent device list request
- **AND** the device list SHALL refresh using that shop filter
- **AND** the device list request SHALL keep its existing search semantics and append `shop_id` in addition to them
#### Scenario: Selected shop has children
- **WHEN** a shop node returned from `GET /api/admin/shops/cascade` has `has_children = true`
- **THEN** the UI SHALL allow the user to continue drilling down to child shops
- **AND** the UI SHALL use `parent_id` to load the next level
#### Scenario: User clears the shop filter
- **WHEN** the user clears the shop filter
- **THEN** subsequent IoT card list requests SHALL omit `shop_id`
- **AND** subsequent device list requests SHALL omit `shop_id`
#### Scenario: Device tab is active
- **WHEN** the active asset tab is `设备`
- **THEN** the shop filter SHALL still be shown
- **AND** device list requests SHALL support `shop_id`
### Requirement: Agent Asset Search Input SHALL Be Fixed By Active Tab
The agent asset list SHALL remove the manual search-type selector and bind search semantics directly to the active tab.
#### Scenario: IoT card tab search
- **WHEN** the active tab is `IoT卡`
- **THEN** the search field SHALL target `iccid`
- **AND** the search placeholder SHALL be `搜索ICCID`
- **AND** the UI SHALL NOT show a search-type switcher
#### Scenario: Device tab search
- **WHEN** the active tab is `设备`
- **THEN** the search field SHALL target `virtual_no`
- **AND** the user-facing search label SHALL be `设备号`
- **AND** the search placeholder SHALL be `搜索设备号`
- **AND** the UI SHALL NOT show a search-type switcher
### Requirement: Agent Asset List Controls SHALL Stay Fixed At The Top
The agent-side asset list page SHALL keep the search input and the filter/tab control area fixed at the top of the page while the list content scrolls independently underneath.
#### Scenario: User scrolls a long IoT card list
- **WHEN** the active tab is `IoT卡` and the user scrolls the list
- **THEN** the search input SHALL remain visible at the top
- **AND** the filter and tab controls directly below it SHALL remain visible at the top
- **AND** only the list content area SHALL scroll
#### Scenario: User scrolls a long device list
- **WHEN** the active tab is `设备` and the user scrolls the list
- **THEN** the search input SHALL remain visible at the top
- **AND** the filter and tab controls directly below it SHALL remain visible at the top
- **AND** only the list content area SHALL scroll
#### Scenario: Fixed controls do not cover list content
- **WHEN** the fixed top control area is rendered
- **THEN** the list content SHALL start below that area
- **AND** the first list item SHALL remain fully visible and operable
### Requirement: Agent Asset Lists SHALL Use Explicit Load-More Pagination
The agent-side IoT card list and device list SHALL stop automatic pagination triggered by scrolling to the bottom and SHALL use an explicit footer button to load the next page.
#### Scenario: IoT card tab still has more pages
- **WHEN** the active tab is `IoT卡` and the current IoT card result set has more pages available
- **THEN** the footer SHALL show a button labeled `加载更多`
- **AND** the next page SHALL NOT be requested until the user clicks that button
#### Scenario: Device tab still has more pages
- **WHEN** the active tab is `设备` and the current device result set has more pages available
- **THEN** the footer SHALL show a button labeled `加载更多`
- **AND** the next page SHALL NOT be requested until the user clicks that button
#### Scenario: User clicks the load-more button
- **WHEN** the user clicks the `加载更多` button for the active tab
- **THEN** the UI SHALL request the next page for that tab using the existing active filters and search parameters
- **AND** the returned items SHALL append to the existing list instead of replacing it
#### Scenario: No more pages remain
- **WHEN** the active list has no more pages available
- **THEN** the `加载更多` button SHALL NOT be shown

View File

@@ -0,0 +1,20 @@
## 1. Spec
- [x] 1.1 Initialize OpenSpec in the repository
- [x] 1.2 Capture project context in `openspec/project.md`
- [x] 1.3 Create change proposal and delta specs for asset list and asset detail
- [x] 1.4 Review and approve the proposal before implementation
## 2. Implementation
- [x] 2.1 Extend asset list typing and request parameters for `device_virtual_no` and `shop_id`
- [x] 2.2 Update agent asset list UI for binding status, shop filter, and fixed search semantics
- [x] 2.3 Update asset detail UI for field removal, bound-card emphasis, and realtime-status changes
- [x] 2.4 Update package-list date presentation in detail pages
- [ ] 2.5 Verify card and device flows manually after implementation
- [x] 2.6 Remove `保护状态` and `切卡模式`, and move bound-card real-name status next to the slot title
- [x] 2.7 Restrict package effective-period display to packages in active status only
- [x] 2.8 Remove the current-package order number and replace it with a `生效期:开始时间 - 到期时间` line
- [x] 2.9 Apply the cascaded shop filter to the device list request and device tab UI
- [x] 2.10 Replace bottom-reached auto pagination with an explicit `加载更多` button for the IoT card and device lists
- [x] 2.11 Keep the asset-list search input and filter controls fixed at the top while the list scrolls underneath

68
openspec/project.md Normal file
View File

@@ -0,0 +1,68 @@
# Project Context
## Purpose
This project is a Uni-app based agent and enterprise management frontend for IoT assets. It provides asset list, asset search/detail, package display, commission, withdrawal, and related operational pages for two user roles: agent accounts and enterprise accounts.
## Tech Stack
- TypeScript
- Vue 3 with `<script setup>`
- Uni-app
- Vite
- UnoCSS
- Pinia
- uview-plus
## Project Conventions
### Code Style
- Use Composition API and `<script setup lang="ts">`.
- Keep API types close to the request layer in `src/api/*.ts`.
- Prefer small, direct page-level logic over premature abstraction.
- Existing pages use UnoCSS utility classes heavily; UI changes should preserve the current visual language.
- User-facing copy is primarily Chinese.
### Architecture Patterns
- Business pages live under `src/pages/agent-system/`.
- Backend calls are wrapped in `src/api/*.ts`.
- Shared formatting helpers live in `src/utils/format` and nearby page helpers.
- Asset pages distinguish between two primary resource types: `card` (IoT card) and `device`.
### Testing Strategy
- Run targeted validation with `pnpm type-check`, lint, and manual page checks when implementation begins.
- For spec work, validate proposals with `openspec validate --strict`.
- User-visible asset changes should be verified on both card and device flows.
### Git Workflow
- The repository may contain unrelated user changes; do not revert them.
- Use OpenSpec change proposals for user-visible feature changes before implementation.
- Keep proposals focused and capability-scoped.
## Domain Context
- Assets are mainly IoT cards and devices.
- 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`
- Package display and realtime status are important parts of the asset detail experience.
## Important Constraints
- Agent and enterprise views are not identical; requirements in one role should not be assumed to apply globally.
- Existing backend contracts are documented partly in `docs/*.md` and partly in the TypeScript API layer.
- UI changes should preserve current behavior unless the proposal explicitly changes it.
- Search semantics, display labels, and field visibility are product requirements and must be documented precisely.
## External Dependencies
- Backend admin APIs under `/api/admin/...`
- Shop cascade API for hierarchical shop filtering
- Asset resolve, realtime status, current package, and package list APIs
- Uni-app runtime and uview-plus components