7171
7272 <div v-if =" loading" class =" loading" >正在加载数据...</div >
7373
74- <template v-else-if =" filteredData .length > 0 " >
74+ <!-- 修改:把分组汇总放到主列表上方,且只显示三列(标题 / 类型 / 总计) -->
75+ <div v-if =" groupedSummaries.length > 0" class =" group-summary-container" >
76+ <h2 class =" group-summary-title" >按标题分组 — 类型与总计 (USDC)</h2 >
77+ <div class =" group-table" >
78+ <table >
79+ <thead >
80+ <tr >
81+ <th >标题</th >
82+ <th >类型</th >
83+ <th >份额</th >
84+ <th >总计 (USDC)</th >
85+ </tr >
86+ </thead >
87+ <tbody >
88+ <tr v-for =" group in groupedSummaries" :key =" group.title + '::' + group.type" >
89+ <td class =" group-title" >{{ group.title }}</td >
90+ <td :class =" group.type === 'Up' ? 'group-up' : 'group-down'" >{{ group.type }}</td >
91+ <td class =" group-total" >{{ formatNumber(group.size) }}</td >
92+ <td class =" group-total" >{{ formatNumber(group.total) }}</td >
93+ </tr >
94+ </tbody >
95+ </table >
96+ </div >
97+ </div >
98+
99+ <template v-if =" filteredData .length > 0 " >
75100 <div class =" result-info" >
76101 共找到 {{ filteredData.length }} 条记录
77102 <span v-if =" responseTimeMs !== null" class =" response-time" >
91116 <th >方向</th >
92117 <th >价格</th >
93118 <th >数量</th >
94- <th >价格 × 数量</th >
95119 <th >USDC 数量</th >
96120 </tr >
97121 </thead >
114138 </td >
115139 <td >{{ formatNumber(item.price) }}</td >
116140 <td >{{ formatNumber(item.size) }}</td >
117- <td class =" price-calc" >{{ calculateTotal(item.price, item.size) }}</td >
118141 <td >{{ formatNumber(item.usdcSize) }}</td >
119142 </tr >
120143 </tbody >
121144 </table >
122145 </div >
123146 </template >
124-
125- <div v-else-if =" !loading && rawData.length > 0" class =" no-data" >
126- 没有符合筛选条件的数据
127- </div >
128-
129- <div v-else-if =" !loading" class =" no-data" >
130- 暂无数据
131- </div >
132147 </div >
133148</template >
134149
135150<script setup>
136151import { ref , computed , onMounted } from ' vue' ;
137- import axios from ' axios' ; // ✅ 修复:正确的导入方式
152+ import axios from ' axios' ;
138153
139154const searchParams = ref ({
140155 proxyWallet: ' 0x0d32e5fc366d846bbca8a82c6d60a6dd718b6336' ,
141156 title: ' ' ,
142- limit: 100 , // 新增:默认返回条数
143- sortDirection: ' DESC' , // 新增:默认排序方向
157+ limit: 100 ,
158+ sortDirection: ' DESC' ,
144159 type: " TRADE"
145160});
146161
147162const rawData = ref ([]);
148163const loading = ref (false );
149164const error = ref (null );
150- // 1. 新增:存储响应时间
165+
151166const responseTimeMs = ref (null );
152167
153168const filteredData = computed (() => {
169+ console .log (" Computing filteredData..." );
154170 let data = rawData .value ;
155171
156172 if (searchParams .value .title ) {
@@ -159,11 +175,11 @@ const filteredData = computed(() => {
159175 item .title && item .title .toLowerCase ().includes (titleLower)
160176 );
161177 }
162-
178+ console . log (data, " ----filteredData " );
163179 return data;
164180});
165181
166- // ✅ 新增:验证以太坊地址格式
182+
167183const isValidAddress = (address ) => {
168184 return / ^ 0x[a-fA-F0-9 ] {40} $ / .test (address);
169185};
@@ -179,15 +195,15 @@ const fetchData = async () => {
179195 return ;
180196 }
181197
182- // 新增:limit 校验
198+
183199 const allowedLimits = [5 , 10 , 25 , 50 , 100 , 200 , 500 , 1000 ];
184200 const limit = Number (searchParams .value .limit );
185201 if (! Number .isInteger (limit) || limit <= 0 || ! allowedLimits .includes (limit)) {
186202 error .value = ` limit 值不合法,请选择:${ allowedLimits .join (' , ' )} ` ;
187203 return ;
188204 }
189205
190- // 新增:sortDirection 校验
206+
191207 const sortDir = String (searchParams .value .sortDirection ).toUpperCase ();
192208 if (sortDir !== ' ASC' && sortDir !== ' DESC' ) {
193209 error .value = ' sortDirection 必须为 ASC 或 DESC' ;
@@ -352,6 +368,29 @@ const formatTimestamp = (timestamp) => {
352368 });
353369};
354370
371+ // 替换:按 title + outcome (Up/Down) 分组,返回数组:{ title, type, total }
372+ // 改为基于 filteredData,这样在模板显示 groupedSummaries 时会触发 filteredData 的 computed 执行
373+ const groupedSummaries = computed (() => {
374+ const map = new Map (); // key = `${title}||${type}`
375+ console .log (filteredData .value , " ----groupedSummaries filteredData" );
376+ (filteredData .value || []).forEach (item => {
377+ const title = item .title || ' 未命名' ;
378+ const outcome = String (item .outcome || ' ' ).trim ();
379+ if (outcome !== ' Up' && outcome !== ' Down' ) return ; // 只统计 Up/Down
380+ const usdc = Number (item .usdcSize ) || 0 ;
381+ const size = Number (item .size ) || 0 ;
382+ const key = ` ${ title} ||${ outcome} ` ;
383+ const prev = map .get (key);
384+ if (prev) {
385+ prev .total += usdc;
386+ prev .size += size;
387+ } else {
388+ map .set (key, { title, type: outcome, total: usdc, size: size });
389+ }
390+ });
391+ return Array .from (map .values ()).map (e => ({ ... e, total: Number (e .total ), size: Number (e .size ) }));
392+ });
393+
355394onMounted (() => {
356395 fetchData ();
357396});
@@ -571,4 +610,63 @@ tbody tr:hover {
571610 padding : 4px 8px ;
572611 border-radius : 4px ;
573612}
613+
614+ /* 新增:分组汇总样式 */
615+ .group-summary-container {
616+ margin-bottom : 16px ;
617+ padding : 12px ;
618+ background : #fafafa ;
619+ border : 1px solid #eee ;
620+ border-radius : 6px ;
621+ }
622+
623+ .group-summary-title {
624+ margin : 0 0 8px 0 ;
625+ font-size : 15px ;
626+ color : #333 ;
627+ }
628+
629+ .group-table {
630+ overflow-x : auto ;
631+ }
632+
633+ .group-table table {
634+ width : 100% ;
635+ border-collapse : collapse ;
636+ font-size : 13px ;
637+ }
638+
639+ .group-table th {
640+ text-align : left ;
641+ padding : 8px ;
642+ background : #f5f5f5 ;
643+ border-bottom : 1px solid #e6e6e6 ;
644+ }
645+
646+ .group-table td {
647+ padding : 8px ;
648+ border-bottom : 1px solid #f0f0f0 ;
649+ vertical-align : top ;
650+ white-space : nowrap ;
651+ }
652+
653+ .group-title {
654+ font-weight : 600 ;
655+ color : #333 ;
656+ }
657+
658+ .group-up {
659+ color : #2e7d32 ;
660+ font-weight : 600 ;
661+ }
662+
663+ .group-down {
664+ color : #c62828 ;
665+ font-weight : 600 ;
666+ }
667+
668+ .group-total {
669+ color : #1976d2 ;
670+ font-weight : 700 ;
671+ }
574672 </style >
0 commit comments