edit.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. <template>
  2. <basic-container>
  3. <div class="box-dashed flex">
  4. <div class="retain-box">
  5. <el-checkbox v-model="isRetain"></el-checkbox>
  6. <span>保留</span>
  7. <el-input-number
  8. v-model="retainNum"
  9. :step="1"
  10. :disabled="!isRetain"
  11. size="small"
  12. ></el-input-number>
  13. <span class="retain">位</span>
  14. </div>
  15. <div>
  16. <el-menu
  17. :default-active="activeIndex"
  18. class="el-menu-demo"
  19. mode="horizontal"
  20. @select="handleSelect"
  21. >
  22. <el-submenu v-for="(value,key,index) in formulaList" :key="key" :index="key">
  23. <template slot="title">
  24. <span>{{key}}</span>
  25. </template>
  26. <el-menu-item v-for="(item2,index2) in value" :key="index2" :index="(index+1) +'-' + (index2+1)">{{item2.name}}</el-menu-item>
  27. </el-submenu>
  28. </el-menu>
  29. </div>
  30. </div>
  31. <div class="box-dashed">
  32. <div class="mg-b-20">函数公式</div>
  33. <div class="edit-text">
  34. <span>
  35. <formula-item
  36. v-for="(item,index) in resultFormula" :key="index"
  37. :item="item" @click="obj => equationClick(obj,index,'resultFormula')"
  38. >
  39. </formula-item>
  40. </span>
  41. <span>=</span>
  42. <span>
  43. <formula-item
  44. v-for="(item,index) in processFormula" :key="index"
  45. :item="item" @click="obj => equationClick(obj,index,'processFormula')"
  46. >
  47. </formula-item>
  48. </span>
  49. </div>
  50. <div class="flex jc-sb">
  51. <div></div>
  52. <div><el-button type="info" size="small" @click="operationEdit">重置函数</el-button></div>
  53. </div>
  54. </div>
  55. <div v-show="operationVisible" class="operation-box">
  56. <div>选择参数设置</div>
  57. <div>
  58. <el-row :gutter="20">
  59. <el-col :span="8">
  60. <el-card shadow="never">
  61. <el-scrollbar style="height: 460px">
  62. <el-tree
  63. class="filter-tree"
  64. lazy
  65. :load="loadNode"
  66. @node-click="getNodeDetail"
  67. :props="defaultProps"
  68. :expand-on-click-node="false"
  69. highlight-current
  70. node-key="id"
  71. ref="tree"
  72. >
  73. </el-tree>
  74. </el-scrollbar>
  75. </el-card>
  76. </el-col>
  77. <el-col :span="16">
  78. <el-select v-model="eleTableId" @change="getTableEle" placeholder="请选择元素表">
  79. <el-option v-for="item in eleTableList" :key="item.id" :label="item.tableName" :value="item.id"></el-option>
  80. </el-select>
  81. <div class="mg-t-10 mg-b-10 no-mb-col">
  82. <el-scrollbar style="height: 210px">
  83. <el-row>
  84. <el-col :span="6" v-for="item in eleList" :key="item.id">
  85. <div class="ele-box">
  86. <span>{{item.eName}}</span>
  87. <el-checkbox v-model="item.checked" @change="value => eleCheckHandle(value,item)"></el-checkbox>
  88. </div>
  89. </el-col>
  90. </el-row>
  91. </el-scrollbar>
  92. </div>
  93. <div class="flex jc-sb">
  94. <div>定位数据位置:</div>
  95. <div>
  96. <el-link :underline="false" type="primary" class="mg-r-20" @click="eleAddFormula">元素添加到公式</el-link>
  97. <el-link :underline="false" type="primary" class="mg-r-10" @click="addBrackets('(',false)">(</el-link>
  98. <el-link :underline="false" type="primary" class="mg-r-10" @click="addBrackets(')',true)">)</el-link>
  99. <el-link :underline="false" type="primary" class="mg-r-10" @click="addBrackets('[',false)">【</el-link>
  100. <el-link :underline="false" type="primary" class="mg-r-10" @click="addBrackets(']',true)">】</el-link>
  101. <el-link :underline="false" type="primary" @click="addText">输入值</el-link>
  102. </div>
  103. </div>
  104. <div class="border-grey sele-ele-box">
  105. <div>
  106. <formula-item
  107. v-for="(item,index) in selectEleFormula" :key="index"
  108. :item="item" @click="obj => eleFormulaClick(obj,index)"
  109. >
  110. </formula-item>
  111. </div>
  112. </div>
  113. </el-col>
  114. </el-row>
  115. <div class="text-align-c">
  116. <el-button size="small" @click="operationHandle" type="primary">保存</el-button>
  117. <el-button size="small" @click="operationVisible = false">取消</el-button>
  118. </div>
  119. </div>
  120. </div>
  121. <div v-show="!operationVisible && !showFunDetail">
  122. <div class="box-dashed">
  123. <div class="mg-b-20">函数公式运算执行溯源</div>
  124. <div>
  125. <el-select
  126. v-model="projectId"
  127. @change="projectChange"
  128. placeholder="请选择项目"
  129. style="width: 380px"
  130. >
  131. <el-option
  132. v-for="item in projectList"
  133. :key="item.id"
  134. :label="item.projectName"
  135. :value="item.id"
  136. ></el-option>
  137. </el-select>
  138. <el-select v-model="contractId" placeholder="请选择合同段">
  139. <el-option
  140. v-for="item in contractList"
  141. :key="item.id"
  142. :label="item.contractName"
  143. :value="item.id"
  144. ></el-option>
  145. </el-select>
  146. <el-button type="info">查询</el-button>
  147. </div>
  148. </div>
  149. <div class="text-align-c">
  150. <el-button type="warning">取消</el-button>
  151. <el-button type="primary" @click="saveFormula">保存</el-button>
  152. </div>
  153. </div>
  154. <div v-if="!operationVisible && showFunDetail">
  155. <el-tabs v-model="actiFunIndex" closable @tab-remove="removeFun" :before-leave="funLeave">
  156. <el-tab-pane v-for="(item,index) in equationSelectEle.children" :key="item.name" :label="item.name" :name="index.toString()">
  157. <formula-template :formulainfo="item" :curele="equationSelectEle" @sele-ele-handle="showChooseEle">
  158. </formula-template>
  159. </el-tab-pane>
  160. </el-tabs>
  161. </div>
  162. <el-dialog title="输入值" :visible.sync="inputVisible" width="300px" append-to-body :close-on-click-modal="false">
  163. <el-input v-model="inputText" placeholder="请输入内容"></el-input>
  164. <div class="text-align-c mg-t-10">
  165. <el-button size="small" @click="addTextHandle" type="primary">保存</el-button>
  166. <el-button size="small" @click="inputVisible = false">取消</el-button>
  167. </div>
  168. </el-dialog>
  169. <el-dialog title="选择元素" :visible.sync="chooseEleVisible" width="800px" append-to-body :close-on-click-modal="false">
  170. <div>
  171. <el-row :gutter="20">
  172. <el-col :span="8">
  173. <el-card shadow="never">
  174. <el-scrollbar style="height: 460px">
  175. <el-tree
  176. class="filter-tree"
  177. lazy
  178. :load="loadNode"
  179. @node-click="getNodeDetail"
  180. :props="defaultProps"
  181. :expand-on-click-node="false"
  182. highlight-current
  183. node-key="id"
  184. ref="tree"
  185. >
  186. </el-tree>
  187. </el-scrollbar>
  188. </el-card>
  189. </el-col>
  190. <el-col :span="16">
  191. <el-select v-model="eleTableId" @change="getTableEle" placeholder="请选择元素表">
  192. <el-option v-for="item in eleTableList" :key="item.id" :label="item.tableName" :value="item.id"></el-option>
  193. </el-select>
  194. <div class="mg-t-10 mg-b-10 no-mb-col">
  195. <el-scrollbar style="height: 210px">
  196. <el-row>
  197. <el-col :span="6" v-for="item in eleList" :key="item.id">
  198. <div class="ele-box">
  199. <span>{{item.eName}}</span>
  200. <el-checkbox v-model="item.checked" @change="value => eleCheckHandle(value,item)"></el-checkbox>
  201. </div>
  202. </el-col>
  203. </el-row>
  204. </el-scrollbar>
  205. </div>
  206. </el-col>
  207. </el-row>
  208. <div class="text-align-c">
  209. <el-button size="small" @click="chooseEleHandle" type="primary">保存</el-button>
  210. <el-button size="small" @click="chooseEleVisible = false">取消</el-button>
  211. </div>
  212. </div>
  213. </el-dialog>
  214. </basic-container>
  215. </template>
  216. <script>
  217. import { getLazytree,selectByNodeTable,selectFormElements} from "@/api/manager/wbstree";
  218. import { getProjectList } from "@/api/manager/projectinfo";
  219. import { findContractByProjectId } from "@/api/manager/contractinfo";
  220. import { getDetail as getEleDeatil } from "@/api/manager/wbsformelement";
  221. import { getTypeMap } from "@/api/formula/formula";
  222. import {mapGetters} from "vuex";
  223. import formulaItem from "./component/formulaItem"
  224. import formulaTemplate from "./component/formulaTemplate"
  225. import {formulaArrayToString} from "./formulaArrayToString"
  226. export default {
  227. components: {
  228. formulaItem,
  229. formulaTemplate
  230. },
  231. data() {
  232. return {
  233. wbsid: "", //从哪个wbs树过来的
  234. eleid: "", //元素id
  235. isRetain: false, //是否保留小数
  236. retainNum: 2, //保留几位小数
  237. formulaList:{},
  238. activeIndex: "1-1", //当前选择的公式
  239. projectList: [], //项目备选列表
  240. projectId: "", //溯源的项目ID
  241. curProjiect: {}, //当前项目对象
  242. contractList: [], //合同段备选列表
  243. contractId: "", //合同段id
  244. operationVisible: false, //基础运算弹窗
  245. defaultProps: {
  246. children: "children",
  247. label: "title",
  248. isLeaf: function (data) {
  249. return !data.hasChildren || (data.isExistForm==1);
  250. },
  251. },
  252. eleTableId:'',//选中的元素表id
  253. eleTableList:[],
  254. eleList:[],
  255. selectEleFormula:[],
  256. curSeleEleIndex:0,//公式文字里面选中的元素索引
  257. inputVisible:false,//输入弹窗
  258. inputText:"",//输入值
  259. resultFormula:[],//=等号左边的数组
  260. processFormula:[],//=等号右边的数组
  261. processType:'',//选中的元素在等号哪边
  262. processSelectIndex:0,//选中的索引
  263. actiFunIndex:0,//元素下挂载的计算式的索引
  264. chooseEleVisible:false,//选择元素弹窗
  265. argumenObj:{},
  266. symbolReg:/(\+|-|\*|\/)(.+)/,
  267. };
  268. },
  269. computed: {
  270. ...mapGetters(["userInfo"]),
  271. // selectEleFormulaText:function(){
  272. // let text = '';
  273. // this.selectEleFormula.forEach((Element)=>{
  274. // text+=Element.name;
  275. // })
  276. // return text
  277. // }
  278. //等式中选中的元素
  279. equationSelectEle:function(){
  280. if(this.processType){
  281. return this[this.processType][this.processSelectIndex];
  282. }else{
  283. return null;
  284. }
  285. },
  286. //是否显示元素下挂载的计算式信息
  287. showFunDetail:function(){
  288. if(this.equationSelectEle && this.equationSelectEle.children && this.equationSelectEle.children.length>0){
  289. return true;
  290. }else{
  291. return false;
  292. }
  293. }
  294. },
  295. created() {
  296. this.wbsid = this.$route.query.wbsid;
  297. this.eleid = this.$route.query.eleid;
  298. this.init();
  299. },
  300. methods: {
  301. init() {
  302. this.getEleDeatil();
  303. this.getProjectList();
  304. this.getTypeMap();
  305. },
  306. //懒加载树
  307. loadNode(node, resolve) {
  308. let pid = 0;
  309. if (node.level != 0) {
  310. pid = node.data.id;
  311. }
  312. getLazytree(this.wbsid, pid, this.userInfo.tenant_id).then((res) => {
  313. let arr = [];
  314. if (Array.isArray(res.data.data)) {
  315. arr = res.data.data;
  316. }
  317. return resolve(arr);
  318. });
  319. },
  320. //获取项目列表
  321. getProjectList() {
  322. getProjectList(1, 999).then((res) => {
  323. this.projectList = res.data.data.records;
  324. });
  325. },
  326. //选择公式处理
  327. handleSelect(index,indexPath) {
  328. //console.log(index,'index')
  329. //console.log(indexPath,'indexPath')
  330. if(this.operationVisible){
  331. this.openerationSelect(index,indexPath)
  332. }else{
  333. this.equationSelect(index,indexPath)
  334. }
  335. },
  336. //在选择元素模式下点选计算式
  337. openerationSelect(index,indexPath){
  338. if(indexPath[0]!='基础运算'){
  339. this.$message({
  340. type: "warning",
  341. message: "当前只能使用基础运算"
  342. });
  343. return;
  344. }
  345. let formulaindex = Number(indexPath[1].split('-')[1])-1;
  346. this.eleAddFormulaHandle(this.formulaList[indexPath[0]][formulaindex]);
  347. },
  348. //溯源项目id切换
  349. projectChange(id) {
  350. for (let i = 0; i < this.projectList.length; i++) {
  351. if (id == this.projectList[i].id) {
  352. this.curProjiect = this.projectList[i];
  353. //根据项目id获取合同段列表
  354. findContractByProjectId(this.curProjiect.id).then((res) => {
  355. this.contractList = res.data.data;
  356. this.contractId = "";
  357. });
  358. return;
  359. }
  360. }
  361. },
  362. operationEdit(){
  363. this.selectEleFormula= JSON.parse(JSON.stringify(this.processFormula));
  364. this.operationVisible = true;
  365. },
  366. eleAddFormula(){
  367. for (let i = 0; i < this.eleList.length; i++) {
  368. if (this.eleList[i].checked) {
  369. this.eleAddFormulaHandle(this.eleList[i]);
  370. break;
  371. }
  372. }
  373. },
  374. //把元素加到公式里
  375. eleAddFormulaHandle(ele){
  376. if(ele.tableElementKey){
  377. //元素
  378. this.selectEleFormula.push({
  379. type:'Element',
  380. name:ele.eName,
  381. id:ele.id,
  382. selected:false,
  383. tableElementKey:ele.tableElementKey,
  384. children:[],
  385. })
  386. }else if(ele.template && ele.example){
  387. //运算符号
  388. this.selectEleFormula.push({
  389. type:'Operator',
  390. name:this.symbolReg.exec(ele.name)[1],
  391. selected:false,
  392. template:ele.template
  393. })
  394. }else if(ele.type == 'Brackets'){
  395. //括号
  396. this.selectEleFormula.splice(ele.selectIndex,0,{
  397. type:'Brackets',
  398. name:ele.name,
  399. selected:false,
  400. })
  401. }else if(ele.type == 'Text'){
  402. //输入值
  403. this.selectEleFormula.push({
  404. type:'Text',
  405. name:ele.name,
  406. selected:false,
  407. })
  408. }
  409. },
  410. //添加括号
  411. addBrackets(text,type){
  412. //type 是true 表示在元素右边插入
  413. if(this.curSeleEleIndex == Number(this.curSeleEleIndex)){
  414. this.eleAddFormulaHandle({
  415. type:'Brackets',
  416. name:text,
  417. selectIndex:type?Number(this.curSeleEleIndex)+1:this.curSeleEleIndex
  418. })
  419. //如果在左边插入index要增1
  420. if(!type){
  421. this.curSeleEleIndex = Number(this.curSeleEleIndex)+1;
  422. }
  423. }
  424. },
  425. addText(){
  426. this.inputVisible = true;
  427. },
  428. //添加输入值
  429. addTextHandle(){
  430. this.eleAddFormulaHandle({
  431. type:'Text',
  432. name:this.inputText
  433. })
  434. this.inputVisible = false;
  435. },
  436. //勾选元素
  437. eleCheckHandle(checked,item){
  438. if(checked){
  439. this.eleList.forEach((ele)=>{
  440. this.$set(ele,'checked',false);
  441. //ele.checked = false;
  442. })
  443. item.checked = true;
  444. }
  445. },
  446. //点选公式中的元素
  447. eleFormulaClick({selected,item},index){
  448. if(selected){
  449. this.selectEleFormula.forEach((ele)=>{
  450. ele.selected = false;
  451. })
  452. item.selected = true;
  453. this.curSeleEleIndex = index;
  454. }else{
  455. this.curSeleEleIndex = 0;
  456. }
  457. },
  458. //赋值给等号右边的数组
  459. operationHandle(){
  460. this.processFormula = JSON.parse(JSON.stringify(this.selectEleFormula));
  461. this.operationVisible = false;
  462. },
  463. //点选等式中的元素
  464. equationClick({selected,item},index,arrName){
  465. if(selected){
  466. this.resultFormula.forEach((ele)=>{
  467. ele.selected = false;
  468. })
  469. this.processFormula.forEach((ele)=>{
  470. ele.selected = false;
  471. })
  472. this.processSelectIndex = index;
  473. this.processType = arrName;
  474. item.selected = true;
  475. }else{
  476. this.processType = '';
  477. }
  478. },
  479. //在等式模式下点选计算式
  480. equationSelect(index,indexPath){
  481. if(!this.equationSelectEle && this.equationSelectEle.type == 'Element'){
  482. this.$message({
  483. type: "warning",
  484. message: "请先选中元素"
  485. });
  486. return;
  487. }
  488. let formulaindex = Number(indexPath[1].split('-')[1])-1;
  489. let expression = this.formulaList[indexPath[0]][formulaindex];
  490. if(expression.type ==1){
  491. return;
  492. }
  493. //console.log(JSON.parse(expression.template));
  494. let obj = Object.assign({}, expression);
  495. obj.template = JSON.parse(obj.template);
  496. obj.arguments = new Array(obj.template.args.length);
  497. obj.arguments[0] = {
  498. id:this.equationSelectEle.id,
  499. name:this.equationSelectEle.name,
  500. selected:false,
  501. tableElementKey:this.equationSelectEle.tableElementKey,
  502. type:"Element",
  503. };
  504. this.equationSelectEle.children.push(obj);
  505. },
  506. //选择元素
  507. chooseEleHandle(){
  508. for (let i = 0; i < this.eleList.length; i++) {
  509. if (this.eleList[i].checked) {
  510. let ele = this.eleList[i]
  511. let obj = {
  512. type:'Element',
  513. name:ele.eName,
  514. id:ele.id,
  515. selected:false,
  516. tableElementKey:ele.tableElementKey,
  517. children:[],
  518. }
  519. this.$set(this.argumenObj.arguments,this.argumenObj.index,obj);
  520. this.chooseEleVisible = false;
  521. break;
  522. }
  523. }
  524. },
  525. //显示选择元素弹窗
  526. showChooseEle(argumenObj){
  527. this.argumenObj = argumenObj;
  528. this.chooseEleVisible = true;
  529. },
  530. //移除挂载的函数
  531. removeFun(name){
  532. //console.log(name)
  533. this.equationSelectEle.children.splice(Number(name), 1);
  534. },
  535. //切换公式tab标签
  536. funLeave(activeName, oldActiveName){
  537. if(oldActiveName){
  538. let formula = this.equationSelectEle.children[Number(oldActiveName)];
  539. if(formula){
  540. return this.checkFormulaLegal(formula);
  541. }
  542. }
  543. },
  544. //检测公式合法
  545. checkFormulaLegal(formula){
  546. if(!formula.arguments){
  547. return false;
  548. }
  549. //当前选中的元素
  550. let curEle = this.equationSelectEle;
  551. let isIn = false;
  552. for (let i = 0; i < formula.arguments.length; i++) {
  553. if(formula.arguments[i] && formula.arguments[i].id ==curEle.id){
  554. isIn = true;
  555. break;
  556. }
  557. }
  558. if(!isIn){
  559. this.$message({
  560. type: "warning",
  561. message: "参数必须有一个值是当前元素"
  562. });
  563. return false;
  564. }
  565. return true;
  566. },
  567. //保存公式
  568. saveFormula(){
  569. let text = formulaArrayToString(this.processFormula,this.resultFormula);
  570. console.log(text);
  571. },
  572. getNodeDetail(data) {
  573. selectByNodeTable(data.id).then((res)=>{
  574. if(res.data.data.length){
  575. this.eleTableList = res.data.data;
  576. }else{
  577. this.eleTableList = [];
  578. }
  579. })
  580. },
  581. getEleDeatil(){
  582. getEleDeatil(this.eleid).then((res)=>{
  583. let ele = res.data.data;
  584. this.resultFormula = [{
  585. type:'Element',
  586. name:ele.eName,
  587. id:ele.id,
  588. selected:false,
  589. tableElementKey:ele.tableElementKey,
  590. children:[],
  591. }]
  592. })
  593. },
  594. getTableEle(tableId){
  595. selectFormElements(tableId).then((res)=>{
  596. this.eleList = res.data.data;
  597. })
  598. },
  599. getTypeMap(){
  600. getTypeMap().then((res)=>{
  601. //console.log(res)
  602. this.formulaList = res.data.data;
  603. })
  604. }
  605. },
  606. };
  607. </script>
  608. <style scoped lang="scss">
  609. .box-dashed {
  610. border: 1px dashed #bbbbbb;
  611. border-radius: 6px;
  612. padding: 10px;
  613. margin-bottom: 10px;
  614. }
  615. .retain-box {
  616. border-right: 1px dashed #bbbbbb;
  617. }
  618. .retain {
  619. line-height: 50px;
  620. margin-right: 10px;
  621. }
  622. .edit-text {
  623. font-size: 26px;
  624. margin-left: 20px;
  625. }
  626. .el-menu--popup .el-menu-item.is-active {
  627. background-color: #fff;
  628. }
  629. .ele-box{
  630. border: 1px solid #bbb;
  631. height: 26px;
  632. display: flex;
  633. justify-content: space-between;
  634. align-items: center;
  635. padding: 6px;
  636. }
  637. .no-mb-col .el-col{
  638. margin-bottom: 0px;
  639. }
  640. .sele-ele-box{
  641. height: 160px;
  642. padding: 20px;
  643. margin-top: 10px;;
  644. }
  645. </style>