|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { Toast, Pagination } from '@douyinfe/semi-ui'; |
|
|
import { toastConstants } from '../constants'; |
|
|
import React from 'react'; |
|
|
import { toast } from 'react-toastify'; |
|
|
import { |
|
|
THINK_TAG_REGEX, |
|
|
MESSAGE_ROLES, |
|
|
} from '../constants/playground.constants'; |
|
|
import { TABLE_COMPACT_MODES_KEY } from '../constants'; |
|
|
import { MOBILE_BREAKPOINT } from '../hooks/common/useIsMobile'; |
|
|
|
|
|
const HTMLToastContent = ({ htmlContent }) => { |
|
|
return <div dangerouslySetInnerHTML={{ __html: htmlContent }} />; |
|
|
}; |
|
|
export default HTMLToastContent; |
|
|
export function isAdmin() { |
|
|
let user = localStorage.getItem('user'); |
|
|
if (!user) return false; |
|
|
user = JSON.parse(user); |
|
|
return user.role >= 10; |
|
|
} |
|
|
|
|
|
export function isRoot() { |
|
|
let user = localStorage.getItem('user'); |
|
|
if (!user) return false; |
|
|
user = JSON.parse(user); |
|
|
return user.role >= 100; |
|
|
} |
|
|
|
|
|
export function getSystemName() { |
|
|
let system_name = localStorage.getItem('system_name'); |
|
|
if (!system_name) return 'New API'; |
|
|
return system_name; |
|
|
} |
|
|
|
|
|
export function getLogo() { |
|
|
let logo = localStorage.getItem('logo'); |
|
|
if (!logo) return '/logo.png'; |
|
|
return logo; |
|
|
} |
|
|
|
|
|
export function getUserIdFromLocalStorage() { |
|
|
let user = localStorage.getItem('user'); |
|
|
if (!user) return -1; |
|
|
user = JSON.parse(user); |
|
|
return user.id; |
|
|
} |
|
|
|
|
|
export function getFooterHTML() { |
|
|
return localStorage.getItem('footer_html'); |
|
|
} |
|
|
|
|
|
export async function copy(text) { |
|
|
let okay = true; |
|
|
try { |
|
|
await navigator.clipboard.writeText(text); |
|
|
} catch (e) { |
|
|
try { |
|
|
|
|
|
const textarea = window.document.createElement('textarea'); |
|
|
textarea.value = text; |
|
|
textarea.setAttribute('readonly', ''); |
|
|
textarea.style.position = 'fixed'; |
|
|
textarea.style.left = '-9999px'; |
|
|
textarea.style.top = '-9999px'; |
|
|
window.document.body.appendChild(textarea); |
|
|
textarea.select(); |
|
|
window.document.execCommand('copy'); |
|
|
window.document.body.removeChild(textarea); |
|
|
} catch (e) { |
|
|
okay = false; |
|
|
console.error(e); |
|
|
} |
|
|
} |
|
|
return okay; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let showErrorOptions = { autoClose: toastConstants.ERROR_TIMEOUT }; |
|
|
let showWarningOptions = { autoClose: toastConstants.WARNING_TIMEOUT }; |
|
|
let showSuccessOptions = { autoClose: toastConstants.SUCCESS_TIMEOUT }; |
|
|
let showInfoOptions = { autoClose: toastConstants.INFO_TIMEOUT }; |
|
|
let showNoticeOptions = { autoClose: false }; |
|
|
|
|
|
const isMobileScreen = window.matchMedia( |
|
|
`(max-width: ${MOBILE_BREAKPOINT - 1}px)`, |
|
|
).matches; |
|
|
if (isMobileScreen) { |
|
|
showErrorOptions.position = 'top-center'; |
|
|
|
|
|
|
|
|
showSuccessOptions.position = 'top-center'; |
|
|
|
|
|
|
|
|
showInfoOptions.position = 'top-center'; |
|
|
|
|
|
|
|
|
showNoticeOptions.position = 'top-center'; |
|
|
|
|
|
} |
|
|
|
|
|
export function showError(error) { |
|
|
console.error(error); |
|
|
if (error.message) { |
|
|
if (error.name === 'AxiosError') { |
|
|
switch (error.response.status) { |
|
|
case 401: |
|
|
|
|
|
localStorage.removeItem('user'); |
|
|
|
|
|
window.location.href = '/login?expired=true'; |
|
|
break; |
|
|
case 429: |
|
|
Toast.error('错误:请求次数过多,请稍后再试!'); |
|
|
break; |
|
|
case 500: |
|
|
Toast.error('错误:服务器内部错误,请联系管理员!'); |
|
|
break; |
|
|
case 405: |
|
|
Toast.info('本站仅作演示之用,无服务端!'); |
|
|
break; |
|
|
default: |
|
|
Toast.error('错误:' + error.message); |
|
|
} |
|
|
return; |
|
|
} |
|
|
Toast.error('错误:' + error.message); |
|
|
} else { |
|
|
Toast.error('错误:' + error); |
|
|
} |
|
|
} |
|
|
|
|
|
export function showWarning(message) { |
|
|
Toast.warning(message); |
|
|
} |
|
|
|
|
|
export function showSuccess(message) { |
|
|
Toast.success(message); |
|
|
} |
|
|
|
|
|
export function showInfo(message) { |
|
|
Toast.info(message); |
|
|
} |
|
|
|
|
|
export function showNotice(message, isHTML = false) { |
|
|
if (isHTML) { |
|
|
toast(<HTMLToastContent htmlContent={message} />, showNoticeOptions); |
|
|
} else { |
|
|
Toast.info(message); |
|
|
} |
|
|
} |
|
|
|
|
|
export function openPage(url) { |
|
|
window.open(url); |
|
|
} |
|
|
|
|
|
export function removeTrailingSlash(url) { |
|
|
if (!url) return ''; |
|
|
if (url.endsWith('/')) { |
|
|
return url.slice(0, -1); |
|
|
} else { |
|
|
return url; |
|
|
} |
|
|
} |
|
|
|
|
|
export function getTodayStartTimestamp() { |
|
|
var now = new Date(); |
|
|
now.setHours(0, 0, 0, 0); |
|
|
return Math.floor(now.getTime() / 1000); |
|
|
} |
|
|
|
|
|
export function timestamp2string(timestamp) { |
|
|
let date = new Date(timestamp * 1000); |
|
|
let year = date.getFullYear().toString(); |
|
|
let month = (date.getMonth() + 1).toString(); |
|
|
let day = date.getDate().toString(); |
|
|
let hour = date.getHours().toString(); |
|
|
let minute = date.getMinutes().toString(); |
|
|
let second = date.getSeconds().toString(); |
|
|
if (month.length === 1) { |
|
|
month = '0' + month; |
|
|
} |
|
|
if (day.length === 1) { |
|
|
day = '0' + day; |
|
|
} |
|
|
if (hour.length === 1) { |
|
|
hour = '0' + hour; |
|
|
} |
|
|
if (minute.length === 1) { |
|
|
minute = '0' + minute; |
|
|
} |
|
|
if (second.length === 1) { |
|
|
second = '0' + second; |
|
|
} |
|
|
return ( |
|
|
year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second |
|
|
); |
|
|
} |
|
|
|
|
|
export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour') { |
|
|
let date = new Date(timestamp * 1000); |
|
|
|
|
|
let month = (date.getMonth() + 1).toString(); |
|
|
let day = date.getDate().toString(); |
|
|
let hour = date.getHours().toString(); |
|
|
if (day === '24') { |
|
|
console.log('timestamp', timestamp); |
|
|
} |
|
|
if (month.length === 1) { |
|
|
month = '0' + month; |
|
|
} |
|
|
if (day.length === 1) { |
|
|
day = '0' + day; |
|
|
} |
|
|
if (hour.length === 1) { |
|
|
hour = '0' + hour; |
|
|
} |
|
|
let str = month + '-' + day; |
|
|
if (dataExportDefaultTime === 'hour') { |
|
|
str += ' ' + hour + ':00'; |
|
|
} else if (dataExportDefaultTime === 'week') { |
|
|
let nextWeek = new Date(timestamp * 1000 + 6 * 24 * 60 * 60 * 1000); |
|
|
let nextMonth = (nextWeek.getMonth() + 1).toString(); |
|
|
let nextDay = nextWeek.getDate().toString(); |
|
|
if (nextMonth.length === 1) { |
|
|
nextMonth = '0' + nextMonth; |
|
|
} |
|
|
if (nextDay.length === 1) { |
|
|
nextDay = '0' + nextDay; |
|
|
} |
|
|
str += ' - ' + nextMonth + '-' + nextDay; |
|
|
} |
|
|
return str; |
|
|
} |
|
|
|
|
|
export function downloadTextAsFile(text, filename) { |
|
|
let blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); |
|
|
let url = URL.createObjectURL(blob); |
|
|
let a = document.createElement('a'); |
|
|
a.href = url; |
|
|
a.download = filename; |
|
|
a.click(); |
|
|
} |
|
|
|
|
|
export const verifyJSON = (str) => { |
|
|
try { |
|
|
JSON.parse(str); |
|
|
} catch (e) { |
|
|
return false; |
|
|
} |
|
|
return true; |
|
|
}; |
|
|
|
|
|
export function verifyJSONPromise(value) { |
|
|
try { |
|
|
JSON.parse(value); |
|
|
return Promise.resolve(); |
|
|
} catch (e) { |
|
|
return Promise.reject('不是合法的 JSON 字符串'); |
|
|
} |
|
|
} |
|
|
|
|
|
export function shouldShowPrompt(id) { |
|
|
let prompt = localStorage.getItem(`prompt-${id}`); |
|
|
return !prompt; |
|
|
} |
|
|
|
|
|
export function setPromptShown(id) { |
|
|
localStorage.setItem(`prompt-${id}`, 'true'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function compareObjects(oldObject, newObject) { |
|
|
const changedProperties = []; |
|
|
|
|
|
|
|
|
for (const key in oldObject) { |
|
|
if (oldObject.hasOwnProperty(key) && newObject.hasOwnProperty(key)) { |
|
|
if (oldObject[key] !== newObject[key]) { |
|
|
changedProperties.push({ |
|
|
key: key, |
|
|
oldValue: oldObject[key], |
|
|
newValue: newObject[key], |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return changedProperties; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let messageId = 4; |
|
|
export const generateMessageId = () => `${messageId++}`; |
|
|
|
|
|
|
|
|
export const getTextContent = (message) => { |
|
|
if (!message || !message.content) return ''; |
|
|
|
|
|
if (Array.isArray(message.content)) { |
|
|
const textContent = message.content.find((item) => item.type === 'text'); |
|
|
return textContent?.text || ''; |
|
|
} |
|
|
return typeof message.content === 'string' ? message.content : ''; |
|
|
}; |
|
|
|
|
|
|
|
|
export const processThinkTags = (content, reasoningContent = '') => { |
|
|
if (!content || !content.includes('<think>')) { |
|
|
return { content, reasoningContent }; |
|
|
} |
|
|
|
|
|
const thoughts = []; |
|
|
const replyParts = []; |
|
|
let lastIndex = 0; |
|
|
let match; |
|
|
|
|
|
THINK_TAG_REGEX.lastIndex = 0; |
|
|
while ((match = THINK_TAG_REGEX.exec(content)) !== null) { |
|
|
replyParts.push(content.substring(lastIndex, match.index)); |
|
|
thoughts.push(match[1]); |
|
|
lastIndex = match.index + match[0].length; |
|
|
} |
|
|
replyParts.push(content.substring(lastIndex)); |
|
|
|
|
|
const processedContent = replyParts |
|
|
.join('') |
|
|
.replace(/<\/?think>/g, '') |
|
|
.trim(); |
|
|
const thoughtsStr = thoughts.join('\n\n---\n\n'); |
|
|
const processedReasoningContent = |
|
|
reasoningContent && thoughtsStr |
|
|
? `${reasoningContent}\n\n---\n\n${thoughtsStr}` |
|
|
: reasoningContent || thoughtsStr; |
|
|
|
|
|
return { |
|
|
content: processedContent, |
|
|
reasoningContent: processedReasoningContent, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
export const processIncompleteThinkTags = (content, reasoningContent = '') => { |
|
|
if (!content) return { content: '', reasoningContent }; |
|
|
|
|
|
const lastOpenThinkIndex = content.lastIndexOf('<think>'); |
|
|
if (lastOpenThinkIndex === -1) { |
|
|
return processThinkTags(content, reasoningContent); |
|
|
} |
|
|
|
|
|
const fragmentAfterLastOpen = content.substring(lastOpenThinkIndex); |
|
|
if (!fragmentAfterLastOpen.includes('</think>')) { |
|
|
const unclosedThought = fragmentAfterLastOpen |
|
|
.substring('<think>'.length) |
|
|
.trim(); |
|
|
const cleanContent = content.substring(0, lastOpenThinkIndex); |
|
|
const processedReasoningContent = unclosedThought |
|
|
? reasoningContent |
|
|
? `${reasoningContent}\n\n---\n\n${unclosedThought}` |
|
|
: unclosedThought |
|
|
: reasoningContent; |
|
|
|
|
|
return processThinkTags(cleanContent, processedReasoningContent); |
|
|
} |
|
|
|
|
|
return processThinkTags(content, reasoningContent); |
|
|
}; |
|
|
|
|
|
|
|
|
export const buildMessageContent = ( |
|
|
textContent, |
|
|
imageUrls = [], |
|
|
imageEnabled = false, |
|
|
) => { |
|
|
if (!textContent && (!imageUrls || imageUrls.length === 0)) { |
|
|
return ''; |
|
|
} |
|
|
|
|
|
const validImageUrls = imageUrls.filter((url) => url && url.trim() !== ''); |
|
|
|
|
|
if (imageEnabled && validImageUrls.length > 0) { |
|
|
return [ |
|
|
{ type: 'text', text: textContent || '' }, |
|
|
...validImageUrls.map((url) => ({ |
|
|
type: 'image_url', |
|
|
image_url: { url: url.trim() }, |
|
|
})), |
|
|
]; |
|
|
} |
|
|
|
|
|
return textContent || ''; |
|
|
}; |
|
|
|
|
|
|
|
|
export const createMessage = (role, content, options = {}) => ({ |
|
|
role, |
|
|
content, |
|
|
createAt: Date.now(), |
|
|
id: generateMessageId(), |
|
|
...options, |
|
|
}); |
|
|
|
|
|
|
|
|
export const createLoadingAssistantMessage = () => |
|
|
createMessage(MESSAGE_ROLES.ASSISTANT, '', { |
|
|
reasoningContent: '', |
|
|
isReasoningExpanded: true, |
|
|
isThinkingComplete: false, |
|
|
hasAutoCollapsed: false, |
|
|
status: 'loading', |
|
|
}); |
|
|
|
|
|
|
|
|
export const hasImageContent = (message) => { |
|
|
return ( |
|
|
message && |
|
|
Array.isArray(message.content) && |
|
|
message.content.some((item) => item.type === 'image_url') |
|
|
); |
|
|
}; |
|
|
|
|
|
|
|
|
export const formatMessageForAPI = (message) => { |
|
|
if (!message) return null; |
|
|
|
|
|
return { |
|
|
role: message.role, |
|
|
content: message.content, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
export const isValidMessage = (message) => { |
|
|
return message && message.role && (message.content || message.content === ''); |
|
|
}; |
|
|
|
|
|
|
|
|
export const getLastUserMessage = (messages) => { |
|
|
if (!Array.isArray(messages)) return null; |
|
|
|
|
|
for (let i = messages.length - 1; i >= 0; i--) { |
|
|
if (messages[i].role === MESSAGE_ROLES.USER) { |
|
|
return messages[i]; |
|
|
} |
|
|
} |
|
|
return null; |
|
|
}; |
|
|
|
|
|
|
|
|
export const getLastAssistantMessage = (messages) => { |
|
|
if (!Array.isArray(messages)) return null; |
|
|
|
|
|
for (let i = messages.length - 1; i >= 0; i--) { |
|
|
if (messages[i].role === MESSAGE_ROLES.ASSISTANT) { |
|
|
return messages[i]; |
|
|
} |
|
|
} |
|
|
return null; |
|
|
}; |
|
|
|
|
|
|
|
|
export const getRelativeTime = (publishDate) => { |
|
|
if (!publishDate) return ''; |
|
|
|
|
|
const now = new Date(); |
|
|
const pubDate = new Date(publishDate); |
|
|
|
|
|
|
|
|
if (isNaN(pubDate.getTime())) return publishDate; |
|
|
|
|
|
const diffMs = now.getTime() - pubDate.getTime(); |
|
|
const diffSeconds = Math.floor(diffMs / 1000); |
|
|
const diffMinutes = Math.floor(diffSeconds / 60); |
|
|
const diffHours = Math.floor(diffMinutes / 60); |
|
|
const diffDays = Math.floor(diffHours / 24); |
|
|
const diffWeeks = Math.floor(diffDays / 7); |
|
|
const diffMonths = Math.floor(diffDays / 30); |
|
|
const diffYears = Math.floor(diffDays / 365); |
|
|
|
|
|
|
|
|
if (diffMs < 0) { |
|
|
return formatDateString(pubDate); |
|
|
} |
|
|
|
|
|
|
|
|
if (diffSeconds < 60) { |
|
|
return '刚刚'; |
|
|
} else if (diffMinutes < 60) { |
|
|
return `${diffMinutes} 分钟前`; |
|
|
} else if (diffHours < 24) { |
|
|
return `${diffHours} 小时前`; |
|
|
} else if (diffDays < 7) { |
|
|
return `${diffDays} 天前`; |
|
|
} else if (diffWeeks < 4) { |
|
|
return `${diffWeeks} 周前`; |
|
|
} else if (diffMonths < 12) { |
|
|
return `${diffMonths} 个月前`; |
|
|
} else if (diffYears < 2) { |
|
|
return '1 年前'; |
|
|
} else { |
|
|
|
|
|
return formatDateString(pubDate); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
export const formatDateString = (date) => { |
|
|
const year = date.getFullYear(); |
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|
|
const day = String(date.getDate()).padStart(2, '0'); |
|
|
return `${year}-${month}-${day}`; |
|
|
}; |
|
|
|
|
|
|
|
|
export const formatDateTimeString = (date) => { |
|
|
const year = date.getFullYear(); |
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|
|
const day = String(date.getDate()).padStart(2, '0'); |
|
|
const hours = String(date.getHours()).padStart(2, '0'); |
|
|
const minutes = String(date.getMinutes()).padStart(2, '0'); |
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`; |
|
|
}; |
|
|
|
|
|
function readTableCompactModes() { |
|
|
try { |
|
|
const json = localStorage.getItem(TABLE_COMPACT_MODES_KEY); |
|
|
return json ? JSON.parse(json) : {}; |
|
|
} catch { |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
|
|
|
function writeTableCompactModes(modes) { |
|
|
try { |
|
|
localStorage.setItem(TABLE_COMPACT_MODES_KEY, JSON.stringify(modes)); |
|
|
} catch { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
export function getTableCompactMode(tableKey = 'global') { |
|
|
const modes = readTableCompactModes(); |
|
|
return !!modes[tableKey]; |
|
|
} |
|
|
|
|
|
export function setTableCompactMode(compact, tableKey = 'global') { |
|
|
const modes = readTableCompactModes(); |
|
|
modes[tableKey] = compact; |
|
|
writeTableCompactModes(modes); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const selectFilter = (input, option) => { |
|
|
if (!input) return true; |
|
|
|
|
|
const keyword = input.trim().toLowerCase(); |
|
|
const valueText = (option?.value ?? '').toString().toLowerCase(); |
|
|
const labelText = (option?.label ?? '').toString().toLowerCase(); |
|
|
|
|
|
return valueText.includes(keyword) || labelText.includes(keyword); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export const calculateModelPrice = ({ |
|
|
record, |
|
|
selectedGroup, |
|
|
groupRatio, |
|
|
tokenUnit, |
|
|
displayPrice, |
|
|
currency, |
|
|
precision = 4, |
|
|
}) => { |
|
|
|
|
|
let usedGroup = selectedGroup; |
|
|
let usedGroupRatio = groupRatio[selectedGroup]; |
|
|
|
|
|
if (selectedGroup === 'all' || usedGroupRatio === undefined) { |
|
|
|
|
|
let minRatio = Number.POSITIVE_INFINITY; |
|
|
if ( |
|
|
Array.isArray(record.enable_groups) && |
|
|
record.enable_groups.length > 0 |
|
|
) { |
|
|
record.enable_groups.forEach((g) => { |
|
|
const r = groupRatio[g]; |
|
|
if (r !== undefined && r < minRatio) { |
|
|
minRatio = r; |
|
|
usedGroup = g; |
|
|
usedGroupRatio = r; |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (usedGroupRatio === undefined) { |
|
|
usedGroupRatio = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (record.quota_type === 0) { |
|
|
|
|
|
const inputRatioPriceUSD = record.model_ratio * 2 * usedGroupRatio; |
|
|
const completionRatioPriceUSD = |
|
|
record.model_ratio * record.completion_ratio * 2 * usedGroupRatio; |
|
|
|
|
|
const unitDivisor = tokenUnit === 'K' ? 1000 : 1; |
|
|
const unitLabel = tokenUnit === 'K' ? 'K' : 'M'; |
|
|
|
|
|
const rawDisplayInput = displayPrice(inputRatioPriceUSD); |
|
|
const rawDisplayCompletion = displayPrice(completionRatioPriceUSD); |
|
|
|
|
|
const numInput = |
|
|
parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor; |
|
|
const numCompletion = |
|
|
parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor; |
|
|
|
|
|
let symbol = '$'; |
|
|
if (currency === 'CNY') { |
|
|
symbol = '¥'; |
|
|
} else if (currency === 'CUSTOM') { |
|
|
try { |
|
|
const statusStr = localStorage.getItem('status'); |
|
|
if (statusStr) { |
|
|
const s = JSON.parse(statusStr); |
|
|
symbol = s?.custom_currency_symbol || '¤'; |
|
|
} else { |
|
|
symbol = '¤'; |
|
|
} |
|
|
} catch (e) { |
|
|
symbol = '¤'; |
|
|
} |
|
|
} |
|
|
return { |
|
|
inputPrice: `${symbol}${numInput.toFixed(precision)}`, |
|
|
completionPrice: `${symbol}${numCompletion.toFixed(precision)}`, |
|
|
unitLabel, |
|
|
isPerToken: true, |
|
|
usedGroup, |
|
|
usedGroupRatio, |
|
|
}; |
|
|
} |
|
|
|
|
|
if (record.quota_type === 1) { |
|
|
|
|
|
const priceUSD = parseFloat(record.model_price) * usedGroupRatio; |
|
|
const displayVal = displayPrice(priceUSD); |
|
|
|
|
|
return { |
|
|
price: displayVal, |
|
|
isPerToken: false, |
|
|
usedGroup, |
|
|
usedGroupRatio, |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
price: '-', |
|
|
isPerToken: false, |
|
|
usedGroup, |
|
|
usedGroupRatio, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
export const formatPriceInfo = (priceData, t) => { |
|
|
if (priceData.isPerToken) { |
|
|
return ( |
|
|
<> |
|
|
<span style={{ color: 'var(--semi-color-text-1)' }}> |
|
|
{t('输入')} {priceData.inputPrice}/{priceData.unitLabel} |
|
|
</span> |
|
|
<span style={{ color: 'var(--semi-color-text-1)' }}> |
|
|
{t('输出')} {priceData.completionPrice}/{priceData.unitLabel} |
|
|
</span> |
|
|
</> |
|
|
); |
|
|
} |
|
|
|
|
|
return ( |
|
|
<> |
|
|
<span style={{ color: 'var(--semi-color-text-1)' }}> |
|
|
{t('模型价格')} {priceData.price} |
|
|
</span> |
|
|
</> |
|
|
); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const createCardProPagination = ({ |
|
|
currentPage, |
|
|
pageSize, |
|
|
total, |
|
|
onPageChange, |
|
|
onPageSizeChange, |
|
|
isMobile = false, |
|
|
pageSizeOpts = [10, 20, 50, 100], |
|
|
showSizeChanger = true, |
|
|
t = (key) => key, |
|
|
}) => { |
|
|
if (!total || total <= 0) return null; |
|
|
|
|
|
const start = (currentPage - 1) * pageSize + 1; |
|
|
const end = Math.min(currentPage * pageSize, total); |
|
|
const totalText = `${t('显示第')} ${start} ${t('条 - 第')} ${end} ${t('条,共')} ${total} ${t('条')}`; |
|
|
|
|
|
return ( |
|
|
<> |
|
|
{/* 桌面端左侧总数信息 */} |
|
|
{!isMobile && ( |
|
|
<span |
|
|
className='text-sm select-none' |
|
|
style={{ color: 'var(--semi-color-text-2)' }} |
|
|
> |
|
|
{totalText} |
|
|
</span> |
|
|
)} |
|
|
|
|
|
{/* 右侧分页控件 */} |
|
|
<Pagination |
|
|
currentPage={currentPage} |
|
|
pageSize={pageSize} |
|
|
total={total} |
|
|
pageSizeOpts={pageSizeOpts} |
|
|
showSizeChanger={showSizeChanger} |
|
|
onPageSizeChange={onPageSizeChange} |
|
|
onPageChange={onPageChange} |
|
|
size={isMobile ? 'small' : 'default'} |
|
|
showQuickJumper={isMobile} |
|
|
showTotal |
|
|
/> |
|
|
</> |
|
|
); |
|
|
}; |
|
|
|
|
|
|
|
|
const DEFAULT_PRICING_FILTERS = { |
|
|
search: '', |
|
|
showWithRecharge: false, |
|
|
currency: 'USD', |
|
|
showRatio: false, |
|
|
viewMode: 'card', |
|
|
tokenUnit: 'M', |
|
|
filterGroup: 'all', |
|
|
filterQuotaType: 'all', |
|
|
filterEndpointType: 'all', |
|
|
filterVendor: 'all', |
|
|
filterTag: 'all', |
|
|
currentPage: 1, |
|
|
}; |
|
|
|
|
|
|
|
|
export const resetPricingFilters = ({ |
|
|
handleChange, |
|
|
setShowWithRecharge, |
|
|
setCurrency, |
|
|
setShowRatio, |
|
|
setViewMode, |
|
|
setFilterGroup, |
|
|
setFilterQuotaType, |
|
|
setFilterEndpointType, |
|
|
setFilterVendor, |
|
|
setFilterTag, |
|
|
setCurrentPage, |
|
|
setTokenUnit, |
|
|
}) => { |
|
|
handleChange?.(DEFAULT_PRICING_FILTERS.search); |
|
|
setShowWithRecharge?.(DEFAULT_PRICING_FILTERS.showWithRecharge); |
|
|
setCurrency?.(DEFAULT_PRICING_FILTERS.currency); |
|
|
setShowRatio?.(DEFAULT_PRICING_FILTERS.showRatio); |
|
|
setViewMode?.(DEFAULT_PRICING_FILTERS.viewMode); |
|
|
setTokenUnit?.(DEFAULT_PRICING_FILTERS.tokenUnit); |
|
|
setFilterGroup?.(DEFAULT_PRICING_FILTERS.filterGroup); |
|
|
setFilterQuotaType?.(DEFAULT_PRICING_FILTERS.filterQuotaType); |
|
|
setFilterEndpointType?.(DEFAULT_PRICING_FILTERS.filterEndpointType); |
|
|
setFilterVendor?.(DEFAULT_PRICING_FILTERS.filterVendor); |
|
|
setFilterTag?.(DEFAULT_PRICING_FILTERS.filterTag); |
|
|
setCurrentPage?.(DEFAULT_PRICING_FILTERS.currentPage); |
|
|
}; |
|
|
|