index.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import styles from './index.less';
  2. import { createSchemaField, observer } from '@formily/react';
  3. import { ArrayTable, Form, FormItem, Input, Select } from '@formily/antd';
  4. import { useEffect, useMemo, useRef, useState } from 'react';
  5. import type { Field } from '@formily/core';
  6. import { createForm } from '@formily/core';
  7. import type { ISchema } from '@formily/json-schema';
  8. import FAutoComplete from '@/components/FAutoComplete';
  9. import { Descriptions, Tooltip } from 'antd';
  10. import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
  11. import type { WebsocketPayload } from '@/hooks/websocket/typings';
  12. import { State } from '@/components/FRuleEditor';
  13. import moment from 'moment';
  14. import DB from '@/db';
  15. import type { PropertyMetadata } from '@/pages/device/Product/typings';
  16. import { action } from '@formily/reactive';
  17. interface Props {
  18. virtualRule?: any;
  19. }
  20. const Debug = observer((props: Props) => {
  21. const SchemaField = createSchemaField({
  22. components: {
  23. FormItem,
  24. Input,
  25. Select,
  26. ArrayTable,
  27. FAutoComplete,
  28. },
  29. });
  30. const form = useMemo(() => createForm(), []);
  31. const useAsyncDataSource = (services: (arg0: Field) => Promise<any>) => (field: Field) => {
  32. field.loading = true;
  33. services(field).then(
  34. action.bound!((data: PropertyMetadata[]) => {
  35. field.dataSource = data.map((item) => ({
  36. label: item.name,
  37. value: item.id,
  38. }));
  39. field.loading = false;
  40. }),
  41. );
  42. };
  43. const getProperty = async () => DB.getDB().table('properties').toArray();
  44. const virtualIdRef = useRef(new Date().getTime());
  45. const ws = useRef<any>();
  46. const wsAgain = useRef<any>();
  47. const [isBeginning, setIsBeginning] = useState<any>(true);
  48. const schema: ISchema = {
  49. type: 'object',
  50. properties: {
  51. properties: {
  52. type: 'array',
  53. 'x-decorator': 'FormItem',
  54. 'x-component': 'ArrayTable',
  55. 'x-component-props': {
  56. pagination: {
  57. pageSize: 9999,
  58. },
  59. style: {
  60. maxHeight: 260,
  61. overflowY: 'auto',
  62. },
  63. scroll: { y: 240 },
  64. },
  65. items: {
  66. type: 'object',
  67. properties: {
  68. column1: {
  69. type: 'void',
  70. 'x-component': 'ArrayTable.Column',
  71. 'x-component-props': {
  72. title: '属性ID',
  73. },
  74. properties: {
  75. id: {
  76. 'x-decorator': 'FormItem',
  77. 'x-component': 'FAutoComplete',
  78. 'x-reactions': '{{useAsyncDataSource(getProperty)}}',
  79. },
  80. },
  81. },
  82. column2: {
  83. type: 'void',
  84. 'x-component': 'ArrayTable.Column',
  85. 'x-component-props': {
  86. title: '当前值',
  87. },
  88. properties: {
  89. current: {
  90. 'x-decorator': 'FormItem',
  91. 'x-component': 'Input',
  92. },
  93. },
  94. },
  95. column3: {
  96. type: 'void',
  97. 'x-component': 'ArrayTable.Column',
  98. 'x-component-props': {
  99. title: '上一值',
  100. },
  101. properties: {
  102. last: {
  103. 'x-decorator': 'FormItem',
  104. 'x-component': 'Input',
  105. },
  106. },
  107. },
  108. column6: {
  109. type: 'void',
  110. 'x-component': 'ArrayTable.Column',
  111. 'x-component-props': {
  112. title: '',
  113. dataIndex: 'operations',
  114. width: 50,
  115. fixed: 'right',
  116. },
  117. properties: {
  118. item: {
  119. type: 'void',
  120. 'x-component': 'FormItem',
  121. properties: {
  122. remove: {
  123. type: 'void',
  124. 'x-component': 'ArrayTable.Remove',
  125. },
  126. },
  127. },
  128. },
  129. },
  130. },
  131. },
  132. properties: {
  133. add: {
  134. type: 'void',
  135. 'x-component': 'ArrayTable.Addition',
  136. title: '添加条目',
  137. },
  138. },
  139. },
  140. },
  141. };
  142. const [subscribeTopic] = useSendWebsocketMessage();
  143. const runScript = async () => {
  144. const propertiesList = await DB.getDB().table('properties').toArray();
  145. const _properties = form.values?.properties.map((item: any) => {
  146. const _item = propertiesList.find((i) => i.id === item.id);
  147. return { ...item, type: _item?.valueType?.type };
  148. });
  149. if (ws.current) {
  150. ws.current.unsubscribe();
  151. }
  152. ws.current = subscribeTopic?.(
  153. `virtual-property-debug-${State.property}-${new Date().getTime()}`,
  154. '/virtual-property-debug',
  155. {
  156. virtualId: `${virtualIdRef.current}-virtual-id`,
  157. property: State.property,
  158. virtualRule: {
  159. ...props.virtualRule,
  160. },
  161. properties: _properties || [],
  162. },
  163. )?.subscribe(
  164. (data: WebsocketPayload) => {
  165. State.log.push({ time: new Date().getTime(), content: JSON.stringify(data.payload) });
  166. },
  167. // () => { },
  168. // () => {
  169. // setIsBeginning(true);
  170. // }
  171. );
  172. };
  173. const runScriptAgain = async () => {
  174. if (wsAgain.current) {
  175. wsAgain.current.unsubscribe();
  176. }
  177. const propertiesList = await DB.getDB().table('properties').toArray();
  178. const _properties = form.values?.properties.map((item: any) => {
  179. const _item = propertiesList.find((i) => i.id === item.id);
  180. return { ...item, type: _item?.valueType?.type };
  181. });
  182. wsAgain.current = subscribeTopic?.(
  183. `virtual-property-debug-${State.property}-${new Date().getTime()}`,
  184. '/virtual-property-debug',
  185. {
  186. virtualId: `${virtualIdRef.current}-virtual-id`,
  187. property: State.property,
  188. virtualRule: {
  189. ...props.virtualRule,
  190. },
  191. properties: _properties || [],
  192. },
  193. )?.subscribe(() => {
  194. // setIsBeginning(true);
  195. });
  196. };
  197. useEffect(() => {
  198. return () => {
  199. if (ws.current) {
  200. ws.current.unsubscribe();
  201. }
  202. if (wsAgain.current) {
  203. wsAgain.current.unsubscribe();
  204. }
  205. };
  206. }, []);
  207. return (
  208. <div className={styles.container}>
  209. <div className={styles.left}>
  210. <div className={styles.header}>
  211. <div>
  212. <div className={styles.title}>
  213. 属性赋值
  214. <div className={styles.description}>请对上方规则使用的属性进行赋值</div>
  215. </div>
  216. {!isBeginning && props.virtualRule?.type === 'window' && (
  217. <div
  218. className={styles.action}
  219. onClick={() => {
  220. runScriptAgain();
  221. }}
  222. >
  223. <a style={{ marginLeft: 75 }}>发送数据</a>
  224. </div>
  225. )}
  226. </div>
  227. </div>
  228. <Form form={form}>
  229. <SchemaField schema={schema} scope={{ useAsyncDataSource, getProperty }} />
  230. </Form>
  231. </div>
  232. <div className={styles.right}>
  233. <div className={styles.header}>
  234. <div className={styles.title}>
  235. <div>运行结果</div>
  236. </div>
  237. <div className={styles.action}>
  238. <div>
  239. {isBeginning ? (
  240. <a
  241. onClick={() => {
  242. setIsBeginning(false);
  243. runScript();
  244. }}
  245. >
  246. 开始运行
  247. </a>
  248. ) : (
  249. <a
  250. onClick={() => {
  251. setIsBeginning(true);
  252. if (ws.current) {
  253. ws.current.unsubscribe();
  254. }
  255. }}
  256. >
  257. 停止运行
  258. </a>
  259. )}
  260. </div>
  261. <div>
  262. <a
  263. onClick={() => {
  264. // console.log(props.virtualRule, 222222222222)
  265. // console.log(virtualIdRef.current)
  266. State.log = [];
  267. }}
  268. >
  269. 清空
  270. </a>
  271. </div>
  272. </div>
  273. </div>
  274. <div className={styles.log}>
  275. <Descriptions>
  276. {State.log.map((item) => (
  277. <Descriptions.Item
  278. label={moment(item.time).format('HH:mm:ss')}
  279. key={item.time}
  280. span={3}
  281. >
  282. <Tooltip placement="top" title={item.content}>
  283. {item.content}
  284. </Tooltip>
  285. </Descriptions.Item>
  286. ))}
  287. </Descriptions>
  288. </div>
  289. </div>
  290. </div>
  291. );
  292. });
  293. export default Debug;