fittingResidueDia.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <template>
  2. <el-dialog
  3. :visible.sync="isShow"
  4. :close-on-click-modal="false"
  5. :modal-append-to-body="false"
  6. @close="cancelHandle"
  7. custom-class="fitting-dailog"
  8. center
  9. width="1200px"
  10. top="5vh"
  11. v-dialogDrag
  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; margin-right: 5px"
  17. />
  18. <span style="font-size: 16px">拟合残差</span>
  19. </div>
  20. <div class="dialog-main">
  21. <el-form
  22. ref="form"
  23. class="form-cont"
  24. label-position="left"
  25. label-width="110px"
  26. :model="formData"
  27. :rules="formRules"
  28. :disabled="operationForm.view"
  29. >
  30. <el-form-item label="自变量" prop="self_variate">
  31. <el-select
  32. v-model="formData.self_variate"
  33. v-loadMore="searchLoad"
  34. :filterable="!formData.self_variate"
  35. clearable
  36. placeholder="请输入指标名称"
  37. style="width: 70%"
  38. remote
  39. :remote-method="getTarget"
  40. @click.native="inputFocusHandle"
  41. @blur="search_have_more = false"
  42. @change="changeTarget('self',$event)"
  43. :disabled="operationForm.view"
  44. >
  45. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  46. <el-option
  47. v-for="item in searchOptions"
  48. :key="item.EdbInfoId"
  49. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  50. :value="item.EdbInfoId"
  51. >
  52. </el-option>
  53. </el-select>
  54. <span v-if="formData.self_variate" style="color: #409EFF;margin-left: 20px;">{{formData.self_edb_date[0]+'至'+ formData.self_edb_date[1]}}</span>
  55. </el-form-item>
  56. <el-form-item label="" style="display: block;">
  57. <el-radio-group v-model="formData.self_move_type" style="margin-right: 10px" @change="formData.self_move_val=''">
  58. <el-radio :label="0">标准指标</el-radio>
  59. <el-radio :label="1">领先天数</el-radio>
  60. </el-radio-group>
  61. <template v-if="formData.self_move_type !== 0">
  62. <el-input
  63. style="width: 80px"
  64. type="number"
  65. size="mini"
  66. v-model="formData.self_move_val"
  67. @keyup.native="filterCode(formData)"
  68. ></el-input>
  69. </template>
  70. </el-form-item>
  71. <el-form-item label="因变量" prop="depend_variate">
  72. <el-select
  73. v-model="formData.depend_variate"
  74. v-loadMore="searchLoad"
  75. :filterable="!formData.depend_variate"
  76. clearable
  77. placeholder="请输入指标名称"
  78. style="width: 70%"
  79. remote
  80. :remote-method="getTarget"
  81. @click.native="inputFocusHandle"
  82. @blur="search_have_more = false"
  83. @change="changeTarget('depend',$event)"
  84. :disabled="operationForm.view"
  85. >
  86. <i slot="prefix" class="el-input__icon el-icon-search"></i>
  87. <el-option
  88. v-for="item in searchOptions"
  89. :key="item.EdbInfoId"
  90. :label="$parent.currentLang==='en'?(item.EdbNameEn||item.EdbName):item.EdbName"
  91. :value="item.EdbInfoId"
  92. >
  93. </el-option>
  94. </el-select>
  95. <span v-if="formData.depend_variate" style="color: #409EFF;margin-left: 20px;">{{formData.depend_edb_date[0]+'至'+ formData.depend_edb_date[1]}}</span>
  96. </el-form-item>
  97. <!-- <el-form-item label="" style="display: block;">
  98. <el-radio-group v-model="formData.depend_move_type" style="margin-right: 10px" @change="formData.depend_move_val=''">
  99. <el-radio :label="0">标准指标</el-radio>
  100. <el-radio :label="1">领先天数</el-radio>
  101. </el-radio-group>
  102. <template v-if="formData.depend_move_type !== 0">
  103. <el-input
  104. style="width: 80px"
  105. type="number"
  106. min="0"
  107. size="mini"
  108. v-model="formData.depend_move_val"
  109. @keyup.native="filterCode(formData)"
  110. ></el-input>
  111. </template>
  112. </el-form-item> -->
  113. <el-form-item label="拟合时间段" prop="date">
  114. <el-date-picker
  115. v-model="formData.date[0]"
  116. range-separator="至"
  117. placeholder="开始日期"
  118. value-format="yyyy-MM-dd"
  119. style="width: 25%"
  120. @change="changeDate"
  121. />
  122. <span style="margin: 0 10px">至</span>
  123. <el-date-picker
  124. v-model="formData.date[1]"
  125. placeholder="结束日期"
  126. value-format="yyyy-MM-dd"
  127. style="width: 25%"
  128. @change="changeDate"
  129. :picker-options="endDateOptions"
  130. />
  131. </el-form-item>
  132. <el-form-item label="指标名称" prop="edb_name" class="target-form-cont">
  133. <el-input
  134. v-model="formData.edb_name"
  135. style="width: 70%"
  136. placeholder="指标名称"></el-input>
  137. </el-form-item>
  138. <el-form-item label="指标目录" prop="menu">
  139. <el-cascader
  140. v-model="formData.menu"
  141. :options="options"
  142. :props="levelProps"
  143. style="width:70%"
  144. clearable
  145. placeholder="请选择指标目录"
  146. />
  147. </el-form-item>
  148. <el-form-item label="频度" prop="frequency">
  149. <el-select
  150. v-model="formData.frequency"
  151. placeholder="请选择频度"
  152. style="width:70%"
  153. clearable>
  154. <el-option
  155. v-for="item in frequencyArr"
  156. :key="item"
  157. :label="item"
  158. :value="item">
  159. </el-option>
  160. </el-select>
  161. </el-form-item>
  162. <el-form-item label="单位" prop="unit">
  163. <selectUnit v-model="formData.unit" style="width:70%" />
  164. </el-form-item>
  165. </el-form>
  166. </div>
  167. <div class="dia-bot" v-if="!operationForm.view">
  168. <el-button
  169. type="primary"
  170. style="margin-right: 20px"
  171. @click="saveHandle"
  172. :loading="loading"
  173. >{{loading ? '计算中...' : operationForm.edb_id ? '保存' : '拟合残差计算'}}</el-button
  174. >
  175. <el-button type="primary" plain @click="cancelHandle('cancel')">取消</el-button>
  176. </div>
  177. <el-popover
  178. placement="top-start"
  179. width="360"
  180. trigger="click">
  181. <p v-if="isPredict" style="padding:30px;line-height:25px;" v-html="$parent.tips.get(50)"/>
  182. <p v-else style="padding:30px;line-height:25px;" v-html="$parent.tips.get(37)"/>
  183. <span slot="reference" class="tip-label">公式说明</span>
  184. </el-popover>
  185. </el-dialog>
  186. </template>
  187. <script>
  188. import { dataBaseInterface } from '@/api/api.js';
  189. import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
  190. import { unitArr } from '@/utils/defaultOptions';
  191. export default {
  192. name:'',
  193. props: {
  194. isShow: {
  195. type: Boolean
  196. },
  197. type: {
  198. type: Number
  199. },
  200. operationForm: {
  201. type: Object,
  202. },
  203. isPredict:{//是否是预测指标
  204. type: Boolean,
  205. default:false
  206. }
  207. },
  208. watch: {
  209. isShow(newval) {
  210. newval && this.getMenu();
  211. if(newval && this.operationForm.edb_id) {
  212. const backData = _.cloneDeep(this.operationForm);
  213. this.formData = {
  214. date: backData.date.split(','),
  215. edb_name: backData.targetName,
  216. menu: backData.menu,
  217. frequency: backData.frequency,
  218. unit: backData.unit,
  219. self_variate: backData.from_arr[0].FromEdbInfoId,
  220. self_move_type: backData.from_arr[0].MoveValue === 0 ? 0 : 1,
  221. self_move_val: backData.from_arr[0].MoveValue,
  222. depend_variate: backData.from_arr[1].FromEdbInfoId,
  223. // depend_move_type: backData.from_arr[1].MoveValue === 0 ? 0 : 1,
  224. // depend_move_val: backData.from_arr[1].MoveValue,
  225. depend_edb_date: [backData.from_arr[1].StartDate,backData.from_arr[1].EndDate],
  226. self_edb_date: [backData.from_arr[0].StartDate,backData.from_arr[0].EndDate],
  227. }
  228. //options 回显
  229. this.searchOptions = backData.from_arr.map(item => ({
  230. EdbInfoId: item.FromEdbInfoId,
  231. EdbName: item.FromEdbName,
  232. StartDate: item.StartDate,
  233. EndDate: item.EndDate,
  234. }))
  235. }
  236. }
  237. },
  238. data () {
  239. return {
  240. searchOptions:[],//指标列表
  241. formData: {
  242. self_variate: '',
  243. self_move_type: 0,
  244. self_edb_date:[],
  245. self_move_val: '',
  246. depend_variate: '',
  247. depend_edb_date: [],
  248. depend_move_type: 0,
  249. depend_move_val: '',
  250. date: ['',this.$moment().format('YYYY-MM-DD')],
  251. edb_name:'',
  252. unit:'',
  253. menu:'',
  254. frequency:'',
  255. },
  256. formRules: {
  257. self_variate:[
  258. { required: true, message: '自变量不能为空', trigger: 'change' },
  259. ],
  260. depend_variate:[
  261. { required: true, message: '因变量不能为空', trigger: 'change' },
  262. ],
  263. date:[
  264. { required: true, message: '拟合时间段不能为空', trigger: 'change' },
  265. ],
  266. edb_name:[
  267. { required: true, message: '指标名称不能为空', trigger: 'change' },
  268. ],
  269. menu:[
  270. { required: true, message: '所属目录不能为空', trigger: 'change' },
  271. ],
  272. frequency:[
  273. { required: true, message: '频度不能为空', trigger: 'change' },
  274. ],
  275. unit:[
  276. { required: true, message: '单位不能为空', trigger: ['blur','change'] },
  277. ]
  278. },
  279. unitArr,
  280. options: [],
  281. levelProps: {
  282. label: 'ClassifyName',
  283. value: 'ClassifyId',
  284. children: 'Children',
  285. },
  286. frequencyArr: ['日度', '周度','旬度', '月度', '季度', '年度'],
  287. fre_options: ['天','周','月','季','年'],
  288. loading:false,
  289. search_have_more: false,
  290. search_page: 1,
  291. current_search: '',
  292. endDateOptions: {
  293. shortcuts: [{
  294. text: '至今',
  295. onClick(picker) {
  296. const date = new Date();
  297. picker.$emit('pick',date)
  298. }
  299. }]
  300. },
  301. depend_edb_name: '',
  302. self_edb_name: ''
  303. };
  304. },
  305. methods: {
  306. /* 指标列表 */
  307. getTarget(query) {
  308. this.search_page = 1;
  309. this.current_search = query;
  310. this.searchApi(this.current_search);
  311. },
  312. /* 聚焦获取当前检索 */
  313. inputFocusHandle(e) {
  314. this.search_page = 1;
  315. this.current_search = e.target.value;
  316. this.searchApi(this.current_search);
  317. },
  318. async searchApi(query,page=1) {
  319. const params={
  320. KeyWord:query,
  321. CurrentIndex: page,
  322. FilterSource: this.type === 5 ? 2 : this.type === 14 ? 3 : 1
  323. }
  324. const res=this.isPredict?await preDictEdbInterface.edbSearch(params):await dataBaseInterface.targetSearchByPage(params)
  325. // dataBaseInterface.targetSearchByPage({
  326. // KeyWord:query,
  327. // CurrentIndex: page,
  328. // FilterSource: this.type === 5 ? 2 : this.type === 14 ? 3 : 1
  329. // }).then(res => {
  330. if(res.Ret !== 200) return
  331. const { List,Paging } = res.Data;
  332. this.search_have_more = page < Paging.Pages;
  333. let arr = page === 1 ? List : this.searchOptions.concat(List);
  334. this.searchOptions = this.operationForm.edb_id ? arr.filter(item => item.EdbInfoId !== this.operationForm.edb_id) : arr;
  335. // })
  336. },
  337. searchLoad() {
  338. if(!this.search_have_more) return;
  339. this.searchApi(this.current_search,++this.search_page)
  340. },
  341. /* 获取目录结构 */
  342. async getMenu() {
  343. const res=this.isPredict?await preDictEdbInterface.classifyListV2():await dataBaseInterface.menuListV3()
  344. // dataBaseInterface.menuList().then((res) => {
  345. if (res.Ret === 200) {
  346. /* if(!this.isPredict){
  347. this.filterNodes(res.Data.AllNodes);
  348. this.options = res.Data.AllNodes || [];
  349. }else{
  350. this.options = res.Data.AllNodes || [];
  351. } */
  352. this.options = res.Data.AllNodes || [];
  353. }
  354. // });
  355. },
  356. // 递归改变第三级目录结构
  357. filterNodes(arr) {
  358. arr.length &&
  359. arr.forEach((item) => {
  360. item.Children.length && this.filterNodes(item.Children);
  361. if (item.Level === 2) {
  362. delete item.Children;
  363. }
  364. });
  365. },
  366. /* 过滤负数 小数点*/
  367. filterCode(item) {
  368. item.moveVal=item.moveVal.replace(/[^\.\d]/g,'').replace('.','');
  369. },
  370. /* 选择指标时同步日期 */
  371. changeTarget(type,val) {
  372. const choose_obj = this.searchOptions.find(item => item.EdbInfoId === val);
  373. type === 'depend' ? this.depend_edb_name = choose_obj && choose_obj.EdbName : this.self_edb_name = choose_obj&&choose_obj.EdbName||'';
  374. //默认拼接名称
  375. if(this.formData.depend_variate && this.formData.self_variate) {
  376. this.formData.edb_name = `${this.depend_edb_name}拟合残差/${this.self_edb_name}`
  377. }
  378. if (val) {
  379. type === 'self'
  380. ? this.formData.self_edb_date = [choose_obj.StartDate,choose_obj.EndDate]
  381. : this.formData.depend_edb_date = [choose_obj.StartDate,choose_obj.EndDate]
  382. }
  383. },
  384. /* 选择日期 */
  385. changeDate() {
  386. console.log(this.formData.date)
  387. if(this.formData.date[0] && this.formData.date[1]) {
  388. let differ = this.$moment(this.formData.date[1]).diff(
  389. this.$moment(this.formData.date[0]),
  390. 'days',
  391. true
  392. )
  393. if(differ < 2) {
  394. this.formData.date = [];
  395. this.$message.warning(`${differ<0?'开始日期不能晚于结束日期':'日期间隔不得少于两天'}`)
  396. }
  397. }
  398. },
  399. init() {
  400. this.searchOptions = [];
  401. this.formData = {
  402. self_variate: '',
  403. self_move_type: 0,
  404. self_move_val: '',
  405. self_edb_date:[],
  406. depend_edb_date: [],
  407. depend_variate: '',
  408. depend_move_type: 0,
  409. depend_move_val: '',
  410. date: ['',this.$moment().format('YYYY-MM-DD')],
  411. edb_name:'',
  412. unit:'',
  413. menu:'',
  414. frequency:'',
  415. };
  416. this.$refs.form.resetFields();
  417. },
  418. /* 保存 */
  419. async saveHandle() {
  420. // if(!this.select_target) this.$message.warning('指标不能为空')
  421. await this.$refs.form.validate();
  422. this.loading = true;
  423. const {
  424. self_variate,
  425. self_move_type,
  426. self_move_val,
  427. depend_variate,
  428. date,
  429. edb_name,
  430. unit,
  431. menu,
  432. frequency
  433. } = this.formData;
  434. let params ={
  435. Source: this.type,
  436. EdbName: edb_name,
  437. Unit: unit,
  438. ClassifyId: menu[menu.length - 1],
  439. Frequency: frequency,
  440. Formula: date.join(','),
  441. EdbInfoIdArr: [
  442. {
  443. EdbInfoId: self_variate,
  444. FromTag: 'A',
  445. MoveValue: self_move_type === 0 ? 0 : Number(self_move_val)
  446. },
  447. {
  448. EdbInfoId: depend_variate,
  449. FromTag: 'B',
  450. MoveValue: 0
  451. },
  452. ],
  453. }
  454. const res = this.operationForm.edb_id
  455. ? this.isPredict?await preDictEdbInterface.operateEdbSave({...params,EdbInfoId: this.operationForm.edb_id}):await dataBaseInterface.calculateTargetEdit({ ...params,EdbInfoId: this.operationForm.edb_id })
  456. : this.isPredict?await preDictEdbInterface.operateEdbSave(params):await dataBaseInterface.calculateTargetSave(params)
  457. this.loading = false;
  458. if (res.Ret !== 200) return
  459. this.$message.success(res.Msg);
  460. this.operationForm.edb_id
  461. ? this.$emit('addCallBack','edit')
  462. : this.$emit('addCallBack','add',{ code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:params.ClassifyId });
  463. this.init();
  464. },
  465. /* 关闭弹窗 */
  466. cancelHandle(type) {
  467. this.init()
  468. this.$emit('cancel');
  469. type==='cancel' && !this.operationForm.edb_id && this.$emit('openPrev');
  470. },
  471. },
  472. }
  473. </script>
  474. <style lang='scss'>
  475. .fitting-dailog {
  476. position: relative;
  477. div::-webkit-scrollbar {
  478. width: 6px !important;
  479. }
  480. .el-input-number .el-input__inner {
  481. padding: 0 34px 0 4px;
  482. }
  483. .el-dialog__body {
  484. max-height: 780px;
  485. overflow: auto;
  486. }
  487. .target-form-cont {
  488. margin-top: 30px;
  489. padding-top: 30px;
  490. border-top: 1px dashed #AAB4CC;
  491. }
  492. .dialog-main {
  493. padding: 0 50px 30px;
  494. .el-cascader .el-input {
  495. width: 100%;
  496. }
  497. .form-cont {
  498. padding-top: 30px;
  499. input::-webkit-outer-spin-button,
  500. input::-webkit-inner-spin-button {
  501. -webkit-appearance: none;
  502. }
  503. input[type="number"]{
  504. -moz-appearance: textfield;
  505. }
  506. }
  507. }
  508. .dia-bot {
  509. padding: 20px 0 30px;
  510. display: flex;
  511. justify-content: center;
  512. }
  513. .tip-cont {
  514. padding: 30px;
  515. }
  516. .tip-label {
  517. position: absolute;
  518. bottom: 30px;
  519. right: 30px;
  520. color: #409EFF;
  521. cursor: pointer;
  522. }
  523. }
  524. </style>