StoredProcedureEditForm.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. <!-- eslint-disable vue/no-parsing-error -->
  2. <template>
  3. <div
  4. v-loading="saveLoading"
  5. class="inner-container"
  6. :element-loading-text="saveText"
  7. >
  8. <el-scrollbar class="data-set-scrollbar">
  9. <div class="header">
  10. <el-page-header class="bs-el-page-header">
  11. <template slot="content">
  12. <div class="page-header">
  13. <div class="page-header-left">
  14. {{ !isEdit ? '存储过程数据集详情' : dataForm.id ? '编辑存储过程数据集' : '新增存储过程数据集' }}
  15. </div>
  16. <div class="page-header-right">
  17. <el-button
  18. class="bs-el-button-default"
  19. @click="openNewWindow('https://www.yuque.com/chuinixiongkou/bigscreen/procedure_dataset')"
  20. >
  21. 帮助
  22. </el-button>
  23. <el-button
  24. v-if="isEdit"
  25. type="primary"
  26. @click="save('form')"
  27. >
  28. 保存
  29. </el-button>
  30. <el-button
  31. class="bs-el-button-default"
  32. @click="goBack"
  33. >
  34. 返回
  35. </el-button>
  36. </div>
  37. </div>
  38. </template>
  39. </el-page-header>
  40. </div>
  41. <el-row style="margin: 16px 16px 0;">
  42. <el-col :span="isEdit ? 16 : 24">
  43. <el-form
  44. ref="form"
  45. :model="dataForm"
  46. :rules="rules"
  47. label-width="120px"
  48. style="padding: 16px 16px 0;"
  49. class="bs-el-form"
  50. >
  51. <el-row :gutter="20">
  52. <el-col :span="12">
  53. <el-form-item
  54. label="数据集名称"
  55. prop="name"
  56. >
  57. <el-input
  58. v-model="dataForm.name"
  59. class="bs-el-input"
  60. clearable
  61. :disabled="!isEdit"
  62. />
  63. </el-form-item>
  64. </el-col>
  65. <el-col :span="12">
  66. <el-form-item
  67. label="分组"
  68. prop="typeId"
  69. >
  70. <el-select
  71. ref="selectParentName"
  72. v-model="dataForm.typeId"
  73. class="bs-el-select"
  74. popper-class="bs-el-select"
  75. placeholder="请选择分组"
  76. clearable
  77. :disabled="!isEdit"
  78. filterable
  79. :filter-method="selectorFilter"
  80. @clear="clearType"
  81. @visible-change="setCurrentNode"
  82. >
  83. <el-option
  84. style="height: auto;padding: 0;"
  85. :label="typeName"
  86. :value="dataForm.typeId"
  87. >
  88. <div>
  89. <el-tree
  90. ref="categorySelectTree"
  91. :data="categoryData"
  92. node-key="id"
  93. :indent="0"
  94. :props="{ label: 'name', children: 'children' }"
  95. :default-expand-all="true"
  96. :highlight-current="true"
  97. :expand-on-click-node="false"
  98. class="bs-el-tree"
  99. :filter-node-method="treeFilter"
  100. @node-click="selectParentCategory"
  101. >
  102. <span
  103. slot-scope="{ data }"
  104. class="custom-tree-node"
  105. >
  106. <span>
  107. <i
  108. :class="data.children && data.children.length ? 'el-icon el-icon-folder' : 'el-icon el-icon-document'"
  109. />
  110. {{ data.name }}
  111. </span>
  112. </span>
  113. </el-tree>
  114. </div>
  115. </el-option>
  116. </el-select>
  117. </el-form-item>
  118. </el-col>
  119. </el-row>
  120. <el-row :gutter="20">
  121. <el-col :span="12">
  122. <el-form-item
  123. label="备注"
  124. prop="remark"
  125. >
  126. <el-input
  127. v-model="dataForm.remark"
  128. class="bs-el-input"
  129. :disabled="!isEdit"
  130. />
  131. </el-form-item>
  132. </el-col>
  133. <el-col :span="12">
  134. <el-form-item
  135. label="数据源"
  136. prop="sourceId"
  137. >
  138. <el-select
  139. v-model="dataForm.sourceId"
  140. clearable
  141. class="bs-el-select"
  142. popper-class="bs-el-select"
  143. filterable
  144. placeholder="请选择数据源"
  145. :disabled="!isEdit"
  146. @change="setSqlProcess($event)"
  147. >
  148. <el-option
  149. v-for="source in sourceList"
  150. :key="source.id"
  151. :label="source.sourceName"
  152. :value="source.id"
  153. />
  154. </el-select>
  155. </el-form-item>
  156. </el-col>
  157. </el-row>
  158. <el-row :gutter="20">
  159. <el-col :span="12">
  160. <el-form-item
  161. label="缓存"
  162. prop="cache"
  163. >
  164. <el-radio-group
  165. v-model="dataForm.cache"
  166. class="bs-el-radio-group"
  167. >
  168. <el-radio :label="1">
  169. 开启
  170. </el-radio>
  171. <el-radio :label="0">
  172. 关闭
  173. </el-radio>
  174. </el-radio-group>
  175. <el-tooltip
  176. class="item"
  177. effect="light"
  178. content="开启缓存:会在首次调用该数据集时,将结果缓存,在接下来的十分钟内,若再次被调用则直接返回缓存中的数据,注意:在当前数据集编辑页面缓存不生效"
  179. placement="top"
  180. >
  181. <i
  182. class="el-icon-warning-outline"
  183. style="color: #E3C98C;margin-left: 16px;font-size:14px"
  184. />
  185. </el-tooltip>
  186. </el-form-item>
  187. </el-col>
  188. <el-col :span="12">
  189. <el-form-item
  190. label="标签"
  191. prop="labelIds"
  192. >
  193. <LabelSelect
  194. :dataset-id="datasetId"
  195. :id-list="dataForm.labelIds"
  196. @commit="(ids) => { dataForm.labelIds = ids }"
  197. />
  198. </el-form-item>
  199. </el-col>
  200. </el-row>
  201. </el-form>
  202. <div
  203. v-if="isEdit"
  204. class="sql-config"
  205. >
  206. <div>
  207. <codemirror
  208. ref="targetInSql"
  209. v-model="dataForm.sqlProcess"
  210. :options="cOptions"
  211. style="margin-top: 2px"
  212. />
  213. <div class="bs-codemirror-bottom-text">
  214. 示例:
  215. <strong>call 存储过程名称(<span style="color: #CC7832;">${参数名称}</span>,?),SqlServer数据源使用:exec 存储过程名称 <span style="color: #CC7832;">@参数名称</span>=?</strong>
  216. </div>
  217. </div>
  218. <div style="text-align: center; padding: 16px 0;">
  219. <el-button
  220. type="primary"
  221. @click="buildParamsAndRun"
  222. >
  223. 解析并运行
  224. </el-button>
  225. </div>
  226. </div>
  227. </el-col>
  228. <el-col
  229. v-if="isEdit"
  230. :span="8"
  231. >
  232. <div class="right-setting">
  233. <div class="paramConfig">
  234. <div class="title-style bs-title-style">
  235. 存储过程参数
  236. <el-button
  237. type="text"
  238. style="float: right;border: none;margin-top: -4px;"
  239. @click="openParamsConfig"
  240. >
  241. 配置
  242. </el-button>
  243. </div>
  244. <div class="field-wrap bs-field-wrap bs-scrollbar">
  245. <div
  246. v-for="param in dataForm.paramsList"
  247. :key="param.name"
  248. class="field-item"
  249. @click="openParamsConfig"
  250. >
  251. <span>{{ param.name }}</span>&nbsp;<span
  252. v-show="param.remark"
  253. style="color: #909399;"
  254. >
  255. ({{ param.remark }})
  256. </span>
  257. <el-button
  258. class="edit_field"
  259. type="text"
  260. style="float: right;border: none;margin-top: 2px;"
  261. @click="openParamsConfig"
  262. >
  263. 配置
  264. </el-button>
  265. </div>
  266. </div>
  267. </div>
  268. <div class="structure">
  269. <div class="title-style bs-title-style">
  270. 输出字段
  271. <el-button
  272. type="text"
  273. style="float: right;border: none;margin-top: -4px;"
  274. @click="fieldsetVisible = true"
  275. >
  276. 配置
  277. </el-button>
  278. </div>
  279. <div class="field-wrap bs-field-wrap bs-scrollbar">
  280. <div
  281. v-for="field in structurePreviewList"
  282. :key="field.fieldName"
  283. class="field-item"
  284. @click="fieldsetVisible = true"
  285. >
  286. <span>{{ field.fieldName }}</span>&nbsp;<span
  287. v-show="field.fieldDesc"
  288. style="color: #909399;"
  289. >({{
  290. field.fieldDesc }})</span>
  291. <el-button
  292. class="edit_field"
  293. type="text"
  294. style="float: right;border: none;margin-top: 2px;"
  295. @click="fieldsetVisible = true"
  296. >
  297. 配置
  298. </el-button>
  299. </div>
  300. </div>
  301. <!-- </divclass="field-wrap> -->
  302. </div>
  303. </div>
  304. </el-col>
  305. </el-row>
  306. <div
  307. v-if="isEdit"
  308. style="margin-top: 12px;"
  309. >
  310. <div class="result-view">
  311. 数据预览
  312. </div>
  313. <div class="bs-table-box is-Edit">
  314. <el-table
  315. align="center"
  316. :data="dataPreviewList"
  317. max-height="100%"
  318. class="bs-el-table"
  319. >
  320. <!-- 第一个表格列固定 -->
  321. <el-table-column
  322. v-for="(value, key) in dataPreviewList[0] ? dataPreviewList[0] : noDataTableDisplayFields"
  323. :key="key"
  324. :label="key"
  325. :fixed="left"
  326. align="center"
  327. show-overflow-tooltip
  328. >
  329. <template slot-scope="scope">
  330. <span>{{ scope.row[key] }}</span>
  331. </template>
  332. </el-table-column>
  333. </el-table>
  334. <div style="margin: 8px 0;color:var(--bs-el-text)">
  335. <span v-show="dataPreviewList.length">数据预览中,存储过程仅展示20条数据</span>
  336. </div>
  337. </div>
  338. </div>
  339. <!-- 字段填充方式 -->
  340. <el-dialog
  341. title="提示"
  342. :visible.sync="fieldDescVisible"
  343. width="420px"
  344. append-to-body
  345. :close-on-click-modal="false"
  346. custom-class="fieldDescCheck"
  347. class="bs-dialog-wrap bs-el-dialog"
  348. >
  349. <p style="color:var(--bs-el-text);line-height: 24px;padding-left: 10px;display: flex;">
  350. <i
  351. class="el-icon-warning"
  352. style="color: #E6A23C;font-size: 24px;margin-right: 5px;"
  353. />
  354. 存在字段描述信息为空,请确认
  355. </p>
  356. <span
  357. slot="footer"
  358. class="dialog-footer"
  359. >
  360. <el-button
  361. class="bs-el-button-default"
  362. @click="fieldDescFill"
  363. >
  364. 使用字段名填充
  365. </el-button>
  366. <el-button
  367. class="bs-el-button-default"
  368. @click="fieldDescEdit"
  369. >
  370. 进入编辑
  371. </el-button>
  372. <el-button
  373. type="primary"
  374. @click="toSave"
  375. >继续保存</el-button>
  376. </span>
  377. </el-dialog>
  378. <!-- 字段填充 -->
  379. <el-dialog
  380. title="输出字段配置"
  381. :visible.sync="fieldsetVisible"
  382. width="1000px"
  383. append-to-body
  384. :close-on-click-modal="false"
  385. :before-close="cancelField"
  386. class="bs-dialog-wrap bs-el-dialog"
  387. >
  388. <div class="bs-table-box">
  389. <el-table
  390. :data="structurePreviewListCopy"
  391. :border="true"
  392. align="center"
  393. class="bs-el-table"
  394. >
  395. <el-empty slot="empty" />
  396. <el-table-column
  397. align="left"
  398. show-overflow-tooltip
  399. prop="fieldName"
  400. label="字段值"
  401. />
  402. <el-table-column
  403. align="center"
  404. show-overflow-tooltip
  405. prop="fieldType"
  406. label="字段类型"
  407. />
  408. <el-table-column
  409. align="center"
  410. prop="fieldDesc"
  411. label="字段描述"
  412. >
  413. <template slot-scope="scope">
  414. <el-input
  415. v-if="isEdit"
  416. v-model="scope.row.fieldDesc"
  417. size="small"
  418. class="labeldsc bs-el-input"
  419. />
  420. <span v-else>{{ scope.row.fieldDesc }}</span>
  421. </template>
  422. </el-table-column>
  423. <el-table-column
  424. align="center"
  425. prop="orderNum"
  426. label="字段排序"
  427. sortable
  428. >
  429. <template slot-scope="scope">
  430. <el-input
  431. v-if="isEdit"
  432. v-model="scope.row.orderNum"
  433. size="small"
  434. class="labeldsc bs-el-input"
  435. />
  436. <span v-else>{{ scope.row.orderNum }}</span>
  437. </template>
  438. </el-table-column>
  439. <!-- 添加一个插槽,供其他人可扩展表格列,并把表格列的数据返回出去 -->
  440. <slot name="output-field-table-column" />
  441. </el-table>
  442. </div>
  443. <span
  444. slot="footer"
  445. class="dialog-footer"
  446. >
  447. <el-button @click="cancelField">取消</el-button>
  448. <el-button
  449. type="primary"
  450. @click="setField"
  451. >确定</el-button>
  452. </span>
  453. </el-dialog>
  454. <!-- 参数配置 -->
  455. <el-dialog
  456. title="存储过程参数配置"
  457. :visible.sync="paramsVisible"
  458. width="1000px"
  459. append-to-body
  460. :close-on-click-modal="false"
  461. :before-close="cancelParam"
  462. class="bs-dialog-wrap bs-el-dialog"
  463. >
  464. <div class="bs-table-box">
  465. <el-table
  466. ref="singleTable"
  467. :data="paramsListCopy"
  468. :border="true"
  469. align="center"
  470. class="bs-el-table"
  471. >
  472. <el-empty slot="empty" />
  473. <el-table-column
  474. prop="name"
  475. label="参数名称"
  476. align="center"
  477. />
  478. <el-table-column
  479. prop="type"
  480. label="参数类型"
  481. align="center"
  482. width="200"
  483. filterable
  484. >
  485. <template slot-scope="scope">
  486. <el-select
  487. v-model="scope.row.type"
  488. popper-class="bs-el-select"
  489. class="bs-el-select"
  490. placeholder="请选择"
  491. >
  492. <el-option
  493. v-for="item in typeSelect"
  494. :key="item.value"
  495. :label="item.value"
  496. :value="item.value"
  497. />
  498. </el-select>
  499. </template>
  500. </el-table-column>
  501. <el-table-column
  502. prop="require"
  503. label="是否必填"
  504. align="center"
  505. width="200"
  506. filterable
  507. >
  508. <template slot-scope="scope">
  509. <el-radio-group
  510. v-model="scope.row.require"
  511. class="bs-el-radio-group"
  512. >
  513. <el-radio :label="1">
  514. </el-radio>
  515. <el-radio :label="0">
  516. </el-radio>
  517. </el-radio-group>
  518. </template>
  519. </el-table-column>
  520. <el-table-column
  521. prop="value"
  522. label="参数值"
  523. align="center"
  524. >
  525. <template slot-scope="scope">
  526. <el-date-picker
  527. v-if="scope.row.type === 'Date'"
  528. v-model="scope.row.value"
  529. type="datetime"
  530. value-format="yyyy-MM-dd HH:mm:ss"
  531. placeholder="选择日期时间"
  532. />
  533. <el-input
  534. v-else
  535. v-model="scope.row.value"
  536. class="bs-el-input"
  537. clearable
  538. placeholder="请输入值"
  539. />
  540. </template>
  541. </el-table-column>
  542. <el-table-column
  543. prop="remark"
  544. label="备注"
  545. align="center"
  546. >
  547. <template slot-scope="scope">
  548. <el-input
  549. v-model="scope.row.remark"
  550. clearable
  551. class="bs-el-input"
  552. placeholder="请输入备注"
  553. rows="2"
  554. maxlength="200"
  555. />
  556. </template>
  557. </el-table-column>
  558. <el-table-column
  559. label="操作"
  560. width="105"
  561. align="center"
  562. >
  563. <template slot="header">
  564. <el-button
  565. icon="el-icon-plus"
  566. type="text"
  567. class="no-border"
  568. @click="addParam"
  569. >
  570. 添加
  571. </el-button>
  572. </template>
  573. <template slot-scope="scope">
  574. <el-button
  575. type="text"
  576. style="color: #e47470;"
  577. class="no-border"
  578. @click="delRow(scope.$index)"
  579. >
  580. 删除
  581. </el-button>
  582. </template>
  583. </el-table-column>
  584. </el-table>
  585. </div>
  586. <span
  587. slot="footer"
  588. class="dialog-footer"
  589. >
  590. <el-button
  591. class="bs-el-button-default"
  592. @click="cancelParam"
  593. >
  594. 取消
  595. </el-button>
  596. <el-button
  597. type="primary"
  598. @click="setParam"
  599. >
  600. 确定
  601. </el-button>
  602. </span>
  603. </el-dialog>
  604. </el-scrollbar>
  605. </div>
  606. </template>
  607. <script>
  608. import LabelSelect from 'data-room-ui/DataSetLabelManagement/src/LabelSelect.vue'
  609. import {
  610. nameCheckRepeat,
  611. datasetAdd,
  612. datasetUpdate,
  613. getCategoryTree,
  614. datasetExecuteTest,
  615. getDataset
  616. } from 'data-room-ui/js/utils/datasetConfigService'
  617. import { datasourceList } from 'data-room-ui/js/utils/dataSourceService'
  618. import { codemirror } from 'vue-codemirror'
  619. import 'codemirror/lib/codemirror.css'
  620. import 'codemirror/theme/nord.css'
  621. import 'codemirror/mode/sql/sql.js'
  622. // import _ from 'lodash'
  623. import cloneDeep from 'lodash/cloneDeep'
  624. import { datasetMixins } from 'data-room-ui/js/mixins/datasetMixin'
  625. export default {
  626. name: 'StoredProcedureEditForm',
  627. components: {
  628. codemirror,
  629. LabelSelect
  630. },
  631. mixins: [datasetMixins],
  632. data () {
  633. const validateName = (rule, value, callback) => {
  634. nameCheckRepeat({
  635. id: this.datasetId,
  636. name: value,
  637. moduleCode: this.appCode
  638. }).then((r) => {
  639. if (r) {
  640. callback(new Error('数据集名称已存在'))
  641. } else {
  642. callback()
  643. }
  644. })
  645. }
  646. return {
  647. dataForm: {
  648. id: '',
  649. name: '',
  650. typeId: '',
  651. datasetType: 'storedProcedure',
  652. remark: '',
  653. labelIds: [],
  654. // 以下为config配置
  655. sourceId: '',
  656. cache: 0,
  657. sqlProcess: 'call ',
  658. paramsList: [],
  659. fieldDesc: {},
  660. fieldList: [],
  661. code: '',
  662. script: '',
  663. cacheCoherence: null
  664. },
  665. rules: {
  666. name: [
  667. { required: true, message: '请输入数据集名称', trigger: 'blur' },
  668. { validator: validateName, trigger: 'blur' }
  669. ],
  670. sourceId: [
  671. { required: true, message: '请选择数据源', trigger: 'blur' }
  672. ],
  673. typeId: [
  674. { required: true, message: '请选择分组', trigger: 'blur' }
  675. ]
  676. },
  677. cOptions: {
  678. mode: 'text/x-mysql',
  679. lineNumbers: true,
  680. lineWrapping: true,
  681. theme: 'nord',
  682. extraKey: { Ctrl: 'autocomplete' },
  683. hintOptions: {
  684. completeSingle: true
  685. }
  686. },
  687. sourceList: [],
  688. msg: '',
  689. exception: '',
  690. passTest: false, // 通过测试
  691. paramsVisible: false,
  692. tableNameList: [],
  693. paramsListCopy: [],
  694. isTest: false // 是否执行测试
  695. }
  696. },
  697. computed: {
  698. checkPass () {
  699. return {
  700. sqlProcess: this.dataForm.sqlProcess,
  701. script: this.dataForm.script,
  702. paramsList: this.dataForm.paramsList
  703. }
  704. },
  705. noDataTableDisplayFields () {
  706. // 表格列对象
  707. const tableColumnObject = {}
  708. this.structurePreviewList.forEach(item => {
  709. tableColumnObject[item.fieldName] = ''
  710. })
  711. return tableColumnObject
  712. }
  713. },
  714. watch: {
  715. checkPass: {
  716. handler (value) {
  717. this.passTest = false
  718. },
  719. deep: true
  720. }
  721. },
  722. mounted () {
  723. this.init()
  724. },
  725. methods: {
  726. /**
  727. * 初始化
  728. * 1. 获取分类树
  729. * 2. 获取数据源列表
  730. * 3. 获取数据集详情
  731. */
  732. async init () {
  733. this.categoryData = await getCategoryTree({ type: 'dataset', moduleCode: this.appCode })
  734. if (this.typeId) {
  735. this.dataForm.typeId = this.typeId
  736. this.$nextTick(() => {
  737. try {
  738. this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
  739. } catch (error) {
  740. console.error(error)
  741. }
  742. })
  743. }
  744. this.getDataSource()
  745. if (!this.datasetId) {
  746. return
  747. }
  748. // 获取详情
  749. getDataset(this.datasetId).then(res => {
  750. this.dataForm.id = res.id
  751. this.dataForm.name = res.name
  752. this.dataForm.typeId = res.typeId
  753. this.dataForm.remark = res.remark
  754. this.dataForm.datasetType = res.datasetType
  755. this.dataForm.moduleCode = res.moduleCode
  756. this.dataForm.editable = res.editable
  757. this.dataForm.sourceId = res.sourceId
  758. this.dataForm.cache = res.cache
  759. // config 配置
  760. this.dataForm.sqlProcess = res.config.sqlProcess
  761. this.dataForm.paramsList = res.config.paramsList ? res.config.paramsList : []
  762. this.dataForm.fieldDesc = res.config.fieldDesc
  763. this.dataForm.fieldList = res.config.fieldList
  764. this.dataForm.cacheCoherence = res.config.cacheCoherence
  765. // 使用传入的数据集名称 ?
  766. this.dataForm.name = this.datasetName
  767. this.paramsListCopy = cloneDeep(this.dataForm.paramsList)
  768. if (this.dataForm.typeId) {
  769. this.$nextTick(() => {
  770. try {
  771. this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
  772. } catch (error) {
  773. console.error(error)
  774. }
  775. })
  776. }
  777. this.datasetTest(false)
  778. })
  779. },
  780. /**
  781. * 获取数据源列表
  782. */
  783. getDataSource () {
  784. const params = {
  785. sourceName: '',
  786. sourceType: '',
  787. moduleCode: this.appCode
  788. }
  789. datasourceList(params).then(data => {
  790. this.sourceList = data
  791. })
  792. },
  793. setSqlProcess (v, e) {
  794. for (let i = 0; i < this.sourceList.length; i++) {
  795. if (this.sourceList[i].id === v) {
  796. if (this.sourceList[i].sourceType === 'sqlserver') {
  797. if (this.dataForm.sqlProcess === 'call ') {
  798. this.dataForm.sqlProcess = 'exec '
  799. }
  800. } else {
  801. if (this.dataForm.sqlProcess === 'exec ') {
  802. this.dataForm.sqlProcess = 'call '
  803. }
  804. }
  805. }
  806. }
  807. },
  808. /**
  809. * 打开参数配置弹窗
  810. */
  811. openParamsConfig () {
  812. this.isTest = false
  813. this.paramsVisible = true
  814. },
  815. /**
  816. * 删除参数配置
  817. * @param {*} index
  818. */
  819. delRow (index) {
  820. this.paramsListCopy.splice(index, 1)
  821. },
  822. /**
  823. * 新增参数配置
  824. */
  825. addParam () {
  826. this.paramsListCopy.push({
  827. name: '',
  828. type: '',
  829. value: '',
  830. status: 1,
  831. require: 0,
  832. remark: ''
  833. })
  834. },
  835. /**
  836. * 取消编辑参数
  837. */
  838. cancelParam () {
  839. this.paramsListCopy = cloneDeep(this.dataForm.paramsList)
  840. this.paramsVisible = false
  841. },
  842. /**
  843. * 保存参数设置
  844. */
  845. setParam () {
  846. this.dataForm.paramsList = cloneDeep(this.paramsListCopy)
  847. if (this.isTest) {
  848. this.datasetTest()
  849. }
  850. this.paramsVisible = false
  851. },
  852. /**
  853. * 保存
  854. * @param formName
  855. * @param noCheckToSave 是否跳过检查直接保存
  856. */
  857. save (formName, noCheckToSave = false) {
  858. if (this.passTest === false) {
  859. this.$message.error('请确保数据集SQL加工脚本不为空且运行通过')
  860. return
  861. }
  862. if (!this.structurePreviewList.length) {
  863. this.$message.warning('该存储过程未生成输出字段,请重新检查')
  864. return
  865. }
  866. if (!noCheckToSave) {
  867. const temp = this.structurePreviewList.some(item => {
  868. return item.fieldDesc === '' || !item.hasOwnProperty('fieldDesc')
  869. }) // true-存在为空
  870. if (temp) {
  871. this.fieldDescVisible = true
  872. return
  873. }
  874. }
  875. this.$refs[formName].validate((valid) => {
  876. if (!valid) {
  877. return false
  878. }
  879. // 检查参数名称是否重复
  880. if (this.dataForm.paramsList.length > 0) {
  881. const names = this.dataForm.paramsList.map(value => value.name)
  882. const namesSet = new Set(names)
  883. if (namesSet.size !== names.length) {
  884. this.$message.error('参数名称不能重复,请重新输入')
  885. return
  886. }
  887. }
  888. // 组装输出字段描述
  889. const columnMap = {}
  890. if (this.structurePreviewList.length > 0) {
  891. this.structurePreviewList.forEach(r => {
  892. columnMap[r.fieldName] = r.fieldDesc
  893. })
  894. this.dataForm.fieldDesc = columnMap
  895. }
  896. this.dataForm.fieldList = this.structurePreviewList.length ? this.structurePreviewList : []
  897. this.saveLoading = true
  898. this.saveText = '正在保存...'
  899. const dataSave = this.dataForm.id ? datasetUpdate : datasetAdd
  900. const datasetParams = {
  901. id: this.dataForm.id,
  902. name: this.dataForm.name,
  903. typeId: this.dataForm.typeId,
  904. datasetType: 'storedProcedure',
  905. remark: this.dataForm.remark,
  906. sourceId: this.dataForm.sourceId,
  907. cache: this.dataForm.cache,
  908. moduleCode: this.appCode,
  909. editable: this.appCode ? 1 : 0,
  910. labelIds: this.dataForm.labelIds,
  911. config: {
  912. className: 'com.gccloud.dataset.entity.config.StoredProcedureDataSetConfig',
  913. sourceId: this.dataForm.sourceId,
  914. sqlProcess: this.dataForm.sqlProcess,
  915. paramsList: this.dataForm.paramsList,
  916. fieldList: this.dataForm.fieldList,
  917. fieldDesc: this.dataForm.fieldDesc
  918. }
  919. }
  920. dataSave(datasetParams).then(res => {
  921. this.$message.success('保存成功')
  922. this.$parent.init(false)
  923. this.$parent.setType = null
  924. this.saveLoading = false
  925. this.saveText = ''
  926. this.goBack()
  927. }).catch(() => {
  928. this.saveLoading = false
  929. this.saveText = ''
  930. })
  931. this.saveLoading = false
  932. this.saveText = ''
  933. })
  934. },
  935. /**
  936. * 解析参数配置,并且执行测试
  937. */
  938. buildParamsAndRun () {
  939. this.isTest = true
  940. const reg = /\${(.*?)}/g
  941. const paramNames = [...new Set([...this.dataForm.sqlProcess.matchAll(reg)].map(item => item[1]))]
  942. const names = this.dataForm.paramsList.map(item => item.name)
  943. const params = []
  944. paramNames.forEach(name => {
  945. if (names.includes(name)) {
  946. const param = this.dataForm.paramsList.find(item => item.name === name)
  947. params.push(param)
  948. } else {
  949. params.push({
  950. name: name,
  951. type: 'String',
  952. value: '',
  953. status: 1,
  954. require: 0,
  955. remark: ''
  956. })
  957. }
  958. })
  959. this.dataForm.paramsList = cloneDeep(params)
  960. this.paramsListCopy = cloneDeep(this.dataForm.paramsList)
  961. if (this.dataForm.paramsList.length) {
  962. this.paramsVisible = true
  963. } else {
  964. this.datasetTest()
  965. }
  966. },
  967. goBack () {
  968. this.$emit('back')
  969. },
  970. /**
  971. * 执行测试
  972. * @param val
  973. */
  974. datasetTest (val = true) {
  975. if (this.dataForm.sourceId === '') {
  976. this.$message.error('请选择数据源')
  977. return
  978. }
  979. if (this.dataForm.sqlProcess === '') {
  980. this.$message.error('请输入数据集SQL加工脚本')
  981. return
  982. }
  983. if (this.dataForm.paramsList.length > 0) {
  984. const names = this.dataForm.paramsList.map(value => value.name)
  985. const namesSet = new Set(names)
  986. if (namesSet.size !== names.length) {
  987. this.$message.error('参数名称不能重复,请重新输入')
  988. return
  989. }
  990. }
  991. // 点击测试初始化分页当前页为1
  992. if (val === true) {
  993. this.current = 1
  994. }
  995. this.saveLoading = true
  996. // 组装数据集执行参数
  997. const executeParams = {
  998. dataSourceId: this.dataForm.sourceId,
  999. script: this.dataForm.sqlProcess,
  1000. params: this.dataForm.paramsList,
  1001. dataSetType: 'storedProcedure',
  1002. // 存储过程数据集默认查询20条数据
  1003. size: 20,
  1004. current: 1
  1005. }
  1006. datasetExecuteTest(executeParams).then(res => {
  1007. this.dataPreviewList = res.data.list
  1008. this.structurePreviewList = res.structure
  1009. // 输出字段描述合并
  1010. this.structurePreviewList.forEach(field => {
  1011. const fieldInfo = this.dataForm.fieldList.find(item => item.fieldName === field.fieldName)
  1012. if (fieldInfo) {
  1013. const { fieldDesc, orderNum, sourceTable, ...rest } = fieldInfo
  1014. field.fieldDesc = fieldDesc
  1015. field.orderNum = orderNum
  1016. field.sourceTable = sourceTable
  1017. Object.keys(rest).forEach(key => {
  1018. if (!field.hasOwnProperty(key)) {
  1019. this.$set(field, key, rest[key])
  1020. }
  1021. })
  1022. }
  1023. })
  1024. this.structurePreviewList.forEach(item => {
  1025. if (!item.hasOwnProperty('orderNum')) {
  1026. this.$set(item, 'orderNum', 0)
  1027. }
  1028. if (!item.hasOwnProperty('sourceTable')) {
  1029. this.$set(item, 'sourceTable', '')
  1030. }
  1031. if (!item.hasOwnProperty('fieldDesc')) {
  1032. this.$set(item, 'fieldDesc', '')
  1033. }
  1034. })
  1035. this.totalCount = res.data.totalCount
  1036. this.tableNameList = res.tableNameList
  1037. // 如果只有一个表,自动填充字段表名
  1038. if (this.tableNameList && this.tableNameList.length === 1) {
  1039. this.structurePreviewList.forEach(item => {
  1040. item.sourceTable = this.tableNameList[0]
  1041. })
  1042. }
  1043. this.structurePreviewListCopy = cloneDeep(this.structurePreviewList)
  1044. let paramsNameCheck = false
  1045. this.dataForm.paramsList.forEach(param => {
  1046. const checkList = this.structurePreviewList.filter(item => item.fieldName === param.name)
  1047. if (checkList.length) {
  1048. paramsNameCheck = true
  1049. param.name = ''
  1050. }
  1051. })
  1052. if (paramsNameCheck) {
  1053. this.$message.warning('参数名称不可以与字段名相同!')
  1054. this.passTest = false
  1055. } else {
  1056. if (val) this.$message.success('脚本执行通过')
  1057. this.exception = ''
  1058. this.msg = ''
  1059. this.passTest = true
  1060. }
  1061. this.saveLoading = false
  1062. }).catch((e) => {
  1063. this.passTest = false
  1064. this.saveLoading = false
  1065. })
  1066. },
  1067. selectorFilter (value) {
  1068. this.$refs.categorySelectTree.filter(value)
  1069. },
  1070. treeFilter (value, data) {
  1071. if (!value) return true
  1072. return data.name.indexOf(value) !== -1
  1073. }
  1074. }
  1075. }
  1076. </script>
  1077. <style lang="scss" scoped>
  1078. @import '../../assets/style/bsTheme.scss';
  1079. .data-set-scrollbar {
  1080. height: 100%;
  1081. overflow-y: auto;
  1082. overflow-x: none;
  1083. }
  1084. // .tree-box {
  1085. // padding: 0;
  1086. // max-height: 270px;
  1087. // }
  1088. ::v-deep .el-input__inner {
  1089. width: 100% !important;
  1090. }
  1091. .page-header {
  1092. display: flex;
  1093. position: relative;
  1094. .page-header-right {
  1095. position: absolute;
  1096. right: 16px;
  1097. }
  1098. }
  1099. .sql-config {
  1100. padding: 0 16px;
  1101. }
  1102. .operation {
  1103. ::v-deep .el-select {
  1104. width: 200px !important;
  1105. margin-right: 16px;
  1106. }
  1107. display: flex;
  1108. }
  1109. // .codeStyle {
  1110. // border: 1px solid #EBEEF5;
  1111. // }
  1112. ::v-deep .CodeMirror {
  1113. height: 180px !important;
  1114. font-family: Helvetica, Tahoma;
  1115. // .CodeMirror-scroll {
  1116. // background: #fff;
  1117. // .CodeMirror-gutters {
  1118. // background-color: #f6f7fb;
  1119. // }
  1120. // }
  1121. }
  1122. .no-border {
  1123. border: 0;
  1124. }
  1125. ::v-deep .fieldDescCheck {
  1126. .el-dialog__body {
  1127. height: fit-content !important;
  1128. min-height: unset !important;
  1129. }
  1130. }
  1131. .title-style {
  1132. padding: 8px 12px;
  1133. background-color: #f6f7fb;
  1134. border-left: 5px solid var(--bs-el-color-primary);
  1135. margin: 16px 16px 0 0;
  1136. }
  1137. .field-wrap {
  1138. overflow: auto;
  1139. margin-right: 16px;
  1140. .field-item {
  1141. line-height: 32px;
  1142. padding: 0 12px 0 16px;
  1143. cursor: pointer;
  1144. .edit_field {
  1145. display: none;
  1146. }
  1147. &:hover {
  1148. background-color: #f2f7fe;
  1149. .edit_field {
  1150. display: block;
  1151. }
  1152. }
  1153. }
  1154. }
  1155. .right-setting {
  1156. height: 390px;
  1157. overflow: hidden;
  1158. display: flex;
  1159. flex-direction: column;
  1160. .paramConfig {
  1161. max-height: 195px;
  1162. .field-wrap {
  1163. max-height: 143px;
  1164. }
  1165. }
  1166. .structure {
  1167. flex: 1;
  1168. overflow: hidden;
  1169. .field-wrap {
  1170. height: calc(100% - 40px);
  1171. }
  1172. }
  1173. }
  1174. .result-view {
  1175. font-size: 14px;
  1176. font-weight: 600;
  1177. color: var(--bs-el-text);
  1178. position: relative;
  1179. padding: 16px 0;
  1180. padding-left: 12px;
  1181. border-bottom: 1px solid var(--bs-background-1);
  1182. &::before {
  1183. content: "";
  1184. height: 14px;
  1185. position: absolute;
  1186. left: 0;
  1187. top: 50%;
  1188. transform: translateY(-50%);
  1189. border-left: 4px solid var(--bs-el-color-primary);
  1190. }
  1191. }
  1192. ::v-deep .bs-table-box.is-Edit .el-table {
  1193. max-height: unset !important;
  1194. .el-table__body-wrapper {
  1195. max-height: unset !important;
  1196. }
  1197. }
  1198. .bs-table-box {
  1199. height: 100% !important;
  1200. margin-bottom: 0 !important;
  1201. }
  1202. .bs-el-select{
  1203. width: 100% !important;
  1204. }
  1205. ::v-deep .el-input__inner{
  1206. width: 100% !important;
  1207. }
  1208. </style>