Browse Source

✨ feat: enhance SelectableButtonGroup with checkbox support and refactor pricing display settings (#1365)

- Add withCheckbox prop to SelectableButtonGroup component for checkbox-prefixed buttons
- Support both single value and array activeValue for multi-selection scenarios
- Refactor PricingDisplaySettings to use consistent SelectableButtonGroup styling
- Replace Switch components with checkbox-enabled SelectableButtonGroup
- Replace Select dropdown with SelectableButtonGroup for currency selection
- Maintain unified UI/UX across all pricing filter components
- Add proper JSDoc documentation for new withCheckbox functionality

This improves visual consistency and provides a more cohesive user experience
in the model pricing filter interface.
t0ng7u 7 months ago
parent
commit
6d06cb8fb3

+ 51 - 4
web/src/components/common/ui/SelectableButtonGroup.jsx

@@ -19,7 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
 
 
 import React, { useState, useRef } from 'react';
 import React, { useState, useRef } from 'react';
 import { useIsMobile } from '../../../hooks/common/useIsMobile';
 import { useIsMobile } from '../../../hooks/common/useIsMobile';
-import { Divider, Button, Tag, Row, Col, Collapsible } from '@douyinfe/semi-ui';
+import { Divider, Button, Tag, Row, Col, Collapsible, Checkbox } from '@douyinfe/semi-ui';
 import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
 import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
 
 
 /**
 /**
@@ -27,12 +27,13 @@ import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
  *
  *
  * @param {string} title 标题
  * @param {string} title 标题
  * @param {Array<{value:any,label:string,icon?:React.ReactNode,tagCount?:number}>} items 按钮项
  * @param {Array<{value:any,label:string,icon?:React.ReactNode,tagCount?:number}>} items 按钮项
- * @param {*} activeValue 当前激活的值
+ * @param {*|Array} activeValue 当前激活的值,可以是单个值或数组(多选)
  * @param {(value:any)=>void} onChange 选择改变回调
  * @param {(value:any)=>void} onChange 选择改变回调
  * @param {function} t i18n
  * @param {function} t i18n
  * @param {object} style 额外样式
  * @param {object} style 额外样式
  * @param {boolean} collapsible 是否支持折叠,默认true
  * @param {boolean} collapsible 是否支持折叠,默认true
  * @param {number} collapseHeight 折叠时的高度,默认200
  * @param {number} collapseHeight 折叠时的高度,默认200
+ * @param {boolean} withCheckbox 是否启用前缀 Checkbox 来控制激活状态
  */
  */
 const SelectableButtonGroup = ({
 const SelectableButtonGroup = ({
   title,
   title,
@@ -42,7 +43,8 @@ const SelectableButtonGroup = ({
   t = (v) => v,
   t = (v) => v,
   style = {},
   style = {},
   collapsible = true,
   collapsible = true,
-  collapseHeight = 200
+  collapseHeight = 200,
+  withCheckbox = false
 }) => {
 }) => {
   const [isOpen, setIsOpen] = useState(false);
   const [isOpen, setIsOpen] = useState(false);
   const isMobile = useIsMobile();
   const isMobile = useIsMobile();
@@ -82,7 +84,52 @@ const SelectableButtonGroup = ({
   const contentElement = (
   const contentElement = (
     <Row gutter={[8, 8]} style={{ lineHeight: '32px', ...style }} ref={contentRef}>
     <Row gutter={[8, 8]} style={{ lineHeight: '32px', ...style }} ref={contentRef}>
       {items.map((item) => {
       {items.map((item) => {
-        const isActive = activeValue === item.value;
+        const isActive = Array.isArray(activeValue)
+          ? activeValue.includes(item.value)
+          : activeValue === item.value;
+
+        // 当启用前缀 Checkbox 时,按钮本身不可点击,仅 Checkbox 可控制状态切换
+        if (withCheckbox) {
+          return (
+            <Col
+              {...(isMobile
+                ? { span: 12 }
+                : { xs: 24, sm: 24, md: 24, lg: 12, xl: 8 }
+              )}
+              key={item.value}
+            >
+              <Button
+                onClick={() => { /* disabled */ }}
+                theme={isActive ? 'light' : 'outline'}
+                type={isActive ? 'primary' : 'tertiary'}
+                icon={
+                  <Checkbox
+                    checked={isActive}
+                    onChange={() => onChange(item.value)}
+                    style={{ pointerEvents: 'auto' }}
+                  />
+                }
+                style={{ width: '100%', cursor: 'default' }}
+              >
+                {item.icon && (
+                  <span style={{ marginRight: 4 }}>{item.icon}</span>
+                )}
+                <span style={{ marginRight: item.tagCount !== undefined ? 4 : 0 }}>{item.label}</span>
+                {item.tagCount !== undefined && (
+                  <Tag
+                    color='white'
+                    shape="circle"
+                    size="small"
+                  >
+                    {item.tagCount}
+                  </Tag>
+                )}
+              </Button>
+            </Col>
+          );
+        }
+
+        // 默认行为
         return (
         return (
           <Col
           <Col
             {...(isMobile
             {...(isMobile

+ 64 - 45
web/src/components/table/model-pricing/filter/PricingDisplaySettings.jsx

@@ -18,7 +18,8 @@ For commercial licensing, please contact support@quantumnous.com
 */
 */
 
 
 import React from 'react';
 import React from 'react';
-import { Divider, Switch, Select, Tooltip } from '@douyinfe/semi-ui';
+import { Tooltip } from '@douyinfe/semi-ui';
+import SelectableButtonGroup from '../../../common/ui/SelectableButtonGroup';
 import { IconHelpCircle } from '@douyinfe/semi-icons';
 import { IconHelpCircle } from '@douyinfe/semi-icons';
 
 
 const PricingDisplaySettings = ({
 const PricingDisplaySettings = ({
@@ -30,51 +31,69 @@ const PricingDisplaySettings = ({
   setShowRatio,
   setShowRatio,
   t
   t
 }) => {
 }) => {
-  return (
-    <div className="mb-6">
-      <Divider margin='12px' align='left'>
-        {t('显示设置')}
-      </Divider>
-      <div className="px-2">
-        <div className="flex items-center justify-between mb-3">
-          <span className="text-sm text-gray-700">{t('以充值价格显示')}</span>
-          <Switch
-            checked={showWithRecharge}
-            onChange={setShowWithRecharge}
-            size="small"
-          />
-        </div>
-        {showWithRecharge && (
-          <div className="mt-2 mb-3">
-            <div className="text-xs text-gray-500 mb-1">{t('货币单位')}</div>
-            <Select
-              value={currency}
-              onChange={setCurrency}
+  const items = [
+    {
+      value: 'recharge',
+      label: t('以充值价格显示')
+    },
+    {
+      value: 'ratio',
+      label: (
+        <span className="flex items-center gap-1">
+          {t('显示倍率')}
+          <Tooltip content={t('倍率是用于系统计算不同模型的最终价格用的,如果您不理解倍率,请忽略')}>
+            <IconHelpCircle
               size="small"
               size="small"
-              className="w-full"
-            >
-              <Select.Option value="USD">USD ($)</Select.Option>
-              <Select.Option value="CNY">CNY (¥)</Select.Option>
-            </Select>
-          </div>
-        )}
-        <div className="flex items-center justify-between">
-          <div className="flex items-center gap-1">
-            <span className="text-sm text-gray-700">{t('显示倍率')}</span>
-            <Tooltip content={t('倍率是用于系统计算不同模型的最终价格用的,如果您不理解倍率,请忽略')}>
-              <IconHelpCircle
-                size="small"
-                style={{ color: 'var(--semi-color-text-2)', cursor: 'help' }}
-              />
-            </Tooltip>
-          </div>
-          <Switch
-            checked={showRatio}
-            onChange={setShowRatio}
-            size="small"
-          />
-        </div>
-      </div>
+              style={{ color: 'var(--semi-color-text-2)', cursor: 'help' }}
+            />
+          </Tooltip>
+        </span>
+      ),
+    }
+  ];
+
+  const currencyItems = [
+    { value: 'USD', label: 'USD ($)' },
+    { value: 'CNY', label: 'CNY (¥)' }
+  ];
+
+  const handleChange = (value) => {
+    if (value === 'recharge') {
+      setShowWithRecharge(!showWithRecharge);
+    } else if (value === 'ratio') {
+      setShowRatio(!showRatio);
+    }
+  };
+
+  const getActiveValues = () => {
+    const activeValues = [];
+    if (showWithRecharge) activeValues.push('recharge');
+    if (showRatio) activeValues.push('ratio');
+    return activeValues;
+  };
+
+  return (
+    <div>
+      <SelectableButtonGroup
+        title={t('显示设置')}
+        items={items}
+        activeValue={getActiveValues()}
+        onChange={handleChange}
+        withCheckbox
+        collapsible={false}
+        t={t}
+      />
+
+      {showWithRecharge && (
+        <SelectableButtonGroup
+          title={t('货币单位')}
+          items={currencyItems}
+          activeValue={currency}
+          onChange={setCurrency}
+          collapsible={false}
+          t={t}
+        />
+      )}
     </div>
     </div>
   );
   );
 };
 };