feat: add billing display mode selection and update pricing rendering

Introduce a billing display mode feature allowing users to toggle between price and ratio views. Update relevant components and hooks to support this new functionality, ensuring consistent pricing information is displayed across the application.
This commit is contained in:
CaIon
2026-03-06 23:35:17 +08:00
parent d796578880
commit f9b5ecc955
12 changed files with 1014 additions and 47 deletions

View File

@@ -78,6 +78,9 @@ export const useLogsData = () => {
const STORAGE_KEY = isAdminUser
? 'logs-table-columns-admin'
: 'logs-table-columns-user';
const BILLING_DISPLAY_MODE_STORAGE_KEY = isAdminUser
? 'logs-billing-display-mode-admin'
: 'logs-billing-display-mode-user';
// Statistics state
const [stat, setStat] = useState({
@@ -102,50 +105,6 @@ export const useLogsData = () => {
logType: '0',
};
// Column visibility state
const [visibleColumns, setVisibleColumns] = useState({});
const [showColumnSelector, setShowColumnSelector] = useState(false);
// Compact mode
const [compactMode, setCompactMode] = useTableCompactMode('logs');
// User info modal state
const [showUserInfo, setShowUserInfoModal] = useState(false);
const [userInfoData, setUserInfoData] = useState(null);
// Channel affinity usage cache stats modal state (admin only)
const [
showChannelAffinityUsageCacheModal,
setShowChannelAffinityUsageCacheModal,
] = useState(false);
const [channelAffinityUsageCacheTarget, setChannelAffinityUsageCacheTarget] =
useState(null);
// Load saved column preferences from localStorage
useEffect(() => {
const savedColumns = localStorage.getItem(STORAGE_KEY);
if (savedColumns) {
try {
const parsed = JSON.parse(savedColumns);
const defaults = getDefaultColumnVisibility();
const merged = { ...defaults, ...parsed };
// For non-admin users, force-hide admin-only columns (does not touch admin settings)
if (!isAdminUser) {
merged[COLUMN_KEYS.CHANNEL] = false;
merged[COLUMN_KEYS.USERNAME] = false;
merged[COLUMN_KEYS.RETRY] = false;
}
setVisibleColumns(merged);
} catch (e) {
console.error('Failed to parse saved column preferences', e);
initDefaultColumns();
}
} else {
initDefaultColumns();
}
}, []);
// Get default column visibility based on user role
const getDefaultColumnVisibility = () => {
return {
@@ -166,6 +125,58 @@ export const useLogsData = () => {
};
};
const getInitialVisibleColumns = () => {
const defaults = getDefaultColumnVisibility();
const savedColumns = localStorage.getItem(STORAGE_KEY);
if (!savedColumns) {
return defaults;
}
try {
const parsed = JSON.parse(savedColumns);
const merged = { ...defaults, ...parsed };
if (!isAdminUser) {
merged[COLUMN_KEYS.CHANNEL] = false;
merged[COLUMN_KEYS.USERNAME] = false;
merged[COLUMN_KEYS.RETRY] = false;
}
return merged;
} catch (e) {
console.error('Failed to parse saved column preferences', e);
return defaults;
}
};
const getInitialBillingDisplayMode = () => {
const savedMode = localStorage.getItem(BILLING_DISPLAY_MODE_STORAGE_KEY);
return savedMode === 'price' || savedMode === 'ratio' ? savedMode : 'price';
};
// Column visibility state
const [visibleColumns, setVisibleColumns] = useState(getInitialVisibleColumns);
const [showColumnSelector, setShowColumnSelector] = useState(false);
const [billingDisplayMode, setBillingDisplayMode] = useState(
getInitialBillingDisplayMode,
);
// Compact mode
const [compactMode, setCompactMode] = useTableCompactMode('logs');
// User info modal state
const [showUserInfo, setShowUserInfoModal] = useState(false);
const [userInfoData, setUserInfoData] = useState(null);
// Channel affinity usage cache stats modal state (admin only)
const [
showChannelAffinityUsageCacheModal,
setShowChannelAffinityUsageCacheModal,
] = useState(false);
const [channelAffinityUsageCacheTarget, setChannelAffinityUsageCacheTarget] =
useState(null);
// Initialize default column visibility
const initDefaultColumns = () => {
const defaults = getDefaultColumnVisibility();
@@ -207,6 +218,10 @@ export const useLogsData = () => {
}
}, [visibleColumns]);
useEffect(() => {
localStorage.setItem(BILLING_DISPLAY_MODE_STORAGE_KEY, billingDisplayMode);
}, [BILLING_DISPLAY_MODE_STORAGE_KEY, billingDisplayMode]);
// 获取表单值的辅助函数,确保所有值都是字符串
const getFormValues = () => {
const formValues = formApi ? formApi.getValues() : {};
@@ -406,6 +421,7 @@ export const useLogsData = () => {
other.cache_creation_ratio_1h ||
other.cache_creation_ratio ||
1.0,
billingDisplayMode,
)
: renderLogContent(
other?.model_ratio,
@@ -420,6 +436,7 @@ export const useLogsData = () => {
other.web_search_call_count || 0,
other.file_search || false,
other.file_search_call_count || 0,
billingDisplayMode,
),
});
if (logs[i]?.content) {
@@ -473,6 +490,7 @@ export const useLogsData = () => {
other?.user_group_ratio,
other?.cache_tokens || 0,
other?.cache_ratio || 1.0,
billingDisplayMode,
);
} else if (other?.claude) {
content = renderClaudeModelPrice(
@@ -495,6 +513,7 @@ export const useLogsData = () => {
other.cache_creation_ratio_1h ||
other.cache_creation_ratio ||
1.0,
billingDisplayMode,
);
} else {
content = renderModelPrice(
@@ -521,6 +540,7 @@ export const useLogsData = () => {
other?.audio_input_price || 0,
other?.image_generation_call || false,
other?.image_generation_call_price || 0,
billingDisplayMode,
);
}
expandDataLocal.push({
@@ -764,6 +784,8 @@ export const useLogsData = () => {
visibleColumns,
showColumnSelector,
setShowColumnSelector,
billingDisplayMode,
setBillingDisplayMode,
handleColumnVisibilityChange,
handleSelectAll,
initDefaultColumns,