DictPreference.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. import { ProList } from "@ant-design/pro-components";
  2. import { EditOutlined, CheckOutlined } from "@ant-design/icons";
  3. import type {
  4. IApiResponseDictData,
  5. IPreferenceListResponse,
  6. IPreferenceRequest,
  7. IPreferenceResponse,
  8. } from "../../api/Dict";
  9. import { Button, Input, Space, Tag } from "antd";
  10. import { get, put } from "../../request";
  11. import type { IWbw } from "../template/Wbw/WbwWord";
  12. import WbwFactorsEditor from "../template/Wbw/WbwFactorsEditor";
  13. import { useEffect, useState } from "react";
  14. import WbwLookup from "../template/Wbw/WbwLookup";
  15. import Lookup from "./Lookup";
  16. import WbwParentEditor from "../template/Wbw/WbwParentEditor";
  17. import User from "../auth/User";
  18. import DictConfidence from "./DictConfidence";
  19. export const setValue = async (id: string, value: number) => {
  20. const url = `/v2/dict-preference/${id}`;
  21. const values: IPreferenceRequest = {
  22. confidence: value,
  23. };
  24. console.debug("api request", url, values);
  25. const result = await put<IPreferenceRequest, IPreferenceResponse>(
  26. url,
  27. values
  28. );
  29. return result;
  30. };
  31. interface IOkButton {
  32. data: IApiResponseDictData;
  33. onChange?: (data: IApiResponseDictData) => void;
  34. }
  35. const OkButton = ({ data, onChange }: IOkButton) => {
  36. const [loading, setLoading] = useState(false);
  37. return (
  38. <Button
  39. type="link"
  40. icon={<CheckOutlined />}
  41. loading={loading}
  42. onClick={async () => {
  43. setLoading(true);
  44. const result = await setValue(data.id, 100);
  45. setLoading(false);
  46. console.info("api response", result);
  47. if (result.ok) {
  48. onChange && onChange(result.data);
  49. }
  50. }}
  51. >
  52. 确认
  53. </Button>
  54. );
  55. };
  56. const toWbw = (data: IApiResponseDictData): IWbw => {
  57. return {
  58. book: 1,
  59. para: 1,
  60. sn: [1],
  61. word: { value: data.word, status: 5 },
  62. real: { value: data.word, status: 5 },
  63. factors: { value: data.factors ?? "", status: 5 },
  64. parent: { value: data.parent ?? "", status: 5 },
  65. confidence: data.confidence ?? 0,
  66. };
  67. };
  68. interface IFactorsEditorWidget {
  69. data: IApiResponseDictData;
  70. }
  71. const FactorsEditor = ({ data }: IFactorsEditorWidget) => {
  72. const [wbw, setWbw] = useState(toWbw(data));
  73. const [input, setInput] = useState(data.factors);
  74. const [type, setType] = useState(false);
  75. useEffect(() => setWbw(toWbw(data)), [data]);
  76. const upload = async (value: string) => {
  77. const url = `/v2/dict-preference/${data.id}`;
  78. const values: IPreferenceRequest = {
  79. factors: value,
  80. confidence: 100,
  81. };
  82. console.debug("api request", url, data);
  83. const result = await put<IPreferenceRequest, IPreferenceResponse>(
  84. url,
  85. values
  86. );
  87. console.info("api response", result);
  88. setWbw(toWbw(result.data));
  89. setInput(result.data.factors);
  90. return result;
  91. };
  92. return type ? (
  93. <div style={{ display: "flex" }}>
  94. <Input
  95. width={400}
  96. value={input ?? ""}
  97. placeholder="Title"
  98. onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
  99. setInput(event.target.value);
  100. }}
  101. />
  102. <Button
  103. type="text"
  104. icon={<CheckOutlined />}
  105. onClick={async () => {
  106. upload(input ?? "");
  107. setType(false);
  108. }}
  109. />
  110. </div>
  111. ) : (
  112. <Space>
  113. <WbwFactorsEditor
  114. key="factors"
  115. initValue={wbw}
  116. display={"block"}
  117. onChange={async (e: string): Promise<IPreferenceResponse> => {
  118. const result = upload(e ?? "");
  119. return result;
  120. }}
  121. />
  122. <Button
  123. type="text"
  124. icon={<EditOutlined />}
  125. onClick={() => setType(true)}
  126. />
  127. </Space>
  128. );
  129. };
  130. interface IParentEditorWidget {
  131. data: IApiResponseDictData;
  132. }
  133. const ParentEditor = ({ data }: IParentEditorWidget) => {
  134. const [wbw, setWbw] = useState(toWbw(data));
  135. const [input, setInput] = useState(data.factors);
  136. const [type, setType] = useState(false);
  137. useEffect(() => setWbw(toWbw(data)), [data]);
  138. const upload = async (value: string) => {
  139. const url = `/v2/dict-preference/${data.id}`;
  140. const values: IPreferenceRequest = {
  141. parent: value,
  142. confidence: 100,
  143. };
  144. console.debug("api request", url, data);
  145. const result = await put<IPreferenceRequest, IPreferenceResponse>(
  146. url,
  147. values
  148. );
  149. console.info("api response", result);
  150. setWbw(toWbw(result.data));
  151. setInput(result.data.factors);
  152. return result;
  153. };
  154. return type ? (
  155. <div style={{ display: "flex" }}>
  156. <Input
  157. width={400}
  158. value={input ?? ""}
  159. placeholder="Title"
  160. onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
  161. setInput(event.target.value);
  162. }}
  163. />
  164. <Button
  165. type="text"
  166. icon={<CheckOutlined />}
  167. onClick={async () => {
  168. upload(input ?? "");
  169. setType(false);
  170. }}
  171. />
  172. </div>
  173. ) : (
  174. <Space>
  175. <WbwParentEditor
  176. key="factors"
  177. initValue={wbw}
  178. display={"block"}
  179. onChange={async (e: string): Promise<IPreferenceResponse> => {
  180. const result = upload(e ?? "");
  181. return result;
  182. }}
  183. />
  184. <Button
  185. type="text"
  186. icon={<EditOutlined />}
  187. onClick={() => setType(true)}
  188. />
  189. </Space>
  190. );
  191. };
  192. export interface IDictPreferenceWidget {
  193. currPage?: number;
  194. pageSize?: number;
  195. }
  196. const DictPreference = ({
  197. currPage,
  198. pageSize = 100,
  199. }: IDictPreferenceWidget) => {
  200. const [lookupWords, setLookupWords] = useState<string[]>([]);
  201. const [lookupRun, setLookupRun] = useState(false);
  202. const [data, setData] = useState<IApiResponseDictData[]>([]);
  203. return (
  204. <>
  205. <WbwLookup words={lookupWords} run={lookupRun} />
  206. <ProList<IApiResponseDictData>
  207. search={{
  208. filterType: "light",
  209. }}
  210. rowKey="name"
  211. headerTitle="单词首选项"
  212. dataSource={data}
  213. onDataSourceChange={setData}
  214. request={async (params = {} as Record<string, any>) => {
  215. let url = `/v2/dict-preference`;
  216. const mPageSize = pageSize ?? params.pageSize ?? 100;
  217. const offset = ((currPage ?? params.current ?? 1) - 1) * mPageSize;
  218. url += `?limit=${mPageSize}&offset=${offset}`;
  219. url += params.keyword ? "&keyword=" + params.keyword : "";
  220. console.info("api request", url);
  221. const res = await get<IPreferenceListResponse>(url);
  222. console.info("api response", res);
  223. if (res.ok === false) {
  224. }
  225. return {
  226. data: res.data.rows.map((item, id) => {
  227. return { ...item, sn: id + offset + 1 };
  228. }),
  229. total: res.data.count,
  230. success: true,
  231. };
  232. }}
  233. pagination={
  234. currPage
  235. ? false
  236. : {
  237. showQuickJumper: !currPage,
  238. showSizeChanger: !currPage,
  239. showLessItems: !currPage,
  240. showPrevNextJumpers: !currPage,
  241. pageSize: 100,
  242. }
  243. }
  244. showActions="hover"
  245. onRow={(record) => {
  246. return {
  247. onMouseEnter: () => {
  248. console.info(`点击了行:${record.word}`);
  249. setLookupWords([record.word]);
  250. setLookupRun(true);
  251. },
  252. onMouseLeave: () => {
  253. setLookupRun(false);
  254. },
  255. };
  256. }}
  257. metas={{
  258. title: {
  259. dataIndex: "word",
  260. title: "用户",
  261. render(_dom, entity, _index, _action, _schema) {
  262. return (
  263. <Space>
  264. {`[${entity.sn}]`}
  265. <Lookup search={entity.word}>{entity.word}</Lookup>
  266. </Space>
  267. );
  268. },
  269. },
  270. avatar: {
  271. dataIndex: "sn",
  272. search: false,
  273. render(_dom, entity, _index, _action, _schema) {
  274. return (
  275. <User
  276. {...entity.editor}
  277. showName={false}
  278. showUserName={false}
  279. />
  280. );
  281. },
  282. },
  283. description: {
  284. dataIndex: "title",
  285. search: false,
  286. render(_dom, entity, _index, _action, _schema) {
  287. return (
  288. <Space>
  289. <FactorsEditor data={entity} />
  290. <span>|</span>
  291. <ParentEditor data={entity} />
  292. </Space>
  293. );
  294. },
  295. },
  296. subTitle: {
  297. dataIndex: "labels",
  298. render: (_, row) => {
  299. return (
  300. <Space>
  301. <Tag color="blue" key={row.count}>
  302. {row.count}
  303. </Tag>
  304. <DictConfidence
  305. value={row.confidence}
  306. onChange={async (value) => {
  307. const result = await setValue(row.id, value);
  308. setData((origin) => {
  309. origin.forEach((value, index, array) => {
  310. if (value.id === result.data.id) {
  311. array[index] = {
  312. ...value,
  313. confidence: result.data.confidence,
  314. };
  315. }
  316. });
  317. return origin;
  318. });
  319. }}
  320. />
  321. </Space>
  322. );
  323. },
  324. search: false,
  325. },
  326. actions: {
  327. render: (_text, row) => {
  328. return [
  329. <OkButton
  330. data={row}
  331. onChange={(data) => {
  332. setData((origin) => {
  333. origin.forEach((value, index, array) => {
  334. if (value.id === row.id) {
  335. array[index] = {
  336. ...value,
  337. confidence: data.confidence,
  338. };
  339. }
  340. });
  341. return origin;
  342. });
  343. }}
  344. />,
  345. ];
  346. },
  347. search: false,
  348. },
  349. status: {
  350. // 自己扩展的字段,主要用于筛选,不在列表中显示
  351. title: "状态",
  352. valueType: "select",
  353. valueEnum: {
  354. all: { text: "全部", status: "Default" },
  355. open: {
  356. text: "未解决",
  357. status: "Error",
  358. },
  359. closed: {
  360. text: "已解决",
  361. status: "Success",
  362. },
  363. processing: {
  364. text: "解决中",
  365. status: "Processing",
  366. },
  367. },
  368. },
  369. }}
  370. />
  371. </>
  372. );
  373. };
  374. export default DictPreference;