添加报表
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# Change: Add Bank Report Download Feature
|
||||
|
||||
## Why
|
||||
银行端需要报表统计与下载功能,以便银行工作人员能够按不同维度(支行、网点、部门、人员、客户等)查看和导出业务数据,支持按日、月、季或自定义时间范围筛选,提升数据分析和决策效率。
|
||||
|
||||
## What Changes
|
||||
- 新增报表列表页面,展示四大类报表(多维统计、访客报表、营销报表、权益管理)
|
||||
- 新增报表下载页面,支持日期维度筛选和 Excel 文件下载
|
||||
- 添加报表相关的 mock 数据和 API 接口
|
||||
- 在银行端仪表盘添加报表功能入口
|
||||
|
||||
## Impact
|
||||
- Affected specs: 新增 `bank-report` capability
|
||||
- Affected code:
|
||||
- 新增 `src/pagesBank/report/` 目录及相关页面
|
||||
- 新增 `src/pagesBank/api/report.ts` API 接口
|
||||
- 新增 `src/pagesBank/mock/report.ts` mock 数据
|
||||
- 修改 `src/pagesBank/dashboard/index.vue` 添加报表入口
|
||||
@@ -0,0 +1,66 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Report List Display
|
||||
系统 SHALL 在银行端提供报表列表页面,展示所有可供下载的报表清单,分为四个主要板块:多维统计、访客报表、营销报表、权益管理。
|
||||
|
||||
#### Scenario: User views report list
|
||||
- **WHEN** 用户访问报表列表页面
|
||||
- **THEN** 系统显示四个报表分类板块
|
||||
- **AND** 每个板块包含对应的报表条目
|
||||
- **AND** 所有报表条目均可点击
|
||||
|
||||
#### Scenario: Report categories display correctly
|
||||
- **WHEN** 报表列表页面加载完成
|
||||
- **THEN** 多维统计板块显示:按支行统计、按网点统计、按部门统计、按人员统计、按客户统计
|
||||
- **AND** 访客报表板块显示:支行访客报表、网点访客报表、人员访客报表
|
||||
- **AND** 营销报表板块显示:小额贷业绩报表、小额贷营销汇总、消费贷营销报表、三农部支行汇总、三农部营销报表、公司部营销汇总表
|
||||
- **AND** 权益管理板块显示:我赠送的记录、全员赠送记录、权益二维码
|
||||
|
||||
### Requirement: Report Navigation
|
||||
系统 SHALL 支持用户从报表列表页面点击任意报表条目跳转至报表下载页面,并传递报表类型标识。
|
||||
|
||||
#### Scenario: User clicks report item
|
||||
- **WHEN** 用户点击报表列表中的任意报表条目
|
||||
- **THEN** 系统跳转至报表下载页面
|
||||
- **AND** 传递所选报表的类型标识(ID 或名称)至下载页面
|
||||
|
||||
### Requirement: Report Download Page
|
||||
系统 SHALL 提供报表下载页面,支持日期维度筛选和 Excel 文件下载。
|
||||
|
||||
#### Scenario: Download page displays with default date
|
||||
- **WHEN** 用户进入报表下载页面
|
||||
- **THEN** 页面顶部显示日期维度筛选区(按日、按月、按季、自定义)
|
||||
- **AND** 默认选中"按日"选项
|
||||
- **AND** 显示当前系统日期(默认为 2025-12-25)
|
||||
- **AND** 显示"EXCEL"格式标识
|
||||
- **AND** 显示"点击下载"按钮
|
||||
- **AND** 页面底部显示使用帮助说明
|
||||
|
||||
#### Scenario: User changes date dimension
|
||||
- **WHEN** 用户切换日期维度选项(按日/按月/按季/自定义)
|
||||
- **THEN** 系统更新日期显示格式
|
||||
- **AND** 用户可根据选定的维度修改日期
|
||||
|
||||
#### Scenario: User downloads report
|
||||
- **WHEN** 用户选择日期并点击"点击下载"按钮
|
||||
- **THEN** 系统发起下载请求
|
||||
- **AND** 模拟文件生成过程
|
||||
- **AND** 显示下载成功提示
|
||||
- **AND** 提示用户可以点击手机右上角的【...】进行转发或保存文件
|
||||
|
||||
### Requirement: Report Data Mock
|
||||
系统 SHALL 使用 mock 数据模拟报表下载功能,无需真实后端接口。
|
||||
|
||||
#### Scenario: Mock data generation
|
||||
- **WHEN** 用户请求下载报表
|
||||
- **THEN** 系统使用 mock 数据模拟文件生成
|
||||
- **AND** 模拟下载延迟(约 500ms)
|
||||
- **AND** 返回下载成功状态
|
||||
|
||||
### Requirement: Report Entry in Dashboard
|
||||
系统 SHALL 在银行端仪表盘添加报表功能入口。
|
||||
|
||||
#### Scenario: Dashboard displays report entry
|
||||
- **WHEN** 用户访问银行端仪表盘
|
||||
- **THEN** 快捷操作区域显示"报表下载"入口
|
||||
- **AND** 点击入口跳转至报表列表页面
|
||||
30
openspec/changes/archive/2025-12-25-add-bank-report/tasks.md
Normal file
30
openspec/changes/archive/2025-12-25-add-bank-report/tasks.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## 1. 数据层实现
|
||||
- [ ] 1.1 创建报表类型定义(`src/typings/bank.ts` 添加 ReportType、ReportCategory 等类型)
|
||||
- [ ] 1.2 创建报表 mock 数据(`src/pagesBank/mock/report.ts`)
|
||||
- [ ] 1.3 创建报表 API 接口(`src/pagesBank/api/report.ts`)
|
||||
|
||||
## 2. 报表列表页面
|
||||
- [ ] 2.1 创建报表列表页面(`src/pagesBank/report/list.vue`)
|
||||
- [ ] 2.2 实现报表分类展示(多维统计、访客报表、营销报表、权益管理)
|
||||
- [ ] 2.3 实现报表条目点击跳转逻辑
|
||||
|
||||
## 3. 报表下载页面
|
||||
- [ ] 3.1 创建报表下载页面(`src/pagesBank/report/download.vue`)
|
||||
- [ ] 3.2 实现日期维度筛选组件(按日、按月、按季、自定义)
|
||||
- [ ] 3.3 实现日期选择器(使用 wot-design-uni 组件)
|
||||
- [ ] 3.4 实现下载按钮和下载逻辑
|
||||
- [ ] 3.5 添加使用帮助说明文案
|
||||
|
||||
## 4. 仪表盘集成
|
||||
- [ ] 4.1 修改银行端仪表盘(`src/pagesBank/dashboard/index.vue`)
|
||||
- [ ] 4.2 在快捷操作区域添加"报表下载"入口
|
||||
|
||||
## 5. 类型定义更新
|
||||
- [ ] 5.1 更新 `src/typings/bank.ts` 添加报表相关类型
|
||||
|
||||
## 6. 验证与测试
|
||||
- [ ] 6.1 验证报表列表页面正常显示
|
||||
- [ ] 6.2 验证报表跳转逻辑正确
|
||||
- [ ] 6.3 验证日期筛选功能正常
|
||||
- [ ] 6.4 验证下载功能模拟成功
|
||||
- [ ] 6.5 验证仪表盘入口正常跳转
|
||||
70
openspec/specs/bank-report/spec.md
Normal file
70
openspec/specs/bank-report/spec.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# bank-report Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change add-bank-report. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: Report List Display
|
||||
系统 SHALL 在银行端提供报表列表页面,展示所有可供下载的报表清单,分为四个主要板块:多维统计、访客报表、营销报表、权益管理。
|
||||
|
||||
#### Scenario: User views report list
|
||||
- **WHEN** 用户访问报表列表页面
|
||||
- **THEN** 系统显示四个报表分类板块
|
||||
- **AND** 每个板块包含对应的报表条目
|
||||
- **AND** 所有报表条目均可点击
|
||||
|
||||
#### Scenario: Report categories display correctly
|
||||
- **WHEN** 报表列表页面加载完成
|
||||
- **THEN** 多维统计板块显示:按支行统计、按网点统计、按部门统计、按人员统计、按客户统计
|
||||
- **AND** 访客报表板块显示:支行访客报表、网点访客报表、人员访客报表
|
||||
- **AND** 营销报表板块显示:小额贷业绩报表、小额贷营销汇总、消费贷营销报表、三农部支行汇总、三农部营销报表、公司部营销汇总表
|
||||
- **AND** 权益管理板块显示:我赠送的记录、全员赠送记录、权益二维码
|
||||
|
||||
### Requirement: Report Navigation
|
||||
系统 SHALL 支持用户从报表列表页面点击任意报表条目跳转至报表下载页面,并传递报表类型标识。
|
||||
|
||||
#### Scenario: User clicks report item
|
||||
- **WHEN** 用户点击报表列表中的任意报表条目
|
||||
- **THEN** 系统跳转至报表下载页面
|
||||
- **AND** 传递所选报表的类型标识(ID 或名称)至下载页面
|
||||
|
||||
### Requirement: Report Download Page
|
||||
系统 SHALL 提供报表下载页面,支持日期维度筛选和 Excel 文件下载。
|
||||
|
||||
#### Scenario: Download page displays with default date
|
||||
- **WHEN** 用户进入报表下载页面
|
||||
- **THEN** 页面顶部显示日期维度筛选区(按日、按月、按季、自定义)
|
||||
- **AND** 默认选中"按日"选项
|
||||
- **AND** 显示当前系统日期(默认为 2025-12-25)
|
||||
- **AND** 显示"EXCEL"格式标识
|
||||
- **AND** 显示"点击下载"按钮
|
||||
- **AND** 页面底部显示使用帮助说明
|
||||
|
||||
#### Scenario: User changes date dimension
|
||||
- **WHEN** 用户切换日期维度选项(按日/按月/按季/自定义)
|
||||
- **THEN** 系统更新日期显示格式
|
||||
- **AND** 用户可根据选定的维度修改日期
|
||||
|
||||
#### Scenario: User downloads report
|
||||
- **WHEN** 用户选择日期并点击"点击下载"按钮
|
||||
- **THEN** 系统发起下载请求
|
||||
- **AND** 模拟文件生成过程
|
||||
- **AND** 显示下载成功提示
|
||||
- **AND** 提示用户可以点击手机右上角的【...】进行转发或保存文件
|
||||
|
||||
### Requirement: Report Data Mock
|
||||
系统 SHALL 使用 mock 数据模拟报表下载功能,无需真实后端接口。
|
||||
|
||||
#### Scenario: Mock data generation
|
||||
- **WHEN** 用户请求下载报表
|
||||
- **THEN** 系统使用 mock 数据模拟文件生成
|
||||
- **AND** 模拟下载延迟(约 500ms)
|
||||
- **AND** 返回下载成功状态
|
||||
|
||||
### Requirement: Report Entry in Dashboard
|
||||
系统 SHALL 在银行端仪表盘添加报表功能入口。
|
||||
|
||||
#### Scenario: Dashboard displays report entry
|
||||
- **WHEN** 用户访问银行端仪表盘
|
||||
- **THEN** 快捷操作区域显示"报表下载"入口
|
||||
- **AND** 点击入口跳转至报表列表页面
|
||||
|
||||
@@ -252,6 +252,36 @@
|
||||
"navigationBarTitleText": "客户详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "customer/transaction-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "交易记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "customer/withdraw-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提现记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "me/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "银行中心"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "report/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "报表列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "report/download",
|
||||
"style": {
|
||||
"navigationBarTitleText": "报表下载"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "visit/list",
|
||||
"style": {
|
||||
@@ -261,19 +291,13 @@
|
||||
{
|
||||
"path": "visit/create",
|
||||
"style": {
|
||||
"navigationBarTitleText": "创建拜访计划"
|
||||
"navigationBarTitleText": "创建拜访"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "visit/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "拜访计划详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "me/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "银行中心"
|
||||
"navigationBarTitleText": "拜访详情"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -205,11 +205,6 @@ function handleLogout() {
|
||||
<!-- 常用功能 -->
|
||||
<view class="section-card">
|
||||
<view class="cell-group">
|
||||
<view class="cell" @click="navigateTo('/pages/me/loan-application')">
|
||||
<text class="i-carbon-money icon"></text>
|
||||
<text class="label">我要借钱</text>
|
||||
<text class="i-carbon-chevron-right arrow"></text>
|
||||
</view>
|
||||
<view class="cell" @click="navigateTo('/pages/me/loan-application-records')">
|
||||
<text class="i-carbon-document-attachment icon"></text>
|
||||
<text class="label">助贷申请进度</text>
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
mockVisitPlans,
|
||||
mockMarketingProducts
|
||||
} from '../mock'
|
||||
export { getReportList, downloadReport } from './report'
|
||||
|
||||
/** 获取银行端首页统计 */
|
||||
export function getBankStats(): Promise<BankStats> {
|
||||
|
||||
22
src/pagesBank/api/report.ts
Normal file
22
src/pagesBank/api/report.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { ReportType, ReportDownloadParams } from '@/typings/bank'
|
||||
import { mockReportList } from '../mock'
|
||||
|
||||
/** 获取报表列表 */
|
||||
export function getReportList(): Promise<ReportType[]> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(mockReportList)
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
|
||||
/** 下载报表 */
|
||||
export function downloadReport(params: ReportDownloadParams): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
// 模拟文件生成和下载过程
|
||||
console.log('下载报表:', params)
|
||||
resolve(true)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
@@ -17,6 +17,7 @@ const quickActions = [
|
||||
{ icon: 'i-carbon-group', label: '客户管理', path: '/pagesBank/customer/list' },
|
||||
{ icon: 'i-carbon-calendar', label: '拜访计划', path: '/pagesBank/visit/list' },
|
||||
{ icon: 'i-carbon-add', label: '创建拜访', path: '/pagesBank/visit/create' },
|
||||
{ icon: 'i-carbon-document-download', label: '报表', path: '/pagesBank/report/list' },
|
||||
{ icon: 'i-carbon-settings', label: '设置', path: '/pagesBank/me/index' },
|
||||
]
|
||||
|
||||
|
||||
@@ -12,12 +12,20 @@ const userStore = useUserStore()
|
||||
const config = CLIENT_TYPE_CONFIG[ClientType.BANK]
|
||||
|
||||
const menuList = [
|
||||
{ icon: 'i-carbon-report', label: '数据报表' },
|
||||
{ icon: 'i-carbon-report', label: '数据报表', path: '/pagesBank/report/list' },
|
||||
{ icon: 'i-carbon-settings', label: '系统设置' },
|
||||
{ icon: 'i-carbon-help', label: '帮助中心' },
|
||||
{ icon: 'i-carbon-information', label: '关于我们' },
|
||||
]
|
||||
|
||||
function handleMenuClick(item: { label: string; path?: string }) {
|
||||
if (item.path) {
|
||||
uni.navigateTo({ url: item.path })
|
||||
} else {
|
||||
uni.showToast({ title: '功能开发中', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
@@ -48,7 +56,7 @@ function handleLogout() {
|
||||
|
||||
<!-- 菜单列表 -->
|
||||
<view class="menu-list">
|
||||
<view v-for="item in menuList" :key="item.label" class="menu-item">
|
||||
<view v-for="item in menuList" :key="item.label" class="menu-item" @click="handleMenuClick(item)">
|
||||
<view class="menu-left">
|
||||
<text :class="item.icon" class="menu-icon"></text>
|
||||
<text class="menu-label">{{ item.label }}</text>
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
MarketingProduct
|
||||
} from '@/typings/bank'
|
||||
import { AuditStatus, AuditType, VisitStatus } from '@/typings/bank'
|
||||
export { mockReportList, reportCategoryInfo } from './report'
|
||||
|
||||
// 统计数据 Mock
|
||||
export const mockBankStats: BankStats = {
|
||||
|
||||
119
src/pagesBank/mock/report.ts
Normal file
119
src/pagesBank/mock/report.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { ReportType } from '@/typings/bank'
|
||||
import { ReportCategory } from '@/typings/bank'
|
||||
|
||||
/** 报表列表 Mock 数据 */
|
||||
export const mockReportList: ReportType[] = [
|
||||
// 多维统计
|
||||
{
|
||||
id: 'R001',
|
||||
name: '按支行统计',
|
||||
category: ReportCategory.MULTI_DIMENSION
|
||||
},
|
||||
{
|
||||
id: 'R002',
|
||||
name: '按网点统计',
|
||||
category: ReportCategory.MULTI_DIMENSION
|
||||
},
|
||||
{
|
||||
id: 'R003',
|
||||
name: '按部门统计',
|
||||
category: ReportCategory.MULTI_DIMENSION
|
||||
},
|
||||
{
|
||||
id: 'R004',
|
||||
name: '按人员统计',
|
||||
category: ReportCategory.MULTI_DIMENSION
|
||||
},
|
||||
{
|
||||
id: 'R005',
|
||||
name: '按客户统计',
|
||||
category: ReportCategory.MULTI_DIMENSION
|
||||
},
|
||||
// 访客报表
|
||||
{
|
||||
id: 'R006',
|
||||
name: '支行访客报表',
|
||||
category: ReportCategory.VISITOR
|
||||
},
|
||||
{
|
||||
id: 'R007',
|
||||
name: '网点访客报表',
|
||||
category: ReportCategory.VISITOR
|
||||
},
|
||||
{
|
||||
id: 'R008',
|
||||
name: '人员访客报表',
|
||||
category: ReportCategory.VISITOR
|
||||
},
|
||||
// 营销报表
|
||||
{
|
||||
id: 'R009',
|
||||
name: '小额贷业绩报表',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
{
|
||||
id: 'R010',
|
||||
name: '小额贷营销汇总',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
{
|
||||
id: 'R011',
|
||||
name: '消费贷营销报表',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
{
|
||||
id: 'R012',
|
||||
name: '三农部支行汇总',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
{
|
||||
id: 'R013',
|
||||
name: '三农部营销报表',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
{
|
||||
id: 'R014',
|
||||
name: '公司部营销汇总表',
|
||||
category: ReportCategory.MARKETING
|
||||
},
|
||||
// 权益管理
|
||||
{
|
||||
id: 'R015',
|
||||
name: '我赠送的记录',
|
||||
category: ReportCategory.BENEFIT
|
||||
},
|
||||
{
|
||||
id: 'R016',
|
||||
name: '全员赠送记录',
|
||||
category: ReportCategory.BENEFIT
|
||||
},
|
||||
{
|
||||
id: 'R017',
|
||||
name: '权益二维码',
|
||||
category: ReportCategory.BENEFIT
|
||||
}
|
||||
]
|
||||
|
||||
/** 报表分类信息 */
|
||||
export const reportCategoryInfo = {
|
||||
[ReportCategory.MULTI_DIMENSION]: {
|
||||
name: '多维统计',
|
||||
icon: 'i-carbon-chart-cluster-bar',
|
||||
color: '#00c05a'
|
||||
},
|
||||
[ReportCategory.VISITOR]: {
|
||||
name: '访客报表',
|
||||
icon: 'i-carbon-user-multiple',
|
||||
color: '#4d80f0'
|
||||
},
|
||||
[ReportCategory.MARKETING]: {
|
||||
name: '营销报表',
|
||||
icon: 'i-carbon-ibm-watson-natural-language-understanding',
|
||||
color: '#ff8f0d'
|
||||
},
|
||||
[ReportCategory.BENEFIT]: {
|
||||
name: '权益管理',
|
||||
icon: 'i-carbon-gift',
|
||||
color: '#fa4350'
|
||||
}
|
||||
}
|
||||
461
src/pagesBank/report/download.vue
Normal file
461
src/pagesBank/report/download.vue
Normal file
@@ -0,0 +1,461 @@
|
||||
<script lang="ts" setup>
|
||||
import { downloadReport } from '@/pagesBank/api'
|
||||
import { DateDimension } from '@/typings/bank'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '报表下载',
|
||||
},
|
||||
})
|
||||
|
||||
// 从路由参数获取报表信息
|
||||
const reportId = ref('')
|
||||
const reportName = ref('')
|
||||
|
||||
// 日期维度选项
|
||||
const dimensionOptions = [
|
||||
{ label: '按日', value: DateDimension.DAY },
|
||||
{ label: '按月', value: DateDimension.MONTH },
|
||||
{ label: '按季', value: DateDimension.QUARTER },
|
||||
{ label: '自定义', value: DateDimension.CUSTOM },
|
||||
]
|
||||
|
||||
// 当前选中的日期维度
|
||||
const activeDimension = ref(DateDimension.DAY)
|
||||
|
||||
// 当前选中的日期
|
||||
const selectedDate = ref('2025-12-25')
|
||||
|
||||
// 日期选择器显示状态
|
||||
const showDatePicker = ref(false)
|
||||
|
||||
// 下载中状态
|
||||
const downloading = ref(false)
|
||||
|
||||
// 获取日期维度标签
|
||||
function getDimensionLabel(dimension: DateDimension) {
|
||||
const option = dimensionOptions.find(opt => opt.value === dimension)
|
||||
return option?.label || ''
|
||||
}
|
||||
|
||||
// 切换日期维度
|
||||
function handleDimensionChange(dimension: DateDimension) {
|
||||
activeDimension.value = dimension
|
||||
// 根据维度更新默认日期
|
||||
updateDefaultDate(dimension)
|
||||
}
|
||||
|
||||
// 根据维度更新默认日期
|
||||
function updateDefaultDate(dimension: DateDimension) {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(now.getDate()).padStart(2, '0')
|
||||
|
||||
switch (dimension) {
|
||||
case DateDimension.DAY:
|
||||
selectedDate.value = `${year}-${month}-${day}`
|
||||
break
|
||||
case DateDimension.MONTH:
|
||||
selectedDate.value = `${year}-${month}`
|
||||
break
|
||||
case DateDimension.QUARTER:
|
||||
const quarter = Math.floor(now.getMonth() / 3) + 1
|
||||
selectedDate.value = `${year}-Q${quarter}`
|
||||
break
|
||||
case DateDimension.CUSTOM:
|
||||
selectedDate.value = `${year}-${month}-${day}`
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 显示日期选择器
|
||||
function showDateSelector() {
|
||||
showDatePicker.value = true
|
||||
}
|
||||
|
||||
// 日期选择确认
|
||||
function handleDateConfirm(e: any) {
|
||||
const { value } = e
|
||||
const date = new Date(value)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
|
||||
if (activeDimension.value === DateDimension.MONTH) {
|
||||
selectedDate.value = `${year}-${month}`
|
||||
} else {
|
||||
selectedDate.value = `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
showDatePicker.value = false
|
||||
}
|
||||
|
||||
// 下载报表
|
||||
async function handleDownload() {
|
||||
if (downloading.value) return
|
||||
|
||||
downloading.value = true
|
||||
try {
|
||||
await downloadReport({
|
||||
reportId: reportId.value,
|
||||
dimension: activeDimension.value,
|
||||
date: selectedDate.value
|
||||
})
|
||||
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
downloading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (options.reportId) {
|
||||
reportId.value = options.reportId
|
||||
}
|
||||
if (options.reportName) {
|
||||
reportName.value = decodeURIComponent(options.reportName)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="download-page">
|
||||
<!-- 报表信息卡片 -->
|
||||
<view class="report-info-card">
|
||||
<view class="report-icon">
|
||||
<text class="i-carbon-document"></text>
|
||||
</view>
|
||||
<view class="report-details">
|
||||
<text class="report-name">{{ reportName }}</text>
|
||||
<text class="report-id">报表编号: {{ reportId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日期维度筛选 -->
|
||||
<view class="dimension-section">
|
||||
<view class="section-title">选择日期维度</view>
|
||||
<view class="dimension-tabs">
|
||||
<view
|
||||
v-for="option in dimensionOptions"
|
||||
:key="option.value"
|
||||
class="dimension-tab"
|
||||
:class="{ active: activeDimension === option.value }"
|
||||
@click="handleDimensionChange(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日期选择 -->
|
||||
<view class="date-section">
|
||||
<view class="section-title">选择日期</view>
|
||||
<view class="date-picker" @click="showDateSelector">
|
||||
<view class="date-display">
|
||||
<text class="i-carbon-calendar"></text>
|
||||
<text class="date-value">{{ selectedDate }}</text>
|
||||
</view>
|
||||
<text class="i-carbon-chevron-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 下载区域 -->
|
||||
<view class="download-section">
|
||||
<view class="format-tag">EXCEL</view>
|
||||
<button
|
||||
class="download-btn"
|
||||
:class="{ loading: downloading }"
|
||||
:disabled="downloading"
|
||||
@click="handleDownload"
|
||||
>
|
||||
<text v-if="downloading" class="i-carbon-circle-dash loading-icon"></text>
|
||||
<text>{{ downloading ? '下载中...' : '点击下载' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 使用帮助 -->
|
||||
<view class="help-section">
|
||||
<view class="help-title">
|
||||
<text class="i-carbon-help"></text>
|
||||
<text>Excel格式: 使用帮助</text>
|
||||
</view>
|
||||
<view class="help-content">
|
||||
<view class="help-item">
|
||||
<text class="help-number">1</text>
|
||||
<text class="help-text">选择要下载报表的日期</text>
|
||||
</view>
|
||||
<view class="help-item">
|
||||
<text class="help-number">2</text>
|
||||
<text class="help-text">点击下载按钮进行下载</text>
|
||||
</view>
|
||||
<view class="help-item">
|
||||
<text class="help-number">3</text>
|
||||
<text class="help-text">下载后自动打开文件,可以点击手机右上角的【...】进行转发或保存文件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 日期选择器弹窗 -->
|
||||
<wd-datetime-picker
|
||||
v-model="selectedDate"
|
||||
v-model:show="showDatePicker"
|
||||
type="date"
|
||||
@confirm="handleDateConfirm"
|
||||
@cancel="showDatePicker = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.download-page {
|
||||
min-height: 100vh;
|
||||
background: #f8f9fa;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.report-info-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
|
||||
.report-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
background: linear-gradient(135deg, #00c05a 0%, #34d19d 100%);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
font-size: 48rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.report-details {
|
||||
flex: 1;
|
||||
|
||||
.report-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.report-id {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dimension-section,
|
||||
.date-section {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.dimension-tabs {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.dimension-tab {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.active {
|
||||
background: #00c05a;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.date-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
text:first-child {
|
||||
font-size: 36rpx;
|
||||
color: #00c05a;
|
||||
}
|
||||
|
||||
.date-value {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
text:last-child {
|
||||
font-size: 28rpx;
|
||||
color: #adb5bd;
|
||||
}
|
||||
}
|
||||
|
||||
.download-section {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 30rpx;
|
||||
|
||||
.format-tag {
|
||||
font-size: 24rpx;
|
||||
color: #00c05a;
|
||||
background: rgba(0, 192, 90, 0.1);
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
background: linear-gradient(135deg, #00c05a 0%, #34d19d 100%);
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active:not(:disabled) {
|
||||
opacity: 0.8;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
font-size: 32rpx;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.help-section {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
|
||||
.help-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
text:first-child {
|
||||
font-size: 32rpx;
|
||||
color: #4d80f0;
|
||||
}
|
||||
|
||||
text:last-child {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.help-content {
|
||||
.help-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.help-number {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: #4d80f0;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
217
src/pagesBank/report/list.vue
Normal file
217
src/pagesBank/report/list.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<script lang="ts" setup>
|
||||
import { getReportList } from '@/pagesBank/api'
|
||||
import type { ReportType } from '@/typings/bank'
|
||||
import { ReportCategory } from '@/typings/bank'
|
||||
import { reportCategoryInfo } from '@/pagesBank/mock'
|
||||
// i-carbon-chart-cluster-bar
|
||||
// i-carbon-user-multiple
|
||||
// i-carbon-ibm-watson-natural-language-understanding
|
||||
// i-carbon-gift
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '报表下载',
|
||||
},
|
||||
})
|
||||
|
||||
const reports = ref<ReportType[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 按分类分组报表
|
||||
const groupedReports = computed(() => {
|
||||
const groups: Record<string, ReportType[]> = {}
|
||||
reports.value.forEach(report => {
|
||||
if (!groups[report.category]) {
|
||||
groups[report.category] = []
|
||||
}
|
||||
groups[report.category].push(report)
|
||||
})
|
||||
return groups
|
||||
})
|
||||
|
||||
// 获取分类信息
|
||||
function getCategoryInfo(category: ReportCategory) {
|
||||
return reportCategoryInfo[category] || { name: '未知', icon: '', color: '#999' }
|
||||
}
|
||||
|
||||
// 点击报表跳转到下载页
|
||||
function handleReportClick(report: ReportType) {
|
||||
uni.navigateTo({
|
||||
url: `/pagesBank/report/download?reportId=${report.id}&reportName=${encodeURIComponent(report.name)}`
|
||||
})
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getReportList()
|
||||
reports.value = res
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="report-list-page">
|
||||
<view v-if="loading && reports.length === 0" class="loading-state">
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else class="report-container">
|
||||
<view
|
||||
v-for="(groupReports, category) in groupedReports"
|
||||
:key="category"
|
||||
class="report-category"
|
||||
>
|
||||
<view class="category-header">
|
||||
<view class="category-info">
|
||||
<text :class="['category-icon', getCategoryInfo(category as ReportCategory).icon]"></text>
|
||||
<text class="category-name">{{ getCategoryInfo(category as ReportCategory).name }}</text>
|
||||
</view>
|
||||
<view class="category-count">{{ groupReports.length }} 个报表</view>
|
||||
</view>
|
||||
|
||||
<view class="report-grid">
|
||||
<view
|
||||
v-for="report in groupReports"
|
||||
:key="report.id"
|
||||
class="report-item"
|
||||
@click="handleReportClick(report)"
|
||||
>
|
||||
<view class="report-icon">
|
||||
<text :class="getCategoryInfo(report.category).icon"></text>
|
||||
</view>
|
||||
<text class="report-name">{{ report.name }}</text>
|
||||
<view class="report-arrow">
|
||||
<text class="i-carbon-chevron-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.report-list-page {
|
||||
min-height: 100vh;
|
||||
background: #f8f9fa;
|
||||
padding-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
color: #adb5bd;
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.report-container {
|
||||
padding: 24rpx 30rpx;
|
||||
}
|
||||
|
||||
.report-category {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.category-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f1f3f5;
|
||||
|
||||
.category-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.category-icon {
|
||||
font-size: 36rpx;
|
||||
color: #00c05a;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.category-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.report-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.report-item {
|
||||
background: #f8f9fa;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
background: #e9ecef;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.report-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text {
|
||||
font-size: 40rpx;
|
||||
color: #00c05a;
|
||||
}
|
||||
}
|
||||
|
||||
.report-name {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.report-arrow {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #adb5bd;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -100,6 +100,36 @@ export interface CompleteVisitPlanParams {
|
||||
photos: string[]
|
||||
}
|
||||
|
||||
/** 报表分类 */
|
||||
export enum ReportCategory {
|
||||
MULTI_DIMENSION = 'multi_dimension', // 多维统计
|
||||
VISITOR = 'visitor', // 访客报表
|
||||
MARKETING = 'marketing', // 营销报表
|
||||
BENEFIT = 'benefit', // 权益管理
|
||||
}
|
||||
|
||||
/** 日期维度 */
|
||||
export enum DateDimension {
|
||||
DAY = 'day', // 按日
|
||||
MONTH = 'month', // 按月
|
||||
QUARTER = 'quarter', // 按季
|
||||
CUSTOM = 'custom', // 自定义
|
||||
}
|
||||
|
||||
/** 报表类型 */
|
||||
export interface ReportType {
|
||||
id: string
|
||||
name: string
|
||||
category: ReportCategory
|
||||
}
|
||||
|
||||
/** 报表下载参数 */
|
||||
export interface ReportDownloadParams {
|
||||
reportId: string
|
||||
dimension: DateDimension
|
||||
date: string
|
||||
}
|
||||
|
||||
/** 银行统计指标 */
|
||||
export interface BankStats {
|
||||
pendingAuditStore: number // 待审核商户
|
||||
|
||||
Reference in New Issue
Block a user