CustomEditForm.vue 42 KB

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