You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

513 lines
15 KiB

12 months ago
12 months ago
12 months ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
12 months ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
12 months ago
11 months ago
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>消毒API测试页面</title>
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  7. <script src="./vue.global.js"></script>
  8. <script src="./dayjs/dayjs.min.js"></script>
  9. <script src="./dayjs/plugin/customParseFormat.js"></script>
  10. <script src="./dayjs/plugin/weekday.js"></script>
  11. <script src="./dayjs/plugin/localeData.js"></script>
  12. <script src="./dayjs/plugin/weekOfYear.js"></script>
  13. <script src="./dayjs/plugin/weekYear.js"></script>
  14. <script src="./dayjs/plugin/advancedFormat.js"></script>
  15. <script src="./dayjs/plugin/quarterOfYear.js"></script>
  16. <script src="./ant-design-vue/antd.min.js"></script>
  17. <link href="./ant-design-vue/reset.min.css" rel="stylesheet">
  18. <script src="./virtual-keyboard.js"></script>
  19. </head>
  20. <body>
  21. <div id="app" class="h-full">
  22. <a-row class="h-full">
  23. <a-col :span="3" class="sidebar">
  24. <div style="display:flex;padding:5px;">
  25. <a-input v-model:value="wsUrl" style="margin-right:5px;"></a-input>
  26. <a-button v-if="null === ws" @click="actionConnect" type="primary" class="connect-btn">连接</a-button>
  27. <a-button v-else @click="actionDisconnect" type="primary" danger class="disconnect-btn">断开</a-button>
  28. </div>
  29. <a-menu mode="inline" :items="actionMenuItems" @click="actionGroupMenuItemClick" class="menu"></a-menu>
  30. </a-col>
  31. <a-col :span="15" style="display:flex;flex-direction: column;" class="main-content">
  32. <div v-if="null !== actionActiveGroup" class="action-container">
  33. <div v-for="item in actionActiveGroup.items" class="action-item">
  34. <a-button type="primary" class="action-btn"
  35. @click="actionActionExecute(item)">
  36. {{item.fnDispName}}</a-button>
  37. <div v-for="param in item.params" >
  38. <a-select v-if="item.paramInfoMap[param].isEnum" class="param-input" v-model:value="item.values[param]"
  39. :placeholder="param" :dropdownMatchSelectWidth="false">
  40. <a-select-option v-for="enumValue in item.paramInfoMap[param].enumValues" :key="enumValue"
  41. :value="enumValue">{{enumValue}}</a-select-option>
  42. </a-select>
  43. <a-input v-else v-model:value="item.values[param]" :placeholder="param" class="param-input"
  44. @click="showKeyboard(item.values, param)"></a-input>
  45. </div>
  46. </div>
  47. </div>
  48. </a-col>
  49. <a-col :span="6" class="log-section">
  50. <div style="text-align: right; padding:10px;">
  51. <a-button @click="actionClearLogs" class="clear-btn">清空日志</a-button>
  52. </div>
  53. <div class="log-container">
  54. <a-collapse>
  55. <a-collapse-panel v-for="(entry,index) in logs" :key="index" :header="entry.title">
  56. <div class="log-content">
  57. {{entry.content}}
  58. </div>
  59. </a-collapse-panel>
  60. </a-collapse>
  61. </div>
  62. <div class="report-container">
  63. <a-collapse>
  64. <a-collapse-panel v-for="(entry,index) in reports" :key="index" :header="entry.title">
  65. <div class="report-content">
  66. {{entry.content}}
  67. </div>
  68. </a-collapse-panel>
  69. </a-collapse>
  70. </div>
  71. </a-col>
  72. </a-row>
  73. </div>
  74. <style>
  75. :root {
  76. --primary-color: #1890ff; /* 主蓝色 */
  77. --primary-hover: #40a9ff2a; /* 悬停蓝色 */
  78. --success-color: #52c41a; /* 成功绿色 */
  79. --warning-color: #faad14; /* 警告黄色 */
  80. --error-color: #f5222d; /* 错误红色 */
  81. --text-color: rgba(0, 0, 0, 0.85); /* 主文本颜色 */
  82. --text-color-secondary: rgba(0, 0, 0, 0.45); /* 次文本颜色 */
  83. --border-color: #d9d9d9; /* 边框颜色 */
  84. --background-color: #f5f5f5; /* 背景色 */
  85. --sidebar-bg: #f0f2f5; /* 侧边栏背景 */
  86. --card-bg: #ffffff; /* 卡片背景 */
  87. --action-btn-bg: #1890ff; /* 操作按钮背景 */
  88. --action-btn-hover: #40a9ff; /* 操作按钮悬停 */
  89. --disinfect-color: #1890ff; /* 消毒主题色 - */
  90. }
  91. body {
  92. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  93. color: var(--text-color);
  94. background-color: var(--background-color);
  95. margin: 0;
  96. padding: 0;
  97. height: 100vh;
  98. }
  99. .h-full {
  100. height: 100%;
  101. }
  102. /* 侧边栏样式 */
  103. .sidebar {
  104. background-color: var(--sidebar-bg);
  105. border-right: 1px solid var(--border-color);
  106. height: 100%;
  107. padding: 10px;
  108. }
  109. .menu {
  110. border-right: none;
  111. }
  112. .menu .ant-menu-item {
  113. margin: 4px 0;
  114. border-radius: 4px;
  115. }
  116. .menu .ant-menu-item:hover {
  117. background-color: #e6f7ff;
  118. }
  119. .menu .ant-menu-item-selected {
  120. background-color: var(--primary-color);
  121. color: white;
  122. }
  123. /* 主内容区样式 */
  124. .main-content {
  125. padding: 15px;
  126. background-color: #f0f2f5;
  127. }
  128. .action-container {
  129. height: 0;
  130. flex-grow: 1;
  131. overflow-y: auto;
  132. margin-bottom: 10px;
  133. padding: 10px;
  134. background-color:#f0f2f5;
  135. border-radius: 4px;
  136. box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
  137. }
  138. .action-item {
  139. display: flex;
  140. flex-direction: row;
  141. margin-bottom: 10px;
  142. padding: 10px;
  143. background-color: var(--primary-hover);
  144. border-radius: 4px;
  145. border-left: 3px solid var(--primary-color);
  146. transition: all 0.3s;
  147. }
  148. .action-item:hover {
  149. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
  150. }
  151. .action-btn {
  152. margin-right: 10px;
  153. min-width: 230px;
  154. font-weight: bold;
  155. text-align: left;
  156. background-color: var(--disinfect-color);
  157. border-color: var(--disinfect-color);
  158. }
  159. .action-btn:hover {
  160. background-color: #36cfc9;
  161. border-color: #36cfc9;
  162. }
  163. .param-input {
  164. margin-right: 10px;
  165. width: 150px;
  166. }
  167. /* 日志区域样式 */
  168. .log-section {
  169. height: 100%;
  170. display: flex;
  171. flex-direction: column;
  172. background-color: white;
  173. border-left: 1px solid var(--border-color);
  174. padding: 0 10px;
  175. }
  176. .clear-btn {
  177. color: var(--text-color-secondary);
  178. border-color: var(--border-color);
  179. }
  180. .clear-btn:hover {
  181. color: var(--primary-color);
  182. border-color: var(--primary-color);
  183. }
  184. .log-container, .report-container {
  185. height: 600px;
  186. flex-grow: 1;
  187. overflow-y: auto;
  188. margin-bottom: 10px;
  189. background-color: white;
  190. border-radius: 4px;
  191. }
  192. .report-container {
  193. height: 600px;
  194. }
  195. .ant-collapse {
  196. background-color: white;
  197. border: none;
  198. }
  199. .ant-collapse-item {
  200. border-bottom: 1px solid var(--border-color);
  201. }
  202. .ant-collapse-header {
  203. font-weight: 500;
  204. color: var(--text-color);
  205. }
  206. .log-content, .report-content {
  207. white-space: pre-wrap;
  208. font-family: 'Courier New', Courier, monospace;
  209. font-size: 13px;
  210. color: var(--text-color);
  211. background-color: #fafafa;
  212. padding: 10px;
  213. border-radius: 2px;
  214. }
  215. /* 连接按钮样式 */
  216. .connect-btn {
  217. background-color: var(--success-color);
  218. border-color: var(--success-color);
  219. }
  220. .connect-btn:hover {
  221. background-color: #73d13d;
  222. border-color: #73d13d;
  223. }
  224. .disconnect-btn {
  225. background-color: var(--error-color);
  226. border-color: var(--error-color);
  227. }
  228. .disconnect-btn:hover {
  229. background-color: #ff4d4f;
  230. border-color: #ff4d4f;
  231. }
  232. /* 响应式调整 */
  233. @media (max-width: 1200px) {
  234. .action-item {
  235. flex-direction: column;
  236. }
  237. .action-btn {
  238. margin-bottom: 8px;
  239. width: 100%;
  240. }
  241. .param-input {
  242. width: 100%;
  243. margin-right: 0;
  244. margin-bottom: 8px;
  245. }
  246. }
  247. </style>
  248. <!-- 保留原有的script内容 -->
  249. <script>
  250. // 原有的JavaScript代码保持不变
  251. const { createApp } = Vue
  252. createApp({
  253. data() {
  254. return {
  255. actions: [],
  256. menuList: [],
  257. actionActiveGroup: null,
  258. rawRequestContent: '',
  259. logs: [],
  260. reports: [],
  261. wsUrl: "ws://" + window.location.hostname + ":19001",
  262. ws: null,
  263. wsMessageIndex: 0,
  264. requests: {},
  265. reportId: 0,
  266. map: new Map(),
  267. counter: 0
  268. }
  269. },
  270. computed: {
  271. actionMenuItems() {
  272. return this.menuList.map(i => ({ key: i.className, label: i.classDispName }));
  273. }
  274. },
  275. mounted() {
  276. },
  277. methods: {
  278. // connect
  279. getReportId(str) {
  280. // 如果字符串已存在,返回已存储的id
  281. if (this.map.has(str)) {
  282. return this.map.get(str);
  283. }
  284. // 否则,生成一个新的ID
  285. const id = this.counter++;
  286. this.map.set(str, id);
  287. return id;
  288. },
  289. actionConnect() {
  290. this.ws = new WebSocket(this.wsUrl);
  291. this.ws.onclose = () => this.ws = null;
  292. this.ws.onmessage = event => {
  293. let data = JSON.parse(event.data);
  294. let responseEntry = {};
  295. responseEntry.title = `${data.fromClass}.${data.fromFn}`;
  296. responseEntry.content = JSON.stringify(data, null, 2);
  297. if ('Ack' === data.messageType && 'FNScheduler' === data.fromClass && 'geFnList' === data.fromFn) {
  298. this.actionListReload(data);
  299. }
  300. if ('Ack' === data.messageType && 'FNScheduler' === data.fromClass && 'geMenuList' === data.fromFn) {
  301. this.menuListReload(data);
  302. }
  303. if ('Ack' === data.messageType) {
  304. this.logs.push(responseEntry);
  305. }
  306. if ('Report' === data.messageType) {
  307. let reportuuid = data.fromClass + '.' + data.fromFn;
  308. let reportId = this.getReportId(reportuuid);
  309. // this.reports.unshift(reportId,responseEntry);
  310. this.reports[reportId] = responseEntry;
  311. }
  312. };
  313. this.ws.onopen = () => {
  314. {
  315. this.wsMessageIndex++;
  316. let request = {};
  317. request.messageId = this.wsMessageIndex;
  318. request.timeStamp = Math.floor(Date.now() / 1000);
  319. request.messageType = 'Command';
  320. request.className = 'FNScheduler';
  321. request.fnName = 'geFnList';
  322. request.params = {};
  323. this.wsCall(request);
  324. }
  325. {
  326. this.wsMessageIndex++;
  327. let request = {};
  328. request.messageId = this.wsMessageIndex;
  329. request.timeStamp = Math.floor(Date.now() / 1000);
  330. request.messageType = 'Command';
  331. request.className = 'FNScheduler';
  332. request.fnName = 'geMenuList';
  333. request.params = {};
  334. this.wsCall(request);
  335. }
  336. }
  337. },
  338. // disconnect
  339. actionDisconnect() {
  340. this.ws.close();
  341. },
  342. // clear logs
  343. actionClearLogs() {
  344. this.logs = [];
  345. },
  346. showKeyboard(values, param) {
  347. // const inputElement = document.querySelector(`input[placeholder="${param}"]`);
  348. // if (inputElement) {
  349. // VirtualKeyboard.show(inputElement);
  350. // this.updateDisplayContainer(inputElement);
  351. // }
  352. },
  353. updateDisplayContainer(inputElement) {
  354. const displayContainer = document.querySelector('.virtual-keyboard-display');
  355. if (displayContainer) {
  356. inputElement.addEventListener('input', () => {
  357. displayContainer.textContent = inputElement.value;
  358. });
  359. }
  360. },
  361. menuListReload(response) {
  362. let data = response.rely;
  363. if (undefined === data) {
  364. return;
  365. }
  366. this.menuList = data;
  367. },
  368. // action list reload
  369. actionListReload(response) {
  370. let data = response.rely;
  371. if (undefined === data) {
  372. return;
  373. }
  374. this.actions = [];
  375. for (let action of data.fnlist) {
  376. let group = this.actions.find(i => i.key === action.className);
  377. if (undefined === group) {
  378. group = { key: action.className, items: [] };
  379. this.actions.push(group);
  380. }
  381. item = JSON.parse(JSON.stringify(action)); // Replace structuredClone with JSON.parse(JSON.stringify())
  382. item.values = {};
  383. item.paramInfoMap = {};
  384. for (let i = 0; i < item.paramsTypeInfo.length; i++) {
  385. let typeName = item.paramsTypeInfo[i];
  386. let info = data.typeInfoList.find(p => p.typeName === typeName);
  387. let name = item.params[i];
  388. item.paramInfoMap[name] = info;
  389. }
  390. group.items.push(item);
  391. }
  392. },
  393. // action group menu item click
  394. actionGroupMenuItemClick(event) {
  395. this.actionActiveGroup = this.actions.find(i => i.key === event.key);
  396. },
  397. // action execute
  398. actionActionExecute(item) {
  399. this.wsMessageIndex++;
  400. let request = {};
  401. request.messageId = this.wsMessageIndex;
  402. request.timeStamp = Math.floor(Date.now() / 1000);
  403. request.messageType = 'Command';
  404. request.className = item.className;
  405. request.fnName = item.fnName;
  406. request.params = {};
  407. for (let i = 0; i < item.params.length; i++) {
  408. let param = item.params[i];
  409. let type = item.paramsTypeInfo[i];
  410. let value = item.values[param];
  411. switch (type) {
  412. case 'json':
  413. case 'vector<bool>':
  414. case 'vector<json>':
  415. case 'vector<double>':
  416. case 'vector<int32_t>':
  417. case 'vector<string>': value = JSON.parse(value); break;
  418. case 'bool': value = '1' === value ? true : false; break;
  419. case 'float':
  420. case 'double':
  421. case 'int': value *= 1; break;
  422. case 'string': /* nothing to do here */ break;
  423. default: /* throw an exception is required. */
  424. }
  425. request.params[param] = value;
  426. }
  427. this.logs = [];
  428. this.wsCall(request);
  429. },
  430. // raw request send
  431. actionSendRawRequest() {
  432. let request = JSON.parse(this.rawRequestContent);
  433. this.wsCall(request);
  434. },
  435. // ws call
  436. wsCall(request) {
  437. if (null === this.ws) {
  438. return;
  439. }
  440. let requestEntry = {};
  441. requestEntry.title = `${request.messageType} : ${request.className}.${request.fnName}`;
  442. requestEntry.content = JSON.stringify(request, null, 2);
  443. this.logs.push(requestEntry);
  444. this.ws.send(JSON.stringify(request));
  445. },
  446. },
  447. })
  448. .use(antd)
  449. .mount('#app');
  450. </script>
  451. </body>
  452. </html>