Browse Source

🚀 feat(frontend): add robust boolean handling across settings pages

Summary
-------
1. Introduced a reusable `toBoolean` utility (`web/src/helpers/boolean.js`) that converts
   strings (`'true'/'false'`, `'1'/'0'`), numbers, and native booleans to a proper boolean.
2. Re-exported `toBoolean` via `web/src/helpers/index.js` for simple one-line imports.

Refactors
---------
• Systematically replaced all legacy `item.value === 'true'` checks with `toBoolean(item.value)` in
  the following components:
  – `SystemSetting.js`
  – `OperationSetting.js`
  – `PaymentSetting.js`
  – `RatioSetting.js`
  – `RateLimitSetting.js`
  – `ModelSetting.js`
  – `DrawingSetting.js`
  – `DashboardSetting.js`
  – `ChatsSetting.js`

• Unified import statements to
  `import { …, toBoolean } from '../../helpers';`
  removing redundant `../../helpers/boolean` paths.

Why
---
SQLite sometimes returns `1/0` or boolean literals instead of the string `'true'/'false'`, causing
checkbox states to reset on page reload. The new utility guarantees consistent boolean parsing,
fixing the issue across all environments (SQLite, MySQL, etc.) while improving code clarity.
t0ng7u 6 months ago
parent
commit
b2b018ab93

+ 2 - 2
web/src/components/settings/ChatsSetting.js

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import React, { useEffect, useState } from 'react';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import SettingsChats from '../../pages/Setting/Chat/SettingsChats.js';
 import SettingsChats from '../../pages/Setting/Chat/SettingsChats.js';
-import { API, showError } from '../../helpers';
+import { API, showError, toBoolean } from '../../helpers';
 
 
 const ChatsSetting = () => {
 const ChatsSetting = () => {
   let [inputs, setInputs] = useState({
   let [inputs, setInputs] = useState({
@@ -21,7 +21,7 @@ const ChatsSetting = () => {
           item.key.endsWith('Enabled') ||
           item.key.endsWith('Enabled') ||
           ['DefaultCollapseSidebar'].includes(item.key)
           ['DefaultCollapseSidebar'].includes(item.key)
         ) {
         ) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 2
web/src/components/settings/DashboardSetting.js

@@ -1,6 +1,6 @@
 import React, { useEffect, useState, useMemo } from 'react';
 import React, { useEffect, useState, useMemo } from 'react';
 import { Card, Spin, Button, Modal } from '@douyinfe/semi-ui';
 import { Card, Spin, Button, Modal } from '@douyinfe/semi-ui';
-import { API, showError, showSuccess } from '../../helpers';
+import { API, showError, showSuccess, toBoolean } from '../../helpers';
 import SettingsAPIInfo from '../../pages/Setting/Dashboard/SettingsAPIInfo.js';
 import SettingsAPIInfo from '../../pages/Setting/Dashboard/SettingsAPIInfo.js';
 import SettingsAnnouncements from '../../pages/Setting/Dashboard/SettingsAnnouncements.js';
 import SettingsAnnouncements from '../../pages/Setting/Dashboard/SettingsAnnouncements.js';
 import SettingsFAQ from '../../pages/Setting/Dashboard/SettingsFAQ.js';
 import SettingsFAQ from '../../pages/Setting/Dashboard/SettingsFAQ.js';
@@ -45,7 +45,7 @@ const DashboardSetting = () => {
         }
         }
         if (item.key.endsWith('Enabled') &&
         if (item.key.endsWith('Enabled') &&
           (item.key === 'DataExportEnabled')) {
           (item.key === 'DataExportEnabled')) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         }
         }
       });
       });
       setInputs(newInputs);
       setInputs(newInputs);

+ 2 - 2
web/src/components/settings/DrawingSetting.js

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import React, { useEffect, useState } from 'react';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import SettingsDrawing from '../../pages/Setting/Drawing/SettingsDrawing.js';
 import SettingsDrawing from '../../pages/Setting/Drawing/SettingsDrawing.js';
-import { API, showError } from '../../helpers';
+import { API, showError, toBoolean } from '../../helpers';
 
 
 const DrawingSetting = () => {
 const DrawingSetting = () => {
   let [inputs, setInputs] = useState({
   let [inputs, setInputs] = useState({
@@ -23,7 +23,7 @@ const DrawingSetting = () => {
       let newInputs = {};
       let newInputs = {};
       data.forEach((item) => {
       data.forEach((item) => {
         if (item.key.endsWith('Enabled')) {
         if (item.key.endsWith('Enabled')) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 2
web/src/components/settings/ModelSetting.js

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import React, { useEffect, useState } from 'react';
 import { Card, Spin, Tabs } from '@douyinfe/semi-ui';
 import { Card, Spin, Tabs } from '@douyinfe/semi-ui';
 
 
-import { API, showError, showSuccess } from '../../helpers';
+import { API, showError, showSuccess, toBoolean } from '../../helpers';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import SettingGeminiModel from '../../pages/Setting/Model/SettingGeminiModel.js';
 import SettingGeminiModel from '../../pages/Setting/Model/SettingGeminiModel.js';
 import SettingClaudeModel from '../../pages/Setting/Model/SettingClaudeModel.js';
 import SettingClaudeModel from '../../pages/Setting/Model/SettingClaudeModel.js';
@@ -44,7 +44,7 @@ const ModelSetting = () => {
           }
           }
         }
         }
         if (item.key.endsWith('Enabled') || item.key.endsWith('enabled')) {
         if (item.key.endsWith('Enabled') || item.key.endsWith('enabled')) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 2
web/src/components/settings/OperationSetting.js

@@ -5,7 +5,7 @@ import SettingsSensitiveWords from '../../pages/Setting/Operation/SettingsSensit
 import SettingsLog from '../../pages/Setting/Operation/SettingsLog.js';
 import SettingsLog from '../../pages/Setting/Operation/SettingsLog.js';
 import SettingsMonitoring from '../../pages/Setting/Operation/SettingsMonitoring.js';
 import SettingsMonitoring from '../../pages/Setting/Operation/SettingsMonitoring.js';
 import SettingsCreditLimit from '../../pages/Setting/Operation/SettingsCreditLimit.js';
 import SettingsCreditLimit from '../../pages/Setting/Operation/SettingsCreditLimit.js';
-import { API, showError } from '../../helpers';
+import { API, showError, toBoolean } from '../../helpers';
 
 
 const OperationSetting = () => {
 const OperationSetting = () => {
   let [inputs, setInputs] = useState({
   let [inputs, setInputs] = useState({
@@ -54,7 +54,7 @@ const OperationSetting = () => {
           item.key.endsWith('Enabled') ||
           item.key.endsWith('Enabled') ||
           ['DefaultCollapseSidebar'].includes(item.key)
           ['DefaultCollapseSidebar'].includes(item.key)
         ) {
         ) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 2
web/src/components/settings/PaymentSetting.js

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment.js';
 import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment.js';
 import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js';
 import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js';
-import { API, showError } from '../../helpers';
+import { API, showError, toBoolean } from '../../helpers';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 const PaymentSetting = () => {
 const PaymentSetting = () => {
@@ -42,7 +42,7 @@ const PaymentSetting = () => {
             break;
             break;
           default:
           default:
             if (item.key.endsWith('Enabled')) {
             if (item.key.endsWith('Enabled')) {
-              newInputs[item.key] = item.value === 'true' ? true : false;
+              newInputs[item.key] = toBoolean(item.value);
             } else {
             } else {
               newInputs[item.key] = item.value;
               newInputs[item.key] = item.value;
             }
             }

+ 2 - 2
web/src/components/settings/RateLimitSetting.js

@@ -1,7 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import React, { useEffect, useState } from 'react';
 import { Card, Spin } from '@douyinfe/semi-ui';
 import { Card, Spin } from '@douyinfe/semi-ui';
 
 
-import { API, showError } from '../../helpers/index.js';
+import { API, showError, toBoolean } from '../../helpers/index.js';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import RequestRateLimit from '../../pages/Setting/RateLimit/SettingsRequestRateLimit.js';
 import RequestRateLimit from '../../pages/Setting/RateLimit/SettingsRequestRateLimit.js';
 
 
@@ -28,7 +28,7 @@ const RateLimitSetting = () => {
         }
         }
 
 
         if (item.key.endsWith('Enabled')) {
         if (item.key.endsWith('Enabled')) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 2
web/src/components/settings/RatioSetting.js

@@ -8,7 +8,7 @@ import ModelSettingsVisualEditor from '../../pages/Setting/Ratio/ModelSettingsVi
 import ModelRatioNotSetEditor from '../../pages/Setting/Ratio/ModelRationNotSetEditor.js';
 import ModelRatioNotSetEditor from '../../pages/Setting/Ratio/ModelRationNotSetEditor.js';
 import UpstreamRatioSync from '../../pages/Setting/Ratio/UpstreamRatioSync.js';
 import UpstreamRatioSync from '../../pages/Setting/Ratio/UpstreamRatioSync.js';
 
 
-import { API, showError } from '../../helpers';
+import { API, showError, toBoolean } from '../../helpers';
 
 
 const RatioSetting = () => {
 const RatioSetting = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();
@@ -51,7 +51,7 @@ const RatioSetting = () => {
           }
           }
         }
         }
         if (['DefaultUseAutoGroup', 'ExposeRatioEnabled'].includes(item.key)) {
         if (['DefaultUseAutoGroup', 'ExposeRatioEnabled'].includes(item.key)) {
-          newInputs[item.key] = item.value === 'true' ? true : false;
+          newInputs[item.key] = toBoolean(item.value);
         } else {
         } else {
           newInputs[item.key] = item.value;
           newInputs[item.key] = item.value;
         }
         }

+ 2 - 1
web/src/components/settings/SystemSetting.js

@@ -17,6 +17,7 @@ import {
   removeTrailingSlash,
   removeTrailingSlash,
   showError,
   showError,
   showSuccess,
   showSuccess,
+  toBoolean,
 } from '../../helpers';
 } from '../../helpers';
 import axios from 'axios';
 import axios from 'axios';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
@@ -106,7 +107,7 @@ const SystemSetting = () => {
           case 'LinuxDOOAuthEnabled':
           case 'LinuxDOOAuthEnabled':
           case 'oidc.enabled':
           case 'oidc.enabled':
           case 'WorkerAllowHttpImageRequestEnabled':
           case 'WorkerAllowHttpImageRequestEnabled':
-            item.value = item.value === 'true';
+            item.value = toBoolean(item.value);
             break;
             break;
           case 'Price':
           case 'Price':
           case 'MinTopUp':
           case 'MinTopUp':

+ 10 - 0
web/src/helpers/boolean.js

@@ -0,0 +1,10 @@
+export const toBoolean = (value) => {
+  // 兼容字符串、数字以及布尔原生类型
+  if (typeof value === 'boolean') return value;
+  if (typeof value === 'number') return value === 1;
+  if (typeof value === 'string') {
+    const v = value.toLowerCase();
+    return v === 'true' || v === '1';
+  }
+  return false;
+}; 

+ 1 - 0
web/src/helpers/index.js

@@ -6,3 +6,4 @@ export * from './render';
 export * from './log';
 export * from './log';
 export * from './data';
 export * from './data';
 export * from './token';
 export * from './token';
+export * from './boolean';