debugging.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <template>
  2. <div class="content" v-if="interfaceData">
  3. <div class="head">
  4. <Input v-model="interfaceData.path">
  5. <template #prepend>
  6. <Select v-model="interfaceData.method" style="width: 120px">
  7. <Option v-for="(item, index) in requestTypeList" :key="index" :value="item.value">{{ item.label }}</Option>
  8. </Select>
  9. </template>
  10. </Input>
  11. <Button class="ml20" type="primary" @click="requestData">请求</Button>
  12. <Button v-if="codes" class="ml10 copy-btn" type="success" @click="insertCopy()">复制结果</Button>
  13. </div>
  14. <div class="params">
  15. <Tabs class="mt10" v-model="paramsType" @on-click="changeTab">
  16. <TabPane label="Params" name="Params"> </TabPane>
  17. <TabPane label="Body" name="Body"> </TabPane>
  18. <TabPane label="Header" name="Header"> </TabPane>
  19. </Tabs>
  20. <div v-show="paramsType === 'Params'">
  21. <vxe-table
  22. class="mt10"
  23. resizable
  24. show-overflow
  25. keep-source
  26. ref="xTable"
  27. row-id="id"
  28. :print-config="{}"
  29. :export-config="{}"
  30. :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
  31. :data="interfaceData.request"
  32. >
  33. <vxe-column field="attribute" width="150" title="属性" tree-node :edit-render="{}">
  34. <template #default="{ row }">
  35. <vxe-input v-model="row.attribute" type="text"></vxe-input>
  36. </template>
  37. </vxe-column>
  38. <vxe-column field="value" title="参数值" :edit-render="{}">
  39. <template #default="{ row }">
  40. <vxe-input v-model="row.value" type="text"></vxe-input>
  41. </template>
  42. </vxe-column>
  43. <vxe-column field="type" title="类型" width="120" :edit-render="{}">
  44. <template #default="{ row }">
  45. <vxe-select
  46. v-model="row.type"
  47. transfer
  48. @change="
  49. (val) => {
  50. handleChange(val, row, 'xTable');
  51. }
  52. "
  53. >
  54. <vxe-option
  55. v-for="item in typeList"
  56. :key="item.value"
  57. :value="item.value"
  58. :label="item.label"
  59. ></vxe-option>
  60. </vxe-select>
  61. </template>
  62. </vxe-column>
  63. <!-- <vxe-column field="must" title="必填" width="50" :edit-render="{}">
  64. <template #default="{ row }">
  65. <span>{{ row.must == '1' ? '是' : '否' }}</span>
  66. </template>
  67. </vxe-column>
  68. <vxe-column field="trip" width="150" title="说明" :edit-render="{}">
  69. <template #default="{ row }">
  70. <vxe-input v-model="row.trip" type="text"></vxe-input>
  71. </template>
  72. </vxe-column> -->
  73. <vxe-column title="操作" width="120">
  74. <template #default="{ row }">
  75. <vxe-button
  76. type="text"
  77. v-if="['array', 'object'].includes(row.type)"
  78. status="primary"
  79. @click="insertRow(row, 'xTable')"
  80. >插入</vxe-button
  81. >
  82. <vxe-button type="text" status="primary" @click="removeRow(row, 'xTable')">删除</vxe-button>
  83. </template>
  84. </vxe-column>
  85. </vxe-table>
  86. <Button class="mt10" type="primary" @click="insertEvent('xTable')">添加参数</Button>
  87. </div>
  88. <div v-show="paramsType === 'Body'">
  89. <RadioGroup v-model="bodyType" class="mt10">
  90. <Radio label="form-data"></Radio>
  91. <Radio label="json"></Radio>
  92. </RadioGroup>
  93. <vxe-table
  94. v-if="bodyType == 'form-data'"
  95. class="mt10"
  96. resizable
  97. show-overflow
  98. keep-source
  99. ref="yTable"
  100. row-id="id"
  101. :print-config="{}"
  102. :export-config="{}"
  103. :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
  104. :data="interfaceData.request_body"
  105. >
  106. <vxe-column field="attribute" width="150" title="属性" tree-node :edit-render="{}">
  107. <template #default="{ row }">
  108. <vxe-input v-model="row.attribute" type="text"></vxe-input>
  109. </template>
  110. </vxe-column>
  111. <vxe-column field="value" title="参数值" :edit-render="{}">
  112. <template #default="{ row }">
  113. <vxe-input v-model="row.value" type="text"></vxe-input>
  114. </template>
  115. </vxe-column>
  116. <vxe-column field="type" title="类型" width="120" :edit-render="{}">
  117. <template #default="{ row }">
  118. <vxe-select
  119. v-model="row.type"
  120. transfer
  121. @change="
  122. (val) => {
  123. handleChange(val, row, 'yTable');
  124. }
  125. "
  126. >
  127. <vxe-option
  128. v-for="item in typeList"
  129. :key="item.value"
  130. :value="item.value"
  131. :label="item.label"
  132. ></vxe-option>
  133. </vxe-select>
  134. </template>
  135. </vxe-column>
  136. <!-- <vxe-column field="must" title="必填" width="50" :edit-render="{}">
  137. <template #default="{ row }">
  138. <span>{{ row.must == '1' ? '是' : '否' }}</span>
  139. </template>
  140. </vxe-column>
  141. <vxe-column field="trip" title="说明" width="150" :edit-render="{}">
  142. <template #default="{ row }">
  143. <vxe-input v-model="row.trip" type="text"></vxe-input>
  144. </template>
  145. </vxe-column> -->
  146. <vxe-column title="操作" width="120">
  147. <template #default="{ row }">
  148. <vxe-button
  149. type="text"
  150. v-if="['array', 'object'].includes(row.type)"
  151. status="primary"
  152. @click="insertRow(row, 'yTable')"
  153. >插入</vxe-button
  154. >
  155. <vxe-button type="text" status="primary" @click="removeRow(row, 'yTable')">删除</vxe-button>
  156. </template>
  157. </vxe-column>
  158. </vxe-table>
  159. <div v-else>
  160. <Input v-model="jsonBody" type="textarea" :rows="8" placeholder="请求数据" />
  161. </div>
  162. <Button v-if="bodyType == 'form-data'" class="mt10" type="primary" @click="insertEvent('yTable')"
  163. >添加参数</Button
  164. >
  165. </div>
  166. <div v-show="paramsType === 'Header'">
  167. <vxe-table
  168. class="mt10"
  169. resizable
  170. show-overflow
  171. keep-source
  172. ref="zTable"
  173. row-id="id"
  174. :print-config="{}"
  175. :export-config="{}"
  176. :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
  177. :data="interfaceData.headerData"
  178. >
  179. <vxe-column field="attribute" width="300" title="属性" tree-node :edit-render="{}">
  180. <template #default="{ row }">
  181. <vxe-input v-model="row.attribute" type="text"></vxe-input>
  182. </template>
  183. </vxe-column>
  184. <vxe-column field="value" title="参数值" :edit-render="{}">
  185. <template #default="{ row }">
  186. <vxe-input v-model="row.value" type="text"></vxe-input>
  187. </template>
  188. </vxe-column>
  189. <vxe-column title="操作" width="100">
  190. <template #default="{ row }">
  191. <vxe-button
  192. type="text"
  193. v-if="['array', 'object'].includes(row.type)"
  194. status="primary"
  195. @click="insertRow(row, 'zTable')"
  196. >插入</vxe-button
  197. >
  198. <vxe-button type="text" status="primary" @click="removeRow(row, 'zTable')">删除</vxe-button>
  199. </template>
  200. </vxe-column>
  201. </vxe-table>
  202. <Button class="mt10" type="primary" @click="insertEvent('zTable')">添加参数</Button>
  203. <!-- <h4 class="mt10 title">全局Header参数</h4>
  204. <vxe-table
  205. class="mt10"
  206. resizable
  207. show-overflow
  208. keep-source
  209. ref="zaTable"
  210. row-id="id"
  211. :print-config="{}"
  212. :export-config="{}"
  213. :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
  214. :data="interfaceData.allHeaderData"
  215. >
  216. <vxe-column field="attribute" width="300" title="属性" tree-node :edit-render="{}">
  217. <template #default="{ row }">
  218. <span>{{ row.attribute || '' }}</span>
  219. </template>
  220. </vxe-column>
  221. <vxe-column field="value" title="参数值" :edit-render="{}">
  222. <template #default="{ row }">
  223. <span>{{ row.value || '' }}</span>
  224. </template>
  225. </vxe-column>
  226. <vxe-column field="type" title="类型" width="200" :edit-render="{}">
  227. <template #default="{ row }">
  228. <span>{{ row.type || '' }}</span>
  229. </template>
  230. </vxe-column>
  231. <vxe-column field="trip" title="说明" :edit-render="{}">
  232. <template #default="{ row }">
  233. <span>{{ row.trip || '' }}</span>
  234. </template>
  235. </vxe-column>
  236. </vxe-table> -->
  237. </div>
  238. </div>
  239. <div class="res mt10 mb10" v-if="codes">
  240. <MonacoEditor :codes="codes" :readOnly="true" />
  241. </div>
  242. </div>
  243. </template>
  244. <script>
  245. import request from './request';
  246. import MonacoEditor from './components/MonacoEditor.vue';
  247. import vuedraggable from 'vuedraggable';
  248. import { getCookies } from '@/libs/util';
  249. function requestMethod(url, method, params, data, headerItem) {
  250. return request({
  251. url,
  252. method,
  253. params,
  254. data,
  255. headerItem,
  256. });
  257. }
  258. export default {
  259. components: { MonacoEditor },
  260. props: {
  261. formValidate: {
  262. type: Object,
  263. default: () => {
  264. return {};
  265. },
  266. },
  267. requestTypeList: {
  268. type: Array,
  269. default: () => {
  270. return [];
  271. },
  272. },
  273. typeList: {
  274. type: Array,
  275. default: () => {
  276. return [];
  277. },
  278. },
  279. apiType: {
  280. type: String,
  281. default: 'adminapi',
  282. },
  283. },
  284. data() {
  285. return {
  286. bodyType: 'form-data',
  287. interfaceData: undefined,
  288. paramsType: 'Params',
  289. editor: '', //当前编辑器对象
  290. codes: '',
  291. jsonBody: '',
  292. };
  293. },
  294. created() {
  295. this.interfaceData = this.formValidate;
  296. this.interfaceData.request_body = JSON.parse(JSON.stringify(this.interfaceData.request));
  297. },
  298. mounted() {},
  299. methods: {
  300. async handleChange(e, row, type) {
  301. if (e.value !== 'array' && e.value !== 'object') {
  302. if (row.children.length) {
  303. let arr = this.$refs[type].getTableData().tableData;
  304. let id = row.children[0].parentId;
  305. const $table = this.$refs[type];
  306. for (let i = 0; i < arr.length; i++) {
  307. if (arr[i].parentId == id) {
  308. await $table.remove(arr[i]);
  309. }
  310. }
  311. }
  312. }
  313. },
  314. insertCopy() {
  315. this.$copyText(this.codes)
  316. .then((message) => {
  317. this.$Message.success('复制成功');
  318. })
  319. .catch((err) => {
  320. this.$Message.error('复制失败');
  321. });
  322. },
  323. async requestData() {
  324. let url, method, params, body, headers;
  325. url = this.apiType + '/' + this.interfaceData.path;
  326. method = this.interfaceData.method;
  327. params = this.filtersData((await this.$refs.xTable.getTableData().tableData) || []);
  328. body =
  329. this.bodyType === 'json'
  330. ? this.jsonBody
  331. : this.filtersData((await this.$refs.yTable.getTableData().tableData) || []);
  332. let h = this.filtersData((await this.$refs.zTable.getTableData().tableData) || []);
  333. headers = h;
  334. this.codes = '';
  335. console.log(url);
  336. requestMethod(url, method, params, body, headers)
  337. .then((res) => {
  338. if (!res) return this.$Message.error('接口异常');
  339. this.codes = JSON.stringify(res);
  340. })
  341. .catch((err) => {
  342. if (!err) return this.$Message.error('接口异常');
  343. this.codes = JSON.stringify(err);
  344. });
  345. },
  346. filtersData(arr) {
  347. try {
  348. let x = {};
  349. arr.map((e) => {
  350. if (!e.parentId) {
  351. for (let i in e) {
  352. if (i == 'attribute') {
  353. if (e.type === 'object') {
  354. let obj = {};
  355. e.children.map((item, index) => {
  356. obj = this.filtersObj(item, 1);
  357. });
  358. x[e[i]] = obj;
  359. } else if (e.type !== 'array') {
  360. x[e[i]] = e.value || '';
  361. } else {
  362. let arr = [];
  363. e.children.map((item, index) => {
  364. arr[index] = this.filtersObj(item);
  365. });
  366. x[e[i]] = arr;
  367. }
  368. }
  369. }
  370. }
  371. });
  372. return x;
  373. } catch (error) {
  374. console.log(error);
  375. }
  376. },
  377. // type 1 为obj属性
  378. filtersObj(obj, type) {
  379. let x = {};
  380. for (let i in obj) {
  381. if (i == 'attribute') {
  382. if (obj.type === 'object') {
  383. let oj = {};
  384. obj.children.map((item, index) => {
  385. oj[obj.attribute] = this.filtersObj(item);
  386. });
  387. x = oj;
  388. } else if (obj.type !== 'array') {
  389. if (type) {
  390. x[obj.attribute] = obj.value || '';
  391. } else {
  392. x[obj[i]] = obj.value || '';
  393. }
  394. } else {
  395. let arr = [];
  396. obj.children.map((item, index) => {
  397. arr[index] = this.filtersObj(item);
  398. });
  399. x[obj[i]] = arr;
  400. }
  401. }
  402. }
  403. return x;
  404. },
  405. changeTab(name) {
  406. if (name === 'Header') {
  407. if (!this.$refs.zTable.getTableData().tableData.length) {
  408. this.insertEvent('zTable', {
  409. attribute: 'Authori-Zation',
  410. value: 'Bearer ' + getCookies('token'),
  411. });
  412. this.insertEvent('zaTable');
  413. }
  414. }
  415. },
  416. async insertEvent(type, d) {
  417. const $table = this.$refs[type];
  418. let newRow;
  419. if (type == 'xTable') {
  420. newRow = {
  421. attribute: '',
  422. type: 'string',
  423. must: 0,
  424. value: '',
  425. trip: '',
  426. };
  427. } else if (type == 'yTable') {
  428. newRow = {
  429. attribute: '',
  430. type: 'string',
  431. value: '',
  432. must: 0,
  433. trip: '',
  434. };
  435. } else if (type == 'zTable') {
  436. newRow = {
  437. attribute: '',
  438. type: '',
  439. value: '',
  440. trip: '',
  441. };
  442. newRow = { ...newRow, ...d };
  443. } else if (type == 'zaTable') {
  444. newRow = {
  445. attribute: 'token',
  446. type: 'string',
  447. value: '',
  448. must: 0,
  449. trip: '',
  450. };
  451. } else {
  452. newRow = {
  453. code: '',
  454. value: '',
  455. solution: '',
  456. };
  457. }
  458. const { row: data } = await $table.insertAt(newRow, -1);
  459. await $table.setActiveCell(data, 'name');
  460. },
  461. async insertRow(currRow, type) {
  462. const $table = this.$refs[type];
  463. // 如果 null 则插入到目标节点顶部
  464. // 如果 -1 则插入到目标节点底部
  465. // 如果 row 则有插入到效的目标节点该行的位置
  466. let record;
  467. if (type == 'xTable') {
  468. record = {
  469. attribute: '',
  470. type: 'string',
  471. must: 0,
  472. value: '',
  473. trip: '',
  474. id: Date.now(),
  475. parentId: currRow.id, // 需要指定父节点,自动插入该节点中
  476. };
  477. } else {
  478. record = {
  479. code: '',
  480. value: '',
  481. solution: '',
  482. id: Date.now(),
  483. parentId: currRow.id, // 需要指定父节点,自动插入该节点中
  484. };
  485. }
  486. const { row: newRow } = await $table.insertAt(record, -1);
  487. await $table.setTreeExpand(currRow, true); // 将父节点展开
  488. await $table.setActiveRow(newRow); // 插入子节点
  489. },
  490. async removeRow(row, type) {
  491. const $table = this.$refs[type];
  492. await $table.remove(row);
  493. },
  494. },
  495. };
  496. </script>
  497. <style>
  498. .vxe-select--panel.is--transfer {
  499. z-index: 99999 !important;
  500. }
  501. </style>
  502. <style lang="scss" scoped>
  503. .content {
  504. padding: 12px;
  505. .head {
  506. display: flex;
  507. align-items: center;
  508. .item {
  509. display: flex;
  510. align-items: center;
  511. margin-bottom: 12px;
  512. font-size: 14px;
  513. .title {
  514. margin-right: 14px;
  515. }
  516. }
  517. }
  518. }
  519. .copy-btn {
  520. display: flex;
  521. justify-content: right;
  522. }
  523. /deep/ .monaco-editor {
  524. min-height: 700px;
  525. }
  526. </style>