index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import { Modal } from 'antd';
  2. import type { FirmwareItem } from '@/pages/device/Firmware/typings';
  3. import { createSchemaField } from '@formily/react';
  4. import { Form, FormGrid, FormItem, Input, Select, ArrayTable } from '@formily/antd';
  5. import type { Field } from '@formily/core';
  6. import { onFieldValueChange, onFormInit } from '@formily/core';
  7. import { createForm } from '@formily/core';
  8. import type { ISchema } from '@formily/json-schema';
  9. import FUpload from '@/components/FUpload';
  10. import { action } from '@formily/reactive';
  11. import { service } from '@/pages/device/Firmware';
  12. import { useRef } from 'react';
  13. import type { ProductItem } from '@/pages/device/Product/typings';
  14. import { onlyMessage } from '@/utils/util';
  15. interface Props {
  16. data?: FirmwareItem;
  17. close: () => void;
  18. visible: boolean;
  19. }
  20. const Save = (props: Props) => {
  21. const { data, close, visible } = props;
  22. const fileInfo = useRef<any>({});
  23. const signMethod = useRef<'md5' | 'sha256'>('md5');
  24. const form = createForm({
  25. validateFirst: true,
  26. initialValues: data,
  27. effects: () => {
  28. onFormInit(async (form1) => {
  29. if (!data?.id) return;
  30. form1.setInitialValues({ ...data, upload: { url: data?.url } });
  31. });
  32. onFieldValueChange('signMethod', (field) => {
  33. const value = (field as Field).value;
  34. signMethod.current = value;
  35. });
  36. onFieldValueChange('upload', (field) => {
  37. const value = (field as Field).value;
  38. fileInfo.current = value;
  39. });
  40. },
  41. });
  42. const products = useRef<ProductItem[]>([]);
  43. const useAsyncDataSource = (services: (arg0: Field) => Promise<any>) => (field: Field) => {
  44. field.loading = true;
  45. services(field).then(
  46. action.bound!((list: any) => {
  47. field.dataSource = list.result.map((item: any) => ({ label: item.name, value: item.id }));
  48. products.current = list.result;
  49. field.loading = false;
  50. }),
  51. );
  52. };
  53. const loadData = async () => service.queryProduct();
  54. const SchemaField = createSchemaField({
  55. components: {
  56. FormItem,
  57. FormGrid,
  58. Input,
  59. FUpload,
  60. Select,
  61. ArrayTable,
  62. },
  63. });
  64. const save = async () => {
  65. const values: any = await form.submit();
  66. const product = products.current?.find((item) => item.id === values.productId);
  67. values.productName = product?.name || '';
  68. const { upload, ...extra } = values;
  69. const params = {
  70. ...extra,
  71. url: upload.url || data?.url,
  72. size: upload.length || data?.size,
  73. };
  74. const resp = (await service.update(params)) as any;
  75. if (resp.status === 200) {
  76. onlyMessage('保存成功!');
  77. close();
  78. }
  79. };
  80. const schema: ISchema = {
  81. type: 'object',
  82. properties: {
  83. grid: {
  84. type: 'void',
  85. 'x-component': 'FormGrid',
  86. 'x-component-props': {
  87. minColumns: 2,
  88. maxColumns: 2,
  89. },
  90. properties: {
  91. name: {
  92. title: '名称',
  93. 'x-decorator': 'FormItem',
  94. 'x-component': 'Input',
  95. 'x-component-props': {
  96. placeholder: '请输入固件名称',
  97. },
  98. required: true,
  99. 'x-decorator-props': {
  100. gridSpan: 2,
  101. },
  102. 'x-validator': [
  103. {
  104. required: true,
  105. message: '请输入固件名称',
  106. },
  107. {
  108. max: 64,
  109. message: '最多可输入64个字符',
  110. },
  111. ],
  112. },
  113. productId: {
  114. title: '所属产品',
  115. 'x-decorator': 'FormItem',
  116. 'x-component': 'Select',
  117. 'x-reactions': ['{{useAsyncDataSource(loadData)}}'],
  118. 'x-component-props': {
  119. placeholder: '请选择所属产品',
  120. },
  121. 'x-decorator-props': {
  122. gridSpan: 2,
  123. },
  124. required: true,
  125. 'x-validator': [
  126. {
  127. required: true,
  128. message: '请选择所属产品',
  129. },
  130. ],
  131. },
  132. version: {
  133. title: '版本号',
  134. 'x-decorator': 'FormItem',
  135. 'x-component': 'Input',
  136. 'x-component-props': {
  137. placeholder: '请输入版本号',
  138. },
  139. 'x-decorator-props': {
  140. gridSpan: 1,
  141. },
  142. required: true,
  143. 'x-validator': [
  144. {
  145. required: true,
  146. message: '请输入版本号',
  147. },
  148. {
  149. max: 64,
  150. message: '最多可输入64个字符',
  151. },
  152. ],
  153. },
  154. versionOrder: {
  155. title: '版本序号',
  156. 'x-decorator': 'FormItem',
  157. 'x-component': 'Input',
  158. 'x-component-props': {
  159. placeholder: '请输入版本序号',
  160. },
  161. 'x-decorator-props': {
  162. gridSpan: 1,
  163. },
  164. required: true,
  165. 'x-validator': [
  166. {
  167. required: true,
  168. message: '请输入版本号',
  169. },
  170. {
  171. maximum: 99999,
  172. minimum: 1,
  173. },
  174. ],
  175. },
  176. signMethod: {
  177. title: '签名方式',
  178. 'x-decorator': 'FormItem',
  179. 'x-component': 'Select',
  180. enum: [
  181. { label: 'MD5', value: 'md5' },
  182. { label: 'SHA256', value: 'sha256' },
  183. ],
  184. 'x-component-props': {
  185. placeholder: '请选择签名方式',
  186. },
  187. 'x-decorator-props': {
  188. gridSpan: 1,
  189. },
  190. required: true,
  191. 'x-validator': [
  192. {
  193. required: true,
  194. message: '请选择签名方式',
  195. },
  196. ],
  197. },
  198. sign: {
  199. title: '签名',
  200. 'x-decorator': 'FormItem',
  201. 'x-component': 'Input',
  202. 'x-component-props': {
  203. placeholder: '请输入签名',
  204. },
  205. 'x-decorator-props': {
  206. tooltip: '请输入本地文件进行签名加密后的值',
  207. gridSpan: 1,
  208. },
  209. required: true,
  210. 'x-validator': [
  211. {
  212. required: true,
  213. message: '请输入签名',
  214. },
  215. // {
  216. // validator: (value: string) => {
  217. // return new Promise((resolve, reject) => {
  218. // if (value !== '' && signMethod.current && fileInfo.current[signMethod.current]) {
  219. // if (value !== fileInfo.current[signMethod.current]) {
  220. // return reject(new Error('签名不一致,请检查文件是否上传正确'));
  221. // }
  222. // }
  223. // return resolve('');
  224. // });
  225. // },
  226. // },
  227. ],
  228. 'x-reactions': [
  229. {
  230. dependencies: ['.upload', 'signMethod'],
  231. fulfill: {
  232. state: {
  233. selfErrors:
  234. '{{$deps[0] && $deps[1] && $deps[0][$deps[1]] && $self.value && $self.value !== $deps[0][$deps[1]] ? "签名不一致,请检查文件是否上传正确" : ""}}',
  235. },
  236. },
  237. },
  238. ],
  239. },
  240. upload: {
  241. title: '文件上传',
  242. 'x-decorator': 'FormItem',
  243. 'x-component': 'FUpload',
  244. 'x-component-props': {
  245. type: 'file',
  246. placeholder: '请上传文件',
  247. },
  248. 'x-decorator-props': {
  249. gridSpan: 2,
  250. },
  251. required: true,
  252. 'x-validator': [
  253. {
  254. required: true,
  255. message: '请上传文件',
  256. },
  257. ],
  258. },
  259. properties: {
  260. type: 'array',
  261. 'x-decorator': 'FormItem',
  262. 'x-component': 'ArrayTable',
  263. title: '其他配置',
  264. 'x-component-props': {
  265. pagination: { pageSize: 10 },
  266. scroll: { x: '100%' },
  267. },
  268. 'x-decorator-props': {
  269. gridSpan: 2,
  270. },
  271. items: {
  272. type: 'object',
  273. properties: {
  274. column1: {
  275. type: 'void',
  276. 'x-component': 'ArrayTable.Column',
  277. 'x-component-props': { title: 'KEY' },
  278. properties: {
  279. id: {
  280. type: 'string',
  281. 'x-decorator': 'Editable',
  282. 'x-component': 'Input',
  283. },
  284. },
  285. },
  286. column2: {
  287. type: 'void',
  288. 'x-component': 'ArrayTable.Column',
  289. 'x-component-props': { title: 'VALUE' },
  290. properties: {
  291. value: {
  292. type: 'string',
  293. 'x-decorator': 'FormItem',
  294. 'x-component': 'Input',
  295. },
  296. },
  297. },
  298. column3: {
  299. type: 'void',
  300. 'x-component': 'ArrayTable.Column',
  301. 'x-component-props': {
  302. title: '操作',
  303. dataIndex: 'operations',
  304. },
  305. properties: {
  306. item: {
  307. type: 'void',
  308. 'x-component': 'FormItem',
  309. properties: {
  310. remove: {
  311. type: 'void',
  312. 'x-component': 'ArrayTable.Remove',
  313. },
  314. },
  315. },
  316. },
  317. },
  318. },
  319. },
  320. properties: {
  321. add: {
  322. type: 'void',
  323. 'x-component': 'ArrayTable.Addition',
  324. title: '添加条目',
  325. },
  326. },
  327. },
  328. description: {
  329. title: '说明',
  330. 'x-decorator': 'FormItem',
  331. 'x-component': 'Input.TextArea',
  332. 'x-decorator-props': {
  333. gridSpan: 2,
  334. },
  335. 'x-component-props': {
  336. rows: 3,
  337. showCount: true,
  338. maxLength: 200,
  339. placeholder: '请输入说明',
  340. },
  341. },
  342. },
  343. },
  344. },
  345. };
  346. return (
  347. <Modal
  348. maskClosable={false}
  349. width="50vw"
  350. title={data?.id ? '编辑' : '新增'}
  351. onCancel={() => close()}
  352. onOk={() => save()}
  353. visible={visible}
  354. >
  355. <Form form={form} labelCol={5} wrapperCol={16} layout="vertical">
  356. <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} />
  357. </Form>
  358. </Modal>
  359. );
  360. };
  361. export default Save;