Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
ai-box
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
青山
ai-box
Commits
3c5c57b9
Commit
3c5c57b9
authored
Feb 06, 2025
by
fisherdaddy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feature: add gemini 2.0 flash price
parent
b3f1dde1
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
205 additions
and
41 deletions
+205
-41
PricingChart.jsx
src/components/PricingChart.jsx
+63
-2
PricingCharts.jsx
src/components/PricingCharts.jsx
+1
-1
llm-pricing.json
src/data/llm-pricing.json
+12
-0
openai-pricing.json
src/data/openai-pricing.json
+20
-20
vision-model-pricing.json
src/data/vision-model-pricing.json
+12
-0
PricingChart.css
src/styles/PricingChart.css
+97
-18
No files found.
src/components/PricingChart.jsx
View file @
3c5c57b9
// PricingChart.jsx
// PricingChart.jsx
import
React
,
{
useState
}
from
'react'
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'react'
;
import
{
useScrollToTop
}
from
'../hooks/useScrollToTop'
;
import
{
useScrollToTop
}
from
'../hooks/useScrollToTop'
;
import
'../styles/PricingChart.css'
;
import
'../styles/PricingChart.css'
;
...
@@ -105,6 +105,54 @@ const PricingChart = ({ data }) => {
...
@@ -105,6 +105,54 @@ const PricingChart = ({ data }) => {
input
:
true
,
input
:
true
,
output
:
true
,
output
:
true
,
});
});
const
chartAreaRef
=
useRef
(
null
);
const
[
hasScroll
,
setHasScroll
]
=
useState
(
false
);
const
[
showScrollHint
,
setShowScrollHint
]
=
useState
(
false
);
useEffect
(()
=>
{
const
checkScroll
=
()
=>
{
if
(
chartAreaRef
.
current
)
{
const
{
scrollWidth
,
clientWidth
,
scrollLeft
}
=
chartAreaRef
.
current
;
setHasScroll
(
scrollWidth
>
clientWidth
);
// 只在滚动到最左侧时显示提示
setShowScrollHint
(
scrollWidth
>
clientWidth
&&
scrollLeft
===
0
);
}
};
const
handleScroll
=
()
=>
{
if
(
chartAreaRef
.
current
)
{
const
{
scrollLeft
}
=
chartAreaRef
.
current
;
// 当用户开始滚动时隐藏提示
if
(
scrollLeft
>
0
)
{
setShowScrollHint
(
false
);
}
}
};
checkScroll
();
window
.
addEventListener
(
'resize'
,
checkScroll
);
if
(
chartAreaRef
.
current
)
{
chartAreaRef
.
current
.
addEventListener
(
'scroll'
,
handleScroll
);
}
return
()
=>
{
window
.
removeEventListener
(
'resize'
,
checkScroll
);
if
(
chartAreaRef
.
current
)
{
chartAreaRef
.
current
.
removeEventListener
(
'scroll'
,
handleScroll
);
}
};
},
[
data
]);
const
handleScrollHintClick
=
()
=>
{
if
(
chartAreaRef
.
current
)
{
const
{
scrollWidth
,
clientWidth
}
=
chartAreaRef
.
current
;
chartAreaRef
.
current
.
scrollTo
({
left
:
scrollWidth
-
clientWidth
,
behavior
:
'smooth'
});
setShowScrollHint
(
false
);
}
};
const
handleLegendClick
=
(
barType
)
=>
{
const
handleLegendClick
=
(
barType
)
=>
{
setHighlightedBarTypes
((
prevState
)
=>
({
setHighlightedBarTypes
((
prevState
)
=>
({
...
@@ -130,7 +178,7 @@ const PricingChart = ({ data }) => {
...
@@ -130,7 +178,7 @@ const PricingChart = ({ data }) => {
<
ChartLegend
onLegendClick=
{
handleLegendClick
}
highlightedBarTypes=
{
highlightedBarTypes
}
/>
<
ChartLegend
onLegendClick=
{
handleLegendClick
}
highlightedBarTypes=
{
highlightedBarTypes
}
/>
<
div
className=
"chart-area"
>
<
div
className=
{
`chart-area ${hasScroll ? 'has-scroll' : ''}`
}
ref=
{
chartAreaRef
}
>
<
YAxis
maxPrice=
{
maxPrice
}
/>
<
YAxis
maxPrice=
{
maxPrice
}
/>
<
div
className=
"chart-container"
>
<
div
className=
"chart-container"
>
<
GridLines
/>
<
GridLines
/>
...
@@ -143,6 +191,19 @@ const PricingChart = ({ data }) => {
...
@@ -143,6 +191,19 @@ const PricingChart = ({ data }) => {
/>
/>
))
}
))
}
</
div
>
</
div
>
{
showScrollHint
&&
(
<
div
className=
"scroll-hint-container"
onClick=
{
handleScrollHintClick
}
style=
{
{
cursor
:
'pointer'
}
}
>
<
div
className=
"scroll-hint"
>
<
svg
width=
"24"
height=
"24"
viewBox=
"0 0 24 24"
fill=
"currentColor"
>
<
path
d=
"M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"
/>
</
svg
>
</
div
>
</
div
>
)
}
</
div
>
</
div
>
</
div
>
</
div
>
);
);
...
...
src/components/PricingCharts.jsx
View file @
3c5c57b9
...
@@ -11,7 +11,7 @@ import LoadingOverlay from './LoadingOverlay';
...
@@ -11,7 +11,7 @@ import LoadingOverlay from './LoadingOverlay';
const
PricingCharts
=
()
=>
{
const
PricingCharts
=
()
=>
{
useScrollToTop
();
useScrollToTop
();
const
isLoading
=
usePageLoading
();
const
isLoading
=
usePageLoading
();
const
lastUpdateTime
=
'2025-0
1-09 10
:00'
;
const
lastUpdateTime
=
'2025-0
2-06 18
:00'
;
return
(
return
(
<>
<>
...
...
src/data/llm-pricing.json
View file @
3c5c57b9
...
@@ -20,6 +20,12 @@
...
@@ -20,6 +20,12 @@
"inputPrice"
:
1
,
"inputPrice"
:
1
,
"outputPrice"
:
1
"outputPrice"
:
1
},
},
{
"name"
:
"gemini 2.0 Flash-Lite"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"inputPrice"
:
0.53
,
"outputPrice"
:
2.1
},
{
{
"name"
:
"gemini 1.5 Flash"
,
"name"
:
"gemini 1.5 Flash"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"logo"
:
"/assets/icon/google_small.svg"
,
...
@@ -50,6 +56,12 @@
...
@@ -50,6 +56,12 @@
"inputPrice"
:
0.8
,
"inputPrice"
:
0.8
,
"outputPrice"
:
2
"outputPrice"
:
2
},
},
{
"name"
:
"google 2.0 Falsh"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"inputPrice"
:
0.7
,
"outputPrice"
:
2.8
},
{
{
"name"
:
"gpt-4o-mini"
,
"name"
:
"gpt-4o-mini"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
...
...
src/data/openai-pricing.json
View file @
3c5c57b9
...
@@ -8,6 +8,18 @@
...
@@ -8,6 +8,18 @@
"inputPrice"
:
0.15
,
"inputPrice"
:
0.15
,
"outputPrice"
:
0.6
"outputPrice"
:
0.6
},
},
{
"name"
:
"o3-mini"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
1.1
,
"outputPrice"
:
4.4
},
{
"name"
:
"o1-mini"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
1.1
,
"outputPrice"
:
4.4
},
{
{
"name"
:
"gpt-4o"
,
"name"
:
"gpt-4o"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
...
@@ -21,22 +33,22 @@
...
@@ -21,22 +33,22 @@
"outputPrice"
:
15
"outputPrice"
:
15
},
},
{
{
"name"
:
"gpt-4
-turbo
"
,
"name"
:
"gpt-4
o-mini-audio-preview
"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
10
,
"inputPrice"
:
10
,
"outputPrice"
:
3
0
"outputPrice"
:
2
0
},
},
{
{
"name"
:
"
o3-mini
"
,
"name"
:
"
gpt-4o-mini-realtime-preview
"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
1
.1
,
"inputPrice"
:
1
0
,
"outputPrice"
:
4.4
"outputPrice"
:
20
},
},
{
{
"name"
:
"
o1-mini
"
,
"name"
:
"
gpt-4-turbo
"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
1
.1
,
"inputPrice"
:
1
0
,
"outputPrice"
:
4.4
"outputPrice"
:
30
},
},
{
{
"name"
:
"o1"
,
"name"
:
"o1"
,
...
@@ -44,24 +56,12 @@
...
@@ -44,24 +56,12 @@
"inputPrice"
:
15
,
"inputPrice"
:
15
,
"outputPrice"
:
60
"outputPrice"
:
60
},
},
{
"name"
:
"gpt-4o-mini-audio-preview"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
10
,
"outputPrice"
:
20
},
{
{
"name"
:
"gpt-4o-audio-preview"
,
"name"
:
"gpt-4o-audio-preview"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
100
,
"inputPrice"
:
100
,
"outputPrice"
:
200
"outputPrice"
:
200
},
},
{
"name"
:
"gpt-4o-mini-realtime-preview"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"inputPrice"
:
10
,
"outputPrice"
:
20
},
{
{
"name"
:
"gpt-4o-realtime-preview"
,
"name"
:
"gpt-4o-realtime-preview"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
"logo"
:
"/assets/icon/openai_small.svg"
,
...
...
src/data/vision-model-pricing.json
View file @
3c5c57b9
...
@@ -14,12 +14,24 @@
...
@@ -14,12 +14,24 @@
"inputPrice"
:
1.5
,
"inputPrice"
:
1.5
,
"outputPrice"
:
4.5
"outputPrice"
:
4.5
},
},
{
"name"
:
"gemini 2.0 Flash-Lite"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"inputPrice"
:
0.53
,
"outputPrice"
:
2.1
},
{
{
"name"
:
"gemini 1.5 Flash"
,
"name"
:
"gemini 1.5 Flash"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"inputPrice"
:
0.53
,
"inputPrice"
:
0.53
,
"outputPrice"
:
2.1
"outputPrice"
:
2.1
},
},
{
"name"
:
"gemini 2.0 Falsh"
,
"logo"
:
"/assets/icon/google_small.svg"
,
"inputPrice"
:
0.7
,
"outputPrice"
:
2.8
},
{
{
"name"
:
"Doubao-1.5-vision-pro-32k"
,
"name"
:
"Doubao-1.5-vision-pro-32k"
,
"logo"
:
"/assets/icon/doubao.png"
,
"logo"
:
"/assets/icon/doubao.png"
,
...
...
src/styles/PricingChart.css
View file @
3c5c57b9
...
@@ -59,6 +59,11 @@
...
@@ -59,6 +59,11 @@
.chart-area
{
.chart-area
{
display
:
flex
;
display
:
flex
;
width
:
100%
;
overflow-x
:
auto
;
padding-bottom
:
10px
;
position
:
relative
;
scroll-behavior
:
smooth
;
}
}
.y-axis
{
.y-axis
{
...
@@ -77,12 +82,12 @@
...
@@ -77,12 +82,12 @@
.chart-container
{
.chart-container
{
display
:
flex
;
display
:
flex
;
align-items
:
flex-end
;
align-items
:
flex-end
;
gap
:
0.5rem
;
/* 减小柱状图之间的间距 */
gap
:
1rem
;
height
:
3
0
0px
;
height
:
3
8
0px
;
position
:
relative
;
position
:
relative
;
padding-bottom
:
8
0px
;
padding-bottom
:
16
0px
;
width
:
100%
;
/* 确保容器占满可用宽度 */
min-width
:
min-content
;
overflow
:
visible
;
/* 移除水平滚动 */
flex-grow
:
1
;
}
}
.grid-lines
{
.grid-lines
{
...
@@ -103,26 +108,25 @@
...
@@ -103,26 +108,25 @@
.chart-column
{
.chart-column
{
position
:
relative
;
position
:
relative
;
flex
:
1
;
/* 使柱状图列可伸缩 */
min-width
:
60px
;
/* max-width: 60px; */
/* 移除这行 */
max-width
:
80px
;
z-index
:
2
;
z-index
:
2
;
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
align-items
:
center
;
align-items
:
center
;
}
}
/* 让.bar根据可用空间调整宽度 */
.bars-container
{
.bars-container
{
display
:
flex
;
display
:
flex
;
gap
:
2px
;
gap
:
2px
;
align-items
:
flex-end
;
align-items
:
flex-end
;
height
:
100%
;
height
:
100%
;
justify-content
:
center
;
justify-content
:
center
;
flex-grow
:
1
;
/* 新增 */
flex-grow
:
1
;
}
}
.bar
{
.bar
{
width
:
15px
;
/* 减小柱状图的宽度 */
width
:
15px
;
position
:
relative
;
position
:
relative
;
border-radius
:
4px
4px
0
0
;
border-radius
:
4px
4px
0
0
;
transition
:
all
0.3s
cubic-bezier
(
0.4
,
0
,
0.2
,
1
);
transition
:
all
0.3s
cubic-bezier
(
0.4
,
0
,
0.2
,
1
);
...
@@ -144,7 +148,7 @@
...
@@ -144,7 +148,7 @@
top
:
-20px
;
top
:
-20px
;
left
:
50%
;
left
:
50%
;
transform
:
translateX
(
-50%
);
transform
:
translateX
(
-50%
);
font-size
:
0.65rem
;
/* 减小价格标签的字体大小 */
font-size
:
0.65rem
;
white-space
:
nowrap
;
white-space
:
nowrap
;
}
}
...
@@ -159,7 +163,7 @@
...
@@ -159,7 +163,7 @@
}
}
.provider-logo
{
.provider-logo
{
width
:
20px
;
/* 缩小Logo尺寸 */
width
:
20px
;
height
:
20px
;
height
:
20px
;
object-fit
:
contain
;
object-fit
:
contain
;
filter
:
drop-shadow
(
0
2px
4px
rgba
(
0
,
0
,
0
,
0.1
));
filter
:
drop-shadow
(
0
2px
4px
rgba
(
0
,
0
,
0
,
0.1
));
...
@@ -171,19 +175,17 @@
...
@@ -171,19 +175,17 @@
}
}
.provider-name
{
.provider-name
{
font-size
:
1
3
px
;
font-size
:
1
2
px
;
transform
:
translate
(
-18px
,
36px
)
rotate
(
-77deg
);
transform
:
translate
(
-18px
,
36px
)
rotate
(
-77deg
);
transform-origin
:
top
center
;
transform-origin
:
top
center
;
/* white-space: nowrap; */
line-height
:
14px
;
/* margin-top: 35px; */
width
:
90px
;
line-height
:
16px
;
padding-top
:
-11px
;
width
:
100px
;
text-align
:
right
;
text-align
:
right
;
height
:
40px
;
height
:
40px
;
word-break
:
break-all
;
word-break
:
break-all
;
white-space
:
break-spaces
;
white-space
:
break-spaces
;
padding-right
:
18px
;
padding-right
:
18px
;
color
:
var
(
--text-secondary
);
}
}
.pricing-charts-container
{
.pricing-charts-container
{
...
@@ -201,3 +203,80 @@
...
@@ -201,3 +203,80 @@
margin-bottom
:
2rem
;
margin-bottom
:
2rem
;
display
:
inline-block
;
display
:
inline-block
;
}
}
.chart-area
::after
{
content
:
''
;
position
:
sticky
;
top
:
0
;
right
:
0
;
height
:
calc
(
100%
-
10px
);
width
:
60px
;
background
:
linear-gradient
(
to
right
,
transparent
,
rgba
(
0
,
0
,
0
,
0.2
));
pointer-events
:
none
;
opacity
:
0
;
transition
:
opacity
0.3s
ease
;
z-index
:
5
;
}
.chart-area.has-scroll
::after
{
opacity
:
1
;
}
@keyframes
scrollHint
{
0
%
{
transform
:
translateX
(
0
);
}
50
%
{
transform
:
translateX
(
10px
);
}
100
%
{
transform
:
translateX
(
0
);
}
}
.scroll-hint-container
{
position
:
absolute
;
right
:
20px
;
top
:
50%
;
transform
:
translateY
(
-50%
);
z-index
:
6
;
pointer-events
:
auto
;
cursor
:
pointer
;
}
.scroll-hint
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
8px
;
border-radius
:
50%
;
background
:
rgba
(
255
,
255
,
255
,
0.15
);
animation
:
scrollHint
1.5s
infinite
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.2
);
transition
:
transform
0.3s
ease
,
background-color
0.3s
ease
;
}
.scroll-hint
:hover
{
background
:
rgba
(
255
,
255
,
255
,
0.25
);
transform
:
scale
(
1.1
);
}
.chart-area
::-webkit-scrollbar
{
height
:
8px
;
z-index
:
10
;
}
.chart-area
::-webkit-scrollbar-track
{
background
:
rgba
(
255
,
255
,
255
,
0.05
);
border-radius
:
4px
;
}
.chart-area
::-webkit-scrollbar-thumb
{
background
:
rgba
(
255
,
255
,
255
,
0.3
);
border-radius
:
4px
;
border
:
2px
solid
rgba
(
255
,
255
,
255
,
0.05
);
}
.chart-area
::-webkit-scrollbar-thumb:hover
{
background
:
rgba
(
255
,
255
,
255
,
0.4
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment