MixedTable.vue 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. <template>
  2. <div class="table-wrapper">
  3. <template v-if="config.data.length">
  4. <!-- 公式显示区 -->
  5. <div class="formula-wrapper" v-if="!disabled">
  6. <span style="flex-shrink: 0;color:#C0C4CC">公式</span>
  7. <el-input
  8. v-if="selectCell&&selectCell.DataType===6"
  9. v-model="selectCell.Value"
  10. @change="updateValueByFormula"
  11. />
  12. </div>
  13. <table
  14. width="auto"
  15. border="0"
  16. class="table"
  17. :style="disabled ? 'width:100%' : ''"
  18. >
  19. <thead>
  20. <tr>
  21. <!-- 行头 -->
  22. <th class="th-tg sm"></th>
  23. <!-- 列头 -->
  24. <th
  25. v-for="(item, index) in columnHeader"
  26. :key="index"
  27. class="th-tg th-col"
  28. :data-cindex="item"
  29. :data-rindex="-1"
  30. @contextmenu.prevent="rightClickHandle"
  31. >
  32. {{ item }}
  33. </th>
  34. </tr>
  35. </thead>
  36. <tbody>
  37. <tr v-for="(row, index) in config.data" :key="index">
  38. <!-- 行头 -->
  39. <th
  40. class="th-tg th-row sm"
  41. @contextmenu.prevent="rightClickHandle"
  42. :data-rindex="rowHeader[index]"
  43. :data-cindex="-1"
  44. >
  45. {{ rowHeader[index] }}
  46. </th>
  47. <td
  48. v-for="(cell, cell_index) in row"
  49. :key="`${index}_${cell_index}`"
  50. :data-rindex="rowHeader[index]"
  51. :data-cindex="columnHeader[cell_index]"
  52. :data-key="cell.Uid"
  53. @click="clickCell($event, cell)"
  54. @dblclick="dblClickCellHandle($event,cell)"
  55. @contextmenu.prevent="rightClickHandle($event,cell)"
  56. @mouseenter="getRelationEdbInfo(cell)"
  57. @copy="copyCellHandle($event,cell)"
  58. @paste="pasteCellHandle($event,cell)"
  59. >
  60. <!-- 插入单元格禁止编辑 -->
  61. <template
  62. v-if="([4,5,6,7].includes(cell.DataType)&&!cell.CanEdit)
  63. ||disabled
  64. ||(cell.DataType===1&&[1,2].includes(cell.DataTimeType))"
  65. >
  66. <!-- 单元格类型5 7显示指标浮窗 -->
  67. <el-popover
  68. v-if="[5,7].includes(cell.DataType)&&!disabled"
  69. placement="top-start"
  70. width="350"
  71. trigger="hover"
  72. >
  73. <ul>
  74. <li style="display:flex;margin:10px;">
  75. <label style="min-width:80px;">指标名称</label>
  76. {{cellrelationEdbInfo.EdbName}}
  77. </li>
  78. <li style="display:flex;margin:10px;">
  79. <label style="min-width:80px;">最新日期</label>
  80. {{cellrelationEdbInfo.LatestDate}}
  81. </li>
  82. <li style="display:flex;margin:10px;">
  83. <label style="min-width:80px;">指标ID</label>
  84. {{cellrelationEdbInfo.EdbCode}}
  85. </li>
  86. </ul>
  87. <span
  88. slot="reference"
  89. :data-rindex="rowHeader[index]"
  90. :data-cindex="columnHeader[cell_index]"
  91. :data-key="cell.Uid"
  92. >{{ cell.ShowValue }}</span>
  93. </el-popover>
  94. <span
  95. :data-rindex="rowHeader[index]"
  96. :data-cindex="columnHeader[cell_index]"
  97. :data-key="cell.Uid"
  98. v-else
  99. >{{ cell.ShowValue }}</span>
  100. </template>
  101. <el-autocomplete
  102. v-else
  103. v-model="cell.Value"
  104. :ref="`inputRef${cell.Uid}`"
  105. :fetch-suggestions="searchTarget"
  106. popper-class="edb-select-popover"
  107. :data-key="cell.Uid"
  108. :data-rindex="rowHeader[index]"
  109. :data-cindex="columnHeader[cell_index]"
  110. :highlight-first-item="cell.DataType===2"
  111. @select="selectTarget($event,cell)"
  112. @click="clickCell($event, cell)"
  113. @change.native="changeVal($event, cell)"
  114. @keydown.native="keyEnterHandle($event,cell)"
  115. >
  116. <template slot-scope="scope">
  117. <edbDetailPopover :info="scope.item">
  118. <div slot="reference" v-if="cell.DataType===2" class="edb-item">
  119. <span class="edb-item-name text_oneLine">{{ scope.item.EdbName }}</span>
  120. <i class="el-icon-check" style="color:#0052D9;font-size:18px;"/>
  121. </div>
  122. <div slot="reference" v-else>{{ scope.item.EdbName }}</div>
  123. </edbDetailPopover>
  124. </template>
  125. </el-autocomplete>
  126. </td>
  127. </tr>
  128. </tbody>
  129. </table>
  130. <!-- 右键菜单 -->
  131. <div class="contextMenu-wrapper" id="contextMenu-wrapper" @mouseleave="hideContextMenu">
  132. <div :class="['item',{'deletesty': menu.key==='reset'}]" v-for="menu in config.contextMenuOption" :key="menu.key" @click="handleContext(menu.key)">
  133. {{menu.label}}
  134. <i class="el-icon-arrow-right" v-if="menu.children"></i>
  135. <!-- 二级菜单 -->
  136. <div class="subMenu-wrapper" v-if="menu.children">
  137. <div slot="reference" class="item" v-for="submenu in menu.children" :key="submenu.key" @click="edbCalculateInsertOpen(submenu)">
  138. <el-popover
  139. width="300"
  140. trigger="hover"
  141. placement="right"
  142. >
  143. <div v-html="formulaTip.get(submenu.fromEdbKey)"></div>
  144. <div slot="reference" style="width:100%">{{submenu.label}}</div>
  145. </el-popover>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. </template>
  151. <div class="nodata" v-else>
  152. <tableNoData text="暂无数据"/>
  153. </div>
  154. <!-- 选择指标 -->
  155. <selectTargetValueDia
  156. :isShow.sync="isSelectTargetValueDialog"
  157. @insert="insertSelectData"
  158. ref="selectTargetValueRef"
  159. />
  160. <!-- 插入系统/指标日期弹窗 -->
  161. <insertDateDia
  162. :isShow.sync="isInsertDateDialog"
  163. :info="insertDateInfo"
  164. @insert="insertDatehandle"
  165. />
  166. <!-- 指标计算弹窗 -->
  167. <calculateEdbDia
  168. ref="calculateEdbDiaRef"
  169. :isShow.sync="isInsertCalculate"
  170. :info="insertCalculateInfo"
  171. @insert="insertCalculateData"
  172. />
  173. </div>
  174. </template>
  175. <script>
  176. import {
  177. getRowHeaderCode,
  178. getColumnHeaderCode,
  179. selectCellStyle,
  180. selectMoreCellStyle,
  181. setRelationStyle,
  182. getRightClickMenu,
  183. checkDateFormat,
  184. setFocus,
  185. findCellByKey,
  186. resetRelationStyle,
  187. resetDialogCellStyle,
  188. extractFactorsFromFormula,
  189. findCellByFactor,
  190. splitString,
  191. toUpperCase,
  192. findCellKeyByFactor
  193. } from "../common/customTable";
  194. import * as sheetInterface from "@/api/modules/sheetApi.js";
  195. import { dataBaseInterface } from '@/api/api.js';
  196. import md5 from '@/utils/md5.js';
  197. import selectTargetValueDia from './selectTargetValueDia.vue';
  198. import insertDateDia from './insertDateDia.vue';
  199. import calculateEdbDia from './calculateEdbDia.vue';
  200. import { formulaTip } from '@/views/dataEntry_manage/databaseComponents/util';
  201. export default {
  202. props: {
  203. disabled: { //是否只预览
  204. type: Boolean,
  205. default: false,
  206. }
  207. },
  208. components: { selectTargetValueDia,insertDateDia,calculateEdbDia },
  209. computed: {
  210. //列头
  211. columnHeader() {
  212. return getColumnHeaderCode(
  213. this.config.data[0] ? this.config.data[0].length : 0
  214. );
  215. },
  216. //行头
  217. rowHeader() {
  218. let total_length = this.config.data.length;
  219. // console.log(this.config.data)
  220. return getRowHeaderCode(total_length);
  221. },
  222. },
  223. watch:{
  224. 'config.data':{
  225. handler(newVal){
  226. if(!this.disabled && this.hasInit){
  227. this.$emit("autoSave")
  228. }
  229. },
  230. deep:true
  231. },
  232. insertRelationArr:{
  233. handler(newVal){
  234. if(!this.disabled && this.hasInit){
  235. this.$emit("autoSave")
  236. }
  237. },
  238. deep:true
  239. },
  240. },
  241. data() {
  242. return {
  243. config: {
  244. /* 单元格类型
  245. 1手动日期格 DataTimeType 0
  246. 2指标格
  247. 3自定义输入
  248. 4插入值 表格里有关联的日期和指标格 //隐藏 又不要这个功能了
  249. 5弹窗里的插入值 有关联日期格
  250. 6公式计算单元格
  251. 1系统日期导入格 DataTimeType 1
  252. 1指标日期导入格 DataTimeType 2
  253. 7指标计算的插入值单元格
  254. */
  255. data: [],
  256. contextMenuOption: [],
  257. },
  258. selectCell: {},//选中单元格info
  259. rightClickCell: {},//右键单元格 key c r
  260. insertTargetCell: {},//选择右键插入时的单元格 可和右键单元格不一样 key c r
  261. insertRelationArr: [], //表格单元格依赖关系数组
  262. isSelectTargetValueDialog: false,
  263. cellrelationEdbInfo: {}, //指标浮窗信息
  264. copyCellItem: {},//复制时的单元格信息 用于粘贴赋值
  265. calculateClickCell: null,//双击公式单元格时的单元格信息 用于之后选其他单元格拼接公式
  266. isInsertDateDialog: false,//导入日期弹窗
  267. insertDateInfo: {
  268. key: '',
  269. },
  270. calculateChainList: [],//公式链 key数组 后端需要
  271. isInsertCalculate: false,//插入指标计算值
  272. insertCalculateInfo: {},//指标计算单元格info
  273. formulaTip,
  274. hasInit:false,
  275. };
  276. },
  277. mounted() {
  278. if(this.$route.path === '/addMixedSheet' && !this.$route.query.id) this.initData();
  279. },
  280. methods: {
  281. /* 输入时实时搜索 满足日期格式不搜索 有=视为输入公式不搜索 */
  282. async searchTarget(query,cb) {
  283. //又要过滤掉2020-05-这样的奇葩其他格式 不让检索
  284. let dateOtherRegex = /^(?:(?:19|20)\d\d)([-])(0[1-9]|1[0-2])(-?)$/
  285. if(!query
  286. ||checkDateFormat(query)
  287. ||dateOtherRegex.test(query)
  288. ||query.startsWith('=')
  289. ) return cb([])
  290. const { DataType,EdbInfoId } = this.selectCell;
  291. const res = DataType===2
  292. ? await dataBaseInterface.targetDetail({EdbInfoId})
  293. : await sheetInterface.searchTarget({
  294. KeyWord: query,
  295. CurrentIndex: 1,
  296. PageSize: 1000
  297. })
  298. if(res.Ret !== 200) return
  299. let arr = DataType===2 ? [res.Data] : (res.Data.List||[])
  300. cb(arr);
  301. },
  302. /* 单击 */
  303. clickCell(e, cell) {
  304. if(this.disabled) return
  305. selectCellStyle(e);
  306. this.selectCell = cell;
  307. setFocus(e);
  308. /* 如果当前有公式单元格在编辑就拼接当前单元格进公式 */
  309. if(this.calculateClickCell && this.calculateClickCell.Uid!==cell.Uid) {
  310. console.log(this.calculateClickCell)
  311. const { cindex,rindex } = e.target.dataset;
  312. this.calculateClickCell.Value += `${cindex}${rindex}`
  313. }
  314. //是插值单元格时寻找关联依赖的单元格 设置选框
  315. if([4,5,7].includes(cell.DataType)) {
  316. const { key } = e.target.dataset;
  317. if(!this.insertRelationArr.find(_ => _.key===key)) return
  318. let { relation_date,relation_edb } = this.insertRelationArr.find(_ => _.key===key)
  319. relation_date.key && setRelationStyle(relation_date)
  320. relation_edb.key && setRelationStyle(relation_edb)
  321. }
  322. //选择指标弹窗打开时选择日期更新弹窗数据
  323. this.isSelectTargetValueDialog&&this.$refs.selectTargetValueRef.chooseEdb(this.$refs.selectTargetValueRef.edbInfo)
  324. //计算指标弹窗打开时选择日期更新弹窗数据
  325. this.isInsertCalculate&&this.$refs.calculateEdbDiaRef.showResult&&this.$refs.calculateEdbDiaRef.calculateHandle()
  326. },
  327. /* 插入值 往左往上寻找同行同列是否有符合条件的一指标一日期 */
  328. async insertValue() {
  329. let params = this.findNearestCell();
  330. console.log(params)
  331. if(!params) {
  332. this.selectCell.DataType = 3;
  333. this.selectCell.DataTimeType = 0;
  334. this.selectCell.ShowValue = '';
  335. this.selectCell.Value = '';
  336. this.selectCell.DataTime = '';
  337. this.selectCell.EdbInfoId = 0;
  338. this.$message.warning('无法在此处插入值');
  339. return
  340. }
  341. const { EdbInfoId,Date,DataTimeType } = params
  342. const res = await sheetInterface.insertData({EdbInfoId,Date})
  343. if(res.Ret !==200) return
  344. //系统日期无值也要建立关联关系
  345. if(!res.Data&&!DataTimeType){
  346. this.selectCell.DataType = 3;
  347. this.selectCell.DataTimeType = 0;
  348. this.selectCell.ShowValue = '';
  349. this.selectCell.Value = '';
  350. this.selectCell.DataTime = '';
  351. this.selectCell.EdbInfoId = 0;
  352. this.$message.warning('所选指标的所选日期无值')
  353. return
  354. }
  355. res.Data ? this.$message.success('插入成功') : this.$message.warning('当前日期暂无值')
  356. this.selectCell.DataType = 4;
  357. this.selectCell.ShowValue = res.Data;
  358. this.selectCell.Value = res.Data;
  359. this.selectCell.EdbInfoId = EdbInfoId;
  360. this.selectCell.DataTime = Date;
  361. this.setRelation(params)
  362. },
  363. // 建立插入单元格和依赖单元格关联关系
  364. setRelation(data,cellType=4) {
  365. const { insert_cell } = data;
  366. let relation_obj = {
  367. type: cellType,
  368. key: insert_cell.key,
  369. relation_date: {
  370. type: 1,
  371. key: insert_cell.relation_date
  372. },
  373. relation_edb: {
  374. type: 2,
  375. key: insert_cell.relation_edb
  376. }
  377. }
  378. let haveIndex = this.insertRelationArr.findIndex(_ => _.key===insert_cell.key);
  379. if(haveIndex===-1) {
  380. this.insertRelationArr.push(relation_obj)
  381. }else {
  382. this.insertRelationArr.splice(haveIndex,1,relation_obj)
  383. }
  384. console.log(this.insertRelationArr)
  385. },
  386. /* 向左向上找出所有格子 找出离插入单元格最近的两个符合条件的单元格 看是否满足一指标一日期的条件
  387. 不满足就无法插入值
  388. */
  389. findNearestCell() {
  390. let { rindex,cindex,key } = this.rightClickCell;
  391. let index_row = this.rowHeader.findIndex(_ => _===rindex);
  392. let index_col = this.columnHeader.findIndex(_ => _===cindex);
  393. //同行左侧所有格子
  394. let row_cell_arr = this.config.data[index_row].filter((_,cell_index) => cell_index<index_col);
  395. //同列上侧所有格子
  396. let col_cell_arr = this.config.data.filter((row,row_index) => row_index<index_row).map(row=> row[index_col]);
  397. if(!row_cell_arr.length || !col_cell_arr.length){
  398. return null
  399. }
  400. //寻找最近的符合1 2类型的两个格子
  401. let params = null;
  402. for (let i = row_cell_arr.length - 1; i >= 0; i--) {
  403. for (let j = col_cell_arr.length - 1; j >= 0; j--) {
  404. if(!params) {
  405. if((row_cell_arr[i].DataType===1&&col_cell_arr[j].DataType===2)
  406. ||(row_cell_arr[i].DataType===2&&col_cell_arr[j].DataType===1)) {
  407. params = {
  408. DataTimeType: row_cell_arr[i].DataType===1 ? row_cell_arr[i].DataTimeType : col_cell_arr[j].DataTimeType,
  409. Date: row_cell_arr[i].DataType===1 ? row_cell_arr[i].ShowValue : col_cell_arr[j].ShowValue,
  410. EdbInfoId: row_cell_arr[i].DataType===2 ? row_cell_arr[i].EdbInfoId : col_cell_arr[j].EdbInfoId,
  411. insert_cell: {
  412. key,
  413. relation_date: row_cell_arr[i].DataType===1 ? row_cell_arr[i].Uid: col_cell_arr[j].Uid,
  414. relation_edb: row_cell_arr[i].DataType===2 ? row_cell_arr[i].Uid: col_cell_arr[j].Uid,
  415. },
  416. }
  417. break
  418. }
  419. }
  420. }
  421. }
  422. return params;
  423. },
  424. /* 选择指标 单元格类型为2 已经是指标单元格了就重置单元格 否则就视为选择指标*/
  425. selectTarget(e,cell) {
  426. const { EdbName,EdbInfoId } = e;
  427. //如果已经是指标单元格了再次点击就清空
  428. if(cell.DataType===2&&cell.EdbInfoId) {
  429. this.clearCell()
  430. }else {
  431. cell.DataType = 2;
  432. cell.DataTime = '';
  433. cell.ShowValue = EdbName;
  434. cell.Value = EdbName;
  435. cell.EdbInfoId = EdbInfoId;
  436. }
  437. this.checkCellRelation(cell)
  438. },
  439. /* 输入框失焦 设置单元格类型 处理关联关系 */
  440. async changeVal(e, cell) {
  441. // 是日期格式 DataType为1
  442. // 自定义内容 DataType 3
  443. //有=号为输入公式 DataType 6
  444. const {value} = e.target;
  445. if(!value){ //无值重置单元格
  446. cell.DataType = 3;
  447. cell.ShowValue = value;
  448. cell.Value = value;
  449. cell.EdbInfoId = 0;
  450. cell.DataTime = '';
  451. cell.Extra=''
  452. }else {
  453. //指标类型不做格式处理
  454. if(cell.DataType===2) return
  455. console.log(checkDateFormat(value))
  456. if(checkDateFormat(value)) { //是日期格式
  457. cell.DataType = 1;
  458. cell.Extra='';
  459. cell.ShowValue = checkDateFormat(value);
  460. cell.Value = checkDateFormat(value);
  461. }else if(value.startsWith('=')) { //公式单元格
  462. cell.DataType = 6;
  463. let calculateVal = await this.getValueByFormula(value);
  464. cell.ShowValue = calculateVal;
  465. //处理公式关系
  466. this.$set(cell,'Extra',this.dealFormulaConstruction(value))
  467. console.log(cell)
  468. }else {//自定义值
  469. cell.DataType = 3;
  470. cell.ShowValue = value;
  471. cell.Value = value;
  472. cell.EdbInfoId = 0;
  473. cell.DataTime = '';
  474. cell.Extra=''
  475. }
  476. }
  477. //判断是否是有插入值的依赖单元格 更新值或重置关系
  478. this.checkCellRelation(cell)
  479. },
  480. /* 当前单元格是否和插入值有关联 无就不管 */
  481. async checkCellRelation(cell) {
  482. if(!this.insertRelationArr.length) return
  483. const key= cell.Uid;
  484. //有关联的N组数组
  485. let haveRelationArr = this.insertRelationArr.filter(_ => _.relation_date.key===key||_.relation_edb.key===key);
  486. if(!haveRelationArr.length) return
  487. //去处理每一组关联的情况
  488. haveRelationArr.forEach( async(relation) => {
  489. const { relation_date,relation_edb } = relation;
  490. if((relation_date.key === key && cell.DataType === 1) || (relation_edb.key === key && cell.DataType === 2)) { //单元格类型不变只变值仍有关联关系 更新值
  491. //刷新插入值结果
  492. let params = null;
  493. if(relation_date.key === key && cell.DataType === 1) { //修改的是依赖日期格
  494. let { EdbInfoId } = findCellByKey(this.config.data,relation.key)
  495. params = {
  496. EdbInfoId,
  497. Date: cell.ShowValue
  498. }
  499. } else if( relation_edb.key === key && cell.DataType === 2) { //修改的依赖指标格
  500. let {ShowValue} = findCellByKey(this.config.data,relation_date.key)
  501. params = {
  502. EdbInfoId: cell.EdbInfoId,
  503. Date: ShowValue
  504. }
  505. }
  506. const res = await sheetInterface.insertData(params)
  507. if(res.Ret !==200) return
  508. !res.Data && this.updateInsertCell(relation.key);
  509. res.Data && this.config.data.forEach(row => {
  510. row.forEach(cell => {
  511. if(cell.Uid === relation.key) {
  512. cell.DataType = relation.type;
  513. cell.ShowValue = res.Data;
  514. cell.Value = res.Data;
  515. cell.EdbInfoId = params.EdbInfoId;
  516. cell.DataTime = params.Date;
  517. }
  518. })
  519. })
  520. }else {
  521. // 清除插入值单元格式和关联关系
  522. this.updateInsertCell(relation.key);
  523. }
  524. })
  525. },
  526. // 清除插入值单元格式和关联关系
  527. updateInsertCell(key) {
  528. this.config.data.forEach(row => {
  529. row.forEach(cell => {
  530. if(cell.Uid === key) {
  531. cell.DataType = 3;
  532. cell.EdbInfoId = 0;
  533. cell.DataTime = '';
  534. cell.ShowValue = '';
  535. cell.Value = '';
  536. }
  537. })
  538. })
  539. let relationIndex = this.insertRelationArr.findIndex(_ => _.key===key)
  540. this.insertRelationArr.splice(relationIndex,1)
  541. },
  542. /* 输入公式的计算值 */
  543. async getValueByFormula(val) {
  544. // 提取因数数组
  545. let factors = extractFactorsFromFormula(val)
  546. console.log(factors)
  547. //根据因数找单元格
  548. let isAllCell = factors.some(_ => findCellByFactor(_)=== null)
  549. if(isAllCell) {
  550. this.$message.warning('公式有误,参数不存在')
  551. return '';
  552. }
  553. let TagMap = {};
  554. factors.forEach(_ => {
  555. if(!TagMap[_]) {
  556. TagMap[_] = Number(findCellByFactor(_))
  557. }
  558. });
  559. const res = await sheetInterface.calculateCustomCellData({
  560. CalculateFormula: val,
  561. TagMap
  562. })
  563. if(res.Ret !== 200) return
  564. return res.Data
  565. },
  566. /* 顶部公式改变 */
  567. async updateValueByFormula(value) {
  568. this.changeVal({target: {value}},this.selectCell)
  569. },
  570. /* 右键 */
  571. rightClickHandle(e,cell) {
  572. if(this.disabled) return
  573. const { rindex,cindex,key } = e.target.dataset;
  574. this.rightClickCell = {
  575. rindex,
  576. cindex,
  577. key
  578. }
  579. this.selectCell = cell;
  580. let pos;
  581. if(rindex==='-1') { //列头处
  582. pos = 'col'
  583. }else if(cindex==='-1') { //行头
  584. pos = 'row'
  585. }else {//单元格
  586. pos = 'cell'
  587. }
  588. this.config.contextMenuOption = pos === 'cell'
  589. ? getRightClickMenu(pos,(cell.DataType===1&&[1,2].includes(cell.DataTimeType))||cell.DataType===7)
  590. : getRightClickMenu(pos)
  591. const dom = $('#contextMenu-wrapper')[0];
  592. dom.style.left = e.clientX-3 + 'px';
  593. dom.style.top = e.clientY-3 + 'px';
  594. ['col','row'].includes(pos) && selectMoreCellStyle(e);
  595. pos==='cell' && this.clickCell(e,cell);
  596. },
  597. /* */
  598. hideContextMenu() {
  599. const dom = $('#contextMenu-wrapper')[0];
  600. dom.style.left = '-9999px';
  601. dom.style.top = '-9999px';
  602. },
  603. /* 右键事件 */
  604. async handleContext(key) {
  605. let editHandlesMap = {
  606. 1: this.insertDateOpen,
  607. 7: this.edbCalculateInsertOpen
  608. }
  609. const keyMap = {
  610. 'del': this.delColOrRow,//删除
  611. 'insert-col-left': this.insertCol,//向左插入列
  612. 'insert-col-right': this.insertCol,//向右插入列
  613. 'insert-row-up': this.insertRow,//向上插入行
  614. 'insert-row-down': this.insertRow,//向下插入行
  615. 'insert-value': this.insertValue,//插入值
  616. 'choose-target': this.selectTargetOpen,//选择指标插入值
  617. 'insert-sys-date': this.insertDateOpen,//导入系统日期
  618. 'insert-edb-date': this.insertDateOpen,//导入指标日期
  619. 'reset': this.clearCell, //清空
  620. 'cell-edit': this.selectCell ? editHandlesMap[this.selectCell.DataType] : null
  621. }
  622. keyMap[key] && keyMap[key](key)
  623. key!=='insert-edb-calculate' && this.hideContextMenu()
  624. },
  625. /* 打开选择指标弹窗
  626. 打开弹窗后仍可以在页面上点击 多存一个选择指标时的当前单元格信息 */
  627. selectTargetOpen() {
  628. this.insertTargetCell = this.selectCell;
  629. resetDialogCellStyle();
  630. setRelationStyle({ key:this.insertTargetCell.Uid },'td-choose-insert-target')
  631. this.isSelectTargetValueDialog = true;
  632. this.isInsertCalculate = false;
  633. this.$refs.calculateEdbDiaRef.initData();
  634. },
  635. /* 插入选择指标的值 */
  636. insertSelectData({ edbId,value,date }) {
  637. this.insertTargetCell.DataType = 5;
  638. this.insertTargetCell.ShowValue = value;
  639. this.insertTargetCell.Value = value;
  640. this.insertTargetCell.EdbInfoId = edbId;
  641. this.insertTargetCell.DataTime = date;
  642. this.$message.success('插入成功')
  643. //如果有关联表格日期就建立新的关联关系
  644. if(date) {
  645. let relation = {
  646. insert_cell: {
  647. key: this.insertTargetCell.Uid,
  648. relation_date: this.selectCell.Uid,
  649. relation_edb: '',
  650. }
  651. }
  652. this.setRelation(relation,5);
  653. }
  654. },
  655. /* 清除单元格内容 格式 关联关系 */
  656. clearCell() {
  657. if([4,5].includes(this.selectCell.DataType)) resetRelationStyle();
  658. this.selectCell.DataType = 3;
  659. this.selectCell.ShowValue = '';
  660. this.selectCell.Value = '';
  661. this.selectCell.DataTime = '';
  662. this.selectCell.DataTimeType = 0;
  663. this.selectCell.EdbInfoId = 0;
  664. this.checkCellRelation(this.selectCell)
  665. },
  666. /* 删除行列 */
  667. delColOrRow() {
  668. let { rindex,cindex } = this.rightClickCell;
  669. if(rindex==='-1') { //删除列
  670. console.log('删除列',cindex)
  671. if(this.columnHeader.length === 1) return this.$message.warning('请至少保留一列')
  672. let index = this.columnHeader.findIndex(_ => _ === cindex);
  673. //删除时清除关系
  674. if(this.insertRelationArr.length) {
  675. let delCellIds = this.config.data.map(row => row[index].Uid);
  676. this.clearRelationInsertCell(delCellIds);
  677. }
  678. this.config.data.forEach(row => {
  679. row.splice(index,1)
  680. })
  681. }else if(cindex === '-1') { //删除行
  682. console.log('删除行',rindex)
  683. if(this.rowHeader.length === 1) return this.$message.warning('请至少保留一行')
  684. let index = this.rowHeader.findIndex(_ => _ === rindex)
  685. if(this.insertRelationArr.length) {
  686. //删除时清除关系
  687. let delCellIds = this.config.data[index].map(cell => cell.Uid);
  688. this.clearRelationInsertCell(delCellIds);
  689. }
  690. this.config.data.splice(index,1)
  691. }
  692. // console.log(this.insertRelationArr)
  693. },
  694. /* 删除时清除关联关系 和删除单元格有关联的插入值单元格和 */
  695. clearRelationInsertCell(delCellIds) {
  696. //清除关联插入值得单元格
  697. let haveRelationArr = this.insertRelationArr.filter(_ => delCellIds.includes(_.relation_date.key)||delCellIds.includes(_.relation_edb.key));
  698. // console.log(haveRelationArr)
  699. haveRelationArr.forEach(relation => {
  700. !delCellIds.includes(relation)&&this.updateInsertCell(relation.key);
  701. })
  702. this.insertRelationArr = this.insertRelationArr.filter(_ => !delCellIds.includes(_.key)&&!delCellIds.includes(_.relation_date.key)&&!delCellIds.includes(_.relation_edb.key))
  703. },
  704. /* 插入列 */
  705. insertCol(key) {
  706. let { cindex } = this.rightClickCell;
  707. let index = this.columnHeader.findIndex(_ => _ === cindex);
  708. this.config.data.forEach((row,rindex) => {
  709. row.splice(key==='insert-col-left'?index:index+1,0,{
  710. ShowValue: "",
  711. Value: "",
  712. DataType: 3,
  713. DataTime: "",
  714. EdbInfoId: 0,
  715. Uid: md5.hex_md5(`${new Date().getTime()}${rindex}`)
  716. })
  717. })
  718. },
  719. /* 插入行 */
  720. insertRow(key) {
  721. let { rindex } = this.rightClickCell;
  722. let index = this.rowHeader.findIndex(_ => _ === rindex)
  723. let row = new Array(this.columnHeader.length).fill("").map((_,cindex) => ({
  724. ShowValue: "",
  725. Value: "",
  726. DataType: 3,
  727. DataTime: "",
  728. EdbInfoId: 0,
  729. Uid: md5.hex_md5(`${new Date().getTime()}${cindex}`)
  730. }));
  731. this.config.data.splice( key==='insert-row-up'?index:index+1,0,row)
  732. },
  733. /* 单元格类型5 浮到上面展示指标信息浮窗 */
  734. async getRelationEdbInfo({EdbInfoId,DataType}) {
  735. if(![5,7].includes(DataType)||this.disabled) return
  736. const res = await dataBaseInterface.targetDetail({EdbInfoId})
  737. if(res.Ret !== 200) return
  738. this.cellrelationEdbInfo = res.Data;
  739. },
  740. /* 导入系统/指标日期弹窗 */
  741. insertDateOpen(type) {
  742. this.insertTargetCell = this.selectCell;
  743. resetDialogCellStyle();
  744. if(type === 'cell-edit') { //编辑日期
  745. const { DataTimeType } = this.insertTargetCell;
  746. this.insertDateInfo = {
  747. key: DataTimeType===1? 'insert-sys-date' : 'insert-edb-date',
  748. ...this.insertTargetCell
  749. }
  750. }else {
  751. this.insertDateInfo = {
  752. key:type
  753. }
  754. }
  755. this.isInsertDateDialog = true;
  756. },
  757. /* 插入系统/指标日期 */
  758. insertDatehandle({insertValue,dataTimeType,str}) {
  759. this.insertTargetCell.DataType = 1;
  760. this.insertTargetCell.DataTimeType = dataTimeType;
  761. this.insertTargetCell.ShowValue = insertValue;
  762. this.insertTargetCell.Value = str;
  763. this.insertTargetCell.EdbInfoId = 0;
  764. this.insertTargetCell.DataTime = insertValue;
  765. },
  766. /* 指标计算弹窗 */
  767. edbCalculateInsertOpen(item) {
  768. this.insertTargetCell = this.selectCell;
  769. resetDialogCellStyle();
  770. setRelationStyle({ key:this.insertTargetCell.Uid },'td-choose-insert-target');
  771. if(item === 'cell-edit') { //编辑
  772. const { Value } = this.insertTargetCell;
  773. let menuInfo = this.config.contextMenuOption
  774. .find(_ => _.key==='insert-edb-calculate').children
  775. .find(menu => menu.source === JSON.parse(Value).Source);
  776. this.insertCalculateInfo = {
  777. ...menuInfo,
  778. formStr: Value,
  779. }
  780. }else {
  781. this.insertCalculateInfo = {
  782. ...item
  783. }
  784. }
  785. this.isInsertCalculate = true;
  786. this.isSelectTargetValueDialog = false;
  787. this.$refs.selectTargetValueRef.initData();
  788. },
  789. /* 导入指标计算值 */
  790. insertCalculateData(item) {
  791. console.log(item)
  792. const { InsertValue,EdbInfoId,Str,Date } = item;
  793. this.insertTargetCell.DataType = 7;
  794. this.insertTargetCell.ShowValue = InsertValue;
  795. this.insertTargetCell.Value = Str;
  796. this.insertTargetCell.EdbInfoId = EdbInfoId;
  797. this.insertTargetCell.DataTime = Date;
  798. this.$message.success('插入成功')
  799. //如果有关联表格日期就建立新的关联关系
  800. if(Date) {
  801. let relation = {
  802. insert_cell: {
  803. key: this.insertTargetCell.Uid,
  804. relation_date: this.selectCell.Uid,
  805. relation_edb: '',
  806. }
  807. }
  808. this.setRelation(relation,7);
  809. }
  810. },
  811. /* 初始化8行5列 */
  812. initData(initData=null) {
  813. console.log('initData');
  814. this.hasInit=false
  815. if(initData) {
  816. const { CellRelation,Data } = initData;
  817. this.config.data = Data;
  818. this.insertRelationArr = JSON.parse(CellRelation);
  819. }else {
  820. this.config.data = new Array(8).fill("").map((_,_rindex) => {
  821. return new Array(5).fill("").map((cell,_cindex) => ({
  822. ShowValue: "",
  823. Value: "",
  824. DataType: 3,
  825. DataTimeType: 0,
  826. DataTime: "",
  827. EdbInfoId:0,
  828. Uid: md5.hex_md5(`${new Date().getTime()}${_rindex}${_cindex}`)
  829. }));
  830. });
  831. }
  832. this.$nextTick(()=>{
  833. this.hasInit=true
  834. })
  835. },
  836. /* 处理因数结构 =a1+b1 => [{ Tag: a,Row:1,Key:'' }] */
  837. dealFormulaConstruction(val) {
  838. // 提取因数数组
  839. let factors = extractFactorsFromFormula(val)
  840. let arr = factors.map(str => ({
  841. Tag: splitString(toUpperCase(str))[0],
  842. Row: splitString(toUpperCase(str))[1],
  843. Key: findCellKeyByFactor(str)
  844. }))
  845. return JSON.stringify(arr)
  846. },
  847. /* 要支持复制粘贴把公式也带过去 公式单元格类型为6 其余就正常复制文本 */
  848. copyCellHandle(e,cell) {
  849. this.copyCellItem = cell;
  850. // 阻止默认的复制操作
  851. e.preventDefault();
  852. },
  853. /* 要支持复制粘贴把公式也带过去 公式单元格类型为6 其余就正常复制文本 */
  854. pasteCellHandle(e,cell) {
  855. if(this.copyCellItem.DataType === 6) {
  856. cell.DataType = this.copyCellItem.DataType;
  857. cell.ShowValue = this.copyCellItem.ShowValue;
  858. cell.Value = this.copyCellItem.Value;
  859. cell.DataTime = this.copyCellItem.DataTime;
  860. cell.EdbInfoId = this.copyCellItem.EdbInfoId;
  861. }else {
  862. cell.DataType = 3;
  863. cell.ShowValue = this.copyCellItem.ShowValue;
  864. cell.Value = this.copyCellItem.Value;
  865. }
  866. // 阻止默认的粘贴操作
  867. e.preventDefault();
  868. },
  869. /* 单元格enter时切换编辑状态 */
  870. keyEnterHandle(e,cell) {
  871. if(e.keyCode===13) {
  872. //非得搞个要回车失焦
  873. e.target.nodeName && e.target.blur();
  874. this.$refs[`inputRef${e.target.dataset.key}`]&&this.$refs[`inputRef${e.target.dataset.key}`][0].close()
  875. cell.DataType===6 && this.$set(cell,'CanEdit',false)
  876. this.calculateClickCell = null
  877. resetDialogCellStyle()
  878. }
  879. },
  880. /* 支持公式单元格双击切换状态 */
  881. dblClickCellHandle(e,cell) {
  882. if(this.disabled || cell.DataType!==6 || this.calculateClickCell) return
  883. this.$set(cell,'CanEdit',true)
  884. this.calculateClickCell = cell;
  885. setRelationStyle({ key:cell.Uid },'td-choose-insert-target')
  886. this.$nextTick(() => {
  887. if(e.target.childNodes[0].childNodes[0].childNodes[1].nodeName==='INPUT') e.target.childNodes[0].childNodes[0].childNodes[1].focus();
  888. })
  889. },
  890. /* 处理保存的参数 */
  891. getSaveParams() {
  892. const { data } = this.config;
  893. let params = {
  894. CellRelation: JSON.stringify(this.insertRelationArr),
  895. Data: data
  896. }
  897. return params
  898. }
  899. },
  900. };
  901. </script>
  902. <style scoped lang="scss">
  903. .nodata {
  904. text-align: center;
  905. font-size: 16px;
  906. color: #666;
  907. padding: 100px 0;
  908. }
  909. .table-wrapper {
  910. width: 100%;
  911. overflow: auto;
  912. .formula-wrapper {
  913. height: 42px;
  914. display: flex;
  915. align-items: center;
  916. background: #fff;
  917. border-radius: 4px;
  918. box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
  919. border: 1px solid #DCDFE6;
  920. margin-bottom: 15px;
  921. padding: 0 15px;
  922. }
  923. .table td,th {
  924. width: 104px;
  925. min-width: 104px;
  926. height: 35px;
  927. max-height: 35px;
  928. background: #fff;
  929. text-align: center;
  930. word-break: break-all;
  931. border: 1px solid #dcdfe6;
  932. overflow: hidden;
  933. text-overflow: ellipsis;
  934. position: relative;
  935. color: #606266;
  936. &.td-chose::after {
  937. position: absolute;
  938. top: 0;
  939. left: 0;
  940. right: 0;
  941. bottom: 0;
  942. content: "";
  943. display: block;
  944. outline: 0;
  945. border: 2px solid #0033ff;
  946. box-shadow: 0 0 5px rgba(73, 177, 249, 0.5);
  947. }
  948. &.td-relation::after {
  949. position: absolute;
  950. top: 0;
  951. left: 0;
  952. right: 0;
  953. bottom: 0;
  954. content: "";
  955. display: block;
  956. outline: 0;
  957. border: 2px dashed #0033ff;
  958. box-shadow: 0 0 5px rgba(73, 177, 249, 0.5);
  959. }
  960. &.td-col-select::after {
  961. position: absolute;
  962. top: 0;
  963. left: 0;
  964. right: 0;
  965. bottom: 0;
  966. content: "";
  967. display: block;
  968. outline: 0;
  969. border: 1px solid rgb(24, 173, 24);
  970. border-bottom: none;
  971. border-top: none;
  972. }
  973. &.td-row-select::after {
  974. position: absolute;
  975. top: 0;
  976. left: 0;
  977. right: 0;
  978. bottom: 0;
  979. content: "";
  980. display: block;
  981. outline: 0;
  982. border: 1px solid rgb(24, 173, 24);
  983. border-left: none;
  984. border-right: none;
  985. }
  986. &.td-choose-insert-target::after {
  987. position: absolute;
  988. top: 0;
  989. left: 0;
  990. right: 0;
  991. bottom: 0;
  992. content: "";
  993. display: block;
  994. outline: 0;
  995. border: 2px dashed orange;
  996. box-shadow: 0 0 5px rgba(73, 177, 249, 0.5);
  997. }
  998. }
  999. .th-tg {
  1000. background: #ebeef5;
  1001. &:hover {
  1002. cursor: pointer;
  1003. background: #ddd;
  1004. /* border: 2px solid #409eff; */
  1005. }
  1006. &.sm {
  1007. width: 36px;
  1008. min-width: 36px;
  1009. max-width: 36px;
  1010. }
  1011. }
  1012. //整行选中
  1013. tr {
  1014. position: relative;
  1015. &.choose-all::after {
  1016. position: absolute;
  1017. top: 0;
  1018. left: 0;
  1019. right: 0;
  1020. bottom: 0;
  1021. content: "";
  1022. display: block;
  1023. outline: 0;
  1024. border: 2px solid #5897fb;
  1025. box-shadow: 0 0 5px rgba(73, 177, 249, 0.5);
  1026. }
  1027. }
  1028. .contextMenu-wrapper {
  1029. position: fixed;
  1030. z-index: 99;
  1031. top: -9999px;
  1032. left: -9999px;
  1033. background: #fff;
  1034. padding: 10px 0;
  1035. /* border: 1px solid #999; */
  1036. box-shadow: 0 1px 4px #999;
  1037. .item {
  1038. padding: 10px 25px;
  1039. cursor: pointer;
  1040. &:hover {
  1041. background-color: #f5f7fa;
  1042. }
  1043. &:hover .subMenu-wrapper {
  1044. display: block;
  1045. }
  1046. }
  1047. .subMenu-wrapper {
  1048. display: none;
  1049. padding: 10px 0;
  1050. box-shadow: 0 1px 4px #999;
  1051. background: #fff;
  1052. position: absolute;
  1053. right: -172px;
  1054. top: -50%;
  1055. }
  1056. }
  1057. }
  1058. </style>
  1059. <style lang="scss">
  1060. .table-wrapper {
  1061. td {
  1062. .el-input__inner {
  1063. border: none;
  1064. outline: none;
  1065. text-align: center;
  1066. height: 34px;
  1067. line-height: 34px;
  1068. }
  1069. }
  1070. .el-input.is-disabled .el-input__inner {
  1071. background-color: #fff;
  1072. }
  1073. }
  1074. .formula-wrapper .el-input__inner { border: none; outline: none; }
  1075. .edb-select-popover {
  1076. width: 300px !important;
  1077. .edb-item {
  1078. display: flex;
  1079. justify-content: space-between;
  1080. align-items: center;
  1081. .edb-item-name {
  1082. max-width: 260px;
  1083. }
  1084. }
  1085. }
  1086. </style>