@@ -911,8 +911,21 @@ def _build_favicon() -> bytes:
911911 }).join('');
912912}
913913
914+ // ── 按 tiers 顺序排序 vendor 列表 ─────────────────────────
915+ function sortByTierOrder(vendors, tierOrder) {
916+ if (!tierOrder || !tierOrder.length) return vendors.sort();
917+ const orderMap = {};
918+ tierOrder.forEach((name, i) => { orderMap[name] = i; });
919+ const maxIdx = tierOrder.length;
920+ return vendors.sort((a, b) => {
921+ const ia = orderMap[a] ?? maxIdx;
922+ const ib = orderMap[b] ?? maxIdx;
923+ return ia !== ib ? ia - ib : a.localeCompare(b);
924+ });
925+ }
926+
914927// ── 时序折线图(请求量,按 vendor)────────────────────────
915- function buildTimeline(rows) {
928+ function buildTimeline(rows, tierOrder ) {
916929 const vendorDateMap = {};
917930 const allDates = new Set();
918931 for (const r of rows) {
@@ -923,7 +936,7 @@ def _build_favicon() -> bytes:
923936 allDates.add(d);
924937 }
925938 const dates = [...allDates].sort();
926- const vendors = Object.keys(vendorDateMap).sort( );
939+ const vendors = sortByTierOrder( Object.keys(vendorDateMap), tierOrder );
927940
928941 if (chartTimeline) chartTimeline.destroy();
929942 const ctx = document.getElementById('chart-timeline').getContext('2d');
@@ -962,14 +975,14 @@ def _build_favicon() -> bytes:
962975}
963976
964977// ── 供应商分布环形图 ──────────────────────────────────────
965- function buildVendorDist(rows) {
978+ function buildVendorDist(rows, tierOrder ) {
966979 const vendorTotals = {};
967980 for (const r of rows) {
968981 const v = r.vendor;
969982 if (!isValidLabel(v)) continue;
970983 vendorTotals[v] = (vendorTotals[v] || 0) + (r.total_requests || 0);
971984 }
972- const labels = Object.keys(vendorTotals).sort((a,b) => vendorTotals[b]-vendorTotals[a] );
985+ const labels = sortByTierOrder( Object.keys(vendorTotals), tierOrder );
973986 const data = labels.map(v => vendorTotals[v]);
974987
975988 if (chartVendorDist) chartVendorDist.destroy();
@@ -1027,7 +1040,7 @@ def _build_favicon() -> bytes:
10271040}
10281041
10291042// ── Token 量趋势折线图(按 vendor)───────────────────────
1030- function buildTokenTimeline(rows) {
1043+ function buildTokenTimeline(rows, tierOrder ) {
10311044 const vendorDateMap = {};
10321045 const allDates = new Set();
10331046 for (const r of rows) {
@@ -1040,7 +1053,7 @@ def _build_favicon() -> bytes:
10401053 allDates.add(d);
10411054 }
10421055 const dates = [...allDates].sort();
1043- const vendors = Object.keys(vendorDateMap).sort( );
1056+ const vendors = sortByTierOrder( Object.keys(vendorDateMap), tierOrder );
10441057
10451058 if (chartTokenTimeline) chartTokenTimeline.destroy();
10461059 const ctx = document.getElementById('chart-token-timeline').getContext('2d');
@@ -1258,9 +1271,10 @@ def _build_favicon() -> bytes:
12581271 updateChartTitles(days);
12591272
12601273 const rows = timeline.rows || [];
1261- buildTimeline(rows);
1262- buildVendorDist(rows);
1263- buildTokenTimeline(rows);
1274+ const tierOrder = (status.tiers || []).map(t => t.name);
1275+ buildTimeline(rows, tierOrder);
1276+ buildVendorDist(rows, tierOrder);
1277+ buildTokenTimeline(rows, tierOrder);
12641278 buildModelTokenTimeline(rows);
12651279
12661280 document.getElementById('refresh-time').textContent = '上次刷新: ' + now();
0 commit comments