ToolsLibraryDetail.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import React, { useState, useEffect } from "react";
  2. import { Form, Input, Select, Button, Card, Descriptions, Tag, message, Spin, Row, Col, Tooltip } from "antd";
  3. import { useParams, useNavigate, useLocation } from "react-router-dom";
  4. import { ArrowLeftOutlined, CopyOutlined } from "@ant-design/icons";
  5. import { toolsLibraryApi } from "../services/api";
  6. import moment from "moment";
  7. const { TextArea } = Input;
  8. const { Option } = Select;
  9. const ToolsLibraryDetail = () => {
  10. const [form] = Form.useForm();
  11. const [data, setData] = useState(null);
  12. const [loading, setLoading] = useState(true);
  13. const [saving, setSaving] = useState(false);
  14. const { id } = useParams();
  15. const navigate = useNavigate();
  16. const location = useLocation();
  17. const isEditMode = location.search.includes("mode=edit");
  18. const getStatusColor = (status) => {
  19. const statusMap = {
  20. normal: "success",
  21. offline: "default",
  22. unpublished: "default",
  23. published: "success",
  24. };
  25. return statusMap[status] || "warning";
  26. };
  27. const getStatusText = (status) => {
  28. const statusMap = {
  29. normal: "正常",
  30. offline: "已下线",
  31. unpublished: "待发布",
  32. published: "已发布",
  33. };
  34. return statusMap[status] || status;
  35. };
  36. const getCallTypeText = (type) => {
  37. const typeMap = {
  38. api: "API调用",
  39. browser_auto_operate: "浏览器自动操作",
  40. };
  41. return typeMap[type] || type;
  42. };
  43. const getApiProviderText = (provider) => {
  44. const providerMap = {
  45. official: "官方",
  46. "302ai": "302AI",
  47. official_api: "官方API",
  48. };
  49. return providerMap[provider] || provider;
  50. };
  51. const fetchData = async () => {
  52. try {
  53. const response = await toolsLibraryApi.getDetail(id);
  54. setData(response.data);
  55. form.setFieldsValue(response.data);
  56. } catch (error) {
  57. message.error("获取详情失败");
  58. } finally {
  59. setLoading(false);
  60. }
  61. };
  62. const handleSave = async (values) => {
  63. setSaving(true);
  64. try {
  65. await toolsLibraryApi.update(id, values);
  66. message.success("更新成功");
  67. navigate("/tools-library");
  68. } catch (error) {
  69. message.error("更新失败");
  70. } finally {
  71. setSaving(false);
  72. }
  73. };
  74. const handleCopyParams = () => {
  75. const paramsDefinition = form.getFieldValue("params_definition");
  76. if (paramsDefinition) {
  77. navigator.clipboard
  78. .writeText(paramsDefinition)
  79. .then(() => {
  80. message.success("参数定义已复制到剪贴板");
  81. })
  82. .catch(() => {
  83. message.error("复制失败");
  84. });
  85. } else {
  86. message.warning("参数定义为空");
  87. }
  88. };
  89. useEffect(() => {
  90. fetchData();
  91. }, [id]);
  92. if (loading) {
  93. return (
  94. <div style={{ textAlign: "center", padding: "50px" }}>
  95. <Spin size="large" />
  96. </div>
  97. );
  98. }
  99. if (!data) {
  100. return <div>数据不存在</div>;
  101. }
  102. return (
  103. <div className="detail-container">
  104. <Button
  105. icon={<ArrowLeftOutlined />}
  106. onClick={() => navigate("/tools-library")}
  107. style={{ marginBottom: 16 }}
  108. >
  109. 返回列表
  110. </Button>
  111. {isEditMode ? (
  112. <Card
  113. title="编辑工具"
  114. style={{ marginBottom: 24 }}
  115. >
  116. <Form
  117. form={form}
  118. layout="vertical"
  119. onFinish={handleSave}
  120. className="w-full"
  121. >
  122. <Row gutter={24}>
  123. <Col span={12}>
  124. <Form.Item
  125. label="工具名称"
  126. name="tools_name"
  127. rules={[{ required: true, message: "请输入工具名称" }]}
  128. >
  129. <Input />
  130. </Form.Item>
  131. </Col>
  132. <Col span={12}>
  133. {" "}
  134. <Form.Item
  135. label="工具功能名称"
  136. name="tools_function_name"
  137. rules={[{ required: true, message: "请输入工具功能名称" }]}
  138. >
  139. <Input />
  140. </Form.Item>
  141. </Col>
  142. </Row>
  143. <Row gutter={24}>
  144. <Col span={12}>
  145. <Form.Item
  146. label="状态"
  147. name="status"
  148. rules={[{ required: true, message: "请选择状态" }]}
  149. >
  150. <Select>
  151. <Option value="normal">正常</Option>
  152. <Option value="offline">已下线</Option>
  153. <Option value="unpublished">待发布</Option>
  154. <Option value="published">已发布</Option>
  155. </Select>
  156. </Form.Item>
  157. </Col>
  158. <Col span={12}>
  159. <Form.Item
  160. label="调用方式"
  161. name="call_type"
  162. >
  163. <Select>
  164. <Option value="api">API调用</Option>
  165. <Option value="browser_auto_operate">浏览器自动操作</Option>
  166. </Select>
  167. </Form.Item>
  168. </Col>
  169. </Row>
  170. <Row gutter={24}>
  171. <Col span={12}>
  172. {" "}
  173. <Form.Item
  174. label="工具全称"
  175. name="tools_full_name"
  176. >
  177. <Input />
  178. </Form.Item>
  179. </Col>
  180. <Col span={12}>
  181. {" "}
  182. <Form.Item
  183. label="工具版本"
  184. name="tools_version"
  185. >
  186. <Input />
  187. </Form.Item>
  188. </Col>
  189. </Row>
  190. <Row gutter={24}>
  191. <Col span={12}>
  192. <Form.Item
  193. label="API提供方"
  194. name="api_provider"
  195. >
  196. <Select>
  197. <Option value="official">官方</Option>
  198. <Option value="302ai">302AI</Option>
  199. <Option value="official_api">官方API</Option>
  200. </Select>
  201. </Form.Item>
  202. </Col>
  203. <Col span={12}>
  204. {" "}
  205. <Form.Item
  206. label="API路径"
  207. name="api_url_path"
  208. >
  209. <Input />
  210. </Form.Item>
  211. </Col>
  212. </Row>
  213. <Form.Item
  214. label="工具描述"
  215. name="tools_desc"
  216. >
  217. <TextArea rows={4} />
  218. </Form.Item>
  219. <Form.Item
  220. label="操作路径数据"
  221. name="operate_path_data"
  222. >
  223. <TextArea rows={4} />
  224. </Form.Item>
  225. <Form.Item
  226. label={
  227. <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
  228. 参数定义
  229. <Tooltip title="点击复制参数定义">
  230. <Button
  231. type="text"
  232. style={{ color: "blue" }}
  233. size="small"
  234. icon={<CopyOutlined />}
  235. onClick={handleCopyParams}
  236. />
  237. </Tooltip>
  238. </div>
  239. }
  240. name="params_definition"
  241. >
  242. <TextArea rows={6} />
  243. </Form.Item>
  244. <Form.Item
  245. label="响应数据说明"
  246. name="response_desc"
  247. >
  248. <TextArea rows={4} />
  249. </Form.Item>
  250. <div className="button-group">
  251. <Button onClick={() => navigate("/tools-library")}>取消</Button>
  252. <Button
  253. type="primary"
  254. htmlType="submit"
  255. loading={saving}
  256. >
  257. 保存
  258. </Button>
  259. </div>
  260. </Form>
  261. </Card>
  262. ) : (
  263. <Card title="工具详情">
  264. <Descriptions
  265. column={2}
  266. bordered
  267. labelStyle={{ width: "150px" }}
  268. >
  269. <Descriptions.Item label="工具ID">{data.tools_id}</Descriptions.Item>
  270. <Descriptions.Item label="工具名称">{data.tools_name}</Descriptions.Item>
  271. <Descriptions.Item label="工具功能名称">{data.tools_function_name}</Descriptions.Item>
  272. <Descriptions.Item label="工具全称">{data.tools_full_name}</Descriptions.Item>
  273. <Descriptions.Item label="工具版本">
  274. <Tag color="geekblue">{data.tools_version}</Tag>
  275. </Descriptions.Item>
  276. <Descriptions.Item label="自动接入任务ID">{data.access_task_id}</Descriptions.Item>
  277. <Descriptions.Item label="状态">
  278. <Tag color={getStatusColor(data.status)}>{getStatusText(data.status)}</Tag>
  279. </Descriptions.Item>
  280. <Descriptions.Item label="调用方式">
  281. <Tag color="blue">{getCallTypeText(data.call_type)}</Tag>
  282. </Descriptions.Item>
  283. <Descriptions.Item label="API提供方">
  284. <Tag color="purple">{getApiProviderText(data.api_provider)}</Tag>
  285. </Descriptions.Item>
  286. <Descriptions.Item label="API路径">{data.api_url_path}</Descriptions.Item>
  287. <Descriptions.Item
  288. label="工具描述"
  289. span={2}
  290. >
  291. {data.tools_desc}
  292. </Descriptions.Item>
  293. <Descriptions.Item
  294. label="操作路径数据"
  295. span={2}
  296. >
  297. <div style={{ maxHeight: "200px", overflow: "auto" }}>
  298. <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{data.operate_path_data || "无"}</pre>
  299. </div>
  300. </Descriptions.Item>
  301. <Descriptions.Item
  302. label={
  303. <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
  304. 参数定义
  305. <Tooltip title="点击复制参数定义">
  306. <Button
  307. type="text"
  308. style={{ color: "blue" }}
  309. size="small"
  310. icon={<CopyOutlined />}
  311. onClick={() => {
  312. const paramsDefinition = data.params_definition;
  313. if (paramsDefinition) {
  314. navigator.clipboard.writeText(paramsDefinition).then(() => {
  315. message.success("参数定义已复制到剪贴板");
  316. }).catch(() => {
  317. message.error("复制失败,请手动复制");
  318. });
  319. } else {
  320. message.warning("参数定义为空,无法复制");
  321. }
  322. }}
  323. />
  324. </Tooltip>
  325. </div>
  326. }
  327. span={2}
  328. >
  329. <div style={{ maxHeight: "200px", overflow: "auto" }}>
  330. <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{data.params_definition || "无"}</pre>
  331. </div>
  332. </Descriptions.Item>
  333. <Descriptions.Item
  334. label="响应数据说明"
  335. span={2}
  336. >
  337. <div style={{ maxHeight: "200px", overflow: "auto" }}>
  338. <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{data.response_desc || "无"}</pre>
  339. </div>
  340. </Descriptions.Item>
  341. <Descriptions.Item label="创建时间">
  342. {moment(data.create_time).format("YYYY-MM-DD HH:mm:ss")}
  343. </Descriptions.Item>
  344. <Descriptions.Item label="更新时间">
  345. {moment(data.update_time).format("YYYY-MM-DD HH:mm:ss")}
  346. </Descriptions.Item>
  347. </Descriptions>
  348. </Card>
  349. )}
  350. </div>
  351. );
  352. };
  353. export default ToolsLibraryDetail;