This commit is contained in:
@@ -24,7 +24,7 @@ pnpm dev:h5
|
||||
http://localhost:5173/
|
||||
```
|
||||
|
||||
**默认入口页面**: `pages/agent-system/index`
|
||||
**默认入口页面**: `pages/agent-system/home/index`
|
||||
|
||||
---
|
||||
|
||||
@@ -85,7 +85,10 @@ http://localhost:5173/
|
||||
|
||||
```
|
||||
src/pages/agent-system/
|
||||
└── index.vue # 主页面 (所有功能都在这里)
|
||||
├── home/index.vue # 当前首页入口
|
||||
├── commission-center/index.vue # 佣金中心
|
||||
├── withdraw/index.vue # 提现管理
|
||||
└── ... # 其他独立业务页面
|
||||
|
||||
.claude/
|
||||
├── AGENT-SYSTEM-DESIGN.md # 完整设计规范 (50+ 页)
|
||||
@@ -135,7 +138,7 @@ src/pages/agent-system/
|
||||
|
||||
### 添加新菜单项
|
||||
|
||||
修改 `src/pages/agent-system/index.vue`:
|
||||
当前实现已拆分为多页面,请修改对应业务页,例如 `src/pages/agent-system/home/index.vue`:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
|
||||
2
env/.env
vendored
2
env/.env
vendored
@@ -5,7 +5,7 @@ VITE_APP_TITLE=代理企业管理系统
|
||||
VITE_APP_ENV=development
|
||||
|
||||
# 接口地址
|
||||
VITE_API_BASE_URL=https://cmp-api.xm-iot.cn
|
||||
VITE_API_BASE_URL=https://cmp-api.boss160.cn
|
||||
|
||||
# 端口号
|
||||
VITE_APP_PORT=9527
|
||||
|
||||
2
env/.env.test
vendored
2
env/.env.test
vendored
@@ -2,7 +2,7 @@
|
||||
VITE_APP_ENV=staging
|
||||
|
||||
# 接口地址
|
||||
VITE_API_BASE_URL=https://cmp-api.xm-iot.cn
|
||||
VITE_API_BASE_URL=https://cmp-api.boss160.cn
|
||||
|
||||
# 是否使用代理 (测试环境不使用代理)
|
||||
VITE_APP_PROXY=false
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
## Context
|
||||
|
||||
The project already contains a theme file at `src/static/styles/theme.scss`, but the actual app styling is inconsistent:
|
||||
|
||||
- `theme.scss` defines `--brand-*`, text, background, border, and utility classes
|
||||
- `src/uni.scss` and `uno.config.ts` still reference `--theme-*` variables
|
||||
The requested change is a cross-cutting design-system alignment focused on the project-wide theme contract.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals:
|
||||
- Define a single source of truth for the core project palette
|
||||
- Use exactly three core brand colors for layout-level styling:
|
||||
- Primary: `#2F7D32`
|
||||
- Secondary: `#1F2937`
|
||||
- Tertiary: `#F4FBF5`
|
||||
- Ensure shared consumers such as UnoCSS utilities and uView theme variables resolve from the same theme source
|
||||
|
||||
- Non-Goals:
|
||||
- Full repo-wide page cleanup in one implementation pass
|
||||
- Redefining semantic success, warning, and error states for every page
|
||||
- Removing light/dark theme support
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: `src/static/styles/theme.scss` will remain the only file that contains literal core brand values.
|
||||
- Why: this file already acts as the closest thing to a design-token source, and centralizing values here avoids duplicating palette values across styling systems.
|
||||
|
||||
- Decision: `src/uni.scss` and `uno.config.ts` will consume compatibility aliases exported from the same theme source rather than define their own brand colors.
|
||||
- Why: the current `--theme-*` and `--brand-*` split is inconsistent and invites drift. Compatibility aliases let existing consumers keep working while the project converges on one source.
|
||||
|
||||
- Decision: layout-level accents in touched agent-facing pages and shared components will be limited to the approved primary, secondary, tertiary, and neutral tokens.
|
||||
- Why: the project had mixed blue, green, orange, red, purple, and cyan across normal layout decoration, which weakens hierarchy and visual cohesion.
|
||||
|
||||
- Decision: semantic success, warning, and error colors may remain for exceptional states only.
|
||||
- Why: status badges, destructive actions, and warnings still need semantic distinction, but these colors should not become the default decorative palette for dashboards and cards.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Risk: changing the core palette may alter the look of existing shared utilities.
|
||||
- Mitigation: keep the implementation scoped to shared tokens plus touched pages, and validate affected agent pages after the token update.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Define the approved brand palette and compatibility aliases in `src/static/styles/theme.scss`
|
||||
2. Update `src/uni.scss` and `uno.config.ts` to reference the shared aliases
|
||||
3. Verify touched shared utilities and agent pages for palette consistency
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Which additional agent-facing pages should be migrated to the shared token set in the next cleanup pass
|
||||
@@ -0,0 +1,25 @@
|
||||
# Change: Update shared theme tokens
|
||||
|
||||
## Why
|
||||
|
||||
The project currently has inconsistent theme entry points: `src/static/styles/theme.scss` defines `--brand-*` variables, while `src/uni.scss` and `uno.config.ts` still reference `--theme-*` variables. This prevents a clean single-source design system and makes brand updates drift across pages.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Establish a single theme source for the project color system in `src/static/styles/theme.scss`
|
||||
- Re-define the core project palette as:
|
||||
- Primary: `#2F7D32`
|
||||
- Secondary: `#1F2937`
|
||||
- Tertiary: `#F4FBF5`
|
||||
- Require page and component layout accents to consume shared theme tokens instead of page-local hard-coded hex values
|
||||
- Align UnoCSS and uView theme mappings with the shared theme source
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `design-theme-tokens`
|
||||
- Affected code:
|
||||
- `src/static/styles/theme.scss`
|
||||
- `src/uni.scss`
|
||||
- `uno.config.ts`
|
||||
- shared agent-facing components that currently rely on page-local brand hex colors
|
||||
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Project Brand Tokens SHALL Be Defined In One Theme Source
|
||||
|
||||
The project SHALL define its core layout brand palette in `src/static/styles/theme.scss` as the single source of truth. The core brand palette MUST be:
|
||||
|
||||
- Primary: `#2F7D32`
|
||||
- Secondary: `#1F2937`
|
||||
- Tertiary: `#F4FBF5`
|
||||
|
||||
Compatibility aliases and helper tokens MAY exist, but they MUST be declared from the same theme source and MUST NOT introduce unrelated page-local brand colors.
|
||||
|
||||
#### Scenario: Shared brand emphasis resolves from one source
|
||||
|
||||
- **GIVEN** a page or component uses shared brand emphasis such as `text-brand`, `bg-brand`, `border-brand`, UnoCSS theme colors, or uView primary-theme values
|
||||
- **WHEN** the app renders those styles
|
||||
- **THEN** they resolve from the palette defined in `src/static/styles/theme.scss`
|
||||
- **AND** they do not define a separate hard-coded primary hex outside that theme source
|
||||
|
||||
#### Scenario: A core brand value changes
|
||||
|
||||
- **GIVEN** a developer updates one of the core brand values in `src/static/styles/theme.scss`
|
||||
- **WHEN** the project rebuilds
|
||||
- **THEN** shared brand utilities and theme consumers reflect the updated value without needing page-level color edits
|
||||
|
||||
### Requirement: Layout-Level Brand Usage SHALL Consume Shared Tokens
|
||||
|
||||
Agent-facing pages and shared components SHALL use shared theme tokens for layout-level accents instead of page-local hard-coded brand hex values. Semantic success, warning, and error colors MAY remain for exceptional states, destructive actions, or state badges, but SHALL NOT be used as the default decorative palette for normal dashboard and card layouts.
|
||||
|
||||
#### Scenario: Agent-facing layout colors are reviewed
|
||||
|
||||
- **GIVEN** an agent-facing page or shared component is reviewed for colors
|
||||
- **WHEN** it renders navigation states, buttons, highlight cards, decorative surfaces, and metric blocks
|
||||
- **THEN** those layout accents use the primary, secondary, tertiary, or neutral theme tokens
|
||||
- **AND** normal layout decoration does not depend on page-local blue, orange, purple, cyan, or red accent values
|
||||
|
||||
#### Scenario: Shared theme consumers remain aligned
|
||||
|
||||
- **GIVEN** `src/uni.scss` and `uno.config.ts` provide theme mappings for components and utilities
|
||||
- **WHEN** they expose primary colors to downstream styles
|
||||
- **THEN** they reference the shared theme source rather than maintaining independent brand values
|
||||
@@ -0,0 +1,12 @@
|
||||
## 1. Spec
|
||||
|
||||
- [x] 1.1 Review the existing theme/token entry points
|
||||
- [x] 1.2 Draft proposal, design notes, and spec deltas for centralized theme tokens
|
||||
- [x] 1.3 Review and approve the proposal before implementation
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [x] 2.1 Update `src/static/styles/theme.scss` to define the approved primary, secondary, and tertiary palette and expose compatibility aliases from the same file
|
||||
- [x] 2.2 Align `src/uni.scss` and `uno.config.ts` with the shared theme source
|
||||
- [x] 2.3 Replace touched page-local brand hex values with shared tokens where this change already updated agent-facing views
|
||||
- [x] 2.4 Verify that touched layouts and components use the shared theme source and preserve mobile readability
|
||||
@@ -0,0 +1,57 @@
|
||||
## Context
|
||||
|
||||
The agent home page and the commission center both render a withdrawable-commission summary card from the same `GET /api/admin/shops/fund-summary` response, but they currently duplicate the markup separately.
|
||||
|
||||
That duplication has already caused drift:
|
||||
|
||||
- the home page added an inline `佣金中心` text-and-chevron chip inside the card header
|
||||
- the two cards can diverge visually even though they show the same summary concept
|
||||
- new summary fields such as `main_balance` need to be maintained in two places
|
||||
|
||||
The requested change is small in scope, but it introduces a presentation-level reuse rule that should be documented before implementation.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals:
|
||||
- Ensure the home page and commission center render the withdrawable-commission summary with the same visible structure
|
||||
- Remove the extra inline `佣金中心` chip from the home-page card
|
||||
- Centralize the repeated card markup into one shared component
|
||||
- Keep data fetching and navigation ownership in the page containers
|
||||
|
||||
- Non-Goals:
|
||||
- Redesigning commission-center record lists or search/filter areas
|
||||
- Changing the fund-summary API contract beyond the already approved username/main-balance behavior
|
||||
- Moving commission business logic out of the page containers
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: implement one shared presentational component for the withdrawable-commission summary card.
|
||||
- Why: the two pages describe the same summary object and should not maintain separate card markup.
|
||||
|
||||
- Decision: the shared component should own display structure only; parent pages should continue to own data loading and page navigation.
|
||||
- Why: the card is reused across pages, but fetch timing and navigation context are still page-specific concerns.
|
||||
|
||||
- Decision: the home-page card must not render a separate inline `佣金中心` chip/chevron affordance inside the card header.
|
||||
- Why: the product explicitly wants that duplicate callout removed, and the surrounding page already provides navigation context.
|
||||
|
||||
- Decision: the shared summary icon should render directly in the header layout without an extra boxed wrapper surface.
|
||||
- Why: the product explicitly wants the icon treatment simplified, and keeping the icon unboxed reduces visual noise while preserving recognition.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Risk: extracting a shared component can accidentally drop one page-specific field or visual state.
|
||||
- Mitigation: the spec requires the same core title, amount, and summary metric set on both pages.
|
||||
|
||||
- Risk: if navigation behavior is embedded into the shared component, reuse may become rigid.
|
||||
- Mitigation: keep click handling and routing in the parent pages.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Extract the repeated withdrawable-commission card markup into one shared component
|
||||
2. Replace the duplicated home-page and commission-center card markup with the shared component
|
||||
3. Remove the extra inline `佣金中心` chip from the home-page rendering
|
||||
4. Verify that both pages still show the same summary fields and formatting
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Whether the shared component should live in `src/components/` or in an `agent-system`-scoped components directory
|
||||
@@ -0,0 +1,28 @@
|
||||
# Change: Update fund-summary username filter and shared summary card display
|
||||
|
||||
## Why
|
||||
|
||||
The frontend originally sent the current agent username through the `shop_name` query parameter when calling `GET /api/admin/shops/fund-summary`, which no longer matched the backend contract. That response also includes `main_balance`, which must be shown in both the agent home page and commission center withdrawable-commission cards.
|
||||
|
||||
The two withdrawable-commission cards are also duplicated between the home page and commission center. The home page currently renders an extra inline `佣金中心` chip inside the card header, which the product no longer wants. The two pages should render the same summary card through one shared component.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Update the frontend contract for `GET /api/admin/shops/fund-summary` so self-scoped requests send the logged-in agent username under `username`, not `shop_name`
|
||||
- Keep the query value unchanged; only the parameter name changes
|
||||
- Show the backend `main_balance` field in the agent home page and commission center withdrawable-commission cards with the label `预充值余额`
|
||||
- Preserve the existing amount formatting and empty-state behavior for the summary cards
|
||||
- Remove the extra inline `佣金中心` text-and-chevron chip from the home-page withdrawable-commission card
|
||||
- Standardize the home page and commission center withdrawable-commission cards behind one shared component so the visible summary structure stays aligned
|
||||
- Remove the extra decorative icon wrapper so the summary icon is not rendered inside a separate boxed container
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `agent-fund-summary`
|
||||
- Affected code:
|
||||
- `src/api/commission.ts`
|
||||
- shared fund-summary card component to be introduced for agent pages
|
||||
- `src/pages/agent-system/home/index.vue`
|
||||
- `src/pages/agent-system/commission-center/index.vue`
|
||||
- `src/pages/agent-system/withdraw/index.vue`
|
||||
@@ -0,0 +1,76 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Agent Fund Summary Requests SHALL Filter By Username
|
||||
|
||||
The frontend SHALL send the current logged-in agent account identifier under the `username` query parameter when requesting self-scoped data from `GET /api/admin/shops/fund-summary`. The frontend MUST NOT send that same identifier under `shop_name`.
|
||||
|
||||
#### Scenario: Agent home page requests its own fund summary
|
||||
|
||||
- **GIVEN** the logged-in agent has `username = "agent_a"`
|
||||
- **WHEN** the home page requests `GET /api/admin/shops/fund-summary`
|
||||
- **THEN** the request includes `username=agent_a`
|
||||
- **AND** the request does not send `shop_name=agent_a`
|
||||
|
||||
#### Scenario: Commission pages request the current agent fund summary
|
||||
|
||||
- **GIVEN** the logged-in agent has `username = "agent_a"`
|
||||
- **WHEN** the commission center page or withdrawal page requests `GET /api/admin/shops/fund-summary`
|
||||
- **THEN** the request includes `username=agent_a`
|
||||
- **AND** the value is passed through unchanged from the logged-in user information
|
||||
|
||||
#### Scenario: No username is available from login state
|
||||
|
||||
- **GIVEN** the current login state does not provide a username
|
||||
- **WHEN** the frontend requests `GET /api/admin/shops/fund-summary`
|
||||
- **THEN** the frontend omits the username filter
|
||||
- **AND** the frontend does not substitute `shop_name` as a fallback filter key
|
||||
|
||||
### Requirement: Agent Fund Summary Cards SHALL Show Main Balance
|
||||
|
||||
The agent home page and commission center withdrawable-commission summary cards SHALL display the backend `main_balance` field using the label `预充值余额`. The displayed amount MUST use the same currency-formatting rules as the other fund summary amounts.
|
||||
|
||||
#### Scenario: Home page renders main balance
|
||||
|
||||
- **GIVEN** the fund summary response contains `main_balance = 200000`
|
||||
- **WHEN** the agent home page renders the withdrawable-commission card
|
||||
- **THEN** the card shows a metric labeled `预充值余额`
|
||||
- **AND** the amount is rendered using the existing summary amount formatter
|
||||
|
||||
#### Scenario: Commission center renders main balance
|
||||
|
||||
- **GIVEN** the fund summary response contains `main_balance = 200000`
|
||||
- **WHEN** the commission center page renders the withdrawable-commission card
|
||||
- **THEN** the card shows a metric labeled `预充值余额`
|
||||
- **AND** the amount is rendered using the existing summary amount formatter
|
||||
|
||||
#### Scenario: Main balance is unavailable
|
||||
|
||||
- **GIVEN** the fund summary response is missing `main_balance` or the summary has not loaded yet
|
||||
- **WHEN** either summary card renders
|
||||
- **THEN** the `预充值余额` label remains visible
|
||||
- **AND** the corresponding amount renders as `--`
|
||||
|
||||
### Requirement: Agent Fund Summary Card Presentation SHALL Stay Consistent Across Pages
|
||||
|
||||
The agent home page and commission center SHALL render the withdrawable-commission summary through one shared card presentation so the title, primary amount area, summary icon, and summary metric blocks stay visually consistent across both pages. The home-page card MUST NOT render a separate inline `佣金中心` text-and-chevron chip inside the card header. The summary icon MUST NOT be rendered inside a separate boxed wrapper surface.
|
||||
|
||||
#### Scenario: Home page and commission center share the same summary structure
|
||||
|
||||
- **GIVEN** the fund summary response has loaded on both the home page and the commission center page
|
||||
- **WHEN** the withdrawable-commission summary card renders on each page
|
||||
- **THEN** both pages show the same card title, primary amount area, summary icon treatment, and summary metric block order
|
||||
- **AND** both pages use the same summary-card presentation rather than maintaining separate duplicated markup
|
||||
|
||||
#### Scenario: Home page removes the extra commission-center chip
|
||||
|
||||
- **GIVEN** the agent home page renders the withdrawable-commission summary card
|
||||
- **WHEN** the user scans the card header area
|
||||
- **THEN** the card does not display a separate inline `佣金中心` text-and-chevron chip
|
||||
- **AND** the rest of the card content remains available
|
||||
|
||||
#### Scenario: Summary icon is not boxed
|
||||
|
||||
- **GIVEN** either summary card renders on the home page or commission center page
|
||||
- **WHEN** the user views the summary icon area
|
||||
- **THEN** the icon is displayed without an additional boxed wrapper container
|
||||
- **AND** the icon remains visually aligned with the shared card header
|
||||
@@ -0,0 +1,15 @@
|
||||
## 1. Spec
|
||||
|
||||
- [x] 1.1 Create a focused OpenSpec proposal for the fund-summary contract and card display change
|
||||
- [x] 1.2 Review and approve the proposal before implementation
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [x] 2.1 Update `FundSummaryParams` and every `getFundSummary` caller to send `username` instead of `shop_name`
|
||||
- [x] 2.2 Display `main_balance` as `预充值余额` in the agent home withdrawable-commission card
|
||||
- [x] 2.3 Display `main_balance` as `预充值余额` in the commission center withdrawable-commission card
|
||||
- [x] 2.4 Verify request payloads and empty-state rendering for all fund-summary callers
|
||||
- [x] 2.5 Extract the home-page and commission-center withdrawable-commission summary into one shared component
|
||||
- [x] 2.6 Remove the extra inline `佣金中心` chip from the home-page summary card
|
||||
- [x] 2.7 Remove the extra boxed wrapper around the summary icon in the shared card
|
||||
- [x] 2.8 Verify that both pages render the same summary card structure and field order
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 佣金模块 API (代理端)
|
||||
*/
|
||||
import { get, post } from '@/utils/request'
|
||||
import { get } from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 资金概览 (代理端主页用)
|
||||
@@ -141,7 +141,7 @@ export interface ListResponse<T> {
|
||||
}
|
||||
|
||||
export interface FundSummaryParams {
|
||||
shop_name?: string
|
||||
username?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +151,7 @@ export interface FundSummaryParams {
|
||||
export function getFundSummary(params?: FundSummaryParams) {
|
||||
return get<{ items: FundSummary[] }>(
|
||||
'/api/admin/shops/fund-summary',
|
||||
params?.shop_name ? { params } : undefined
|
||||
params?.username ? { params } : undefined
|
||||
).then(res => res.items?.[0])
|
||||
}
|
||||
|
||||
|
||||
88
src/components/AgentFundSummaryCard.vue
Normal file
88
src/components/AgentFundSummaryCard.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { FundSummary } from '@/api/commission'
|
||||
|
||||
type SummaryMetric = {
|
||||
label: string
|
||||
amount?: number | null
|
||||
cardClass: string
|
||||
labelClass: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
summary?: FundSummary | null
|
||||
}>()
|
||||
|
||||
const metrics = computed<SummaryMetric[]>(() => [
|
||||
{
|
||||
label: '预充值余额',
|
||||
amount: props.summary?.main_balance,
|
||||
cardClass: 'bg-[#f4fbf5] border border-[#e3f2e6]',
|
||||
labelClass: 'text-[#2f7d32]',
|
||||
},
|
||||
{
|
||||
label: '累计佣金',
|
||||
amount: props.summary?.total_commission,
|
||||
cardClass: 'bg-[#f8fafc] border border-[#eef2f6]',
|
||||
labelClass: 'text-[#6b7280]',
|
||||
},
|
||||
{
|
||||
label: '已提现',
|
||||
amount: props.summary?.withdrawn_commission,
|
||||
cardClass: 'bg-[#f8fafc] border border-[#eef2f6]',
|
||||
labelClass: 'text-[#6b7280]',
|
||||
},
|
||||
{
|
||||
label: '冻结',
|
||||
amount: props.summary?.frozen_commission,
|
||||
cardClass: 'bg-[#f8fafc] border border-[#eef2f6]',
|
||||
labelClass: 'text-[#6b7280]',
|
||||
},
|
||||
])
|
||||
|
||||
function formatAmount(amount: number) {
|
||||
const yuan = (amount / 100).toFixed(2)
|
||||
const parts = yuan.split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return `¥${parts.join('.')}`
|
||||
}
|
||||
|
||||
function formatAmountDisplay(amount?: number | null) {
|
||||
return typeof amount === 'number' ? formatAmount(amount) : '--'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bg-white rounded-20px shadow-[0_8px_24px_rgba(15,23,42,0.06)] p-5">
|
||||
<view class="rounded-18px bg-gradient-to-br from-[#f5fbf5] via-white to-[#f8fafc] p-4">
|
||||
<view class="flex items-start justify-between gap-3">
|
||||
<view class="min-w-0 flex-1">
|
||||
<view class="inline-flex items-center gap-1 rounded-full bg-[#e8f5e9] px-2.5 py-1">
|
||||
<i class="i-mdi-wallet-outline text-13px text-[#2f7d32]" />
|
||||
<text class="text-11px font-600 text-[#2f7d32]">可提现佣金</text>
|
||||
</view>
|
||||
<text class="text-34px font-700 leading-none text-[#1f2937] block mt-3 break-all">
|
||||
{{ formatAmountDisplay(summary?.available_commission) }}
|
||||
</text>
|
||||
</view>
|
||||
<image src="@/static/icons/佣金中心.png" class="w-16 h-16 shrink-0" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="grid grid-cols-2 gap-3 mt-4">
|
||||
<view
|
||||
v-for="metric in metrics"
|
||||
:key="metric.label"
|
||||
:class="metric.cardClass"
|
||||
class="rounded-16px px-3 py-3"
|
||||
>
|
||||
<text :class="metric.labelClass" class="text-11px block">
|
||||
{{ metric.label }}
|
||||
</text>
|
||||
<text class="text-15px font-700 text-[#1f2937] block mt-2 break-all">
|
||||
{{ formatAmountDisplay(metric.amount) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -340,14 +340,6 @@
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 运营商ID -->
|
||||
<view v-if="cardInfo.carrier_id" class="flex items-start justify-between">
|
||||
<text class="text-12px text-[#999] w-70px">运营商ID</text>
|
||||
<text class="flex-1 text-13px text-[#212121] text-right">
|
||||
{{ cardInfo.carrier_id }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 卡类型 -->
|
||||
<view v-if="cardInfo.card_category" class="flex items-start justify-between">
|
||||
<text class="text-12px text-[#999] w-70px">卡类型</text>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getFundSummary, getCommissionRecords, getCommissionStats } from '@/api/commission'
|
||||
import type { FundSummary, CommissionRecord, CommissionStats } from '@/api/commission'
|
||||
import AgentFundSummaryCard from '@/components/AgentFundSummaryCard.vue'
|
||||
import SimplePicker from '@/components/SimplePicker.vue'
|
||||
|
||||
type LoginUserInfo = {
|
||||
@@ -13,7 +14,6 @@ const commissionSummary = ref<FundSummary | null>(null)
|
||||
const shopId = ref<number>(0)
|
||||
const commissionRecords = ref<CommissionRecord[]>([])
|
||||
const commissionStats = ref<CommissionStats | null>(null)
|
||||
const loading = ref(false)
|
||||
const loadingRecords = ref(false)
|
||||
const page = ref(1)
|
||||
const pageSize = 20
|
||||
@@ -86,7 +86,7 @@ onMounted(() => {
|
||||
async function loadCommissionSummary() {
|
||||
try {
|
||||
const userInfo = getLoginUserInfo()
|
||||
commissionSummary.value = await getFundSummary(userInfo?.username ? { shop_name: userInfo.username } : undefined)
|
||||
commissionSummary.value = await getFundSummary(userInfo?.username ? { username: userInfo.username } : undefined)
|
||||
} catch (error) {
|
||||
console.error('加载佣金概览失败:', error)
|
||||
}
|
||||
@@ -153,12 +153,6 @@ function loadMore() {
|
||||
}
|
||||
}
|
||||
|
||||
function onScroll(e: any) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
if (scrollHeight - scrollTop - clientHeight < 100) {
|
||||
loadMore()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -205,37 +199,7 @@ function onScroll(e: any) {
|
||||
<!-- 内容区域 -->
|
||||
<view class="px-4 pb-4">
|
||||
<!-- 概览卡片 -->
|
||||
<view class="bg-white rounded-16px p-5 mt-4 shadow-[0_2px_12px_rgba(0,0,0,0.08)]">
|
||||
<view class="flex items-center justify-between">
|
||||
<view>
|
||||
<text class="text-12px text-[#999]">可提现佣金</text>
|
||||
<text class="text-32px font-700 text-[#212121] block mt-1">
|
||||
{{ commissionSummary ? formatAmount(commissionSummary.available_commission) : '--' }}
|
||||
</text>
|
||||
</view>
|
||||
<image src="@/static/icons/佣金中心.png" class="w-12 h-12 rounded-12px" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="flex gap-3 mt-4 pt-4 border-t border-[#f5f5f5]">
|
||||
<view class="flex-1 text-center">
|
||||
<text class="text-11px text-[#999]">累计佣金</text>
|
||||
<text class="text-14px font-600 text-[#212121] block mt-1">
|
||||
{{ commissionSummary ? formatAmount(commissionSummary.total_commission) : '--' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-1 text-center border-l border-[#f5f5f5]">
|
||||
<text class="text-11px text-[#999]">已提现</text>
|
||||
<text class="text-14px font-600 text-[#52c41a] block mt-1">
|
||||
{{ commissionSummary ? formatAmount(commissionSummary.withdrawn_commission) : '--' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-1 text-center border-l border-[#f5f5f5]">
|
||||
<text class="text-11px text-[#999]">冻结</text>
|
||||
<text class="text-14px font-600 text-[#ff4d4f] block mt-1">
|
||||
{{ commissionSummary ? formatAmount(commissionSummary.frozen_commission) : '--' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AgentFundSummaryCard class="mt-4" :summary="commissionSummary" />
|
||||
|
||||
<!-- 佣金统计 -->
|
||||
<view class="bg-white rounded-16px p-4 mt-3 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import type { UserInfo } from '@/api/auth'
|
||||
import { getFundSummary, getCommissionDailyStats } from '@/api/commission'
|
||||
import type { FundSummary, DailyCommissionStats } from '@/api/commission'
|
||||
import AgentFundSummaryCard from '@/components/AgentFundSummaryCard.vue'
|
||||
|
||||
|
||||
// 用户信息
|
||||
@@ -36,10 +37,6 @@
|
||||
return `¥${parts.join('.')}`
|
||||
}
|
||||
|
||||
function formatAmountDisplay(amount?: number | null) {
|
||||
return typeof amount === 'number' ? formatAmount(amount) : '--'
|
||||
}
|
||||
|
||||
// 格式化日期 (MM-DD)
|
||||
// 根据用户类型动态生成菜单
|
||||
function formatDate(dateStr : string) {
|
||||
@@ -49,72 +46,15 @@
|
||||
return `${month}-${day}`
|
||||
}
|
||||
|
||||
function getTodayDate() {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
const menuItems = computed(() => {
|
||||
const menus = [
|
||||
{
|
||||
title: '资产列表',
|
||||
desc: userInfo.value?.user_type === 3 ? '代理资产列表' : '企业资产列表',
|
||||
icon: '/static/icons/资产列表.png',
|
||||
path: '/pages/agent-system/assets/index',
|
||||
},
|
||||
]
|
||||
|
||||
// 企业端显示授权卡和授权设备 (仅 user_type === 4)
|
||||
if (userInfo.value?.user_type === 4) {
|
||||
menus.push(
|
||||
{
|
||||
title: '授权卡列表',
|
||||
desc: '企业授权IoT卡',
|
||||
icon: '/static/icons/授权卡列表.png',
|
||||
path: '/pages/agent-system/enterprise-cards/index',
|
||||
},
|
||||
{
|
||||
title: '授权设备列表',
|
||||
desc: '企业授权设备',
|
||||
icon: '/static/icons/授权设备列表.png',
|
||||
path: '/pages/agent-system/enterprise-devices/index',
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 仅代理端显示佣金和提现
|
||||
if (userInfo.value?.user_type === 3) {
|
||||
menus.push(
|
||||
{
|
||||
title: '佣金中心',
|
||||
desc: '收益统计',
|
||||
icon: '/static/icons/佣金中心.png',
|
||||
path: '/pages/agent-system/commission-center/index',
|
||||
},
|
||||
{
|
||||
title: '提现管理',
|
||||
desc: '申请提现',
|
||||
icon: '/static/icons/提现管理.png',
|
||||
path: '/pages/agent-system/withdraw/index',
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return menus
|
||||
})
|
||||
|
||||
// 加载佣金数据
|
||||
async function loadCommissionData(shopName?: string) {
|
||||
async function loadCommissionData(username?: string) {
|
||||
if (!isAgent.value) return
|
||||
|
||||
loadingCommission.value = true
|
||||
|
||||
try {
|
||||
// 1. 加载佣金概览 (新接口 /shops/fund-summary)
|
||||
const summary = await getFundSummary(shopName ? { shop_name: shopName } : undefined)
|
||||
const summary = await getFundSummary(username ? { username } : undefined)
|
||||
commissionSummary.value = summary
|
||||
|
||||
if (!shopId.value) {
|
||||
@@ -122,11 +62,8 @@
|
||||
return
|
||||
}
|
||||
|
||||
const today = getTodayDate()
|
||||
const todayStats = await getCommissionDailyStats(shopId.value, {
|
||||
days: 1,
|
||||
start_date: today,
|
||||
end_date: today,
|
||||
})
|
||||
dailyStats.value = todayStats || []
|
||||
return
|
||||
@@ -155,19 +92,20 @@
|
||||
// 设置店铺ID
|
||||
const loginUserInfo = getLoginUserInfo()
|
||||
shopId.value = user.shop_id || loginUserInfo?.shop_id || 0
|
||||
const fundSummaryShopName = loginUserInfo?.username || user.username
|
||||
const fundSummaryUsername = loginUserInfo?.username || user.username
|
||||
|
||||
console.log('首页数据加载成功:', { user })
|
||||
|
||||
// 如果是代理账号,加载佣金数据
|
||||
if (user.user_type === 3) {
|
||||
await loadCommissionData(fundSummaryShopName)
|
||||
await loadCommissionData(fundSummaryUsername)
|
||||
}
|
||||
}
|
||||
catch (error : any) {
|
||||
console.error('加载首页数据失败:', error)
|
||||
// 如果是前端逻辑错误(非 API 错误),需要手动显示提示
|
||||
if (error instanceof Error && error.message && !error.statusCode) {
|
||||
const errorWithStatus = error as { statusCode?: number } | undefined
|
||||
if (error instanceof Error && error.message && !errorWithStatus?.statusCode) {
|
||||
uni.$u.toast(error.message)
|
||||
}
|
||||
// API 请求错误已由拦截器统一处理
|
||||
@@ -253,54 +191,16 @@
|
||||
|
||||
<!-- 代理端: 佣金展示 -->
|
||||
<view v-if="isAgent" class="px-4 pt-4">
|
||||
<view
|
||||
class="bg-white rounded-16px shadow-[0_2px_12px_rgba(0,0,0,0.06)] p-5 mb-3"
|
||||
<AgentFundSummaryCard
|
||||
class="mb-3"
|
||||
:summary="commissionSummary"
|
||||
@click="navigateTo('/pages/agent-system/commission-center/index')"
|
||||
>
|
||||
<view class="flex items-center justify-between gap-3">
|
||||
<view class="min-w-0 flex-1">
|
||||
<text class="text-12px text-[#999]">可提现佣金</text>
|
||||
<text class="text-32px font-700 text-[#212121] block mt-1 break-all">
|
||||
{{ formatAmountDisplay(commissionSummary?.available_commission) }}
|
||||
</text>
|
||||
<text class="text-11px text-[#999] block mt-2">
|
||||
当前可直接发起提现的佣金金额
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex flex-col items-end gap-2">
|
||||
<image src="@/static/icons/佣金中心.png" class="w-12 h-12 rounded-12px" mode="aspectFit" />
|
||||
<view class="flex items-center gap-1">
|
||||
<text class="text-12px text-[#999]">佣金中心</text>
|
||||
<i class="i-mdi-chevron-right text-16px text-[#cbd5e1]" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex gap-3 mt-4 pt-4 border-t border-[#f5f5f5]">
|
||||
<view class="flex-1 text-center">
|
||||
<text class="text-11px text-[#999]">累计佣金</text>
|
||||
<text class="text-14px font-600 text-[#212121] block mt-1">
|
||||
{{ formatAmountDisplay(commissionSummary?.total_commission) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-1 text-center border-l border-[#f5f5f5]">
|
||||
<text class="text-11px text-[#999]">已提现</text>
|
||||
<text class="text-14px font-600 text-[#52c41a] block mt-1">
|
||||
{{ formatAmountDisplay(commissionSummary?.withdrawn_commission) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-1 text-center border-l border-[#f5f5f5]">
|
||||
<text class="text-11px text-[#999]">冻结</text>
|
||||
<text class="text-14px font-600 text-[#ff4d4f] block mt-1">
|
||||
{{ formatAmountDisplay(commissionSummary?.frozen_commission) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
/>
|
||||
|
||||
<!-- 今日佣金 -->
|
||||
<!-- 最近一天佣金 -->
|
||||
<view>
|
||||
<view class="flex items-center justify-between mb-3">
|
||||
<text class="text-16px font-700 text-[#212121]">今日佣金</text>
|
||||
<text class="text-16px font-700 text-[#212121]">最近一天佣金</text>
|
||||
<text class="text-13px text-[#999]" @click="navigateTo('/pages/agent-system/commission-center/index')">明细</text>
|
||||
</view>
|
||||
<view class="bg-white rounded-16px shadow-[0_2px_12px_rgba(0,0,0,0.06)] p-4">
|
||||
@@ -325,7 +225,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!todayStat" class="mt-3 pt-3 border-t border-[#f5f5f5] text-center">
|
||||
<text class="text-12px text-[#999]">今日暂无佣金数据</text>
|
||||
<text class="text-12px text-[#999]">最近一天暂无佣金数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ const pageSize = 20
|
||||
const total = ref(0)
|
||||
const statusFilter = ref<number | undefined>(undefined)
|
||||
const showStatusPicker = ref(false)
|
||||
const MIN_WITHDRAW_AMOUNT = 100
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '全部状态', value: undefined },
|
||||
@@ -41,7 +42,7 @@ const selectedStatusName = computed(() => {
|
||||
return option?.label || '状态'
|
||||
})
|
||||
|
||||
const tabs = [
|
||||
const tabs: Array<{ key: 'apply' | 'records', label: string }> = [
|
||||
{ key: 'apply', label: '申请提现' },
|
||||
{ key: 'records', label: '提现记录' },
|
||||
]
|
||||
@@ -51,6 +52,8 @@ const availableAmount = computed(() => {
|
||||
return commissionSummary.value.available_commission / 100
|
||||
})
|
||||
|
||||
const canWithdrawByMinAmount = computed(() => availableAmount.value >= MIN_WITHDRAW_AMOUNT)
|
||||
|
||||
function formatAmount(amount: number) {
|
||||
const yuan = (amount / 100).toFixed(2)
|
||||
const parts = yuan.split('.')
|
||||
@@ -95,7 +98,7 @@ async function loadCommissionSummary() {
|
||||
loading.value = true
|
||||
try {
|
||||
const userInfo = getLoginUserInfo()
|
||||
commissionSummary.value = await getFundSummary(userInfo?.username ? { shop_name: userInfo.username } : undefined)
|
||||
commissionSummary.value = await getFundSummary(userInfo?.username ? { username: userInfo.username } : undefined)
|
||||
withdrawAmount.value = availableAmount.value.toFixed(2)
|
||||
} catch (error) {
|
||||
console.error('加载佣金概览失败:', error)
|
||||
@@ -144,6 +147,10 @@ async function handleSubmit() {
|
||||
uni.$u.toast('请输入提现金额')
|
||||
return
|
||||
}
|
||||
if (amount < MIN_WITHDRAW_AMOUNT) {
|
||||
uni.$u.toast(`满${MIN_WITHDRAW_AMOUNT}元才可提现`)
|
||||
return
|
||||
}
|
||||
if (amount > availableAmount.value) {
|
||||
uni.$u.toast('提现金额超过可用余额')
|
||||
return
|
||||
@@ -200,12 +207,6 @@ function loadMore() {
|
||||
}
|
||||
}
|
||||
|
||||
function onScroll(e: any) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
if (scrollHeight - scrollTop - clientHeight < 100) {
|
||||
loadMore()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -293,10 +294,17 @@ function onScroll(e: any) {
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="mt-6 mb-3 flex items-center gap-1.5 px-3 py-2 rounded-12px bg-[var(--color-warning-bg)]">
|
||||
<i class="i-mdi-information-outline text-14px text-warning" />
|
||||
<text class="text-12px" :class="canWithdrawByMinAmount ? 'text-warning' : 'text-error'">
|
||||
满100元才可提现
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view
|
||||
class="bg-brand rounded-full py-4 text-center text-16px font-600 text-white mt-6"
|
||||
:class="{ 'opacity-50': submitting }"
|
||||
class="bg-brand rounded-full py-4 text-center text-16px font-600 text-white"
|
||||
:class="{ 'opacity-50': submitting || !canWithdrawByMinAmount }"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ submitting ? '提交中...' : '确认提现' }}
|
||||
|
||||
@@ -6,47 +6,49 @@
|
||||
:root,
|
||||
page {
|
||||
/* === 品牌色 Brand Colors === */
|
||||
--brand-primary: #1677ff; /* 主色 - 蓝色 */
|
||||
--brand-primary-light: #4096ff; /* 主色浅 */
|
||||
--brand-primary-dark: #0958d9; /* 主色深 */
|
||||
--brand-secondary: #1e293b; /* 辅色 - 深灰/黑色 */
|
||||
--brand-accent: #ffffff; /* 强调 - 白色 */
|
||||
--brand-primary: #2f7d32; /* 主色 */
|
||||
--brand-primary-light: #5a9f5d; /* 主色浅 */
|
||||
--brand-primary-dark: #245f27; /* 主色深 */
|
||||
--brand-secondary: #1f2937; /* 辅色 */
|
||||
--brand-tertiary: #f4fbf5; /* 次色 */
|
||||
--brand-accent: #ffffff; /* 强调白 */
|
||||
|
||||
/* === 语义色 Semantic Colors === */
|
||||
--color-success: #52c41a; /* 成功 - Green */
|
||||
--color-success-bg: #f6ffed; /* 成功背景 */
|
||||
--color-warning: #faad14; /* 警告 - Amber */
|
||||
--color-warning-bg: #fffbe6; /* 警告背景 */
|
||||
--color-error: #ff4d4f; /* 错误 - Red */
|
||||
--color-error-bg: #fff2f0; /* 错误背景 */
|
||||
--color-info: #1677ff; /* 信息 - Blue(同主色) */
|
||||
--color-info-bg: #e6f7ff; /* 信息背景 */
|
||||
--color-success: #2f7d32;
|
||||
--color-success-bg: #edf7ee;
|
||||
--color-warning: #b7791f;
|
||||
--color-warning-bg: #fff7e6;
|
||||
--color-error: #c24141;
|
||||
--color-error-bg: #fdf1f1;
|
||||
--color-info: var(--brand-primary);
|
||||
--color-info-bg: var(--brand-tertiary);
|
||||
|
||||
/* === 文字色阶 Text Colors === */
|
||||
--text-primary: #212121; /* 标题/重要文字 */
|
||||
--text-secondary: #666666; /* 正文 */
|
||||
--text-tertiary: #999999; /* 辅助信息 */
|
||||
--text-quaternary: #cccccc; /* 禁用/占位符 */
|
||||
--text-inverse: #ffffff; /* 反色文字 */
|
||||
--text-primary: #1f2937;
|
||||
--text-secondary: #4b5563;
|
||||
--text-tertiary: #6b7280;
|
||||
--text-quaternary: #9ca3af;
|
||||
--text-inverse: #ffffff;
|
||||
|
||||
/* === 背景色 Background Colors === */
|
||||
--bg-page: #faf0f5; /* 页面背景 */
|
||||
--bg-container: #ffffff; /* 容器/卡片背景 */
|
||||
--bg-elevated: #ffffff; /* 悬浮层背景 */
|
||||
--bg-muted: #f5f5f5; /* 静音背景 */
|
||||
--bg-overlay: rgba(0, 0, 0, 0.45); /* 遮罩层 */
|
||||
--bg-page: var(--brand-tertiary);
|
||||
--bg-container: #ffffff;
|
||||
--bg-elevated: #ffffff;
|
||||
--bg-muted: #f8faf8;
|
||||
--bg-overlay: rgba(31, 41, 55, 0.45);
|
||||
|
||||
/* === 边框色 Border Colors === */
|
||||
--border-primary: #e8e8e8; /* 主边框 */
|
||||
--border-secondary: #f0f0f0; /* 次边框 */
|
||||
--border-light: #fafafa; /* 浅边框 */
|
||||
--border-primary: #e3ece4;
|
||||
--border-secondary: #edf2ee;
|
||||
--border-light: #f7faf7;
|
||||
|
||||
/* === 阴影 Shadows === */
|
||||
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.06);
|
||||
--shadow-md: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
|
||||
--shadow-lg: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
|
||||
--shadow-xl: 0 8px 24px 0 rgba(0, 0, 0, 0.15);
|
||||
--shadow-brand: 0 4px 12px 0 rgba(22, 119, 255, 0.2);
|
||||
--shadow-xs: 0 1px 2px 0 rgba(31, 41, 55, 0.04);
|
||||
--shadow-sm: 0 1px 3px 0 rgba(31, 41, 55, 0.06);
|
||||
--shadow-md: 0 4px 12px 0 rgba(31, 41, 55, 0.08);
|
||||
--shadow-lg: 0 8px 20px 0 rgba(31, 41, 55, 0.12);
|
||||
--shadow-xl: 0 12px 28px 0 rgba(31, 41, 55, 0.16);
|
||||
--shadow-brand: 0 6px 18px 0 rgba(47, 125, 50, 0.18);
|
||||
|
||||
/* === 圆角 Radius === */
|
||||
--radius-xs: 4px;
|
||||
@@ -96,38 +98,53 @@ page {
|
||||
--z-popover: 1060;
|
||||
--z-tooltip: 1070;
|
||||
--z-toast: 1080;
|
||||
|
||||
/* === 兼容主题别名 Compatibility Theme Aliases === */
|
||||
--theme-primary: var(--brand-primary);
|
||||
--theme-primary-light: var(--brand-primary-light);
|
||||
--theme-primary-dark: var(--brand-primary-dark);
|
||||
--theme-success: var(--color-success);
|
||||
--theme-warning: var(--color-warning);
|
||||
--theme-error: var(--color-error);
|
||||
--theme-main-color: var(--text-primary);
|
||||
--theme-content-color: var(--text-secondary);
|
||||
--theme-tips-color: var(--text-tertiary);
|
||||
--theme-light-color: var(--text-quaternary);
|
||||
--theme-disabled-color: var(--text-quaternary);
|
||||
--theme-bg-color: var(--bg-page);
|
||||
--theme-bg-color-secondary: var(--bg-container);
|
||||
--theme-border-color: var(--border-primary);
|
||||
}
|
||||
|
||||
/* === 暗色主题 Dark Theme === */
|
||||
.theme-dark {
|
||||
--brand-primary: #4b9cff; /* 暗色下蓝色更柔和 */
|
||||
--brand-primary-light: #69b1ff;
|
||||
--brand-primary-dark: #1668dc;
|
||||
--color-success: #73d13d;
|
||||
--color-warning: #ffc53d;
|
||||
--color-error: #ff7875;
|
||||
--color-info: #4b9cff;
|
||||
--brand-primary-light: #74b377;
|
||||
--brand-primary-dark: #245f27;
|
||||
--color-warning: #f0b451;
|
||||
--color-error: #ef7f7f;
|
||||
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #b0b8c1;
|
||||
--text-tertiary: #8a8f95;
|
||||
--text-quaternary: #6c757d;
|
||||
--text-inverse: #1a1a1a;
|
||||
--text-primary: #f8fafc;
|
||||
--text-secondary: #cbd5e1;
|
||||
--text-tertiary: #94a3b8;
|
||||
--text-quaternary: #64748b;
|
||||
--text-inverse: #111827;
|
||||
|
||||
--bg-page: #1a1a1a;
|
||||
--bg-container: #2d2d2d;
|
||||
--bg-elevated: #3a3a3a;
|
||||
--bg-muted: #242424;
|
||||
--bg-page: #111827;
|
||||
--bg-container: #1f2937;
|
||||
--bg-elevated: #273548;
|
||||
--bg-muted: #182232;
|
||||
--bg-overlay: rgba(0, 0, 0, 0.65);
|
||||
|
||||
--border-primary: #3a3a3a;
|
||||
--border-secondary: #2d2d2d;
|
||||
--border-light: #242424;
|
||||
--border-primary: #314155;
|
||||
--border-secondary: #273548;
|
||||
--border-light: #1f2937;
|
||||
|
||||
--shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
|
||||
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 2px 8px 0 rgba(0, 0, 0, 0.4);
|
||||
--shadow-lg: 0 4px 16px 0 rgba(0, 0, 0, 0.5);
|
||||
--shadow-xl: 0 8px 24px 0 rgba(0, 0, 0, 0.6);
|
||||
--shadow-md: 0 4px 12px 0 rgba(0, 0, 0, 0.35);
|
||||
--shadow-lg: 0 8px 20px 0 rgba(0, 0, 0, 0.45);
|
||||
--shadow-xl: 0 12px 28px 0 rgba(0, 0, 0, 0.55);
|
||||
--shadow-brand: 0 6px 18px 0 rgba(47, 125, 50, 0.22);
|
||||
}
|
||||
|
||||
/* === 全局重置 Global Resets === */
|
||||
@@ -144,7 +161,9 @@ page {
|
||||
.text-primary { color: var(--text-primary); }
|
||||
.text-secondary { color: var(--text-secondary); }
|
||||
.text-tertiary { color: var(--text-tertiary); }
|
||||
.text-quaternary { color: var(--text-quaternary); }
|
||||
.text-brand { color: var(--brand-primary); }
|
||||
.text-brand-secondary { color: var(--brand-secondary); }
|
||||
.text-success { color: var(--color-success); }
|
||||
.text-warning { color: var(--color-warning); }
|
||||
.text-error { color: var(--color-error); }
|
||||
@@ -153,13 +172,15 @@ page {
|
||||
.bg-container { background-color: var(--bg-container); }
|
||||
.bg-muted { background-color: var(--bg-muted); }
|
||||
.bg-brand { background-color: var(--brand-primary); }
|
||||
.bg-brand-secondary { background-color: var(--brand-secondary); }
|
||||
.bg-brand-tertiary { background-color: var(--brand-tertiary); }
|
||||
.bg-brand-light { background-color: var(--brand-primary-light); }
|
||||
.bg-brand-dark { background-color: var(--brand-primary-dark); }
|
||||
|
||||
.bg-gradient-brand { background: linear-gradient(135deg, var(--brand-primary), var(--brand-primary-light)); }
|
||||
.shadow-brand-colored { box-shadow: 0 4px 16px rgba(22, 119, 255, 0.3); }
|
||||
.shadow-brand-sm { box-shadow: 0 2px 12px rgba(22, 119, 255, 0.15); }
|
||||
.shadow-brand-xs { box-shadow: 0 2px 8px rgba(22, 119, 255, 0.2); }
|
||||
.shadow-brand-colored { box-shadow: 0 8px 22px rgba(47, 125, 50, 0.22); }
|
||||
.shadow-brand-sm { box-shadow: 0 4px 14px rgba(47, 125, 50, 0.14); }
|
||||
.shadow-brand-xs { box-shadow: 0 2px 8px rgba(47, 125, 50, 0.16); }
|
||||
|
||||
.border-brand { border-color: var(--brand-primary); }
|
||||
|
||||
@@ -241,7 +262,7 @@ page {
|
||||
.input:focus {
|
||||
border-color: var(--brand-primary);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(22, 119, 255, 0.1);
|
||||
box-shadow: 0 0 0 3px rgba(47, 125, 50, 0.12);
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
|
||||
24
src/uni.scss
24
src/uni.scss
@@ -4,24 +4,24 @@
|
||||
/* 颜色变量 - 使用CSS变量支持主题切换 */
|
||||
|
||||
/* 行为相关颜色 */
|
||||
$u-primary: var(--theme-primary);
|
||||
$u-primary-dark: var(--theme-primary-dark);
|
||||
$u-success: var(--theme-success);
|
||||
$u-warning: var(--theme-warning);
|
||||
$u-error: var(--theme-error);
|
||||
$u-primary: var(--brand-primary);
|
||||
$u-primary-dark: var(--brand-primary-dark);
|
||||
$u-success: var(--color-success);
|
||||
$u-warning: var(--color-warning);
|
||||
$u-error: var(--color-error);
|
||||
|
||||
/* 文字基本颜色 */
|
||||
$u-main-color: var(--theme-main-color);
|
||||
$u-content-color: var(--theme-content-color);
|
||||
$u-tips-color: var(--theme-tips-color);
|
||||
$u-light-color: var(--theme-light-color);
|
||||
$u-disabled-color: var(--theme-disabled-color);
|
||||
$u-main-color: var(--text-primary);
|
||||
$u-content-color: var(--text-secondary);
|
||||
$u-tips-color: var(--text-tertiary);
|
||||
$u-light-color: var(--text-quaternary);
|
||||
$u-disabled-color: var(--text-quaternary);
|
||||
|
||||
/* 背景颜色 */
|
||||
$u-bg-color: var(--theme-bg-color);
|
||||
$u-bg-color: var(--bg-page);
|
||||
|
||||
/* 边框颜色 */
|
||||
$u-border-color: var(--theme-border-color);
|
||||
$u-border-color: var(--border-primary);
|
||||
|
||||
/* 尺寸变量 */
|
||||
|
||||
|
||||
1
types/components.d.ts
vendored
1
types/components.d.ts
vendored
@@ -8,6 +8,7 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AgentFundSummaryCard: typeof import('./../src/components/AgentFundSummaryCard.vue')['default']
|
||||
AgreePrivacy: typeof import('./../src/components/agree-privacy/index.vue')['default']
|
||||
LangSelect: typeof import('./../src/components/lang-select/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
@@ -44,7 +44,7 @@ export default defineConfig({
|
||||
* @see https://github.com/unocss/unocss#shortcuts
|
||||
*/
|
||||
shortcuts: {
|
||||
'border-base': 'border border-gray-500_10',
|
||||
'border-base': 'border border-[var(--border-primary)]',
|
||||
'center': 'flex justify-center items-center',
|
||||
},
|
||||
rules: [
|
||||
@@ -53,21 +53,25 @@ export default defineConfig({
|
||||
theme: {
|
||||
colors: {
|
||||
// 主题颜色
|
||||
'primary': 'var(--theme-primary)',
|
||||
'success': 'var(--theme-success)',
|
||||
'warning': 'var(--theme-warning)',
|
||||
'error': 'var(--theme-error)',
|
||||
'primary': 'var(--brand-primary)',
|
||||
'secondary': 'var(--brand-secondary)',
|
||||
'tertiary': 'var(--brand-tertiary)',
|
||||
'success': 'var(--color-success)',
|
||||
'warning': 'var(--color-warning)',
|
||||
'error': 'var(--color-error)',
|
||||
// 文字颜色
|
||||
'text-main': 'var(--theme-main-color)',
|
||||
'text-content': 'var(--theme-content-color)',
|
||||
'text-tips': 'var(--theme-tips-color)',
|
||||
'text-light': 'var(--theme-light-color)',
|
||||
'text-disabled': 'var(--theme-disabled-color)',
|
||||
'text-main': 'var(--text-primary)',
|
||||
'text-content': 'var(--text-secondary)',
|
||||
'text-tips': 'var(--text-tertiary)',
|
||||
'text-light': 'var(--text-quaternary)',
|
||||
'text-disabled': 'var(--text-quaternary)',
|
||||
// 背景颜色
|
||||
'bg-main': 'var(--theme-bg-color)',
|
||||
'bg-secondary': 'var(--theme-bg-color-secondary)',
|
||||
'bg-main': 'var(--bg-page)',
|
||||
'bg-secondary': 'var(--bg-container)',
|
||||
'bg-muted': 'var(--bg-muted)',
|
||||
// 边框颜色
|
||||
'border-main': 'var(--theme-border-color)',
|
||||
'border-main': 'var(--border-primary)',
|
||||
'border-secondary': 'var(--border-secondary)',
|
||||
},
|
||||
},
|
||||
transformers: [
|
||||
|
||||
Reference in New Issue
Block a user