Yoobit commited on
Commit
ee7770e
·
1 Parent(s): d17ebe3

feat(chart): 支持 MoE 模型识别与可视化增强

Browse files

- 新增 `_isMoE` 标记支持,将 MoE 模型归类为独立系列并展示为星形标记
- 图表符号大小根据数据量动态调整,提高可读性和交互体验
- 增加对 y 轴单值居中处理逻辑,避免零值被误设为 1
- 引入模型类型排序机制,使图例和系列按照预定义顺序显示
- 添加 `selectedDataNameChart` 用于图表数据源筛选,分离控制维度
- 更新默认自动显示系列配置,支持通配符 "*" 显示所有系列
- 调整图表容器宽度,优化布局适配

src/components/Chart.vue CHANGED
@@ -6,7 +6,7 @@ import * as echarts from 'echarts'
6
  const chartRef = ref(null)
7
  let chart = null
8
 
9
- const { leaderboard, selectedDataName } = useLeaderboardData()
10
 
11
  // 接受一个可选 prop:autoShowSeries(数组),用于指定哪些模型类型在图表加载时自动显示。
12
  // 例如:<Chart :autoShowSeries="['gemma-3','Hunyuan']" />
@@ -19,12 +19,15 @@ function buildSeriesFromLeaderboard(rows = []) {
19
  const groups = {}
20
  rows.forEach(r => {
21
  // 按 model_type 分组;如果 model_type 未填则标记为 未知
22
- const seriesName = r.model_type || '未知'
 
 
 
23
  if (!groups[seriesName]) groups[seriesName] = []
24
  // 数据原本单位为 TFLOPs,转换为 FLOPs(1 TFLOP = 1e12 FLOPs)用于对数尺度显示
25
  const xRaw = parseFloat(r.BF16_TFLOPs) || 0
26
  const x = xRaw > 0 ? xRaw * 1e12 : 0
27
- const y = parseFloat(r.information_capacity ?? r.ic) || 0
28
  const rank = Number(r.rank) || 0
29
  groups[seriesName].push({ x, y, rank })
30
  })
@@ -35,7 +38,10 @@ function buildSeriesFromLeaderboard(rows = []) {
35
  .filter(p => Number.isFinite(p.x) && p.x > 0 && Number.isFinite(p.y))
36
  .sort((a, b) => a.rank - b.rank)
37
  .map(d => [d.x, d.y])
38
- return { name, rawCount: groups[name].length, type: 'line', showSymbol: true, symbolSize: 6, data }
 
 
 
39
  })
40
  }
41
 
@@ -74,9 +80,17 @@ function getChartOption(series) {
74
  if (yMin !== undefined && yMax !== undefined) {
75
  if (yMin === yMax) {
76
  // small symmetric padding
77
- const v = yMin || 1
78
- yMin = v - Math.abs(v) * 0.05
79
- yMax = v + Math.abs(v) * 0.05
 
 
 
 
 
 
 
 
80
  } else {
81
  const pad = (yMax - yMin) * 0.08
82
  yMin = yMin - pad
@@ -93,27 +107,79 @@ function getChartOption(series) {
93
  return s.split('').map(ch => map[ch] || ch).join('')
94
  }
95
  // 可循环使用的视觉样式(颜色/符号/线型)
96
- const colors = ['#1f77b4','#ff7f0e','#2ca02c','#d62728','#9467bd','#8c564b','#e377c2','#7f7f7f','#bcbd22','#17becf']
97
- const symbols = ['circle','rect','triangle','diamond','pin','arrow','star','roundRect','triangle','circle']
98
- const lineStyles = ['solid','solid','solid','solid','dash','solid','solid','dash','solid','dash']
99
 
100
- // 给每个 series 注入视觉样式
101
- const styledSeries = series.map((s, idx) => {
102
- const color = colors[idx % colors.length]
103
- return Object.assign({}, s, {
104
- lineStyle: { width: 2, type: lineStyles[idx % lineStyles.length], color },
105
- itemStyle: { color },
106
- symbol: symbols[idx % symbols.length],
107
- emphasis: { focus: 'series' }
108
- })
109
  })
110
 
111
  // 构建 legend.selected 映射:只有 props.autoShowSeries 中的系列会默认显示,其他需手动点击 legend 打开
112
  const legendSelected = {}
113
- styledSeries.forEach(s => {
 
 
 
 
 
 
 
114
  const name = String(s.name)
115
  legendSelected[name] = Array.isArray(props.autoShowSeries) && props.autoShowSeries.includes(name)
116
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  return {
119
  color: colors,
@@ -147,7 +213,7 @@ function getChartOption(series) {
147
  itemHeight: 10,
148
  textStyle: { color: '#cfd8dc' },
149
  tooltip: { show: true }
150
- }, { selected: legendSelected }),
151
  grid: { left: '6%', right: '20%', bottom: '8%', containLabel: true },
152
  xAxis: {
153
  type: 'log',
@@ -178,7 +244,7 @@ function getChartOption(series) {
178
  min: yMin,
179
  max: yMax
180
  },
181
- series: styledSeries
182
  }
183
  }
184
 
@@ -187,8 +253,8 @@ let resizeHandler = null
187
  function renderChart() {
188
  if (!chart) return
189
  let rows = leaderboard.value || []
190
- // 根据 selectedDataName 过滤数据集(如果未选择或为 'all' 则使用全部)
191
- const sel = selectedDataName && selectedDataName.value ? String(selectedDataName.value) : ''
192
  if (sel && sel !== 'all') {
193
  rows = rows.filter(r => String(r.data_name ?? '') === sel)
194
  }
@@ -213,7 +279,7 @@ watch(leaderboard, () => {
213
  }, { deep: true })
214
 
215
  // 当所选数据集变化时也要重新渲染图表
216
- watch(selectedDataName, () => {
217
  renderChart()
218
  })
219
 
@@ -224,7 +290,7 @@ onBeforeUnmount(() => {
224
  </script>
225
 
226
  <template>
227
- <div ref="chartRef" style="width: 100%; height: 480px;"></div>
228
  </template>
229
 
230
  <style scoped></style>
 
6
  const chartRef = ref(null)
7
  let chart = null
8
 
9
+ const { leaderboard, selectedDataNameChart, modelTypeGroups } = useLeaderboardData()
10
 
11
  // 接受一个可选 prop:autoShowSeries(数组),用于指定哪些模型类型在图表加载时自动显示。
12
  // 例如:<Chart :autoShowSeries="['gemma-3','Hunyuan']" />
 
19
  const groups = {}
20
  rows.forEach(r => {
21
  // 按 model_type 分组;如果 model_type 未填则标记为 未知
22
+ // 优化:如果行上有 _isMoE 标记,则把其归为 "<model_type> (MoE)" 系列,且保留 model_series_base
23
+ const baseType = r.model_type || '未知'
24
+ const isMoE = !!r._isMoE
25
+ const seriesName = isMoE ? `${baseType} (MoE)` : baseType
26
  if (!groups[seriesName]) groups[seriesName] = []
27
  // 数据原本单位为 TFLOPs,转换为 FLOPs(1 TFLOP = 1e12 FLOPs)用于对数尺度显示
28
  const xRaw = parseFloat(r.BF16_TFLOPs) || 0
29
  const x = xRaw > 0 ? xRaw * 1e12 : 0
30
+ const y = parseFloat(r.ic) || 0
31
  const rank = Number(r.rank) || 0
32
  groups[seriesName].push({ x, y, rank })
33
  })
 
38
  .filter(p => Number.isFinite(p.x) && p.x > 0 && Number.isFinite(p.y))
39
  .sort((a, b) => a.rank - b.rank)
40
  .map(d => [d.x, d.y])
41
+ // 根据该系列的样本点数量适度放大 symbolSize,避免点太小难以点击
42
+ const count = groups[name].length || 1
43
+ const symbolSize = Math.min(18, 8 + Math.sqrt(count) * 1.6)
44
+ return { name, rawCount: count, type: 'line', showSymbol: true, symbolSize, data }
45
  })
46
  }
47
 
 
80
  if (yMin !== undefined && yMax !== undefined) {
81
  if (yMin === yMax) {
82
  // small symmetric padding
83
+ // 如果只有单一数值,使用该值作为中心;但若该值为 0,则不要把区间提升到以 1 为中心(之前用 `yMin || 1` 会导致 0 -> 1)
84
+ const v = (yMin !== undefined ? yMin : 1)
85
+ if (v === 0) {
86
+ // 为 0 时给一个小的对称 padding(之后如果原始数据全为正,会被下限为 0)
87
+ const pad = 0.05
88
+ yMin = -pad
89
+ yMax = pad
90
+ } else {
91
+ yMin = v - Math.abs(v) * 0.05
92
+ yMax = v + Math.abs(v) * 0.05
93
+ }
94
  } else {
95
  const pad = (yMax - yMin) * 0.08
96
  yMin = yMin - pad
 
107
  return s.split('').map(ch => map[ch] || ch).join('')
108
  }
109
  // 可循环使用的视觉样式(颜色/符号/线型)
110
+ // 7 种颜色从红到紫,重复使用;非 MoE 使用圆形,MoE 使用星形(star)
111
+ const colors = ['#1F77B4','#ff7f0e','#2CA02C','#D62728','#9467BD','#8C564B','#E377C2','#7F7F7F','#BCBD22','#17BECF']
112
+ const lineStyles = ['solid','solid','solid','solid','dash','solid','solid']
113
 
114
+ // 先构建不带颜色/符号的基础 series(rawSeries),后面会在排序后按顺序分配颜色和符号
115
+ const rawSeries = series.map(s => {
116
+ const baseSize = Number(s.symbolSize) || 8
117
+ return Object.assign({}, s, { symbolSize: baseSize, hoverAnimation: true })
 
 
 
 
 
118
  })
119
 
120
  // 构建 legend.selected 映射:只有 props.autoShowSeries 中的系列会默认显示,其他需手动点击 legend 打开
121
  const legendSelected = {}
122
+ if(props.autoShowSeries.includes("*")){
123
+ rawSeries.forEach(s => {
124
+ const name = String(s.name)
125
+ legendSelected[name] = true
126
+ })
127
+ }
128
+ else{
129
+ rawSeries.forEach(s => {
130
  const name = String(s.name)
131
  legendSelected[name] = Array.isArray(props.autoShowSeries) && props.autoShowSeries.includes(name)
132
  })
133
+ }
134
+
135
+
136
+ // 按 modelTypeMapping 的定义顺序对 series 排序(使用来自 useLeaderboardData 的 modelTypeGroups)
137
+ const preferredOrder = (modelTypeGroups && Array.isArray(modelTypeGroups.value)) ? modelTypeGroups.value : []
138
+ const orderIndex = {}
139
+ preferredOrder.forEach((n, i) => { orderIndex[String(n)] = i })
140
+ // console.log('Preferred series order:', preferredOrder)
141
+ const inOrder = []
142
+ const rest = []
143
+ rawSeries.forEach(s => {
144
+ const name = String(s.name)
145
+ if (Object.prototype.hasOwnProperty.call(orderIndex, name)) {
146
+ inOrder.push(s)
147
+ } else {
148
+ rest.push(s)
149
+ }
150
+ })
151
+ // 按映射顺序排序 inOrder
152
+ inOrder.sort((a, b) => orderIndex[String(a.name)] - orderIndex[String(b.name)])
153
+ // 其余的按名字字母序 (目前全部指定映射了)
154
+ rest.sort((a, b) => String(a.name).localeCompare(String(b.name)))
155
+
156
+ // console.log('Final series order:', inOrder.map(s => s.name), rest.map(s => s.name))
157
+
158
+ const finalSeries = [...inOrder, ...rest]
159
+
160
+ // 现在按 finalSeries 顺序分配颜色和符号,保证颜色顺序与 modelTypeMapping 一致
161
+ const finalSeriesStyled = finalSeries.map((s, idx) => {
162
+ const color = colors[idx % colors.length]
163
+ const lineType = lineStyles[idx % lineStyles.length] || 'solid'
164
+ // 如果 series 名称包含 (MoE) 则使用星形
165
+ const isMoESeries = String(s.name).includes('(MoE)')
166
+ const symbolShape = isMoESeries ? 'star' : 'circle'
167
+ const baseSize = Number(s.symbolSize) || 8
168
+ const emphasisSize = Math.max(12, Math.round(baseSize * 1.6))
169
+ return Object.assign({}, s, {
170
+ lineStyle: { width: 2, type: lineType, color },
171
+ itemStyle: { color },
172
+ symbol: symbolShape,
173
+ symbolSize: baseSize,
174
+ hoverAnimation: true,
175
+ emphasis: {
176
+ focus: 'series',
177
+ symbolSize: emphasisSize,
178
+ itemStyle: { borderWidth: 2, borderColor: '#ffffff' }
179
+ }
180
+ })
181
+ })
182
+ const legendData = finalSeriesStyled.map(s => String(s.name))
183
 
184
  return {
185
  color: colors,
 
213
  itemHeight: 10,
214
  textStyle: { color: '#cfd8dc' },
215
  tooltip: { show: true }
216
+ }, { selected: legendSelected, data: legendData }),
217
  grid: { left: '6%', right: '20%', bottom: '8%', containLabel: true },
218
  xAxis: {
219
  type: 'log',
 
244
  min: yMin,
245
  max: yMax
246
  },
247
+ series: finalSeriesStyled
248
  }
249
  }
250
 
 
253
  function renderChart() {
254
  if (!chart) return
255
  let rows = leaderboard.value || []
256
+ // 根据 selectedDataNameChart 过滤数据集(如果未选择或为 'all' 则使用全部)
257
+ const sel = selectedDataNameChart && selectedDataNameChart.value ? String(selectedDataNameChart.value) : ''
258
  if (sel && sel !== 'all') {
259
  rows = rows.filter(r => String(r.data_name ?? '') === sel)
260
  }
 
279
  }, { deep: true })
280
 
281
  // 当所选数据集变化时也要重新渲染图表
282
+ watch(selectedDataNameChart, () => {
283
  renderChart()
284
  })
285
 
 
290
  </script>
291
 
292
  <template>
293
+ <div ref="chartRef" style="width: 880px; height: 480px;"></div>
294
  </template>
295
 
296
  <style scoped></style>
src/composables/useLeaderboardData.js CHANGED
@@ -10,6 +10,7 @@ const globalState = reactive({
10
  visibleColumns: [],
11
  dataGroups: [],
12
  selectedDataName: '',
 
13
  modelTypeGroups: [],
14
  selectedModelType: '',
15
  DEFAULT_CSV_PATH: '/finalData/filtered.csv',
@@ -18,29 +19,35 @@ const globalState = reactive({
18
 
19
  // 模型类型映射对象:键为模型类型,值为包含的 model_series 数组
20
  const modelTypeMapping = {
21
- 'Qwen1.5':['Qwen1.5'],
22
- 'Qwen2.5':['Qwen2.5'],
23
- 'Gemma-3': ['gemma-3'],
24
- 'Qwen2': ['Qwen2'],
25
- 'Hunyuan': ['Hunyuan'],
26
- 'Qwen3': ['Qwen3'],
27
- 'InternLM2.5': ['internlm2.5'],
28
- 'Llama-3': ['Llama-3.1','Llama-3.2'],
29
- 'DeepSeek-V2': ['DeepSeek-V2'],
30
- 'DeepSeek-V3.1': ['DeepSeek-V3.1-Base'],
31
- 'GLM-4': ['glm-4','GLM-4'],
32
- 'GLM-4.5': ['GLM-4.5-Air-Base','GLM-4.5-Base'],
33
- 'Llama-4': ['Llama-4'],
34
- 'Seed-OSS': ['Seed-OSS'],
 
 
35
  }
36
 
37
- const autoShowSeries = ['Qwen3','Llama-3','InternLM2.5','GLM-4','Seed-OSS','Gemma-3','Hunyuan','DeepSeek-V3.1','DeepSeek-V2','GLM-4.5']
 
 
 
38
 
39
  // 表头显示名称映射(raw header -> 显示名),可以在此添加或由用户修改
40
  const headerDisplayMap = reactive({
41
  'model_name': 'MODEL NAME',
42
  'model_series': 'MODEL SERIES',
43
  'model_size (B)': 'MODEL SIZE (B)',
 
44
  'BF16_TFLOPs': 'BF16 TFLOPs',
45
  'ic': 'IC',
46
  })
@@ -105,9 +112,26 @@ async function fetchAndLoadCsv(path = globalState.DEFAULT_CSV_PATH) {
105
  // 选择用于排序/显示的分数字段(优先 information_capacity, ic, 然后尝试 numeric-like fields)
106
  const scoreKey = headers.find(h => ['information_capacity', 'ic', 'score'].includes(h)) || headers.find(h => /capacity|score|ic/i.test(h)) || headers[0]
107
 
108
- // 按 scoreKey 排序(保持所有字段),然后把每行写入 leaderboard,同时记录 headers
109
- rows.sort((a, b) => (Number(b[scoreKey]) || 0) - (Number(a[scoreKey]) || 0))
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  // 我们希望确保一些关键列按顺序显示
112
  const preferred = ['model_name', 'model_series', 'model_size (B)', 'seq_len', 'uniform_entropy', 'constant', 'conditional_entropy', 'entropy_gain', 'BF16_TFLOPs', 'information_capacity', 'ic']
113
  const ordered = []
@@ -170,14 +194,14 @@ async function fetchAndLoadCsv(path = globalState.DEFAULT_CSV_PATH) {
170
  }
171
 
172
  // 格式化 helper
173
- const numericFloatCols = new Set(['uniform_entropy','conditional_entropy','entropy_gain','information_capacity','ic','constant','BF16_TFLOPs'])
174
  const numericIntCols = new Set(['seq_len'])
175
  // attach formatter per row for rendering convenience (non-reactive simple values)
176
  for (const row of globalState.leaderboard) {
177
  row._formatted = {}
178
  for (const h of ordered) {
179
  const raw = row[h]
180
- if (raw == null || raw === '') { row._formatted[h] = '' ; continue }
181
  if (numericIntCols.has(h)) {
182
  const n = Number(raw)
183
  row._formatted[h] = Number.isFinite(n) ? String(Math.round(n)) : raw
@@ -237,6 +261,10 @@ export function useLeaderboardData() {
237
  get: () => globalState.selectedDataName,
238
  set: (v) => globalState.selectedDataName = v
239
  }),
 
 
 
 
240
  modelTypeGroups: computed(() => globalState.modelTypeGroups),
241
  selectedModelType: computed({
242
  get: () => globalState.selectedModelType,
 
10
  visibleColumns: [],
11
  dataGroups: [],
12
  selectedDataName: '',
13
+ selectedDataNameChart: '',
14
  modelTypeGroups: [],
15
  selectedModelType: '',
16
  DEFAULT_CSV_PATH: '/finalData/filtered.csv',
 
19
 
20
  // 模型类型映射对象:键为模型类型,值为包含的 model_series 数组
21
  const modelTypeMapping = {
22
+ 'Qwen3': ['Qwen3'],
23
+ 'Qwen2.5': ['Qwen2.5'],
24
+ 'Qwen2': ['Qwen2'],
25
+ 'Qwen1.5': ['Qwen1.5'],
26
+ 'Llama-3': ['Llama-3.1', 'Llama-3.2'],
27
+ 'InternLM2.5': ['internlm2.5'],
28
+ 'GLM-4': ['glm-4', 'GLM-4'],
29
+ 'Seed-OSS': ['Seed-OSS'],
30
+ 'Gemma-3': ['gemma-3'],
31
+ 'Hunyuan': ['Hunyuan'],
32
+ 'Qwen2 (MoE)': ['Qwen2 (MoE)'],
33
+ 'Qwen1.5 (MoE)': ['Qwen1.5 (MoE)'],
34
+ 'DeepSeek-V3.1': ['DeepSeek-V3.1-Base'],
35
+ 'DeepSeek-V2': ['DeepSeek-V2'],
36
+ 'GLM-4.5': ['GLM-4.5-Air-Base', 'GLM-4.5-Base'],
37
+ 'Llama-4': ['Llama-4'],
38
  }
39
 
40
+ const MoEModelSeries = ['Qwen2', 'Qwen1.5']
41
+
42
+ // const autoShowSeries = ['Qwen3', 'Llama-3', 'InternLM2.5', 'GLM-4', 'Seed-OSS', 'Gemma-3', 'Hunyuan', 'DeepSeek-V3.1', 'DeepSeek-V2', 'GLM-4.5']
43
+ const autoShowSeries = ["*"]
44
 
45
  // 表头显示名称映射(raw header -> 显示名),可以在此添加或由用户修改
46
  const headerDisplayMap = reactive({
47
  'model_name': 'MODEL NAME',
48
  'model_series': 'MODEL SERIES',
49
  'model_size (B)': 'MODEL SIZE (B)',
50
+ 'conditional_entropy': 'Conditional Entropy',
51
  'BF16_TFLOPs': 'BF16 TFLOPs',
52
  'ic': 'IC',
53
  })
 
112
  // 选择用于排序/显示的分数字段(优先 information_capacity, ic, 然后尝试 numeric-like fields)
113
  const scoreKey = headers.find(h => ['information_capacity', 'ic', 'score'].includes(h)) || headers.find(h => /capacity|score|ic/i.test(h)) || headers[0]
114
 
115
+ // 按 scoreKey 排序(保持所有字段),然后把每行写入 leaderboard,同时记录 headers
116
+ rows.sort((a, b) => (Number(b[scoreKey]) || 0) - (Number(a[scoreKey]) || 0))
117
 
118
+ // 特定处理
119
+ // 判断模型开头是否在 MoEModelSeries 中,是则在 判断尾部是否为-A{number}B这样的格式
120
+ for (const r of rows) {
121
+ const name = r['model_name'] || ''
122
+ for (const moePrefix of MoEModelSeries) {
123
+ if (name.startsWith(moePrefix)) {
124
+ // console.log('Checking MoE model name:', name,name.match(/-A(\d+(?:\.\d+)?)B/))
125
+ const moeSuffixMatch = name.match(/-A(.+)B$/)
126
+ if (moeSuffixMatch) {
127
+ // 更改 model_series 显示名称 为 moePrefix + ' (MoE)'
128
+ r['model_series'] = `${moePrefix} (MoE)`
129
+ console.log('Detected MoE model, updated series:', r['model_series'])
130
+ }
131
+ }
132
+ }
133
+ }
134
+ // console.log('rows', rows)
135
  // 我们希望确保一些关键列按顺序显示
136
  const preferred = ['model_name', 'model_series', 'model_size (B)', 'seq_len', 'uniform_entropy', 'constant', 'conditional_entropy', 'entropy_gain', 'BF16_TFLOPs', 'information_capacity', 'ic']
137
  const ordered = []
 
194
  }
195
 
196
  // 格式化 helper
197
+ const numericFloatCols = new Set(['uniform_entropy', 'conditional_entropy', 'entropy_gain', 'information_capacity', 'ic', 'constant', 'BF16_TFLOPs'])
198
  const numericIntCols = new Set(['seq_len'])
199
  // attach formatter per row for rendering convenience (non-reactive simple values)
200
  for (const row of globalState.leaderboard) {
201
  row._formatted = {}
202
  for (const h of ordered) {
203
  const raw = row[h]
204
+ if (raw == null || raw === '') { row._formatted[h] = ''; continue }
205
  if (numericIntCols.has(h)) {
206
  const n = Number(raw)
207
  row._formatted[h] = Number.isFinite(n) ? String(Math.round(n)) : raw
 
261
  get: () => globalState.selectedDataName,
262
  set: (v) => globalState.selectedDataName = v
263
  }),
264
+ selectedDataNameChart: computed({
265
+ get: () => globalState.selectedDataNameChart,
266
+ set: (v) => globalState.selectedDataNameChart = v
267
+ }),
268
  modelTypeGroups: computed(() => globalState.modelTypeGroups),
269
  selectedModelType: computed({
270
  get: () => globalState.selectedModelType,
src/views/Leaderboard.vue CHANGED
@@ -13,6 +13,7 @@ const {
13
  selectableColumns,
14
  dataGroups,
15
  selectedDataName,
 
16
  autoShowSeries,
17
  headerDisplayMap,
18
  dataNameDisplayMap,
@@ -67,23 +68,24 @@ function toggleTheme() {
67
  </script>
68
 
69
  <template>
70
- <div class="min-h-screen bg-gray-50 dark:bg-gradient-to-b dark:from-gray-900 dark:via-gray-800 dark:to-gray-800 py-12 px-4 sm:px-6 lg:px-8">
 
71
  <div class=" mx-auto">
72
  <header class="mb-6 flex items-start justify-between">
73
  <div>
74
- <h1 class="text-3xl font-extrabold text-gray-900 dark:text-gray-100">LLM Leaderboard</h1>
75
- <p class="mt-1 text-sm text-gray-600 dark:text-gray-300">按分数排序的顶级大模型(演示数据)</p>
76
  </div>
77
  </header>
78
 
79
- <Chart :autoShowSeries="autoShowSeries" />
80
 
81
  <div class="bg-white dark:bg-gray-900/60 shadow rounded-lg overflow-hidden">
82
  <div
83
  class="px-6 py-4 border-b bg-gradient-to-r from-indigo-50 via-white to-white dark:bg-gradient-to-r dark:from-gray-800 dark:via-gray-900 dark:to-gray-900">
84
  <div class="flex items-center justify-between">
85
  <h2 class="text-lg font-medium text-gray-800 dark:text-gray-50">排行榜</h2>
86
- <div class="text-sm text-gray-500 dark:text-gray-300">更新于模拟数据</div>
87
  </div>
88
  </div>
89
 
@@ -95,8 +97,11 @@ function toggleTheme() {
95
  <!-- <label class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer" :class="{'bg-gray-100 dark:bg-gray-700': selectedDataName==='all'}">
96
  <input type="radio" class="hidden" value="all" v-model="selectedDataName" /> 全部
97
  </label> -->
98
- <label v-for="g in dataGroups" :key="g" class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300" :class="{'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedDataName===g}">
99
- <input type="radio" class="hidden" :value="g" v-model="selectedDataName" /> {{ dataNameDisplayMap[g] ? dataNameDisplayMap[g] : g }}
 
 
 
100
  </label>
101
  </div>
102
  <div v-else class="text-sm text-gray-500 dark:text-gray-400">(数据集中未检测到 data_name 列)</div>
@@ -108,10 +113,14 @@ function toggleTheme() {
108
  <div class="flex items-center gap-3">
109
  <span class="text-sm font-medium text-gray-700 dark:text-gray-200">模型类型筛选</span>
110
  <div v-if="modelTypeGroups.length > 0" class="flex gap-2 items-center overflow-x-auto">
111
- <label class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300" :class="{'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedModelType==='all'}">
 
 
112
  <input type="radio" class="hidden" value="all" v-model="selectedModelType" /> 全部
113
  </label>
114
- <label v-for="g in modelTypeGroups" :key="g" class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300" :class="{'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedModelType===g}">
 
 
115
  <input type="radio" class="hidden" :value="g" v-model="selectedModelType" /> {{ g }}
116
  </label>
117
  </div>
@@ -125,15 +134,19 @@ function toggleTheme() {
125
  <div class="flex items-center gap-3">
126
  <span class="text-sm font-medium text-gray-700 dark:text-gray-200">显示列</span>
127
  <div class="flex items-center gap-2">
128
- <button @click.prevent="selectAll" class="text-sm px-2 py-1 bg-white/90 dark:bg-gray-700/60 border border-gray-200 dark:border-gray-600 rounded text-gray-700 dark:text-gray-200 hover:bg-white dark:hover:bg-gray-600 transition">全选</button>
129
- <button @click.prevent="clearAll" class="text-sm px-2 py-1 bg-transparent border border-gray-200 dark:border-gray-700 rounded text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">清除</button>
 
 
130
  </div>
131
  </div>
132
 
133
  <div class="flex-1 overflow-x-auto">
134
  <div class="flex gap-2 items-center px-1 py-2">
135
- <label v-for="h in selectableColumns" :key="h" class="flex items-center gap-2 text-sm bg-white dark:bg-gray-800 px-3 py-1 rounded-full border border-gray-200 dark:border-gray-700 hover:shadow-sm whitespace-nowrap">
136
- <input type="checkbox" :value="h" v-model="visibleColumns" class="h-4 w-4 text-indigo-600 dark:text-indigo-400 bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 rounded" />
 
 
137
  <span class="truncate text-gray-800 dark:text-gray-50">{{ headerDisplayMap[h] || h }}</span>
138
  </label>
139
  </div>
@@ -144,29 +157,51 @@ function toggleTheme() {
144
  <div class="p-4 overflow-x-auto">
145
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
146
  <thead class="bg-gray-50 dark:bg-gradient-to-r dark:from-gray-800 dark:to-gray-900">
147
- <tr>
148
- <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">#</th>
149
- <th v-for="h in displayedColumns" :key="h" class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
150
- {{ headerDisplayMap[h] || h }}
151
- </th>
152
- </tr>
 
 
 
153
  </thead>
154
  <tbody class="bg-white dark:bg-transparent divide-y divide-gray-100 dark:divide-gray-700">
155
- <tr v-for="model in filteredLeaderboard" :key="model.rank" :class="{
156
- 'bg-yellow-50 dark:bg-yellow-700/25': model.rank === 1,
157
- 'bg-gray-50 dark:bg-gray-800/60': model.rank === 2,
158
- 'bg-indigo-50 dark:bg-indigo-700/25': model.rank === 3
159
- }" class="hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-150">
160
- <td class="px-4 py-3 whitespace-nowrap font-medium text-gray-800 dark:text-gray-50">#{{ model.rank }}
161
- </td>
162
- <td v-for="h in displayedColumns" :key="h" class="px-4 py-3 whitespace-nowrap">
163
- <div class="text-sm text-gray-800 dark:text-gray-50">{{ formatCell(h, model) }}</div>
164
- </td>
165
- </tr>
166
  </tbody>
167
  </table>
168
  </div>
169
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  </div>
171
  </div>
172
  </template>
 
13
  selectableColumns,
14
  dataGroups,
15
  selectedDataName,
16
+ selectedDataNameChart,
17
  autoShowSeries,
18
  headerDisplayMap,
19
  dataNameDisplayMap,
 
68
  </script>
69
 
70
  <template>
71
+ <div
72
+ class="min-h-screen bg-gray-50 dark:bg-gradient-to-b dark:from-gray-900 dark:via-gray-800 dark:to-gray-800 py-12 px-4 sm:px-6 lg:px-8">
73
  <div class=" mx-auto">
74
  <header class="mb-6 flex items-start justify-between">
75
  <div>
76
+ <h1 class="text-3xl font-extrabold text-gray-900 dark:text-gray-100">LLM Information Capacity</h1>
77
+ <p class="mt-1 text-sm text-gray-600 dark:text-gray-300">LLM IC Leaderboard Demo</p>
78
  </div>
79
  </header>
80
 
81
+
82
 
83
  <div class="bg-white dark:bg-gray-900/60 shadow rounded-lg overflow-hidden">
84
  <div
85
  class="px-6 py-4 border-b bg-gradient-to-r from-indigo-50 via-white to-white dark:bg-gradient-to-r dark:from-gray-800 dark:via-gray-900 dark:to-gray-900">
86
  <div class="flex items-center justify-between">
87
  <h2 class="text-lg font-medium text-gray-800 dark:text-gray-50">排行榜</h2>
88
+ <div class="text-sm text-gray-500 dark:text-gray-300">更新于xx年xx月</div>
89
  </div>
90
  </div>
91
 
 
97
  <!-- <label class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer" :class="{'bg-gray-100 dark:bg-gray-700': selectedDataName==='all'}">
98
  <input type="radio" class="hidden" value="all" v-model="selectedDataName" /> 全部
99
  </label> -->
100
+ <label v-for="g in dataGroups" :key="g"
101
+ class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300"
102
+ :class="{ 'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedDataName === g }">
103
+ <input type="radio" class="hidden" :value="g" v-model="selectedDataName" /> {{ dataNameDisplayMap[g] ?
104
+ dataNameDisplayMap[g] : g }}
105
  </label>
106
  </div>
107
  <div v-else class="text-sm text-gray-500 dark:text-gray-400">(数据集中未检测到 data_name 列)</div>
 
113
  <div class="flex items-center gap-3">
114
  <span class="text-sm font-medium text-gray-700 dark:text-gray-200">模型类型筛选</span>
115
  <div v-if="modelTypeGroups.length > 0" class="flex gap-2 items-center overflow-x-auto">
116
+ <label
117
+ class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300"
118
+ :class="{ 'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedModelType === 'all' }">
119
  <input type="radio" class="hidden" value="all" v-model="selectedModelType" /> 全部
120
  </label>
121
+ <label v-for="g in modelTypeGroups" :key="g"
122
+ class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300"
123
+ :class="{ 'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedModelType === g }">
124
  <input type="radio" class="hidden" :value="g" v-model="selectedModelType" /> {{ g }}
125
  </label>
126
  </div>
 
134
  <div class="flex items-center gap-3">
135
  <span class="text-sm font-medium text-gray-700 dark:text-gray-200">显示列</span>
136
  <div class="flex items-center gap-2">
137
+ <button @click.prevent="selectAll"
138
+ class="text-sm px-2 py-1 bg-white/90 dark:bg-gray-700/60 border border-gray-200 dark:border-gray-600 rounded text-gray-700 dark:text-gray-200 hover:bg-white dark:hover:bg-gray-600 transition">全选</button>
139
+ <button @click.prevent="clearAll"
140
+ class="text-sm px-2 py-1 bg-transparent border border-gray-200 dark:border-gray-700 rounded text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition">清除</button>
141
  </div>
142
  </div>
143
 
144
  <div class="flex-1 overflow-x-auto">
145
  <div class="flex gap-2 items-center px-1 py-2">
146
+ <label v-for="h in selectableColumns" :key="h"
147
+ class="flex items-center gap-2 text-sm bg-white dark:bg-gray-800 px-3 py-1 rounded-full border border-gray-200 dark:border-gray-700 hover:shadow-sm whitespace-nowrap">
148
+ <input type="checkbox" :value="h" v-model="visibleColumns"
149
+ class="h-4 w-4 text-indigo-600 dark:text-indigo-400 bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 rounded" />
150
  <span class="truncate text-gray-800 dark:text-gray-50">{{ headerDisplayMap[h] || h }}</span>
151
  </label>
152
  </div>
 
157
  <div class="p-4 overflow-x-auto">
158
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
159
  <thead class="bg-gray-50 dark:bg-gradient-to-r dark:from-gray-800 dark:to-gray-900">
160
+ <tr>
161
+ <th
162
+ class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
163
+ #</th>
164
+ <th v-for="h in displayedColumns" :key="h"
165
+ class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
166
+ {{ headerDisplayMap[h] || h }}
167
+ </th>
168
+ </tr>
169
  </thead>
170
  <tbody class="bg-white dark:bg-transparent divide-y divide-gray-100 dark:divide-gray-700">
171
+ <tr v-for="model in filteredLeaderboard" :key="model.rank" :class="{
172
+ 'bg-yellow-50 dark:bg-yellow-700/25': model.rank === 1,
173
+ 'bg-gray-50 dark:bg-gray-800/60': model.rank === 2,
174
+ 'bg-indigo-50 dark:bg-indigo-700/25': model.rank === 3
175
+ }" class="hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-150">
176
+ <td class="px-4 py-3 whitespace-nowrap font-medium text-gray-800 dark:text-gray-50">#{{ model.rank }}
177
+ </td>
178
+ <td v-for="h in displayedColumns" :key="h" class="px-4 py-3 whitespace-nowrap">
179
+ <div class="text-sm text-gray-800 dark:text-gray-50">{{ formatCell(h, model) }}</div>
180
+ </td>
181
+ </tr>
182
  </tbody>
183
  </table>
184
  </div>
185
  </div>
186
+
187
+ <Chart :autoShowSeries="autoShowSeries" />
188
+ <div class="px-6 py-3 border-b bg-gray-50 dark:bg-gradient-to-b dark:from-gray-900 dark:to-gray-800">
189
+ <div class="flex items-center gap-3">
190
+ <span class="text-sm font-medium text-gray-700 dark:text-gray-200">数据集筛选</span>
191
+ <div v-if="dataGroups.length > 0" class="flex gap-2 items-center overflow-x-auto">
192
+ <!-- <label class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer" :class="{'bg-gray-100 dark:bg-gray-700': selectedDataName==='all'}">
193
+ <input type="radio" class="hidden" value="all" v-model="selectedDataName" /> 全部
194
+ </label> -->
195
+ <label v-for="g in dataGroups" :key="g"
196
+ class="px-2 py-1 rounded-full border border-gray-200 dark:border-gray-700 text-sm cursor-pointer dark:text-slate-300"
197
+ :class="{ 'bg-gray-100 dark:bg-gray-700 dark:text-white': selectedDataNameChart === g }">
198
+ <input type="radio" class="hidden" :value="g" v-model="selectedDataNameChart" /> {{ dataNameDisplayMap[g]
199
+ ? dataNameDisplayMap[g] : g }}
200
+ </label>
201
+ </div>
202
+ <div v-else class="text-sm text-gray-500 dark:text-gray-400">(数据集中未检测到 data_name 列)</div>
203
+ </div>
204
+ </div>
205
  </div>
206
  </div>
207
  </template>