jointTargetDia.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <template>
  2. <div class="jointTarget-container">
  3. <el-dialog
  4. :visible.sync="isShow"
  5. :close-on-click-modal="false"
  6. :modal-append-to-body="false"
  7. @close="cancelHandle"
  8. custom-class="joint-dialog"
  9. top="10vh"
  10. v-dialogDrag
  11. width="1000px"
  12. >
  13. <div slot="title" style="display: flex; align-items: center">
  14. <img
  15. :src="$icons.computed"
  16. style="color: #fff; width: 16px; height: 16px; marginright: 5px"
  17. />
  18. <span style="font-size: 16px">&nbsp;指标拼接</span>
  19. </div>
  20. <ul class="label-cont" v-if="!params.view && !params.edb_id">
  21. <li v-for="item in tagList" :key="item.key" :class="{act: default_type === item.key}" @click="toogleType(item.key)">{{item.label}}</li>
  22. </ul>
  23. <el-form
  24. ref="concatForm"
  25. class="form-cont"
  26. label-position="left"
  27. label-width="110px"
  28. :model="formData"
  29. :rules="formRules"
  30. :disabled="params.view"
  31. >
  32. <template v-if="default_type === 1">
  33. <el-form-item label="拼接日期" prop="date">
  34. <el-date-picker
  35. v-model="formData.date"
  36. value-format="yyyy-MM-dd"
  37. type="date"
  38. placeholder="选择日期"
  39. style="width: 70%">
  40. </el-date-picker>
  41. </el-form-item>
  42. <el-form-item label="拼接日期之前" prop="pre_edb">
  43. <el-select
  44. v-model="formData.pre_edb"
  45. v-loadMore="searchLoad"
  46. :filterable="!formData.pre_edb"
  47. clearable
  48. placeholder="指标ID/指标名称"
  49. style="width: 70%"
  50. :disabled="params.view"
  51. remote
  52. :remote-method="query=>{searchHandle(query)}"
  53. @click.native="inputFocusHandle"
  54. @blur="search_have_more = false"
  55. >
  56. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  57. <el-option
  58. v-for="item in searchOptions"
  59. :key="item.EdbInfoId"
  60. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  61. :value="item.EdbInfoId"
  62. >
  63. </el-option>
  64. </el-select>
  65. <span v-if="formData.pre_edb">(起始日期:{{ searchOptions.find(item => item.EdbInfoId === formData.pre_edb) ? searchOptions.find(item => item.EdbInfoId === formData.pre_edb).StartDate : ''}})</span>
  66. </el-form-item>
  67. <el-form-item label="拼接日期之后" prop="after_edb">
  68. <el-select
  69. v-model="formData.after_edb"
  70. v-loadMore="searchLoad"
  71. :filterable="!formData.after_edb"
  72. clearable
  73. placeholder="指标ID/指标名称"
  74. style="width: 70%"
  75. :disabled="params.view"
  76. remote
  77. :remote-method="query=>{searchHandle(query)}"
  78. @click.native="inputFocusHandle"
  79. @blur="search_have_more = false"
  80. >
  81. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  82. <el-option
  83. v-for="item in searchOptions"
  84. :key="item.EdbInfoId"
  85. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  86. :value="item.EdbInfoId"
  87. >
  88. </el-option>
  89. </el-select>
  90. <span v-if="formData.after_edb">(最新日期:{{ searchOptions.find(item => item.EdbInfoId === formData.after_edb) ? searchOptions.find(item => item.EdbInfoId === formData.after_edb).EndDate : ''}})</span>
  91. </el-form-item>
  92. </template>
  93. <template v-else>
  94. <el-form-item label="待拼接指标" prop="old_stay_edb">
  95. <el-select
  96. v-model="formData.old_stay_edb"
  97. v-loadMore="searchLoad2"
  98. :filterable="!formData.old_stay_edb"
  99. clearable
  100. placeholder="指标ID/指标名称"
  101. style="width: 70%"
  102. :disabled="params.view"
  103. remote
  104. :remote-method="query=>{searchHandle(query,'month')}"
  105. @click.native="e => {inputFocusHandle(e,'month')} "
  106. @blur="search_have_more = false"
  107. >
  108. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  109. <el-option
  110. v-for="item in searchMonthOptions"
  111. :key="item.EdbInfoId"
  112. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  113. :value="item.EdbInfoId"
  114. >
  115. </el-option>
  116. </el-select>
  117. <span v-if="formData.old_stay_edb">(截止日期:{{searchMonthOptions.find(item => item.EdbInfoId === formData.old_stay_edb) ? searchMonthOptions.find(item => item.EdbInfoId === formData.old_stay_edb).EndDate : ''}})</span>
  118. </el-form-item>
  119. <el-form-item label="同比值指标" prop="concat_edb">
  120. <el-select
  121. v-model="formData.concat_edb"
  122. v-loadMore="searchLoad"
  123. :filterable="!formData.concat_edb"
  124. clearable
  125. placeholder="指标ID/指标名称"
  126. style="width: 70%"
  127. remote
  128. :remote-method="query=>{searchHandle(query)}"
  129. @click.native="inputFocusHandle"
  130. @blur="search_have_more = false"
  131. >
  132. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  133. <el-option
  134. v-for="item in searchOptions"
  135. :key="item.EdbInfoId"
  136. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  137. :value="item.EdbInfoId"
  138. >
  139. </el-option>
  140. </el-select>
  141. </el-form-item>
  142. </template>
  143. <el-form-item label="指标名称" prop="edb_name" class="target-form-cont">
  144. <el-input
  145. v-model="formData.edb_name"
  146. style="width: 70%"
  147. placeholder="指标名称"></el-input>
  148. </el-form-item>
  149. <el-form-item label="所属目录" prop="menu">
  150. <el-cascader
  151. v-model="formData.menu"
  152. :options="menuOptions"
  153. :props="{
  154. label: 'ClassifyName',
  155. value: 'ClassifyId',
  156. children: 'Children',
  157. checkStrictly: true
  158. }"
  159. style="width: 70%"
  160. clearable
  161. placeholder="请选择所属目录"/>
  162. </el-form-item>
  163. <el-form-item label="频度" prop="frequency">
  164. <el-select
  165. v-model="formData.frequency"
  166. placeholder="请选择频度"
  167. style="width:70%"
  168. clearable >
  169. <el-option
  170. v-for="item in frequencyArr"
  171. :key="item"
  172. :label="item"
  173. :value="item">
  174. </el-option>
  175. </el-select>
  176. </el-form-item>
  177. <el-form-item label="单位" prop="unit">
  178. <selectUnit v-model="formData.unit" style="width:70%" />
  179. </el-form-item>
  180. </el-form>
  181. <div class="dia-bot" v-if="!params.view">
  182. <el-button
  183. type="primary"
  184. style="margin-right: 20px"
  185. @click="saveHandle"
  186. >保存</el-button
  187. >
  188. <el-button type="primary" plain @click="cancelHandle('cancel')">取消</el-button>
  189. </div>
  190. <el-popover
  191. placement="top-start"
  192. :width="default_type === 1 ? 360 : 550"
  193. trigger="click">
  194. <p style="padding:20px;line-height:25px;" v-html="tips.get(default_type)"/>
  195. <span slot="reference" class="tip-label">公式说明</span>
  196. </el-popover>
  197. </el-dialog>
  198. </div>
  199. </template>
  200. <script>
  201. import { dataBaseInterface } from '@/api/api.js'
  202. import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
  203. import { unitArr } from '@/utils/defaultOptions';
  204. export default {
  205. props: {
  206. isShow: {
  207. type: Boolean,
  208. },
  209. params: {
  210. type: Object,
  211. },
  212. isPredict:{//是否是预测指标
  213. type: Boolean,
  214. default:false
  215. }
  216. },
  217. watch: {
  218. isShow(newval) {
  219. newval && this.getMenu();
  220. if(newval && this.params.edb_id) {
  221. // this.searchHandle();
  222. const backData = _.cloneDeep(this.params);
  223. this.default_type = backData.source === 23||backData.source === 47 ? 1 : 2;
  224. this.formData = {
  225. date: backData.date,
  226. pre_edb: backData.pre_edb,
  227. after_edb: backData.after_edb,
  228. old_stay_edb: backData.old_stay_edb,
  229. concat_edb: backData.concat_edb,
  230. edb_name: backData.targetName,
  231. menu: backData.menu,
  232. frequency: backData.frequency,
  233. unit: backData.unit,
  234. }
  235. //options 回显
  236. if(backData.source === 23||backData.source === 47) {
  237. this.searchOptions = backData.from_arr.map(item => ({
  238. EdbInfoId: item.FromEdbInfoId,
  239. EdbName: item.FromEdbName,
  240. StartDate: item.StartDate,
  241. EndDate: item.EndDate,
  242. }))
  243. }else {
  244. this.searchMonthOptions = backData.from_arr.filter(obj => obj.FromTag === 'A').map(item => ({
  245. EdbInfoId: item.FromEdbInfoId,
  246. EdbName: item.FromEdbName,
  247. StartDate: item.StartDate,
  248. EndDate: item.EndDate,
  249. }));
  250. this.searchMonthConcatOptions = backData.from_arr.filter(obj => obj.FromTag === 'B').map(item => ({
  251. EdbInfoId: item.FromEdbInfoId,
  252. EdbName: item.FromEdbName,
  253. StartDate: item.StartDate,
  254. EndDate: item.EndDate,
  255. }))
  256. }
  257. }
  258. }
  259. },
  260. data () {
  261. return {
  262. default_type: 1,
  263. tagList: [
  264. {
  265. label: "直接拼接",
  266. key: 1
  267. },
  268. {
  269. label: "累计值同比拼接",
  270. key: 2
  271. }
  272. ],
  273. formData: {
  274. date: '',
  275. pre_edb: '',
  276. after_edb: '',
  277. old_stay_edb: '',
  278. concat_edb: '',
  279. edb_name: '',
  280. menu: '',
  281. frequency: '',
  282. unit: '',
  283. },
  284. formRules: {
  285. date:[
  286. { required: true, message: '拼接日期不能为空', trigger: 'blur' },
  287. ],
  288. pre_edb:[
  289. { required: true, message: '指标不能为空', trigger: 'blur' },
  290. ],
  291. after_edb:[
  292. { required: true, message: '指标不能为空', trigger: 'blur' },
  293. ],
  294. old_stay_edb:[
  295. { required: true, message: '待拼接指标不能为空', trigger: 'blur' },
  296. ],
  297. concat_edb:[
  298. { required: true, message: '指标不能为空', trigger: 'blur' },
  299. ],
  300. edb_name:[
  301. { required: true, message: '指标名称不能为空', trigger: 'blur' },
  302. ],
  303. menu:[
  304. { required: true, message: '所属目录不能为空', trigger: 'blur' },
  305. ],
  306. frequency:[
  307. { required: true, message: '频度不能为空', trigger: 'blur' },
  308. ],
  309. unit:[
  310. { required: true, message: '单位不能为空', trigger: ['blur','change'] },
  311. ],
  312. },
  313. tips: new Map([
  314. [1,`
  315. 直接拼接说明:<br>
  316. 1、选取拼接日期<br>
  317. 2、在拼接日期之前的数据选择指标A<br>
  318. 3、拼接日期之后的数据选择指标B<br>
  319. 4、新指标的起始日期为A的开始日期,更新时间跟随指标B进行更新<br>
  320. 5、指标A和B可以是原始指标,也可以是计算指标
  321. `],
  322. [2,`
  323. 累计值同比拼接说明:<br>
  324. 1、只支持月频度的指标进行累计值同比拼接<br>
  325. 2、如果出现有空值运算,则返回空值<br>
  326. 3、选取待拼接指标A和同比值指标B<br>
  327. 4、搜索到指标A最后一个12月31日有值的年份,并且向前回溯12个值(12个月),分别乘以下一年同期对应的同比增长率(B),公式为【A*(1+同期增长率(B)/100)】,得到下一年每个月的绝对值,再用新得到的下一年的12个月的值再乘以再下一年同期对应的同比增长率,得到再下一年每个月的绝对值<br>
  328. 5、以此类推,直到运算至指标B的最新值,得到新的序列C<br>
  329. 6、新指标是将序列C和指标A进行直接拼接,拼接日期选取指标A中有值的年份的年末最后一天即12月31日,再与序列C的起始日期做拼接<br>
  330. 7、新指标跟随指标B进行更新
  331. `],
  332. ]),
  333. searchOptions: [],
  334. searchMonthOptions: [],
  335. searchMonthConcatOptions:[],
  336. unitArr,
  337. menuOptions: [],
  338. frequencyArr:['日度','周度','旬度','月度','季度','年度'],
  339. search_page: 1,
  340. search_have_more: false,
  341. current_search: ''
  342. };
  343. },
  344. methods: {
  345. /* 搜索 */
  346. searchHandle(query,type) {
  347. this.search_page = 1;
  348. this.current_search = query;
  349. this.searchApi(this.current_search,1,type);
  350. },
  351. /* 聚焦获取当前检索 */
  352. inputFocusHandle(e,type) {
  353. this.search_page = 1;
  354. this.current_search = e.target.value;
  355. this.searchApi(this.current_search,1,type);
  356. },
  357. async searchApi(query,page=1,type='') {
  358. const params={
  359. KeyWord:query,
  360. CurrentIndex: page,
  361. // FilterSource: type === 'monthConcat' ? 5 : 0,
  362. Frequency: ['month'].includes(type) ? '月度' : ''
  363. }
  364. const res=this.isPredict?await preDictEdbInterface.edbSearch(params):await dataBaseInterface.targetSearchByPage(params)
  365. if(res.Ret !== 200) return
  366. const { List,Paging } = res.Data;
  367. this.search_have_more = page < Paging.Pages;
  368. if(type === 'month') {
  369. this.searchMonthOptions = page === 1 ? List : this.searchMonthOptions.concat(List);
  370. this.searchMonthOptions = this.searchMonthOptions.filter(item => item.EdbInfoId !== this.params.edb_id);
  371. }else if(type === 'monthConcat') {
  372. this.searchMonthConcatOptions = page === 1 ? List : this.searchMonthConcatOptions.concat(List);
  373. this.searchMonthConcatOptions = this.searchMonthConcatOptions.filter(item => item.EdbInfoId !== this.params.edb_id);
  374. }else {
  375. this.searchOptions = page === 1 ? List : this.searchOptions.concat(List);
  376. this.searchOptions = this.searchOptions.filter(item => item.EdbInfoId !== this.params.edb_id);
  377. }
  378. // })
  379. },
  380. searchLoad() {
  381. if(!this.search_have_more) return;
  382. this.searchApi(this.current_search,++this.search_page);
  383. },
  384. //月度加载
  385. searchLoad2() {
  386. if(!this.search_have_more) return;
  387. this.searchApi(this.current_search,++this.search_page,'month');
  388. },
  389. //月度同比加载
  390. searchLoad3() {
  391. console.log(this.search_have_more)
  392. if(!this.search_have_more) return;
  393. this.searchApi(this.current_search,++this.search_page,'monthConcat');
  394. },
  395. toogleType(key) {
  396. this.formData = {};
  397. this.searchOptions = [];
  398. this.$refs.concatForm.resetFields();
  399. this.default_type = key;
  400. },
  401. /* 获取目录结构 */
  402. async getMenu() {
  403. const res=this.isPredict?await preDictEdbInterface.classifyListV2():await dataBaseInterface.menuListV3()
  404. // dataBaseInterface.menuList().then(res => {
  405. if(res.Ret === 200) {
  406. if(!this.isPredict){
  407. this.filterNodes(res.Data.AllNodes||[]);
  408. this.menuOptions = res.Data.AllNodes || [];
  409. }else{
  410. this.menuOptions = res.Data.AllNodes || [];
  411. }
  412. // this.menuOptions = res.Data.AllNodes || [];
  413. }
  414. // })
  415. },
  416. // 递归改变第三级目录结构
  417. filterNodes(arr) {
  418. arr.length && arr.forEach(item => {
  419. item.Children.length && this.filterNodes(item.Children)
  420. if(!item.Children.length) {
  421. delete item.Children
  422. }
  423. })
  424. },
  425. /* 保存 */
  426. saveHandle() {
  427. this.$refs.concatForm.validate((valid) => {
  428. if(!valid) return
  429. let params_base = {
  430. ClassifyId: this.formData.menu[this.formData.menu.length - 1],
  431. EdbName: this.formData.edb_name,
  432. Frequency: this.formData.frequency,
  433. Unit: this.formData.unit
  434. }
  435. let params = this.default_type === 1 ? {
  436. ...params_base,
  437. EdbInfoIdArr: [
  438. { EdbInfoId: this.formData.after_edb }
  439. ],
  440. Formula: this.formData.date,
  441. FromEdbInfoId: this.formData.pre_edb,
  442. Source: this.isPredict?47:23,
  443. } : {
  444. ...params_base,
  445. EdbInfoIdArr: [
  446. { EdbInfoId: this.formData.concat_edb }
  447. ],
  448. FromEdbInfoId: this.formData.old_stay_edb,
  449. Source: this.isPredict?48:24,
  450. }
  451. this.params.edb_id ? this.editEdbHandle(params) : this.addEdbHandle(params);
  452. })
  453. },
  454. /* 新增 */
  455. async addEdbHandle(params) {
  456. const res=this.isPredict?await preDictEdbInterface.operateEdbSave(params):await dataBaseInterface.calculateTargetSave(params)
  457. // dataBaseInterface.calculateTargetSave(params).then(res => {
  458. if (res.Ret !== 200) return
  459. this.$message.success('新增成功');
  460. this.$emit('addCallBack','add',{ code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:params.ClassifyId });
  461. this.init();
  462. // });
  463. },
  464. /* 编辑 */
  465. async editEdbHandle(params) {
  466. let edit_params = {
  467. ...params,
  468. EdbInfoId: this.params.edb_id,
  469. }
  470. const res=this.isPredict?await preDictEdbInterface.operateEdbSave(edit_params):await dataBaseInterface.calculateTargetEdit(edit_params)
  471. // dataBaseInterface.calculateTargetEdit(edit_params).then(res => {
  472. if (res.Ret !== 200) return
  473. this.$message.success('编辑成功');
  474. this.$emit('addCallBack','edit');
  475. this.init();
  476. // });
  477. },
  478. init() {
  479. this.default_type = 1;
  480. this.formData = {};
  481. this.$refs.concatForm.resetFields();
  482. },
  483. cancelHandle(type) {
  484. this.init();
  485. this.$emit('cancel');
  486. type==='cancel' && !this.params.edb_id && this.$emit('openPrev');
  487. }
  488. },
  489. created() {},
  490. mounted() {},
  491. }
  492. </script>
  493. <style lang='scss'>
  494. .jointTarget-container {
  495. .mx-datepicker {
  496. width: 70% !important;
  497. }
  498. .label-cont {
  499. display: flex;
  500. align-items: center;
  501. li {
  502. cursor: pointer;
  503. font-size: 16px;
  504. margin-right: 24px;
  505. margin-bottom: 35px;
  506. &:hover {
  507. color: #409EFF;
  508. }
  509. &.act {
  510. color: #409EFF;
  511. }
  512. }
  513. }
  514. .form-cont {
  515. margin-bottom: 40px;
  516. }
  517. .target-form-cont {
  518. margin-top: 30px;
  519. padding-top: 30px;
  520. border-top: 1px dashed #AAB4CC;
  521. }
  522. .el-cascader .el-input {
  523. width: 100%;
  524. }
  525. .dia-bot {
  526. padding: 10px 0 60px;
  527. display: flex;
  528. justify-content: center;
  529. }
  530. .tip-label {
  531. position: absolute;
  532. bottom: 30px;
  533. right: 30px;
  534. color: #409EFF;
  535. cursor: pointer;
  536. }
  537. }
  538. </style>