CustomEditForm.vue 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476
  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/self_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="back 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. >
  50. <el-row :gutter="20">
  51. <el-col :span="12">
  52. <el-form-item
  53. label="名称"
  54. prop="name"
  55. >
  56. <el-input
  57. v-model="dataForm.name"
  58. class="bs-el-input"
  59. clearable
  60. :disabled="!isEdit"
  61. />
  62. </el-form-item>
  63. </el-col>
  64. <el-col :span="12">
  65. <el-form-item
  66. label="分组"
  67. prop="typeId"
  68. >
  69. <el-select
  70. ref="selectParentName"
  71. v-model="dataForm.typeId"
  72. popper-class="bs-el-select"
  73. class="bs-el-select"
  74. clearable
  75. :disabled="!isEdit"
  76. @clear="clearType"
  77. @visible-change="setCurrentNode"
  78. >
  79. <el-option
  80. style="height: auto;padding: 0;"
  81. :label="typeName"
  82. :value="dataForm.typeId"
  83. >
  84. <div>
  85. <el-tree
  86. ref="categorySelectTree"
  87. :data="categoryData"
  88. node-key="id"
  89. :indent="0"
  90. :props="{ label: 'name', children: 'children' }"
  91. :default-expand-all="true"
  92. :highlight-current="true"
  93. :expand-on-click-node="false"
  94. class="bs-el-tree"
  95. @node-click="selectParentCategory"
  96. >
  97. <span
  98. slot-scope="{ data }"
  99. class="custom-tree-node"
  100. >
  101. <span>
  102. <i
  103. :class="data.children && data.children.length ? 'el-icon el-icon-folder' : 'el-icon el-icon-document'"
  104. />
  105. {{ data.name }}
  106. </span>
  107. </span>
  108. </el-tree>
  109. </div>
  110. </el-option>
  111. </el-select>
  112. </el-form-item>
  113. </el-col>
  114. </el-row>
  115. <el-row :gutter="20">
  116. <el-col :span="12">
  117. <el-form-item
  118. label="描述"
  119. prop="remark"
  120. >
  121. <el-input
  122. v-model="dataForm.remark"
  123. class="bs-el-input"
  124. :disabled="!isEdit"
  125. />
  126. </el-form-item>
  127. </el-col>
  128. <el-col :span="12">
  129. <el-form-item
  130. label="数据源"
  131. prop="sourceId"
  132. >
  133. <el-select
  134. v-model="dataForm.sourceId"
  135. clearable
  136. filterable
  137. class="bs-el-select"
  138. popper-class="bs-el-select"
  139. placeholder="请选择数据源"
  140. :disabled="!isEdit"
  141. >
  142. <el-option
  143. v-for="source in sourceList"
  144. :key="source.id"
  145. :label="source.sourceName"
  146. :value="source.id"
  147. />
  148. </el-select>
  149. </el-form-item>
  150. </el-col>
  151. </el-row>
  152. </el-form>
  153. <div
  154. v-if="isEdit"
  155. class="sql-config"
  156. >
  157. <div>
  158. <codemirror
  159. ref="targetInSql"
  160. v-model="dataForm.sqlProcess"
  161. :options="cOptions"
  162. style="margin-top: 2px"
  163. />
  164. <div class="bs-codemirror-bottom-text">
  165. 示例:
  166. <strong><br>
  167. 1、常规使用 select * from table where table_field = <span style="color: red;">${参数名称}</span><br>
  168. 2、标签使用
  169. <el-tooltip
  170. class="item"
  171. effect="dark"
  172. content="<参数名称></参数名称>为非空标签, 当该参数值为空时, 标签部分不进行处理"
  173. placement="top-start"
  174. ><i class="el-icon-question" />
  175. </el-tooltip>
  176. select * from table where 1=1 <span style="color: blue;">&lt;参数名称&gt;</span> and table_field = <span
  177. style="color: red;"
  178. >${参数名称}</span> <span style="color: blue;">&lt;/参数名称&gt;</span>
  179. </strong>
  180. </div>
  181. </div>
  182. <div style="text-align: center; padding: 16px 0;">
  183. <el-button
  184. type="primary"
  185. @click="buildParamsAndRun"
  186. >
  187. 解析并运行
  188. </el-button>
  189. </div>
  190. </div>
  191. </el-col>
  192. <el-col
  193. v-if="isEdit"
  194. :span="8"
  195. >
  196. <div class="right-setting">
  197. <div class="paramConfig">
  198. <div class="title-style bs-title-style">
  199. SQL参数
  200. <el-button
  201. type="text"
  202. style="float: right;border: none;margin-top: -4px;"
  203. @click="openParamsConfig"
  204. >
  205. 配置
  206. </el-button>
  207. </div>
  208. <div class="field-wrap bs-field-wrap bs-scrollbar">
  209. <div
  210. v-for="param in dataForm.paramsList"
  211. :key="param.name"
  212. class="field-item"
  213. @click="openParamsConfig"
  214. >
  215. <span>{{ param.name }}</span>&nbsp;<span
  216. v-show="param.remark"
  217. style="color: #909399;"
  218. >({{ param.remark
  219. }})</span>
  220. <el-button
  221. class="edit_field"
  222. type="text"
  223. style="float: right;border: none;margin-top: 2px;"
  224. @click="openParamsConfig"
  225. >
  226. 配置
  227. </el-button>
  228. </div>
  229. </div>
  230. </div>
  231. <div class="structure">
  232. <div class="title-style bs-title-style">
  233. 输出字段
  234. <el-button
  235. type="text"
  236. style="float: right;border: none;margin-top: -4px;"
  237. @click="fieldsetVisible = true"
  238. >
  239. 配置
  240. </el-button>
  241. </div>
  242. <div class="field-wrap bs-field-wrap bs-scrollbar">
  243. <div
  244. v-for="field in structurePreviewList"
  245. :key="field.fieldName"
  246. class="field-item"
  247. @click="fieldsetVisible = true"
  248. >
  249. <span>{{ field.fieldName }}</span>&nbsp;<span
  250. v-show="field.fieldDesc"
  251. style="color: #909399;"
  252. >({{
  253. field.fieldDesc }})</span>
  254. <el-button
  255. class="edit_field"
  256. type="text"
  257. style="float: right;border: none;margin-top: 2px;"
  258. @click="fieldsetVisible = true"
  259. >
  260. 配置
  261. </el-button>
  262. </div>
  263. </div>
  264. </div>
  265. </div>
  266. </el-col>
  267. </el-row>
  268. <div
  269. v-if="isEdit"
  270. class="dataPreView"
  271. style="margin-top: 12px;"
  272. >
  273. <div class="result-view">
  274. 数据预览
  275. </div>
  276. <div class="bs-table-box is-Edit">
  277. <el-table
  278. align="center"
  279. :data="dataPreviewList"
  280. max-height="400"
  281. class="bs-el-table bs-scrollbar"
  282. :border="true"
  283. >
  284. <el-table-column
  285. v-for="(value, key) in dataPreviewList[0]"
  286. :key="key"
  287. :label="key"
  288. align="center"
  289. show-overflow-tooltip
  290. :render-header="renderHeader"
  291. >
  292. <template slot-scope="scope">
  293. <span>{{ scope.row[key] }}</span>
  294. </template>
  295. </el-table-column>
  296. </el-table>
  297. </div>
  298. <div class="bs-pagination">
  299. <el-pagination
  300. class="bs-el-pagination"
  301. popper-class="bs-el-pagination"
  302. :current-page="current"
  303. :page-sizes="[10, 20, 50, 100]"
  304. :page-size="size"
  305. :total="totalCount"
  306. background
  307. prev-text="上一页"
  308. next-text="下一页"
  309. layout="total, prev, pager, next,sizes"
  310. @size-change="sizeChangeHandle"
  311. @current-change="currentChangeHandle"
  312. />
  313. </div>
  314. </div>
  315. <div
  316. v-if="!isEdit"
  317. class="dataPreView"
  318. >
  319. <el-tabs v-model="activeName">
  320. <el-tab-pane
  321. v-loading="tableLoading"
  322. label="数据预览"
  323. name="data"
  324. >
  325. <div class="bs-table-box">
  326. <el-table
  327. align="center"
  328. :data="dataPreviewList"
  329. max-height="400"
  330. :border="true"
  331. >
  332. <el-table-column
  333. v-for="(value, key) in dataPreviewList[0]"
  334. :key="key"
  335. :label="key"
  336. align="center"
  337. show-overflow-tooltip
  338. :render-header="renderHeader"
  339. >
  340. <template slot-scope="scope">
  341. <span>{{ scope.row[key] }}</span>
  342. </template>
  343. </el-table-column>
  344. </el-table>
  345. </div>
  346. <div class="bs-pagination">
  347. <el-pagination
  348. class="bs-el-pagination"
  349. popper-class="bs-el-pagination"
  350. :current-page="current"
  351. :page-sizes="[10, 20, 50, 100]"
  352. :page-size="size"
  353. :total="totalCount"
  354. background
  355. prev-text="上一页"
  356. next-text="下一页"
  357. layout="total, prev, pager, next,sizes"
  358. @size-change="sizeChangeHandle"
  359. @current-change="currentChangeHandle"
  360. />
  361. </div>
  362. </el-tab-pane>
  363. <el-tab-pane
  364. v-loading="tableLoading"
  365. label="数据集结构"
  366. name="structure"
  367. >
  368. <div class="bs-table-box">
  369. <el-table
  370. max-height="400"
  371. :data="structurePreviewList"
  372. :border="true"
  373. align="center"
  374. >
  375. <el-table-column
  376. align="center"
  377. show-overflow-tooltip
  378. prop="fieldName"
  379. label="字段值"
  380. />
  381. <el-table-column
  382. align="center"
  383. show-overflow-tooltip
  384. prop="fieldType"
  385. label="字段类型"
  386. />
  387. <el-table-column
  388. align="center"
  389. prop="fieldDesc"
  390. label="字段描述"
  391. >
  392. <template slot-scope="scope">
  393. <el-input
  394. v-if="isEdit"
  395. v-model="scope.row.fieldDesc"
  396. size="small"
  397. class="labeldsc bs-el-input"
  398. />
  399. <span v-else>{{ scope.row.fieldDesc }}</span>
  400. </template>
  401. </el-table-column>
  402. <el-table-column
  403. align="center"
  404. prop="orderNum"
  405. label="字段排序"
  406. sortable
  407. >
  408. <template slot-scope="scope">
  409. <el-input
  410. v-if="isEdit"
  411. v-model="scope.row.orderNum"
  412. size="small"
  413. class="labeldsc bs-el-input"
  414. />
  415. <span v-else>{{ scope.row.orderNum }}</span>
  416. </template>
  417. </el-table-column>
  418. <el-table-column
  419. align="center"
  420. prop="sourceTable"
  421. label="字段来源"
  422. >
  423. <template slot-scope="scope">
  424. <el-select
  425. v-if="isEdit"
  426. v-model="scope.row.sourceTable"
  427. popper-class="bs-el-select"
  428. class="bs-el-select"
  429. clearable
  430. filterable
  431. >
  432. <el-option
  433. v-for="table in tableNameList"
  434. :key="table"
  435. :label="table"
  436. :value="table"
  437. />
  438. </el-select>
  439. <span v-else>{{ scope.row.sourceTable }}</span>
  440. </template>
  441. </el-table-column>
  442. </el-table>
  443. </div>
  444. </el-tab-pane>
  445. </el-tabs>
  446. </div>
  447. <!-- 字段填充方式 -->
  448. <el-dialog
  449. title="提示"
  450. :visible.sync="fieldDescVisible"
  451. width="420px"
  452. append-to-body
  453. :close-on-click-modal="false"
  454. custom-class="fieldDescCheck"
  455. class="bs-dialog-wrap bs-el-dialog"
  456. >
  457. <p style="color:var(--bs-el-text);line-height: 24px;padding-left: 10px;display: flex;">
  458. <i
  459. class="el-icon-warning"
  460. style="color: #E6A23C;font-size: 24px;margin-right: 5px;"
  461. />
  462. 存在字段描述信息为空,请确认
  463. </p>
  464. <span
  465. slot="footer"
  466. class="dialog-footer"
  467. >
  468. <el-button
  469. class="bs-el-button-default"
  470. @click="fieldDescFill"
  471. >使用字段名填充</el-button>
  472. <el-button
  473. class="bs-el-button-default"
  474. @click="fieldDescEdit"
  475. >进入编辑</el-button>
  476. <el-button
  477. type="primary"
  478. @click="toSave"
  479. >继续保存</el-button>
  480. </span>
  481. </el-dialog>
  482. <!-- 字段填充 -->
  483. <el-dialog
  484. title="输出字段配置"
  485. :visible.sync="fieldsetVisible"
  486. width="1000px"
  487. append-to-body
  488. :close-on-click-modal="false"
  489. :before-close="cancelField"
  490. class="bs-dialog-wrap bs-el-dialog"
  491. >
  492. <div class="bs-table-box">
  493. <el-table
  494. class="bs-el-table"
  495. :data="structurePreviewListCopy"
  496. :border="true"
  497. align="center"
  498. >
  499. <el-empty slot="empty" />
  500. <el-table-column
  501. align="left"
  502. show-overflow-tooltip
  503. prop="fieldName"
  504. label="字段值"
  505. />
  506. <el-table-column
  507. align="center"
  508. show-overflow-tooltip
  509. prop="fieldType"
  510. label="字段类型"
  511. />
  512. <el-table-column
  513. align="center"
  514. prop="fieldDesc"
  515. label="字段描述"
  516. >
  517. <template slot-scope="scope">
  518. <el-input
  519. v-if="isEdit"
  520. v-model="scope.row.fieldDesc"
  521. size="small"
  522. class="labeldsc bs-el-input"
  523. />
  524. <span v-else>{{ scope.row.fieldDesc }}</span>
  525. </template>
  526. </el-table-column>
  527. <el-table-column
  528. align="center"
  529. prop="orderNum"
  530. label="字段排序"
  531. sortable
  532. >
  533. <template slot-scope="scope">
  534. <el-input
  535. v-if="isEdit"
  536. v-model="scope.row.orderNum"
  537. size="small"
  538. class="labeldsc bs-el-input"
  539. />
  540. <span v-else>{{ scope.row.orderNum }}</span>
  541. </template>
  542. </el-table-column>
  543. <el-table-column
  544. align="center"
  545. prop="sourceTable"
  546. label="字段来源"
  547. >
  548. <template slot-scope="scope">
  549. <el-select
  550. v-if="isEdit"
  551. v-model="scope.row.sourceTable"
  552. popper-class="bs-el-select"
  553. class="bs-el-select"
  554. clearable
  555. filterable
  556. >
  557. <el-option
  558. v-for="table in tableNameList"
  559. :key="table"
  560. :label="table"
  561. :value="table"
  562. />
  563. </el-select>
  564. <span v-else>{{ scope.row.sourceTable }}</span>
  565. </template>
  566. </el-table-column>
  567. </el-table>
  568. </div>
  569. <span
  570. slot="footer"
  571. class="dialog-footer"
  572. >
  573. <el-button
  574. class="bs-el-button-default"
  575. @click="cancelField"
  576. >
  577. 取消
  578. </el-button>
  579. <el-button
  580. type="primary"
  581. @click="setField"
  582. >
  583. 确定
  584. </el-button>
  585. </span>
  586. </el-dialog>
  587. <!-- 参数配置 -->
  588. <el-dialog
  589. title="SQL参数配置"
  590. :visible.sync="paramsVisible"
  591. width="1000px"
  592. append-to-body
  593. :close-on-click-modal="false"
  594. :before-close="cancelParam"
  595. class="bs-dialog-wrap bs-el-dialog"
  596. >
  597. <div class="bs-table-box">
  598. <el-table
  599. ref="singleTable"
  600. class="bs-el-table"
  601. :data="paramsListCopy"
  602. :border="true"
  603. align="center"
  604. >
  605. <el-empty slot="empty" />
  606. <el-table-column
  607. prop="name"
  608. label="参数名称"
  609. align="center"
  610. />
  611. <el-table-column
  612. prop="type"
  613. label="参数类型"
  614. align="center"
  615. width="200"
  616. filterable
  617. >
  618. <template slot-scope="scope">
  619. <el-select
  620. v-model="scope.row.type"
  621. popper-class="bs-el-select"
  622. class="bs-el-select"
  623. placeholder="请选择"
  624. >
  625. <el-option
  626. v-for="item in typeSelect"
  627. :key="item.value"
  628. :label="item.value"
  629. :value="item.value"
  630. />
  631. </el-select>
  632. </template>
  633. </el-table-column>
  634. <el-table-column
  635. prop="require"
  636. label="是否必填"
  637. align="center"
  638. width="200"
  639. filterable
  640. >
  641. <template slot-scope="scope">
  642. <el-radio-group v-model="scope.row.require">
  643. <el-radio :label="1">
  644. </el-radio>
  645. <el-radio :label="0">
  646. </el-radio>
  647. </el-radio-group>
  648. </template>
  649. </el-table-column>
  650. <el-table-column
  651. prop="value"
  652. label="参数值"
  653. align="center"
  654. >
  655. <template slot-scope="scope">
  656. <el-date-picker
  657. v-if="scope.row.type === 'Date'"
  658. v-model="scope.row.value"
  659. type="datetime"
  660. value-format="yyyy-MM-dd HH:mm:ss"
  661. placeholder="选择日期时间"
  662. />
  663. <el-input
  664. v-else
  665. v-model="scope.row.value"
  666. class="bs-el-input"
  667. clearable
  668. placeholder="请输入值"
  669. />
  670. </template>
  671. </el-table-column>
  672. <el-table-column
  673. prop="remark"
  674. label="备注"
  675. align="center"
  676. >
  677. <template slot-scope="scope">
  678. <el-input
  679. v-model="scope.row.remark"
  680. clearable
  681. class="bs-el-input"
  682. placeholder="请输入备注"
  683. />
  684. </template>
  685. </el-table-column>
  686. <el-table-column
  687. label="操作"
  688. width="105"
  689. align="center"
  690. >
  691. <template slot="header">
  692. <el-button
  693. icon="el-icon-plus"
  694. type="text"
  695. class="no-border"
  696. @click="addParam"
  697. >
  698. 添加
  699. </el-button>
  700. </template>
  701. <template slot-scope="scope">
  702. <el-button
  703. type="text"
  704. style="color: #e47470;"
  705. class="no-border"
  706. @click="delRow(scope.$index)"
  707. >
  708. 删除
  709. </el-button>
  710. </template>
  711. </el-table-column>
  712. </el-table>
  713. </div>
  714. <span
  715. slot="footer"
  716. class="dialog-footer"
  717. >
  718. <el-button
  719. class="bs-el-button-default"
  720. @click="cancelParam"
  721. >取消</el-button>
  722. <el-button
  723. type="primary"
  724. @click="setParam"
  725. >确定</el-button>
  726. </span>
  727. </el-dialog>
  728. </el-scrollbar>
  729. </div>
  730. </template>
  731. <script>
  732. import {
  733. nameCheckRepeat,
  734. datasetAdd,
  735. datasetUpdate,
  736. datasetExecuteTest,
  737. getCategoryTree,
  738. getDataset
  739. } from 'packages/js/utils/datasetConfigService'
  740. import { datasourceList } from 'packages/js/utils/dataSourceService'
  741. import { codemirror } from 'vue-codemirror'
  742. import 'codemirror/mode/sql/sql.js'
  743. import 'codemirror/theme/nord.css'
  744. import 'codemirror/lib/codemirror.css'
  745. import _ from 'lodash'
  746. export default {
  747. name: 'CustomEditForm',
  748. components: {
  749. codemirror
  750. },
  751. props: {
  752. isEdit: {
  753. type: Boolean,
  754. default: false
  755. },
  756. datasetId: {
  757. type: String,
  758. default: null
  759. },
  760. datasetName: {
  761. type: String,
  762. default: ''
  763. },
  764. typeId: {
  765. type: String,
  766. default: ''
  767. },
  768. appCode: {
  769. type: String,
  770. default: ''
  771. }
  772. },
  773. data () {
  774. const validateName = (rule, value, callback) => {
  775. nameCheckRepeat({
  776. id: this.datasetId,
  777. name: value,
  778. moduleCode: this.appCode
  779. }).then((r) => {
  780. if (r) {
  781. callback(new Error('数据集名称已存在'))
  782. } else {
  783. callback()
  784. }
  785. })
  786. }
  787. return {
  788. dataForm: {
  789. id: '',
  790. name: '',
  791. typeId: '',
  792. datasetType: 'custom',
  793. remark: '',
  794. // 以下为config配置
  795. sourceId: '',
  796. sqlProcess: 'select ',
  797. paramsList: [],
  798. fieldDesc: {},
  799. fieldList: [],
  800. script: '',
  801. cacheCoherence: null
  802. },
  803. rules: {
  804. name: [
  805. { required: true, message: '请输入数据集名称', trigger: 'blur' },
  806. { validator: validateName, trigger: 'blur' }
  807. ],
  808. sourceId: [
  809. { required: true, message: '请选择数据源', trigger: 'blur' }
  810. ]
  811. },
  812. cOptions: {
  813. mode: 'text/x-mysql',
  814. lineNumbers: true,
  815. lineWrapping: true,
  816. theme: 'nord',
  817. extraKey: { Ctrl: 'autocomplete' },
  818. hintOptions: {
  819. completeSingle: true
  820. }
  821. },
  822. sourceList: [],
  823. activeName: 'data',
  824. dataPreviewList: [],
  825. structurePreviewList: [],
  826. structurePreviewListCopy: [],
  827. msg: '',
  828. exception: '',
  829. typeSelect: [
  830. { value: 'String' },
  831. { value: 'Integer' },
  832. { value: 'Double' },
  833. { value: 'Long' },
  834. { value: 'Date' }
  835. ],
  836. typeName: '',
  837. categoryData: [],
  838. passTest: false, // 通过测试
  839. current: 1,
  840. size: 10,
  841. totalCount: 0,
  842. fieldDescVisible: false,
  843. fieldsetVisible: false,
  844. paramsVisible: false,
  845. tableLoading: false,
  846. saveLoading: false,
  847. saveText: '',
  848. tableNameList: [],
  849. paramsListCopy: [],
  850. isTest: false // 是否执行测试
  851. }
  852. },
  853. computed: {
  854. checkPass () {
  855. return {
  856. sqlProcess: this.dataForm.sqlProcess,
  857. script: this.dataForm.script,
  858. paramsList: this.dataForm.paramsList
  859. }
  860. }
  861. },
  862. watch: {
  863. // 一旦sql、脚本、参数发生变化,将通过测试置为false
  864. checkPass: {
  865. handler (value) {
  866. this.passTest = false
  867. },
  868. deep: true
  869. }
  870. },
  871. mounted () {
  872. this.init()
  873. },
  874. methods: {
  875. /**
  876. * 初始化
  877. * 1.获取分类树
  878. * 2.获取数据源列表
  879. * 3.如果是编辑,获取数据集详情
  880. */
  881. async init () {
  882. // 获取分类树
  883. this.categoryData = await getCategoryTree({ type: 'dataset', moduleCode: this.appCode })
  884. // 如果传入了分类id,需要设置分类id和名称
  885. if (this.typeId) {
  886. this.dataForm.typeId = this.typeId
  887. this.$nextTick(() => {
  888. try {
  889. this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
  890. } catch (error) {
  891. console.error(error)
  892. }
  893. })
  894. }
  895. // 获取数据源列表
  896. this.getDataSource()
  897. if (!this.datasetId) {
  898. return
  899. }
  900. // 获取详情
  901. getDataset(this.datasetId).then(res => {
  902. this.dataForm.id = res.id
  903. this.dataForm.name = res.name
  904. this.dataForm.typeId = res.typeId
  905. this.dataForm.remark = res.remark
  906. this.dataForm.datasetType = res.datasetType
  907. this.dataForm.moduleCode = res.moduleCode
  908. this.dataForm.editable = res.editable
  909. this.dataForm.sourceId = res.sourceId
  910. // config 配置
  911. this.dataForm.sqlProcess = res.config.sqlProcess
  912. this.dataForm.paramsList = res.config.paramsList ? res.config.paramsList : []
  913. this.dataForm.fieldDesc = res.config.fieldDesc
  914. this.dataForm.fieldList = res.config.fieldList
  915. this.dataForm.cacheCoherence = res.config.cacheCoherence
  916. // 使用传入的数据集名称 ?
  917. this.dataForm.name = this.datasetName
  918. this.paramsListCopy = _.cloneDeep(this.dataForm.paramsList)
  919. if (this.dataForm.typeId) {
  920. this.$nextTick(() => {
  921. try {
  922. this.typeName = this.$refs.categorySelectTree.getNode(this.dataForm.typeId).data.name
  923. } catch (error) {
  924. console.error(error)
  925. }
  926. })
  927. }
  928. this.datasetTest(false)
  929. })
  930. },
  931. /**
  932. * 获取数据源列表
  933. */
  934. getDataSource () {
  935. const params = {
  936. sourceName: '',
  937. sourceType: '',
  938. moduleCode: this.appCode
  939. }
  940. datasourceList(params).then(data => {
  941. this.sourceList = data
  942. })
  943. },
  944. /**
  945. * 打开参数配置弹窗
  946. */
  947. openParamsConfig () {
  948. this.isTest = false
  949. this.paramsVisible = true
  950. },
  951. /**
  952. * 删除参数配置
  953. * @param {*} index
  954. */
  955. delRow (index) {
  956. this.paramsListCopy.splice(index, 1)
  957. },
  958. /**
  959. * 新增参数配置
  960. */
  961. addParam () {
  962. this.paramsListCopy.push({
  963. name: '',
  964. type: '',
  965. value: '',
  966. status: 1,
  967. require: 0,
  968. remark: ''
  969. })
  970. },
  971. /**
  972. * 取消编辑参数
  973. */
  974. cancelParam () {
  975. this.paramsListCopy = _.cloneDeep(this.dataForm.paramsList)
  976. this.paramsVisible = false
  977. },
  978. /**
  979. * 保存参数设置
  980. */
  981. setParam () {
  982. this.dataForm.paramsList = _.cloneDeep(this.paramsListCopy)
  983. if (this.isTest) {
  984. this.datasetTest()
  985. }
  986. this.paramsVisible = false
  987. },
  988. /**
  989. * 使用字段名填充字段描述
  990. */
  991. fieldDescFill () {
  992. this.structurePreviewList.forEach(field => {
  993. if (field.fieldDesc === '' || !field.hasOwnProperty('fieldDesc')) {
  994. field.fieldDesc = field.fieldName
  995. }
  996. })
  997. this.save('form')
  998. this.fieldDescVisible = false
  999. },
  1000. /**
  1001. * 打开字段描述编辑弹窗
  1002. */
  1003. fieldDescEdit () {
  1004. this.fieldDescVisible = false
  1005. this.fieldsetVisible = true
  1006. },
  1007. /**
  1008. * 跳过字段描述编辑直接保存
  1009. */
  1010. toSave () {
  1011. this.save('form', true)
  1012. this.fieldDescVisible = false
  1013. },
  1014. /**
  1015. * 取消编辑字段
  1016. */
  1017. cancelField () {
  1018. this.structurePreviewListCopy = _.cloneDeep(this.structurePreviewList)
  1019. this.fieldsetVisible = false
  1020. },
  1021. /**
  1022. * 保存字段设置
  1023. */
  1024. setField () {
  1025. this.structurePreviewList = _.cloneDeep(this.structurePreviewListCopy)
  1026. this.fieldsetVisible = false
  1027. },
  1028. /**
  1029. * 保存
  1030. * @param formName 表单名称
  1031. * @param noCheckToSave 是否不检查直接保存
  1032. */
  1033. save (formName, noCheckToSave = false) {
  1034. if (this.passTest === false) {
  1035. this.$message.error('请确保数据集SQL加工脚本不为空且测试通过')
  1036. return
  1037. }
  1038. if (!this.structurePreviewList.length) {
  1039. this.$message.warning('该自助数据集未生成输出字段,请重新检查')
  1040. return
  1041. }
  1042. if (!noCheckToSave) {
  1043. const temp = this.structurePreviewList.some(item => {
  1044. return item.fieldDesc === '' || !item.hasOwnProperty('fieldDesc')
  1045. }) // true-存在为空
  1046. if (temp) {
  1047. this.fieldDescVisible = true
  1048. return
  1049. }
  1050. }
  1051. const chineseRegex = /[\u4e00-\u9fa5]/
  1052. let hasChinese = false // 判断有无中文
  1053. let ChineseCode = ''
  1054. for (let i = 0; i < this.structurePreviewList.length; i++) {
  1055. if (chineseRegex.test(this.structurePreviewList[i].fieldName)) {
  1056. hasChinese = true
  1057. ChineseCode = this.structurePreviewList[i].fieldName
  1058. break
  1059. }
  1060. }
  1061. if (hasChinese) {
  1062. this.$confirm(`[${ChineseCode}]字段中包含汉字, 是否保继续保存?`, '提示', {
  1063. confirmButtonText: '确定',
  1064. cancelButtonText: '取消',
  1065. type: 'warning',
  1066. customClass: 'bs-el-message-box'
  1067. }).then(() => {
  1068. this.saveFun(formName)
  1069. }).catch(() => {
  1070. })
  1071. } else {
  1072. this.saveFun(formName)
  1073. }
  1074. },
  1075. /**
  1076. * 保存数据集
  1077. * @param formName
  1078. */
  1079. saveFun (formName) {
  1080. this.$refs[formName].validate((valid) => {
  1081. if (!valid) {
  1082. return false
  1083. }
  1084. // 校验参数名称是否重复
  1085. if (this.dataForm.paramsList.length > 0) {
  1086. const names = this.dataForm.paramsList.map(value => value.name)
  1087. const namesSet = new Set(names)
  1088. if (namesSet.size !== names.length) {
  1089. this.$message.error('参数名称不能重复,请重新输入')
  1090. return
  1091. }
  1092. }
  1093. // 设置字段描述
  1094. const columnMap = {}
  1095. if (this.structurePreviewList.length > 0) {
  1096. this.structurePreviewList.forEach(r => {
  1097. columnMap[r.fieldName] = r.fieldDesc
  1098. })
  1099. this.dataForm.fieldDesc = columnMap
  1100. }
  1101. this.dataForm.fieldList = this.structurePreviewList
  1102. this.saveLoading = true
  1103. this.saveText = '正在保存...'
  1104. const datasetSave = this.dataForm.id === '' ? datasetAdd : datasetUpdate
  1105. const datasetParams = {
  1106. id: this.dataForm.id,
  1107. name: this.dataForm.name,
  1108. typeId: this.dataForm.typeId,
  1109. datasetType: 'custom',
  1110. remark: this.dataForm.remark,
  1111. sourceId: this.dataForm.sourceId,
  1112. moduleCode: this.appCode,
  1113. editable: this.appCode ? 1 : 0,
  1114. config: {
  1115. className: 'com.gccloud.dataset.entity.config.CustomDataSetConfig',
  1116. sourceId: this.dataForm.sourceId,
  1117. sqlProcess: this.dataForm.sqlProcess,
  1118. paramsList: this.dataForm.paramsList,
  1119. fieldList: this.dataForm.fieldList,
  1120. fieldDesc: this.dataForm.fieldDesc
  1121. }
  1122. }
  1123. datasetSave(datasetParams).then(res => {
  1124. this.$message.success('保存成功')
  1125. this.$parent.init(false)
  1126. this.$parent.setType = null
  1127. this.saveLoading = false
  1128. this.saveText = ''
  1129. }).catch(() => {
  1130. this.saveLoading = false
  1131. this.saveText = ''
  1132. })
  1133. this.saveLoading = false
  1134. this.saveText = ''
  1135. })
  1136. },
  1137. /**
  1138. * 解析并运行数据集
  1139. */
  1140. buildParamsAndRun () {
  1141. this.isTest = true
  1142. const reg = /\${(.*?)}/g
  1143. const paramNames = [...new Set([...this.dataForm.sqlProcess.matchAll(reg)].map(item => item[1]))]
  1144. const names = this.dataForm.paramsList.map(item => item.name)
  1145. const params = []
  1146. paramNames.forEach(name => {
  1147. if (names.includes(name)) {
  1148. const param = this.dataForm.paramsList.find(item => item.name === name)
  1149. params.push(param)
  1150. } else {
  1151. params.push({
  1152. name: name,
  1153. type: 'String',
  1154. value: '',
  1155. status: 1,
  1156. require: 0,
  1157. remark: ''
  1158. })
  1159. }
  1160. })
  1161. this.dataForm.paramsList = _.cloneDeep(params)
  1162. this.paramsListCopy = _.cloneDeep(this.dataForm.paramsList)
  1163. if (this.dataForm.paramsList.length) {
  1164. this.paramsVisible = true
  1165. } else {
  1166. this.datasetTest()
  1167. }
  1168. },
  1169. /**
  1170. * 数据集测试
  1171. * @param val
  1172. */
  1173. datasetTest (val = true) {
  1174. if (this.dataForm.sourceId === '') {
  1175. this.$message.error('请选择数据源')
  1176. return
  1177. }
  1178. if (this.dataForm.sqlProcess === '') {
  1179. this.$message.error('请输入数据集SQL加工脚本')
  1180. return
  1181. }
  1182. if (this.dataForm.paramsList.length > 0) {
  1183. const names = this.dataForm.paramsList.map(value => value.name)
  1184. const namesSet = new Set(names)
  1185. if (namesSet.size !== names.length) {
  1186. this.$message.error('参数名称不能重复,请重新输入')
  1187. return
  1188. }
  1189. }
  1190. // 点击测试初始化分页当前页为1
  1191. if (val === true) {
  1192. this.current = 1
  1193. }
  1194. this.saveLoading = true
  1195. const executeParams = {
  1196. dataSourceId: this.dataForm.sourceId,
  1197. script: this.dataForm.sqlProcess,
  1198. params: this.dataForm.paramsList,
  1199. dataSetType: 'custom',
  1200. size: this.size,
  1201. current: this.current
  1202. }
  1203. datasetExecuteTest(executeParams).then(res => {
  1204. this.dataPreviewList = res.data.list
  1205. this.structurePreviewList = res.structure
  1206. // 输出字段描述合并
  1207. this.structurePreviewList.forEach(field => {
  1208. const fieldInfo = this.dataForm.fieldList.find(item => item.fieldName === field.fieldName)
  1209. if (fieldInfo) {
  1210. field.fieldDesc = fieldInfo.fieldDesc
  1211. field.orderNum = fieldInfo.orderNum
  1212. field.sourceTable = fieldInfo.sourceTable
  1213. }
  1214. })
  1215. this.structurePreviewList.forEach(item => {
  1216. if (!item.hasOwnProperty('orderNum')) {
  1217. this.$set(item, 'orderNum', 0)
  1218. }
  1219. if (!item.hasOwnProperty('sourceTable')) {
  1220. this.$set(item, 'sourceTable', '')
  1221. }
  1222. if (!item.hasOwnProperty('fieldDesc')) {
  1223. this.$set(item, 'fieldDesc', '')
  1224. }
  1225. })
  1226. this.totalCount = res.data.totalCount
  1227. this.tableNameList = res.tableNameList
  1228. // 如果只有一个表,自动填充字段表名
  1229. if (this.tableNameList && this.tableNameList.length === 1) {
  1230. this.structurePreviewList.forEach(item => {
  1231. item.sourceTable = this.tableNameList[0]
  1232. })
  1233. }
  1234. this.structurePreviewListCopy = _.cloneDeep(this.structurePreviewList)
  1235. let paramsNameCheck = false
  1236. this.dataForm.paramsList.forEach(param => {
  1237. const checkList = this.structurePreviewList.filter(item => item.fieldName === param.name)
  1238. if (checkList.length) {
  1239. paramsNameCheck = true
  1240. param.name = ''
  1241. }
  1242. })
  1243. if (paramsNameCheck) {
  1244. this.$message.warning('参数名称不可以与字段名相同!')
  1245. this.passTest = false
  1246. } else {
  1247. if (val) this.$message.success('测试成功')
  1248. this.exception = ''
  1249. this.msg = ''
  1250. this.passTest = true
  1251. }
  1252. this.saveLoading = false
  1253. }).catch((e) => {
  1254. console.log('测试失败', e)
  1255. this.passTest = false
  1256. this.saveLoading = false
  1257. })
  1258. },
  1259. /**
  1260. * 清空分类选择
  1261. */
  1262. clearType () {
  1263. this.typeName = ''
  1264. this.dataForm.typeId = ''
  1265. },
  1266. /**
  1267. * 分类展开高亮
  1268. * @param $event
  1269. */
  1270. setCurrentNode ($event) {
  1271. if ($event) {
  1272. const key = this.dataForm.typeId || null
  1273. this.$refs.categorySelectTree.setCurrentKey(key)
  1274. }
  1275. },
  1276. /**
  1277. * 分类选择
  1278. * @param value
  1279. */
  1280. selectParentCategory (value) {
  1281. this.dataForm.typeId = value.id
  1282. this.typeName = value.name
  1283. this.$refs.selectParentName.blur()
  1284. },
  1285. goBack () {
  1286. this.$emit('back')
  1287. },
  1288. // 每页大小改变触发
  1289. sizeChangeHandle (value) {
  1290. this.size = value
  1291. this.current = 1
  1292. this.datasetTest(false)
  1293. },
  1294. // 当前页数改变
  1295. currentChangeHandle (value) {
  1296. this.current = value
  1297. this.datasetTest(false)
  1298. },
  1299. // 表头添加提示
  1300. renderHeader (h, { column, index }) {
  1301. const labelLong = column.label.length // 表头label长度
  1302. const size = 14 // 根据需要定义标尺,直接使用字体大小确定就行,也可以根据需要定义
  1303. column.minWidth = labelLong * size < 120 ? 120 : labelLong * size // 根据label长度计算该表头最终宽度
  1304. return h('span', { class: 'cell-content', style: { width: '100%' } }, [column.label])
  1305. },
  1306. openNewWindow (url) {
  1307. window.open(url, '_blank')
  1308. }
  1309. }
  1310. }
  1311. </script>
  1312. <style lang="scss" scoped>
  1313. @import '~packages/assets/style/bsTheme.scss';
  1314. .data-set-scrollbar {
  1315. height: 100%;
  1316. overflow-y: auto;
  1317. overflow-x: none;
  1318. }
  1319. // .tree-box {
  1320. // padding: 0;
  1321. // max-height: 270px;
  1322. // }
  1323. /deep/ .el-input__inner {
  1324. width: 100% !important;
  1325. }
  1326. .page-header {
  1327. display: flex;
  1328. position: relative;
  1329. .page-header-right {
  1330. position: absolute;
  1331. right: 16px;
  1332. }
  1333. }
  1334. .sql-config {
  1335. padding: 0 16px;
  1336. }
  1337. .operation {
  1338. /deep/ .el-select {
  1339. width: 200px !important;
  1340. margin-right: 16px;
  1341. }
  1342. display: flex;
  1343. }
  1344. /deep/ .CodeMirror {
  1345. height: 180px !important;
  1346. font-family: Helvetica, Tahoma;
  1347. // .CodeMirror-scroll {
  1348. // background: #fff;
  1349. // .CodeMirror-gutters {
  1350. // background-color: #f6f7fb;
  1351. // }
  1352. // }
  1353. }
  1354. .no-border {
  1355. border: 0;
  1356. }
  1357. /deep/ .fieldDescCheck {
  1358. .el-dialog__body {
  1359. height: fit-content !important;
  1360. min-height: unset !important;
  1361. }
  1362. }
  1363. .title-style {
  1364. padding: 8px 12px;
  1365. background-color: #f6f7fb;
  1366. border-left: 5px solid var(--bs-el-color-primary);
  1367. margin: 16px 16px 0 0;
  1368. }
  1369. .field-wrap {
  1370. overflow: auto;
  1371. margin-right: 16px;
  1372. .field-item {
  1373. line-height: 32px;
  1374. padding: 0 12px 0 16px;
  1375. cursor: pointer;
  1376. .edit_field {
  1377. display: none;
  1378. }
  1379. &:hover {
  1380. background-color: #f2f7fe;
  1381. .edit_field {
  1382. display: block;
  1383. }
  1384. }
  1385. }
  1386. }
  1387. .right-setting {
  1388. height: 454px;
  1389. overflow: hidden;
  1390. display: flex;
  1391. flex-direction: column;
  1392. .paramConfig {
  1393. max-height: 227px;
  1394. .field-wrap {
  1395. max-height: 175px;
  1396. }
  1397. }
  1398. .structure {
  1399. flex: 1;
  1400. overflow: hidden;
  1401. .field-wrap {
  1402. height: calc(100% - 40px);
  1403. }
  1404. }
  1405. }
  1406. .result-view {
  1407. font-size: 14px;
  1408. font-weight: 600;
  1409. color: var(--bs-el-text);
  1410. position: relative;
  1411. padding: 16px 0;
  1412. padding-left: 12px;
  1413. border-bottom: 1px solid var(--bs-background-1);
  1414. &::before {
  1415. content: "";
  1416. height: 14px;
  1417. position: absolute;
  1418. left: 0;
  1419. top: 50%;
  1420. transform: translateY(-50%);
  1421. border-left: 4px solid var(--bs-el-color-primary);
  1422. }
  1423. }
  1424. .bs-table-box {
  1425. height: 100% !important;
  1426. margin-bottom: 0 !important;
  1427. }
  1428. /deep/ .bs-table-box.is-Edit .el-table {
  1429. max-height: unset !important;
  1430. .el-table__body-wrapper {
  1431. max-height: unset !important;
  1432. }
  1433. }
  1434. .bs-pagination {
  1435. ::v-deep .el-input__inner {
  1436. width: 110px !important;
  1437. border: none;
  1438. background: var(--bs-el-background-1);
  1439. }
  1440. }
  1441. </style>