index.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 { 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 [isBeginning, setIsBeginning] = useState<any>(true);
  47. const schema: ISchema = {
  48. type: 'object',
  49. properties: {
  50. properties: {
  51. type: 'array',
  52. 'x-decorator': 'FormItem',
  53. 'x-component': 'ArrayTable',
  54. 'x-component-props': {
  55. pagination: {
  56. pageSize: 9999,
  57. },
  58. style: {
  59. maxHeight: 260,
  60. overflowY: 'auto',
  61. },
  62. scroll: { y: 240 },
  63. },
  64. items: {
  65. type: 'object',
  66. properties: {
  67. column1: {
  68. type: 'void',
  69. 'x-component': 'ArrayTable.Column',
  70. 'x-component-props': {
  71. title: '属性ID',
  72. },
  73. properties: {
  74. id: {
  75. 'x-decorator': 'FormItem',
  76. 'x-component': 'FAutoComplete',
  77. 'x-reactions': '{{useAsyncDataSource(getProperty)}}',
  78. },
  79. },
  80. },
  81. column2: {
  82. type: 'void',
  83. 'x-component': 'ArrayTable.Column',
  84. 'x-component-props': {
  85. title: '当前值',
  86. },
  87. properties: {
  88. current: {
  89. 'x-decorator': 'FormItem',
  90. 'x-component': 'Input',
  91. },
  92. },
  93. },
  94. column3: {
  95. type: 'void',
  96. 'x-component': 'ArrayTable.Column',
  97. 'x-component-props': {
  98. title: '上一值',
  99. },
  100. properties: {
  101. last: {
  102. 'x-decorator': 'FormItem',
  103. 'x-component': 'Input',
  104. },
  105. },
  106. },
  107. column6: {
  108. type: 'void',
  109. 'x-component': 'ArrayTable.Column',
  110. 'x-component-props': {
  111. title: '',
  112. dataIndex: 'operations',
  113. width: 50,
  114. fixed: 'right',
  115. },
  116. properties: {
  117. item: {
  118. type: 'void',
  119. 'x-component': 'FormItem',
  120. properties: {
  121. remove: {
  122. type: 'void',
  123. 'x-component': 'ArrayTable.Remove',
  124. },
  125. },
  126. },
  127. },
  128. },
  129. },
  130. },
  131. properties: {
  132. add: {
  133. type: 'void',
  134. 'x-component': 'ArrayTable.Addition',
  135. title: '添加条目',
  136. },
  137. },
  138. },
  139. },
  140. };
  141. const [subscribeTopic] = useSendWebsocketMessage();
  142. const runScript = async () => {
  143. const propertiesList = await DB.getDB().table('properties').toArray();
  144. const _properties = form.values?.properties.map((item: any) => {
  145. const _item = propertiesList.find((i) => i.id === item.id);
  146. return { ...item, type: _item?.valueType?.type };
  147. });
  148. ws.current = subscribeTopic?.(
  149. `virtual-property-debug-${State.property}-${new Date().getTime()}`,
  150. '/virtual-property-debug',
  151. {
  152. virtualId: `${virtualIdRef.current}-virtual-id`,
  153. property: State.property,
  154. virtualRule: {
  155. ...props.virtualRule,
  156. },
  157. properties: _properties || [],
  158. },
  159. )?.subscribe((data: WebsocketPayload) => {
  160. State.log.push({ time: new Date().getTime(), content: JSON.stringify(data.payload) });
  161. setIsBeginning(true);
  162. });
  163. };
  164. return (
  165. <div className={styles.container}>
  166. <div className={styles.left}>
  167. <div className={styles.header}>
  168. <div>
  169. <div className={styles.title}>
  170. 属性赋值
  171. <div className={styles.description}>请对上方规则使用的属性进行赋值</div>
  172. </div>
  173. {!isBeginning && props.virtualRule?.type === 'window' && (
  174. <div
  175. className={styles.action}
  176. onClick={() => {
  177. runScript();
  178. }}
  179. >
  180. <a>发送数据</a>
  181. </div>
  182. )}
  183. </div>
  184. </div>
  185. <Form form={form}>
  186. <SchemaField schema={schema} scope={{ useAsyncDataSource, getProperty }} />
  187. </Form>
  188. </div>
  189. <div className={styles.right}>
  190. <div className={styles.header}>
  191. <div className={styles.title}>
  192. <div>运行结果</div>
  193. </div>
  194. <div className={styles.action}>
  195. <div>
  196. {isBeginning ? (
  197. <a
  198. onClick={() => {
  199. setIsBeginning(false);
  200. runScript();
  201. }}
  202. >
  203. 开始运行
  204. </a>
  205. ) : (
  206. <a
  207. onClick={() => {
  208. setIsBeginning(true);
  209. // ws.current && ws.current.unsubscribe();
  210. }}
  211. >
  212. 停止运行
  213. </a>
  214. )}
  215. </div>
  216. <div>
  217. <a
  218. onClick={() => {
  219. // console.log(props.virtualRule, 222222222222)
  220. // console.log(virtualIdRef.current)
  221. State.log = [];
  222. }}
  223. >
  224. 清空
  225. </a>
  226. </div>
  227. </div>
  228. </div>
  229. <div className={styles.log}>
  230. <Descriptions>
  231. {State.log.map((item) => (
  232. <Descriptions.Item
  233. label={moment(item.time).format('HH:mm:ss')}
  234. key={item.time}
  235. span={3}
  236. >
  237. <Tooltip placement="top" title={item.content}>
  238. {item.content}
  239. </Tooltip>
  240. </Descriptions.Item>
  241. ))}
  242. </Descriptions>
  243. </div>
  244. </div>
  245. </div>
  246. );
  247. });
  248. export default Debug;