ChannelsActions.jsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React from 'react';
  16. import {
  17. Button,
  18. Dropdown,
  19. Modal,
  20. Switch,
  21. Typography,
  22. Select,
  23. } from '@douyinfe/semi-ui';
  24. import CompactModeToggle from '../../common/ui/CompactModeToggle';
  25. const ChannelsActions = ({
  26. enableBatchDelete,
  27. batchDeleteChannels,
  28. setShowBatchSetTag,
  29. testAllChannels,
  30. fixChannelsAbilities,
  31. updateAllChannelsBalance,
  32. deleteAllDisabledChannels,
  33. compactMode,
  34. setCompactMode,
  35. idSort,
  36. setIdSort,
  37. setEnableBatchDelete,
  38. enableTagMode,
  39. setEnableTagMode,
  40. statusFilter,
  41. setStatusFilter,
  42. getFormValues,
  43. loadChannels,
  44. searchChannels,
  45. activeTypeKey,
  46. activePage,
  47. pageSize,
  48. setActivePage,
  49. t,
  50. }) => {
  51. return (
  52. <div className='flex flex-col gap-2'>
  53. {/* 第一行:批量操作按钮 + 设置开关 */}
  54. <div className='flex flex-col md:flex-row justify-between gap-2'>
  55. {/* 左侧:批量操作按钮 */}
  56. <div className='flex flex-wrap md:flex-nowrap items-center gap-2 w-full md:w-auto order-2 md:order-1'>
  57. <Button
  58. size='small'
  59. disabled={!enableBatchDelete}
  60. type='danger'
  61. className='w-full md:w-auto'
  62. onClick={() => {
  63. Modal.confirm({
  64. title: t('确定是否要删除所选通道?'),
  65. content: t('此修改将不可逆'),
  66. onOk: () => batchDeleteChannels(),
  67. });
  68. }}
  69. >
  70. {t('删除所选通道')}
  71. </Button>
  72. <Button
  73. size='small'
  74. disabled={!enableBatchDelete}
  75. type='tertiary'
  76. onClick={() => setShowBatchSetTag(true)}
  77. className='w-full md:w-auto'
  78. >
  79. {t('批量设置标签')}
  80. </Button>
  81. <Dropdown
  82. size='small'
  83. trigger='click'
  84. render={
  85. <Dropdown.Menu>
  86. <Dropdown.Item>
  87. <Button
  88. size='small'
  89. type='tertiary'
  90. className='w-full'
  91. onClick={() => {
  92. Modal.confirm({
  93. title: t('确定?'),
  94. content: t('确定要测试所有通道吗?'),
  95. onOk: () => testAllChannels(),
  96. size: 'small',
  97. centered: true,
  98. });
  99. }}
  100. >
  101. {t('测试所有通道')}
  102. </Button>
  103. </Dropdown.Item>
  104. <Dropdown.Item>
  105. <Button
  106. size='small'
  107. className='w-full'
  108. onClick={() => {
  109. Modal.confirm({
  110. title: t('确定是否要修复数据库一致性?'),
  111. content: t(
  112. '进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用',
  113. ),
  114. onOk: () => fixChannelsAbilities(),
  115. size: 'sm',
  116. centered: true,
  117. });
  118. }}
  119. >
  120. {t('修复数据库一致性')}
  121. </Button>
  122. </Dropdown.Item>
  123. <Dropdown.Item>
  124. <Button
  125. size='small'
  126. type='secondary'
  127. className='w-full'
  128. onClick={() => {
  129. Modal.confirm({
  130. title: t('确定?'),
  131. content: t('确定要更新所有已启用通道余额吗?'),
  132. onOk: () => updateAllChannelsBalance(),
  133. size: 'sm',
  134. centered: true,
  135. });
  136. }}
  137. >
  138. {t('更新所有已启用通道余额')}
  139. </Button>
  140. </Dropdown.Item>
  141. <Dropdown.Item>
  142. <Button
  143. size='small'
  144. type='danger'
  145. className='w-full'
  146. onClick={() => {
  147. Modal.confirm({
  148. title: t('确定是否要删除禁用通道?'),
  149. content: t('此修改将不可逆'),
  150. onOk: () => deleteAllDisabledChannels(),
  151. size: 'sm',
  152. centered: true,
  153. });
  154. }}
  155. >
  156. {t('删除禁用通道')}
  157. </Button>
  158. </Dropdown.Item>
  159. </Dropdown.Menu>
  160. }
  161. >
  162. <Button
  163. size='small'
  164. theme='light'
  165. type='tertiary'
  166. className='w-full md:w-auto'
  167. >
  168. {t('批量操作')}
  169. </Button>
  170. </Dropdown>
  171. <CompactModeToggle
  172. compactMode={compactMode}
  173. setCompactMode={setCompactMode}
  174. t={t}
  175. />
  176. </div>
  177. {/* 右侧:设置开关区域 */}
  178. <div className='flex flex-col md:flex-row items-start md:items-center gap-2 w-full md:w-auto order-1 md:order-2'>
  179. <div className='flex items-center justify-between w-full md:w-auto'>
  180. <Typography.Text strong className='mr-2'>
  181. {t('使用ID排序')}
  182. </Typography.Text>
  183. <Switch
  184. size='small'
  185. checked={idSort}
  186. onChange={(v) => {
  187. localStorage.setItem('id-sort', v + '');
  188. setIdSort(v);
  189. const { searchKeyword, searchGroup, searchModel } =
  190. getFormValues();
  191. if (
  192. searchKeyword === '' &&
  193. searchGroup === '' &&
  194. searchModel === ''
  195. ) {
  196. loadChannels(activePage, pageSize, v, enableTagMode);
  197. } else {
  198. searchChannels(
  199. enableTagMode,
  200. activeTypeKey,
  201. statusFilter,
  202. activePage,
  203. pageSize,
  204. v,
  205. );
  206. }
  207. }}
  208. />
  209. </div>
  210. <div className='flex items-center justify-between w-full md:w-auto'>
  211. <Typography.Text strong className='mr-2'>
  212. {t('开启批量操作')}
  213. </Typography.Text>
  214. <Switch
  215. size='small'
  216. checked={enableBatchDelete}
  217. onChange={(v) => {
  218. localStorage.setItem('enable-batch-delete', v + '');
  219. setEnableBatchDelete(v);
  220. }}
  221. />
  222. </div>
  223. <div className='flex items-center justify-between w-full md:w-auto'>
  224. <Typography.Text strong className='mr-2'>
  225. {t('标签聚合模式')}
  226. </Typography.Text>
  227. <Switch
  228. size='small'
  229. checked={enableTagMode}
  230. onChange={(v) => {
  231. localStorage.setItem('enable-tag-mode', v + '');
  232. setEnableTagMode(v);
  233. setActivePage(1);
  234. loadChannels(1, pageSize, idSort, v);
  235. }}
  236. />
  237. </div>
  238. <div className='flex items-center justify-between w-full md:w-auto'>
  239. <Typography.Text strong className='mr-2'>
  240. {t('状态筛选')}
  241. </Typography.Text>
  242. <Select
  243. size='small'
  244. value={statusFilter}
  245. onChange={(v) => {
  246. localStorage.setItem('channel-status-filter', v);
  247. setStatusFilter(v);
  248. setActivePage(1);
  249. loadChannels(
  250. 1,
  251. pageSize,
  252. idSort,
  253. enableTagMode,
  254. activeTypeKey,
  255. v,
  256. );
  257. }}
  258. >
  259. <Select.Option value='all'>{t('全部')}</Select.Option>
  260. <Select.Option value='enabled'>{t('已启用')}</Select.Option>
  261. <Select.Option value='disabled'>{t('已禁用')}</Select.Option>
  262. </Select>
  263. </div>
  264. </div>
  265. </div>
  266. </div>
  267. );
  268. };
  269. export default ChannelsActions;