ModelsColumnDefs.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. Space,
  19. Tag,
  20. Typography,
  21. Modal
  22. } from '@douyinfe/semi-ui';
  23. import {
  24. timestamp2string,
  25. getLobeHubIcon,
  26. stringToColor
  27. } from '../../../helpers';
  28. import { renderLimitedItems, renderDescription } from './ui/RenderUtils.jsx';
  29. const { Text } = Typography;
  30. // Render timestamp
  31. function renderTimestamp(timestamp) {
  32. return <>{timestamp2string(timestamp)}</>;
  33. }
  34. // Render vendor column with icon
  35. const renderVendorTag = (vendorId, vendorMap, t) => {
  36. if (!vendorId || !vendorMap[vendorId]) return '-';
  37. const v = vendorMap[vendorId];
  38. return (
  39. <Tag
  40. color='white'
  41. shape='circle'
  42. prefixIcon={getLobeHubIcon(v.icon || 'Layers', 14)}
  43. >
  44. {v.name}
  45. </Tag>
  46. );
  47. };
  48. // Render groups (enable_groups)
  49. const renderGroups = (groups) => {
  50. if (!groups || groups.length === 0) return '-';
  51. return renderLimitedItems({
  52. items: groups,
  53. renderItem: (g, idx) => (
  54. <Tag key={idx} size="small" shape='circle' color={stringToColor(g)}>
  55. {g}
  56. </Tag>
  57. ),
  58. });
  59. };
  60. // Render tags
  61. const renderTags = (text) => {
  62. if (!text) return '-';
  63. const tagsArr = text.split(',').filter(Boolean);
  64. return renderLimitedItems({
  65. items: tagsArr,
  66. renderItem: (tag, idx) => (
  67. <Tag key={idx} size="small" shape='circle' color={stringToColor(tag)}>
  68. {tag}
  69. </Tag>
  70. ),
  71. });
  72. };
  73. // Render endpoints
  74. const renderEndpoints = (text) => {
  75. let arr;
  76. try {
  77. arr = JSON.parse(text);
  78. } catch (_) { }
  79. if (!Array.isArray(arr)) return text || '-';
  80. return renderLimitedItems({
  81. items: arr,
  82. renderItem: (ep, idx) => (
  83. <Tag key={idx} color="white" size="small" shape='circle'>
  84. {ep}
  85. </Tag>
  86. ),
  87. });
  88. };
  89. // Render quota type
  90. const renderQuotaType = (qt, t) => {
  91. if (qt === 1) {
  92. return (
  93. <Tag color='teal' size='small' shape='circle'>
  94. {t('按次计费')}
  95. </Tag>
  96. );
  97. }
  98. if (qt === 0) {
  99. return (
  100. <Tag color='violet' size='small' shape='circle'>
  101. {t('按量计费')}
  102. </Tag>
  103. );
  104. }
  105. return qt ?? '-';
  106. };
  107. // Render bound channels
  108. const renderBoundChannels = (channels) => {
  109. if (!channels || channels.length === 0) return '-';
  110. return renderLimitedItems({
  111. items: channels,
  112. renderItem: (c, idx) => (
  113. <Tag key={idx} color="white" size="small" shape='circle'>
  114. {c.name}({c.type})
  115. </Tag>
  116. ),
  117. });
  118. };
  119. // Render operations column
  120. const renderOperations = (text, record, setEditingModel, setShowEdit, manageModel, refresh, t) => {
  121. return (
  122. <Space wrap>
  123. {record.status === 1 ? (
  124. <Button
  125. type='danger'
  126. size="small"
  127. onClick={() => manageModel(record.id, 'disable', record)}
  128. >
  129. {t('禁用')}
  130. </Button>
  131. ) : (
  132. <Button
  133. size="small"
  134. onClick={() => manageModel(record.id, 'enable', record)}
  135. >
  136. {t('启用')}
  137. </Button>
  138. )}
  139. <Button
  140. type='tertiary'
  141. size="small"
  142. onClick={() => {
  143. setEditingModel(record);
  144. setShowEdit(true);
  145. }}
  146. >
  147. {t('编辑')}
  148. </Button>
  149. <Button
  150. type='danger'
  151. size="small"
  152. onClick={() => {
  153. Modal.confirm({
  154. title: t('确定是否要删除此模型?'),
  155. content: t('此修改将不可逆'),
  156. onOk: () => {
  157. (async () => {
  158. await manageModel(record.id, 'delete', record);
  159. await refresh();
  160. })();
  161. },
  162. });
  163. }}
  164. >
  165. {t('删除')}
  166. </Button>
  167. </Space>
  168. );
  169. };
  170. export const getModelsColumns = ({
  171. t,
  172. manageModel,
  173. setEditingModel,
  174. setShowEdit,
  175. refresh,
  176. vendorMap,
  177. }) => {
  178. return [
  179. {
  180. title: t('模型名称'),
  181. dataIndex: 'model_name',
  182. render: (text) => (
  183. <Text copyable onClick={(e) => e.stopPropagation()}>
  184. {text}
  185. </Text>
  186. ),
  187. },
  188. {
  189. title: t('描述'),
  190. dataIndex: 'description',
  191. render: (text) => renderDescription(text, 200),
  192. },
  193. {
  194. title: t('供应商'),
  195. dataIndex: 'vendor_id',
  196. render: (vendorId, record) => renderVendorTag(vendorId, vendorMap, t),
  197. },
  198. {
  199. title: t('标签'),
  200. dataIndex: 'tags',
  201. render: renderTags,
  202. },
  203. {
  204. title: t('端点'),
  205. dataIndex: 'endpoints',
  206. render: renderEndpoints,
  207. },
  208. {
  209. title: t('已绑定渠道'),
  210. dataIndex: 'bound_channels',
  211. render: renderBoundChannels,
  212. },
  213. {
  214. title: t('可用分组'),
  215. dataIndex: 'enable_groups',
  216. render: renderGroups,
  217. },
  218. {
  219. title: t('计费类型'),
  220. dataIndex: 'quota_type',
  221. render: (qt) => renderQuotaType(qt, t),
  222. },
  223. {
  224. title: t('创建时间'),
  225. dataIndex: 'created_time',
  226. render: (text, record, index) => {
  227. return <div>{renderTimestamp(text)}</div>;
  228. },
  229. },
  230. {
  231. title: t('更新时间'),
  232. dataIndex: 'updated_time',
  233. render: (text, record, index) => {
  234. return <div>{renderTimestamp(text)}</div>;
  235. },
  236. },
  237. {
  238. title: '',
  239. dataIndex: 'operate',
  240. fixed: 'right',
  241. render: (text, record, index) => renderOperations(
  242. text,
  243. record,
  244. setEditingModel,
  245. setShowEdit,
  246. manageModel,
  247. refresh,
  248. t
  249. ),
  250. },
  251. ];
  252. };