index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <template>
  2. <el-card class="codecount-container">
  3. <div slot="header" class="header">
  4. <div class="header-left">
  5. <span><!-- 代码运算 -->{{$t('EtaBasePage.algorithm_btn')}}</span>
  6. <span class="opt-btn" @click="operationTipShow=true">
  7. <img src="~@/assets/img/data_m/icon01.png" alt="">
  8. <!-- 操作说明 -->
  9. <span>{{$t('CodeCountPage.opt_tip_btn')}}</span>
  10. </span>
  11. </div>
  12. <div>
  13. <el-button type="primary" @click="runCodeHandle"><!-- 运行 -->{{$t('CodeCountPage.run_btn')}}</el-button>
  14. <el-button type="primary" plain @click="saveHandle" v-if="!isView"><!-- 保存 -->{{$t('Dialog.confirm_save_btn')}}</el-button>
  15. </div>
  16. </div>
  17. <div class="bottom">
  18. <el-col :span="15" class="bottom-wrapper left-wrapper">
  19. <div class="code-editor">
  20. <code-mirror ref="codeRef" :code="runCode" @initResult="initResult"/>
  21. </div>
  22. <div class="code-result">
  23. <span><!-- 结果展示 -->{{$t('CodeCountPage.res_show')}}</span>
  24. <div class="code-result-wrapper" v-if="isShowResult">
  25. <data-table
  26. :data="runResultData"
  27. />
  28. </div>
  29. </div>
  30. </el-col>
  31. <el-col :span="8" class="bottom-wrapper">
  32. <el-tabs>
  33. <el-tab-pane :label="$t('CodeCountPage.tab_info')">
  34. <el-form
  35. ref="diaForm"
  36. label-position="left"
  37. inline
  38. label-width="110px"
  39. :model="formData"
  40. :disabled="isView"
  41. >
  42. <el-form-item :label="$t('Edb.Detail.e_name')" prop="edb_name">
  43. <el-input
  44. v-model="formData.edb_name"
  45. :placeholder="$t('Edb.InputHolderAll.input_name')"
  46. style="width:220px"
  47. />
  48. </el-form-item>
  49. <el-form-item :label="$t('Edb.Detail.e_unit')" prop="unit">
  50. <selectUnit
  51. v-model="formData.unit"
  52. style="width:220px"
  53. />
  54. </el-form-item>
  55. <el-form-item :label="$t('Edb.Detail.e_fre')" prop="frequency">
  56. <el-select
  57. v-model="formData.frequency"
  58. :placeholder="$t('Edb.InputHolderAll.input_fre')"
  59. clearable
  60. style="width:220px">
  61. <el-option
  62. v-for="item in frequencyArr"
  63. :key="item.label"
  64. :label="item.label"
  65. :value="item.value">
  66. </el-option>
  67. </el-select>
  68. </el-form-item>
  69. <el-form-item :label="$t('Edb.Detail.e_menu')" prop="menu">
  70. <cascader
  71. v-model="formData.menu"
  72. :options="menuOptions"
  73. @changeVal="menuChange"
  74. clearable
  75. :placeholder="$t('Edb.InputHolderAll.input_menu')"
  76. cascaderWidth="width:220px"
  77. :config="{ checkStrictly: true, emitPath: false }"
  78. />
  79. </el-form-item>
  80. </el-form>
  81. </el-tab-pane>
  82. <el-tab-pane :label="$t('CodeCountPage.tab_edb')" v-if="!isView">
  83. <el-form
  84. label-position="left"
  85. inline
  86. label-width="120px"
  87. >
  88. <el-form-item :label="$t('Edb.Detail.source')">
  89. <el-cascader
  90. v-model="fromType"
  91. :options="allFromArr"
  92. :props="{
  93. label: 'SourceName',
  94. value: 'EdbSourceId',
  95. children: 'Child',
  96. emitPath: false
  97. }"
  98. clearable
  99. style="width:220px"
  100. :placeholder="$t('Edb.Detail.source')"/>
  101. <!-- <el-select
  102. v-model="fromType"
  103. placeholder="请选择来源"
  104. style="width:220px">
  105. <el-option
  106. v-for="item in allFromArr"
  107. :key="item.key"
  108. :label="item.name"
  109. :value="item.key">
  110. </el-option>
  111. </el-select> -->
  112. </el-form-item>
  113. <el-form-item :label="$t('Edb.InputHolderAll.input_name_orid')">
  114. <el-select
  115. v-model="search_txt"
  116. v-loadMore="searchLoad"
  117. ref="searchRef"
  118. :filterable="!search_txt"
  119. remote
  120. clearable
  121. :placeholder="$t('Edb.InputHolderAll.input_name_orid')"
  122. :remote-method="searchHandle"
  123. @click.native="inputFocusHandle"
  124. style="width:220px"
  125. >
  126. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  127. <el-option
  128. v-for="item in searchOptions"
  129. :key="item.EdbInfoId"
  130. :label="currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  131. :value="item.EdbInfoId"
  132. :disabled="!item.HaveOperaAuth"
  133. >
  134. <div>
  135. <img
  136. :src="$icons.lock_ico2"
  137. width="18"
  138. height="18"
  139. style="vertical-align:middle"
  140. v-if="!item.HaveOperaAuth"
  141. />
  142. {{currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName}}
  143. </div>
  144. </el-option>
  145. </el-select>
  146. </el-form-item>
  147. </el-form>
  148. <div class="search-result">
  149. <p>{{$t('CodeCountPage.res_search')}} <span style="color: #999;">(<!-- 展示指标所在表结构及调取指标代码 -->{{$t('CodeCountPage.res_search_tip')}})</span></p>
  150. <template v-if="resultInfo.sheet_name">
  151. <table border>
  152. <thead>
  153. <tr>
  154. <td v-for="item in resultInfo.headers" :key="item">{{item}}</td>
  155. </tr>
  156. </thead>
  157. <tbody>
  158. <tr v-for="(item,index) in resultInfo.sheetArr" :key="item.key">
  159. <td :rowspan="resultInfo.sheetArr.length" v-if="index === 0">{{resultInfo.sheet_name}}</td>
  160. <td>{{item.key}}</td>
  161. <td>{{item.label}}</td>
  162. </tr>
  163. </tbody>
  164. </table>
  165. <div class="sql-code" v-html="resultInfo.sql_code"></div>
  166. <el-button type="primary" class="copy-btn" @click="copyCode" :data-clipboard-text="resultInfo.sql_code"><!-- 复制代码 -->{{$t('CodeCountPage.copy_code')}}</el-button>
  167. </template>
  168. </div>
  169. </el-tab-pane>
  170. </el-tabs>
  171. </el-col>
  172. </div>
  173. <!-- 操作说明 -->
  174. <el-dialog
  175. :title="$t('CodeCountPage.opt_tip_btn')"
  176. :visible.sync="operationTipShow"
  177. :close-on-click-modal="false"
  178. center
  179. v-dialogDrag
  180. :append-to-body="true"
  181. width="900px"
  182. >
  183. <div class="dialog-container">
  184. <div style="line-height:20px;color:#000000;font-size: 14px;word-break: normal;" v-html="$t('CodeCountPage.opt_tip_btn_text')"></div>
  185. </div>
  186. </el-dialog>
  187. </el-card>
  188. </template>
  189. <script>
  190. import { dataBaseInterface } from '@/api/api.js';
  191. import { unitArr,frequencySelectList } from '@/utils/defaultOptions';
  192. import storage from '@/utils/storage.js';
  193. import codeMirror from './compoments/codeMirror';
  194. import dataTable from './compoments/dataTable';
  195. import { mapState } from 'vuex';
  196. export default {
  197. name:'',
  198. components: { codeMirror,dataTable },
  199. computed: {
  200. ...mapState({
  201. currentLang: state => state.lang,
  202. }),
  203. frequencyArr(){
  204. return frequencySelectList(['半年度'])
  205. },
  206. },
  207. data () {
  208. return {
  209. isView: this.$route.query.isView ? true : false,
  210. formData: {},
  211. fromType:"",
  212. unitArr,
  213. allFromArr:[],//所有指标来源
  214. /* frequencyArr, */
  215. menuOptions:[],//目录数组
  216. search_txt: '',
  217. search_have_more: false,
  218. searchOptions:[],
  219. search_page: 1,
  220. current_search: '',
  221. isShowResult: false,
  222. runCode: '',
  223. runResultData:[],//运行结果
  224. resultInfo: {
  225. // headers: ['表名','字段名','*字段说明'],
  226. headers: [this.$t('CodeCountPage.table_name'),this.$t('CodeCountPage.field_name'),this.$t('CodeCountPage.field_instru')],
  227. sheetArr:[],
  228. sheet_name: '',
  229. sql_code: ''
  230. },
  231. operationTipShow:false
  232. };
  233. },
  234. watch: {
  235. //每搜索一次记录一次
  236. search_txt(newval) {
  237. if(!newval) return;
  238. let historySearch = storage.get('pycode_seachlist') || [];
  239. let obj = {
  240. EdbInfoId: newval,
  241. EdbName: this.searchOptions.find(_ => _.EdbInfoId===newval).EdbName
  242. }
  243. //有搜索记录删除上条
  244. let index = historySearch.findIndex(_ => _.EdbInfoId === newval)
  245. if(index !== -1) historySearch.splice(index, 1);
  246. historySearch.unshift(obj)
  247. storage.set('pycode_seachlist',historySearch,1)
  248. this.getEdbSheeetInfo(newval);
  249. }
  250. },
  251. methods: {
  252. /* 运行代码 */
  253. runCodeHandle: _.debounce(function() {
  254. if(!this.$refs.codeRef.codeContent) return this.$message.warning(this.$t('CodeCountPage.no_code_msg'));
  255. this.isShowResult = false;
  256. const loading = this.$loading({
  257. lock: true,
  258. text: `${this.$t('CodeCountPage.run_ing')}...`,
  259. target:'.left-wrapper',
  260. spinner: 'el-icon-loading'
  261. });
  262. dataBaseInterface.runCode({
  263. PythonCode: encodeURIComponent(this.$refs.codeRef.codeContent)
  264. }).then(res => {
  265. loading.close();
  266. if(res.Ret !== 200) return
  267. // this.$message.success('运行成功')
  268. this.$message.success(this.$t('CodeCountPage.run_success'))
  269. const { date,value } = res.Data;
  270. const data_arr = []
  271. for(let i in date) {
  272. data_arr.push([date[i],value[i]])
  273. }
  274. this.runResultData = data_arr;
  275. this.isShowResult = true;
  276. })
  277. },200),
  278. /* 获取指标详情 */
  279. getEdbInfo() {
  280. if(!this.$route.query.edbid) return;
  281. dataBaseInterface.countCodeDetail({
  282. EdbInfoId: Number(this.$route.query.edbid)
  283. }).then(res => {
  284. if(res.Ret !== 200) return
  285. const { EdbInfoDetail,PythonCode } = res.Data;
  286. const { EdbName,EdbNameEn,ClassifyId,Frequency,Unit,UnitEn } = EdbInfoDetail;
  287. this.formData = {
  288. edb_name: this.currentLang==='en'?EdbNameEn:EdbName,
  289. menu: ClassifyId,
  290. frequency: Frequency,
  291. unit: this.currentLang==='en'?UnitEn:Unit
  292. }
  293. this.runCode = PythonCode
  294. })
  295. },
  296. /* 获取表结构 */
  297. getEdbSheeetInfo(EdbInfoId) {
  298. dataBaseInterface.edbSheetDetail({
  299. EdbInfoId
  300. }).then(res => {
  301. if(res.Ret !== 200) return
  302. const { ColumnList,TableName,TemplateStr } = res.Data;
  303. this.resultInfo.sheet_name = TableName;
  304. this.resultInfo.sql_code = decodeURIComponent(TemplateStr);
  305. this.resultInfo.sheetArr = ColumnList.map(_ => {
  306. let key = Object.keys(_)[0];
  307. return {
  308. key,
  309. label: _[key]
  310. }
  311. })
  312. })
  313. },
  314. /* code变化时重置结果展示 */
  315. initResult() {
  316. this.isShowResult = false
  317. },
  318. /* 复制代码 */
  319. copyCode() {
  320. var clipboard = new this.Clipboard('.copy-btn')
  321. clipboard.on('success', e => {
  322. // this.$message.success('复制成功')
  323. this.$message.success(this.$t('MsgPrompt.copy_success_msg'))
  324. e.clearSelection() // 释放内存
  325. clipboard.destroy()
  326. })
  327. // // 浏览器不支持
  328. clipboard.on('error', e => {
  329. // this.$message.warning('浏览器暂不支持')
  330. this.$message.warning(this.$t('MsgPrompt.browser_not_support'))
  331. // 释放内存
  332. clipboard.destroy()
  333. })
  334. },
  335. /* 保存代码 */
  336. async saveHandle() {
  337. if(!this.formData.edb_name || !this.formData.menu || !this.formData.frequency || !this.formData.unit) return this.$message.warning(this.$t('CodeCountPage.noenough_info_msg'))
  338. if(!this.isShowResult) return this.$message.warning(this.$t('CodeCountPage.run_msg'))
  339. const loading = this.$loading({
  340. lock: true,
  341. text: `${this.$t('MsgPrompt.saveing_msg')}...`,
  342. target:'.codecount-container',
  343. spinner: 'el-icon-loading'
  344. });
  345. const { edb_name,menu,frequency,unit } = this.formData
  346. let params = {
  347. PythonCode: encodeURIComponent(this.$refs.codeRef.codeContent),
  348. EdbName: edb_name,
  349. Frequency: frequency,
  350. Unit: unit,
  351. ClassifyId: menu
  352. }
  353. const { Ret,Data } = this.$route.query.edbid ? await dataBaseInterface.editCountCode({ ...params,EdbInfoId: Number(this.$route.query.edbid) }) : await dataBaseInterface.addCountCode(params);
  354. loading.close();
  355. if( Ret !== 200 ) return
  356. // this.$message.success('保存成功')
  357. this.$message.success(this.$t('MsgPrompt.saved_msg'))
  358. const { UniqueCode,EdbInfoId } = Data;
  359. this.$router.replace({path:'/database', query: {
  360. code: UniqueCode,
  361. id: EdbInfoId
  362. }});
  363. },
  364. /* 获取目录结构 */
  365. getMenu() {
  366. dataBaseInterface.menuListV3().then((res) => {
  367. if (res.Ret !== 200) return
  368. this.filterNodes(res.Data.AllNodes||[]);
  369. this.menuOptions = res.Data.AllNodes || [];
  370. });
  371. },
  372. // 递归改变第三级目录结构
  373. filterNodes(arr) {
  374. arr.length &&
  375. arr.forEach((item) => {
  376. item.Children.length && this.filterNodes(item.Children);
  377. if (!item.Children.length) {
  378. delete item.Children;
  379. }
  380. });
  381. },
  382. /* 选择目录 */
  383. menuChange(val) {
  384. // this.formData.menu = val.length ? val[val.length - 1] : '';
  385. },
  386. /* 搜索 */
  387. searchHandle(query) {
  388. this.search_page = 1;
  389. this.current_search = query;
  390. this.searchApi(this.current_search)
  391. },
  392. searchApi(query,page=1) {
  393. if(!query){ this.searchOptions = []; return }
  394. dataBaseInterface.targetSearchByPage({
  395. KeyWord:query,
  396. CurrentIndex: page,
  397. Source: this.fromType || 0,
  398. }).then(res => {
  399. if(res.Ret !== 200) return
  400. const { List,Paging } = res.Data;
  401. this.search_have_more = page < Paging.Pages;
  402. this.searchOptions = page === 1 ? List : this.searchOptions.concat(List);
  403. })
  404. },
  405. /* 聚焦获取当前检索 */
  406. inputFocusHandle(e) {
  407. this.search_page = 1;
  408. this.current_search = e.target.value;
  409. this.searchOptions = this.fromType ? [] : storage.get('pycode_seachlist');
  410. // this.searchApi(this.current_search);
  411. },
  412. searchLoad() {
  413. if(!this.search_have_more) return;
  414. this.searchApi(this.current_search,++this.search_page);
  415. },
  416. getTargetSource(){
  417. dataBaseInterface.getCodecountEdbSources().then(res=>{
  418. if(res.Ret!==200) return
  419. if(res.Data){
  420. this.allFromArr = res.Data.map(_ => ({
  421. ..._,
  422. Child: _.Child.map(_item => ({
  423. ..._item,
  424. Child:null
  425. }))
  426. }))
  427. }
  428. })
  429. }
  430. },
  431. mounted() {
  432. this.getEdbInfo();
  433. this.getMenu();
  434. this.getTargetSource()
  435. },
  436. }
  437. </script>
  438. <style lang='scss' scoped>
  439. *{ box-sizing: border-box;}
  440. .codecount-container {
  441. .header {
  442. display: flex;
  443. justify-content: space-between;
  444. align-items: center;
  445. font-size: 16px;
  446. .header-left{
  447. display:flex;
  448. align-items: center;
  449. .opt-btn{
  450. display:inline-flex;
  451. align-items: center;
  452. margin-left: 10px;
  453. cursor: pointer;
  454. img{
  455. width: 32px;
  456. height: 34px;
  457. }
  458. span{
  459. color:rgb(64, 158, 255);
  460. }
  461. }
  462. }
  463. }
  464. .bottom {
  465. height: calc(100vh - 240px);
  466. .bottom-wrapper {
  467. padding: 20px;
  468. height: 100%;
  469. border: 1px solid #ECECEC;
  470. border-radius: 4px;
  471. &:first-child {
  472. margin-right: 20px;
  473. padding: 20px 0;
  474. }
  475. .code-editor {
  476. padding: 0 20px;
  477. }
  478. .code-result {
  479. border-top: 1px solid #ECECEC;
  480. margin-top: 20px;
  481. padding: 20px;
  482. }
  483. .search-result {
  484. border-top: 1px solid #ECECEC;
  485. padding: 20px 0;
  486. table {
  487. width: 100%;
  488. margin-top: 22px;
  489. td{ border-color: #DCDFE6; width: 33%; height: 36px;text-align: center;}
  490. }
  491. .sql-code {
  492. min-height: 100px;
  493. border: 1px solid #DCDFE6;
  494. border-top: none;
  495. }
  496. .copy-btn {
  497. display: block;
  498. margin: 20px auto;
  499. }
  500. }
  501. }
  502. }
  503. }
  504. .dialog-container{
  505. padding:0 60px 60px ;
  506. }
  507. </style>
  508. <style lang="scss">
  509. .codecount-container {
  510. .el-tabs__nav-wrap::after {
  511. background-color: #fff;
  512. }
  513. }
  514. </style>