From 80337e33b1ad3e7f7d7da8e962c20a6c1b215fd0 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 10 Mar 2026 15:39:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B5=81=E6=B0=B4=E6=98=8E?= =?UTF-8?q?=E7=BB=86=E6=9F=A5=E8=AF=A2=E5=89=8D=E5=90=8E=E7=AB=AF=E5=AE=9E?= =?UTF-8?q?=E6=96=BD=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ransaction-query-backend-implementation.md | 413 ++++++++++++++++++ ...ansaction-query-frontend-implementation.md | 411 +++++++++++++++++ 2 files changed, 824 insertions(+) create mode 100644 docs/plans/2026-03-10-project-detail-transaction-query-backend-implementation.md create mode 100644 docs/plans/2026-03-10-project-detail-transaction-query-frontend-implementation.md diff --git a/docs/plans/2026-03-10-project-detail-transaction-query-backend-implementation.md b/docs/plans/2026-03-10-project-detail-transaction-query-backend-implementation.md new file mode 100644 index 0000000..a82d695 --- /dev/null +++ b/docs/plans/2026-03-10-project-detail-transaction-query-backend-implementation.md @@ -0,0 +1,413 @@ +# Project Detail Transaction Query Backend Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Build the backend query, filter options, detail, and export endpoints for the project detail bank statement page using local `ccdi_bank_statement` data. + +**Architecture:** Extend the existing `ccdi-project` module with a dedicated bank statement controller and service, while reusing the current `CcdiBankStatementMapper` plus XML dynamic SQL. Keep DTO/VO layering strict, compute direction and display amount in the query layer, and export through `ruoyi-common` `ExcelUtil` so the page and export share one query contract. + +**Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus, MyBatis XML, Mockito + JUnit 5, RuoYi `ExcelUtil` + +--- + +### Task 1: Scaffold DTO/VO contracts and the first failing service test + +**Files:** +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankStatementQueryDTO.java` +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementOptionVO.java` +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementFilterOptionsVO.java` +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementListVO.java` +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementDetailVO.java` +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java` +- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java` + +**Step 1: Write the failing test** + +```java +@ExtendWith(MockitoExtension.class) +class CcdiBankStatementServiceImplTest { + + @InjectMocks + private CcdiBankStatementServiceImpl service; + + @Mock + private CcdiBankStatementMapper bankStatementMapper; + + @Test + void getFilterOptions_shouldReturnProjectWideOptions() { + CcdiBankStatementFilterOptionsVO options = new CcdiBankStatementFilterOptionsVO(); + options.setOurSubjectOptions(List.of(new CcdiBankStatementOptionVO("主体A", "主体A"))); + when(bankStatementMapper.selectFilterOptions(100L)).thenReturn(options); + + CcdiBankStatementFilterOptionsVO result = service.getFilterOptions(100L); + + assertEquals(1, result.getOurSubjectOptions().size()); + } +} +``` + +**Step 2: Run test to verify it fails** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: FAIL with missing `CcdiBankStatementServiceImpl`, DTO, or mapper methods. + +**Step 3: Write minimal implementation** + +```java +public interface ICcdiBankStatementService { + CcdiBankStatementFilterOptionsVO getFilterOptions(Long projectId); +} +``` + +```java +@Data +public class CcdiBankStatementOptionVO { + private String label; + private String value; + + public CcdiBankStatementOptionVO(String label, String value) { + this.label = label; + this.value = value; + } +} +``` + +Add the remaining DTO/VO classes with only the fields already confirmed in the design. + +**Step 4: Run test to verify it still fails only on the missing service implementation** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: FAIL with `CcdiBankStatementServiceImpl` not found or method not implemented. + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankStatementQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementOptionVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementFilterOptionsVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementListVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementDetailVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java +git commit -m "新增流水明细查询后端契约与测试骨架" +``` + +### Task 2: Implement mapper query methods for options, list, detail, and export + +**Files:** +- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java` +- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml` +- Test: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java` + +**Step 1: Write the failing test** + +Add tests that assert the service delegates to mapper methods with normalized arguments: + +```java +@Test +void pageQuery_shouldNormalizeSortFieldAndDirection() { + Page page = new Page<>(1, 10); + CcdiBankStatementQueryDTO queryDTO = new CcdiBankStatementQueryDTO(); + queryDTO.setProjectId(100L); + queryDTO.setOrderBy("amount"); + queryDTO.setOrderDirection("desc"); + + service.selectStatementPage(page, queryDTO); + + verify(bankStatementMapper).selectStatementPage(eq(page), same(queryDTO)); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: FAIL because mapper methods `selectStatementPage`, `selectStatementListForExport`, `selectStatementDetailById`, `selectFilterOptions` are missing. + +**Step 3: Write minimal implementation** + +In `CcdiBankStatementMapper.java`, add: + +```java +Page selectStatementPage(Page page, + @Param("query") CcdiBankStatementQueryDTO query); + +List selectStatementListForExport(@Param("query") CcdiBankStatementQueryDTO query); + +CcdiBankStatementDetailVO selectStatementDetailById(@Param("bankStatementId") Long bankStatementId); + +CcdiBankStatementFilterOptionsVO selectFilterOptions(@Param("projectId") Long projectId); +``` + +In `CcdiBankStatementMapper.xml`, add: + +- a reusable SQL fragment for common filters +- a reusable expression for parsed transaction time +- a reusable expression for signed display amount +- separate selects for page, export, detail, and distinct project options + +**Step 4: Run test to verify it passes or fails only on service logic** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: mapper-signature errors gone; remaining failures only relate to unimplemented service methods. + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java +git commit -m "补充流水明细查询Mapper与动态SQL" +``` + +### Task 3: Implement service normalization, page query, and project-wide filter options + +**Files:** +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java` +- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java` + +**Step 1: Write the failing test** + +Add focused service tests for: + +- default `tabType=all` +- invalid `orderBy` falling back to `trxDate` +- invalid `orderDirection` falling back to `desc` +- blank string fields trimming to `null` + +```java +@Test +void normalizeQuery_shouldFallbackToSafeDefaults() { + CcdiBankStatementQueryDTO queryDTO = new CcdiBankStatementQueryDTO(); + queryDTO.setProjectId(100L); + queryDTO.setOrderBy("drop table"); + queryDTO.setOrderDirection("sideways"); + + service.selectStatementPage(new Page<>(1, 10), queryDTO); + + assertEquals("trxDate", queryDTO.getOrderBy()); + assertEquals("desc", queryDTO.getOrderDirection()); + assertEquals("all", queryDTO.getTabType()); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: FAIL because service normalization logic is missing. + +**Step 3: Write minimal implementation** + +In `CcdiBankStatementServiceImpl.java`: + +```java +@Service +public class CcdiBankStatementServiceImpl implements ICcdiBankStatementService { + + @Resource + private CcdiBankStatementMapper bankStatementMapper; + + @Override + public Page selectStatementPage(Page page, + CcdiBankStatementQueryDTO queryDTO) { + normalizeQuery(queryDTO); + return bankStatementMapper.selectStatementPage(page, queryDTO); + } +} +``` + +Implement `normalizeQuery(queryDTO)` to enforce the white-listed sort fields and normalize blank strings and booleans before delegating to mapper. + +**Step 4: Run test to verify it passes** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java +git commit -m "实现流水明细查询服务层规范化逻辑" +``` + +### Task 4: Add detail lookup and export model with a failing export test + +**Files:** +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiBankStatementExcel.java` +- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java` +- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java` +- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java` + +**Step 1: Write the failing test** + +Add a service test for export mapping: + +```java +@Test +void selectStatementListForExport_shouldMapDisplayColumns() { + CcdiBankStatementListVO row = new CcdiBankStatementListVO(); + row.setTrxDate("2024-02-01 10:33:44"); + row.setLeAccountNo("6222"); + row.setLeAccountName("张三"); + row.setDisplayAmount(new BigDecimal("-8.00")); + when(bankStatementMapper.selectStatementListForExport(any())).thenReturn(List.of(row)); + + List result = service.selectStatementListForExport(new CcdiBankStatementQueryDTO()); + + assertEquals(1, result.size()); + assertEquals("6222", result.get(0).getLeAccountNo()); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: FAIL because export model and export service method do not exist. + +**Step 3: Write minimal implementation** + +Create `CcdiBankStatementExcel.java` with `@Excel` annotations for: + +- `trxDate` +- `leAccountNo` +- `leAccountName` +- `customerAccountName` +- `customerAccountNo` +- `userMemo` +- `cashType` +- `displayAmount` + +Add service methods: + +```java +List selectStatementListForExport(CcdiBankStatementQueryDTO queryDTO); + +CcdiBankStatementDetailVO getStatementDetail(Long bankStatementId); +``` + +Map export rows from `CcdiBankStatementListVO` to `CcdiBankStatementExcel`. + +**Step 4: Run test to verify it passes** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiBankStatementExcel.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java +git commit -m "实现流水明细导出模型与详情查询" +``` + +### Task 5: Add controller endpoints and the first failing controller test + +**Files:** +- Create: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java` +- Create: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java` + +**Step 1: Write the failing test** + +```java +@ExtendWith(MockitoExtension.class) +class CcdiBankStatementControllerTest { + + @InjectMocks + private CcdiBankStatementController controller; + + @Mock + private ICcdiBankStatementService bankStatementService; + + @Test + void options_shouldReturnAjaxResultSuccess() { + when(bankStatementService.getFilterOptions(100L)).thenReturn(new CcdiBankStatementFilterOptionsVO()); + + AjaxResult result = controller.getOptions(100L); + + assertEquals(200, result.get("code")); + } +} +``` + +**Step 2: Run test to verify it fails** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementControllerTest` + +Expected: FAIL because controller class and methods do not exist. + +**Step 3: Write minimal implementation** + +Add endpoints: + +- `GET /ccdi/project/bank-statement/list` +- `GET /ccdi/project/bank-statement/options` +- `GET /ccdi/project/bank-statement/detail/{bankStatementId}` +- `POST /ccdi/project/bank-statement/export` + +Use `TableSupport.buildPageRequest()` for paging and `ExcelUtil` for export: + +```java +ExcelUtil util = new ExcelUtil<>(CcdiBankStatementExcel.class); +util.exportExcel(response, list, "流水明细"); +``` + +Guard permissions with: + +- query/list/detail/options: `ccdi:project:query` +- export: `ccdi:project:export` + +**Step 4: Run test to verify it passes** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementControllerTest` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java +git commit -m "新增流水明细查询控制器接口" +``` + +### Task 6: Verify the backend end-to-end inside the module + +**Files:** +- Modify if needed after failures: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java` +- Modify if needed after failures: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java` +- Modify if needed after failures: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml` + +**Step 1: Run the focused backend tests** + +Run: `mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest,CcdiBankStatementControllerTest` + +Expected: PASS + +**Step 2: Run the module compile** + +Run: `mvn clean compile -pl ccdi-project -am` + +Expected: BUILD SUCCESS + +**Step 3: Smoke-check the mapper XML and endpoint contracts** + +Run: + +```bash +mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest -q +``` + +Expected: PASS without XML binding errors or missing mapper statements. + +**Step 4: Fix any compile or binding failures with the smallest possible patch** + +Typical fixes: + +- wrong `@Param("query")` names +- missing VO field aliases in SQL +- `Page<>` generic mismatch +- `ExcelUtil` export field annotation issues + +**Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java +git commit -m "完成流水明细查询后端实现与校验" +``` diff --git a/docs/plans/2026-03-10-project-detail-transaction-query-frontend-implementation.md b/docs/plans/2026-03-10-project-detail-transaction-query-frontend-implementation.md new file mode 100644 index 0000000..a75793d --- /dev/null +++ b/docs/plans/2026-03-10-project-detail-transaction-query-frontend-implementation.md @@ -0,0 +1,411 @@ +# Project Detail Transaction Query Frontend Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Replace the placeholder `DetailQuery.vue` with the full project detail bank statement query page and remove the obsolete upload-record jump entry. + +**Architecture:** Keep the page inside the existing `ccdiProject/detail.vue` dynamic component system. Use one dedicated API module for list, options, detail, and export calls; preload project-wide select options on page entry; keep the entire interaction inside `DetailQuery.vue` plus a minimal cleanup in `UploadData.vue`. + +**Tech Stack:** Vue 2.6, Element UI 2.15, Axios request wrapper, existing global `this.download`, `npm run build:prod` + manual smoke validation + +--- + +### Task 1: Replace the placeholder with a typed page shell and make the build fail first + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` +- Create: `ruoyi-ui/src/api/ccdiProjectBankStatement.js` + +**Step 1: Write the failing verification** + +In `DetailQuery.vue`, replace the placeholder text with the final top-level data structure and import the new API module before creating it: + +```javascript +import { + listBankStatement, + getBankStatementOptions, + getBankStatementDetail +} from "@/api/ccdiProjectBankStatement"; +``` + +Also add empty methods `getList`, `getOptions`, and `handleExport`. + +**Step 2: Run build to verify it fails** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: FAIL because `@/api/ccdiProjectBankStatement` does not exist yet. + +**Step 3: Write minimal implementation** + +Create `ruoyi-ui/src/api/ccdiProjectBankStatement.js` with: + +```javascript +import request from "@/utils/request"; + +export function listBankStatement(query) { + return request({ + url: "/ccdi/project/bank-statement/list", + method: "get", + params: query + }); +} +``` + +Add stubs for: + +- `getBankStatementOptions(projectId)` +- `getBankStatementDetail(bankStatementId)` + +Do not add export wrapper here; use `this.download` directly in the component. + +**Step 4: Run build to verify it passes** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: PASS or fail only on unfinished component template bindings. + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue ruoyi-ui/src/api/ccdiProjectBankStatement.js +git commit -m "搭建流水明细查询前端页面骨架" +``` + +### Task 2: Implement page state, preload list and project-wide select options + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` + +**Step 1: Write the failing verification** + +Add the real component state before wiring the template: + +```javascript +data() { + return { + loading: false, + optionsLoading: false, + activeTab: "all", + queryParams: { + projectId: this.projectId, + pageNum: 1, + pageSize: 10, + tabType: "all", + transactionStartTime: "", + transactionEndTime: "", + counterpartyName: "", + counterpartyNameEmpty: false, + userMemo: "", + userMemoEmpty: false, + ourSubjects: [], + ourBanks: [], + ourAccounts: [], + amountMin: "", + amountMax: "", + counterpartyAccount: "", + counterpartyAccountEmpty: false, + transactionType: "", + transactionTypeEmpty: false, + orderBy: "trxDate", + orderDirection: "desc" + }, + optionData: { + ourSubjectOptions: [], + ourBankOptions: [], + ourAccountOptions: [] + } + }; +} +``` + +Call both `getList()` and `getOptions()` in `created()`. + +**Step 2: Run build to verify it fails** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: FAIL because the template has not been updated to use the new reactive state yet, or methods are incomplete. + +**Step 3: Write minimal implementation** + +Implement: + +- `getList()` +- `getOptions()` +- `syncProjectId()` +- `handleQuery()` +- `resetQuery()` + +Ensure `created()` and the `projectId` watcher both call: + +```javascript +this.queryParams.projectId = this.projectId; +this.getOptions(); +this.getList(); +``` + +**Step 4: Run build to verify it passes** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue +git commit -m "实现流水明细查询页面初始加载逻辑" +``` + +### Task 3: Build the left filter panel and local-search multi-selects + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` + +**Step 1: Write the failing verification** + +Replace the old placeholder template with the final left column filter markup using Element UI controls: + +- `el-date-picker` for transaction time range +- `el-input` + `el-checkbox` for counterparty name, summary, counterparty account, transaction type +- `el-select multiple filterable collapse-tags` for subject, bank, account +- amount range inputs + +Do not wire the search/reset buttons yet. + +**Step 2: Run build to verify it fails** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: FAIL because methods or bound state used in the template are incomplete. + +**Step 3: Write minimal implementation** + +Wire the filter controls to `queryParams` and `dateRange`: + +```javascript +watch: { + dateRange(value) { + this.queryParams.transactionStartTime = value && value[0] ? value[0] : ""; + this.queryParams.transactionEndTime = value && value[1] ? value[1] : ""; + } +} +``` + +Add the left action buttons: + +- `查询` +- `重置` + +Add local filter support inside each `el-select` using Element UI `filterable`. + +**Step 4: Run build to verify it passes** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue +git commit -m "完成流水明细查询筛选栏布局" +``` + +### Task 4: Build the right result area, tabs, table, pagination, and detail drawer + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` + +**Step 1: Write the failing verification** + +Add the right-side structure: + +- top tabs `全部 / 流入 / 流出` +- export button +- `el-table` +- `pagination` +- detail drawer or dialog + +Reference the final columns: + +- `trxDate` +- `leAccountNo / leAccountName` +- `customerAccountName / customerAccountNo` +- `userMemo / cashType` +- `displayAmount` +- `详情` + +**Step 2: Run build to verify it fails** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: FAIL because event handlers like `handleTabChange`, `handleSortChange`, `handleViewDetail`, or `handlePageChange` are missing. + +**Step 3: Write minimal implementation** + +Implement: + +- `handleTabChange(tab)` +- `handleSortChange({ prop, order })` +- `handlePageChange(pageNum)` +- `handleViewDetail(row)` +- `closeDetailDialog()` + +Sort mapping: + +```javascript +const sortMap = { + trxDate: "trxDate", + displayAmount: "amount" +}; +``` + +Tab mapping: + +```javascript +const tabMap = { + all: "all", + in: "in", + out: "out" +}; +``` + +**Step 4: Run build to verify it passes** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue +git commit -m "完成流水明细查询结果区与详情展示" +``` + +### Task 5: Wire export, empty/error states, and responsive styles + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` + +**Step 1: Write the failing verification** + +Add the `导出流水` button click handler without implementation: + +```javascript +handleExport() { + // TODO +} +``` + +Show the button in the UI but keep it clickable. + +**Step 2: Run build to verify it fails functionally in manual smoke** + +Run: + +```bash +cd ruoyi-ui +npm run build:prod +``` + +Expected: BUILD PASS, but manual smoke in browser should show export button with no behavior yet. + +**Step 3: Write minimal implementation** + +Implement export with the existing global helper: + +```javascript +this.download( + "ccdi/project/bank-statement/export", + { ...this.queryParams }, + `${this.projectInfo.projectName || "项目"}_流水明细_${Date.now()}.xlsx` +); +``` + +Add: + +- empty state when `list.length === 0` +- inline error tip when list load fails +- disabled export when total is `0` +- responsive layout styles for mobile widths + +**Step 4: Run build and manual smoke verification** + +Run: + +```bash +cd ruoyi-ui +npm run build:prod +``` + +Expected: BUILD PASS + +Manual smoke: + +1. Open project detail page +2. Switch to `流水明细查询` +3. Confirm left filters render +4. Confirm export button is disabled on empty data and enabled on non-empty data + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue +git commit -m "补齐流水明细查询导出与状态反馈" +``` + +### Task 6: Remove obsolete upload-page operation column and finish smoke verification + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` +- Modify if needed: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` + +**Step 1: Write the failing verification** + +Delete only the old upload-table operation column usage in the template, but do not clean methods yet. + +**Step 2: Run build to verify it fails** + +Run: `cd ruoyi-ui && npm run build:prod` + +Expected: FAIL because `handleViewFlow` or `handleViewError` remains unused or referenced. + +**Step 3: Write minimal implementation** + +In `UploadData.vue`: + +- remove the upload-record operation column entirely +- remove methods: + - `handleViewFlow` + - `handleViewError` +- remove any event payload that tried to change menu to `detail` + +Keep upload, polling, and record-list refresh behavior unchanged. + +**Step 4: Run final verification** + +Run: + +```bash +cd ruoyi-ui +npm run build:prod +``` + +Expected: BUILD PASS + +Manual smoke: + +1. Open one project detail page +2. Confirm `上传数据` 页不再出现“查看流水”入口 +3. Confirm `流水明细查询` 可独立查询、筛选、分页、导出 +4. Confirm手机宽度下左右布局能够折叠为上下布局 + +**Step 5: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue ruoyi-ui/src/api/ccdiProjectBankStatement.js +git commit -m "完成流水明细查询前端实现并移除旧跳转入口" +```