dk-pivot.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. <template>
  2. <div :style="'height: ' + tableHeight + 'px'">
  3. <DkSplit ref="toolSplit" v-model="split">
  4. <div slot="left">
  5. <!--字段-->
  6. <DkButton ref="button" class="btn-class" size="small">{{ '字段' }}
  7. <Icon v-if="hideFlag" type="md-arrow-dropleft" @click.stop="handleTools"/>
  8. <Icon v-else type="md-arrow-dropright" @click.stop="handleTools"/>
  9. </DkButton>
  10. <DkSplit mode="vertical" :height="tableHeight" v-model="leftSplit">
  11. <div slot="top" style="display: flex; justify-content: space-between; "
  12. :style="{height:(this.tableHeight * leftSplit -10) + 'px'}">
  13. <!-- all fields -->
  14. <div class="flex-fill drag-area" :class="dragAreaClass" style="margin-right: 4px">
  15. <div class="drag-area-title mb-3">{{ availableFieldsLabelText }}</div>
  16. <!-- <div class="d-flex flex-column align-items-start gutter-sm drag-area-zone"></div>-->
  17. <draggable
  18. class="d-flex flex-column align-items-start gutter-sm drag-area-zone"
  19. v-model="internal.availableFieldKeys"
  20. :scroll="true" animation="300"
  21. group="fields"
  22. handle=".btn-draggable"
  23. @start="start"
  24. @end="end">
  25. <div v-for="key in internal.availableFieldKeys" :key="key" class="field">
  26. <DkFilterButton :title="fieldsWithValues[key].label" label-key="key" value-key="key"
  27. :multiple="true"
  28. :disabled="true"
  29. :filter="!!fieldsWithValues[key].valueFilter"
  30. v-model="fieldsWithValues[key].values"
  31. :options="fieldsWithValues[key].optionValues"
  32. @on-select="handleSelect(Object.assign({key:key},{model:$event}))"></DkFilterButton>
  33. </div>
  34. </draggable>
  35. </div>
  36. <!-- Row fields -->
  37. <div class="drag-area border-primary" :class="dragAreaClass">
  38. <div class="drag-area-title mb-3">{{ rowsLabelText }}</div>
  39. <draggable
  40. v-model="internal.rowFieldKeys"
  41. class="d-flex flex-column align-items-start gutter-sm drag-area-zone"
  42. group="fields"
  43. handle=".btn-draggable"
  44. :scroll="true" animation="300"
  45. @start="start"
  46. @end="end">
  47. <div v-for="key in internal.rowFieldKeys" :key="key" class="field">
  48. <DkFilterButton :title="fieldsWithValues[key].label" label-key="key" value-key="key"
  49. :multiple="true"
  50. :filter="!!fieldsWithValues[key].valueFilter"
  51. v-model="fieldsWithValues[key].values"
  52. :options="fieldsWithValues[key].optionValues"
  53. @on-select="handleSelect(Object.assign({key:key},{model:$event}))"></DkFilterButton>
  54. </div>
  55. </draggable>
  56. </div>
  57. </div>
  58. <div slot="bottom" style="display: flex; justify-content: space-between;"
  59. :style="{height:(this.tableHeight * (1-leftSplit) -10) + 'px'}">
  60. <!-- Column fields -->
  61. <div class="flex-fill drag-area border-primary" :class="dragAreaClass" style="margin-right: 4px">
  62. <div class="drag-area-title mb-3">{{ colsLabelText }}</div>
  63. <draggable
  64. :disabled="false"
  65. v-model="internal.colFieldKeys"
  66. class="d-flex flex-column align-items-start gutter-sm drag-area-zone"
  67. group="fields"
  68. handle=".btn-draggable"
  69. @start="start"
  70. @end="end">
  71. <div v-for="key in internal.colFieldKeys" :key="key" class="field">
  72. <DkFilterButton :title="fieldsWithValues[key].label" label-key="key" value-key="key"
  73. :multiple="true"
  74. :filter="!!fieldsWithValues[key].valueFilter"
  75. v-model="fieldsWithValues[key].values"
  76. :options="fieldsWithValues[key].optionValues"
  77. @on-select="handleSelect(Object.assign({key:key},{model:$event}))"></DkFilterButton>
  78. </div>
  79. </draggable>
  80. </div>
  81. <!-- data fields -->
  82. <div class="drag-area border-primary" :class="dragAreaClass">
  83. <div class="drag-area-title mb-3">{{ dataLabelText }}</div>
  84. <draggable
  85. :disabled="false"
  86. v-model="internal.dataFieldKeys"
  87. class="d-flex flex-column align-items-start gutter-sm drag-area-zone"
  88. group="fields"
  89. handle=".btn-draggable"
  90. @start="start"
  91. @end="end">
  92. <div v-for="key in internal.dataFieldKeys" :key="key" class="field">
  93. <DkFilterButton :title="fieldsWithValues[key].label" label-key="key" value-key="key"
  94. :multiple="true"
  95. :filter="false"
  96. v-model="fieldsWithValues[key].values"
  97. :options="fieldsWithValues[key].optionValues"
  98. @on-select="handleSelect(Object.assign({key:key},{model:$event}))"></DkFilterButton>
  99. </div>
  100. </draggable>
  101. </div>
  102. </div>
  103. </DkSplit>
  104. <div style="height: 50px">
  105. <DkButton @click="currentType='table'">表格</DkButton>
  106. <DkButton @click="currentType='bar'">柱状图</DkButton>
  107. <!-- <DkButton @click="currentType='line'">折线图</DkButton>-->
  108. <!-- <DkButton @click="currentType='pie'">饼状图</DkButton>-->
  109. </div>
  110. </div>
  111. <DkTable slot="right" v-show="currentType==='table'"
  112. :id="'table-'+$options.name" ref="table-select" :data="tableData"
  113. :height="tableHeight"
  114. :choose-flag="false"
  115. :show-setting-flag="false"
  116. :pageFlag="false"
  117. :perspective-flag="false"
  118. :loading="isDataLoading">
  119. <!--行维度-->
  120. <div v-for="(item,index) in rowFields" :key="index">
  121. <DkTableColumn :field="item.key" width="auto" :filter="false"
  122. :title="item.label"></DkTableColumn>
  123. </div>
  124. <div v-if="colFlag">
  125. <div v-if="colFields && colFields.length > 0">
  126. <!--列维度(单维度)-->
  127. <div v-if="colFields.length === 1 && dataFields.length <= 1">
  128. <DkTableColumn v-for="(item,index) in [...colFields[0].valuesFiltered]"
  129. ref="col"
  130. :key="index"
  131. :field="item+''"
  132. width="auto" :filter="false"
  133. :title="getTitle(item)"></DkTableColumn>
  134. </div>
  135. <!--列维度(多维度)-->
  136. <div v-else>
  137. <!--顶层-->
  138. <vxe-colgroup :title="itemTop+''" v-for="(itemTop,index) in [...colFields[0].valuesFiltered]"
  139. :key="index">
  140. <!--中间层-->
  141. <div v-if="groupCols && groupCols.length > 0">
  142. <div v-for="(it,gIndex) in groupCols" :key="gIndex">
  143. <vxe-colgroup :title="item+''" v-for="(item,index) in [...groupCols[gIndex].valuesFiltered]"
  144. :key="index">
  145. <div v-if="gIndex === groupCols.length - 1 && dataFields.length <= 1">
  146. <!--子级(无值列或者一个值列)-->
  147. <DkTableColumn v-for="(cItem,cIndex) in [...colFields[colFields.length - 1].valuesFiltered]"
  148. ref="col"
  149. :key="cIndex"
  150. :field="itemTop + '_' + getField(groupCols,index) + cItem"
  151. width="auto" :filter="false"
  152. :title="getTitle(cItem)"></DkTableColumn>
  153. </div>
  154. <div v-if="gIndex === groupCols.length - 1 && dataFields.length > 1">
  155. <!--子级(有多个值列)-->
  156. <DkTableColumn v-for="(cItem,cIndex) in dataFields"
  157. ref="col"
  158. :key="cIndex"
  159. :field="itemTop + '_' + getField(groupCols,index) + cItem.key"
  160. width="auto" :filter="false"
  161. :digits="cItem.params?cItem.params.digits:0"
  162. :title="cItem.label"></DkTableColumn>
  163. </div>
  164. </vxe-colgroup>
  165. </div>
  166. </div>
  167. <div v-else>
  168. <div v-if="dataFields.length <= 1">
  169. <!--子级(无值列或者一个值列)-->
  170. <DkTableColumn v-for="(cItem,cIndex) in [...colFields[colFields.length - 1].valuesFiltered]"
  171. ref="col"
  172. :key="cIndex"
  173. :field="itemTop + '_' + cItem"
  174. width="auto" :filter="false"
  175. :title="getTitle(cItem)"></DkTableColumn>
  176. </div>
  177. <div v-else>
  178. <!--子级(有多个值列)-->
  179. <DkTableColumn v-for="(cItem,cIndex) in dataFields"
  180. ref="col"
  181. :key="cIndex"
  182. :field="itemTop + '_' + cItem.key"
  183. width="auto" :filter="false"
  184. :digits="cItem.params?cItem.params.digits:0"
  185. :title="cItem.label"></DkTableColumn>
  186. </div>
  187. </div>
  188. </vxe-colgroup>
  189. <!-- </vxe-colgroup>-->
  190. <!-- <vxe-colgroup :title="item+''" v-for="(item,index) in [...colFields[0].valuesFiltered]" :key="index">-->
  191. <!-- <div v-for="(cItem,cIndex) in [...colFields[1].valuesFiltered]" :key="cIndex">-->
  192. <!-- <DkTableColumn-->
  193. <!-- :field="cItem+''"-->
  194. <!-- width="auto" :filter="false" :freeze="false"-->
  195. <!-- :title="cItem+''"></DkTableColumn>-->
  196. <!-- </div>-->
  197. <!-- </vxe-colgroup>-->
  198. </div>
  199. </div>
  200. <!--列维度(无维度)-->
  201. <div v-else>
  202. <DkTableColumn v-for="(cItem,cIndex) in dataFields"
  203. ref="col"
  204. :key="cIndex"
  205. :field="cItem.key"
  206. width="auto" :filter="false"
  207. :digits="cItem.params?cItem.params.digits:0"
  208. :title="cItem.label"></DkTableColumn>
  209. </div>
  210. </div>
  211. </DkTable>
  212. <Echarts slot="right" v-if="currentType!='table'" :type="currentType" :columns="internal"
  213. :style="'height: '+tableHeight+'px; width: 100%'" :table-instance="$refs['table-select']"/>
  214. </DkSplit>
  215. </div>
  216. </template>
  217. <script>
  218. import Draggable from 'vuedraggable'
  219. import naturalSort from 'javascript-natural-sort'
  220. import HashTable from "_c/base/dk-perspective/HashTable";
  221. import Echarts from './echarts'
  222. export default {
  223. name: 'dk-pivot',
  224. components: {Draggable, Echarts},
  225. props: {
  226. data: {
  227. type: Array,
  228. default: () => []
  229. },
  230. fields: {
  231. type: Array,
  232. default: () => []
  233. },
  234. availableFieldKeys: {
  235. type: Array,
  236. default: () => []
  237. },
  238. rowFieldKeys: {
  239. type: Array,
  240. default: () => []
  241. },
  242. dataFieldKeys: {
  243. type: Array,
  244. default: () => []
  245. },
  246. colFieldKeys: {
  247. type: Array,
  248. default: () => []
  249. },
  250. defaultShowSettings: {
  251. type: Boolean,
  252. default: true
  253. },
  254. availableFieldsLabelText: {
  255. type: String,
  256. default: 'Available fields'
  257. },
  258. colsLabelText: {
  259. type: String,
  260. default: 'Columns'
  261. },
  262. rowsLabelText: {
  263. type: String,
  264. default: 'Rows'
  265. },
  266. dataLabelText: {
  267. type: String,
  268. default: 'data'
  269. },
  270. hideSettingsText: {
  271. type: String,
  272. default: 'Hide settings'
  273. },
  274. showSettingsText: {
  275. type: String,
  276. default: 'Show settings'
  277. },
  278. noDataWarningText: {
  279. type: String,
  280. default: 'No data to display.'
  281. },
  282. selectAllText: {
  283. type: String,
  284. default: 'Select all'
  285. },
  286. unselectAllText: {
  287. type: String,
  288. default: 'Unselect all'
  289. },
  290. },
  291. data: function () {
  292. const fieldValues = {}
  293. this.fields.filter(field => field.valueFilter).forEach(field => {
  294. fieldValues[field.key] = {}
  295. })
  296. const vm = Window.vm
  297. return {
  298. vm: vm,
  299. internal: {
  300. availableFieldKeys: this.availableFieldKeys,
  301. rowFieldKeys: this.rowFieldKeys,
  302. colFieldKeys: this.colFieldKeys,
  303. dataFieldKeys: this.dataFieldKeys,
  304. },
  305. hideFlag: false,
  306. split: 0.4,
  307. leftSplit: 0.5,
  308. tableHeight: 750,
  309. tableData: [],
  310. groupCols: [],// 分组列集合
  311. fieldValues,
  312. dragging: false,
  313. showSettings: true,
  314. colFlag: false,
  315. computingInterval: null,
  316. rowKeys: [],// 行列
  317. isDataLoading: false,
  318. reducer: (sum, item) => sum + 1,
  319. currentType: 'table'
  320. }
  321. },
  322. computed: {
  323. // Fields with values extracted from data (if field has valueFilter)
  324. fieldsWithValues: function () {
  325. // Create object: field.key => field
  326. const fieldsWithValues = {}
  327. this.fields.forEach(field => {
  328. fieldsWithValues[field.key] = field
  329. })
  330. // Add valuesSet
  331. const valueFilterableFields = this.fields.filter(field => field.valueFilter)
  332. // Create valuesSet for each value filterable field
  333. valueFilterableFields.forEach(field => {
  334. fieldsWithValues[field.key].valuesSet = new Set()
  335. })
  336. // 获取没有过滤,需要处理值,这些列都可能作为列和行出现
  337. const valueNoFilterableFields = this.fields.filter(field => !field.valueFilter)
  338. valueNoFilterableFields.forEach(field => {
  339. fieldsWithValues[field.key].valuesFiltered = new Set()
  340. })
  341. // Iterate on data once
  342. this.data.forEach(item => {
  343. // 有过滤
  344. valueFilterableFields.forEach(field => {
  345. let value = field.getter(item);
  346. if (typeof value === 'boolean') {
  347. fieldsWithValues[field.key].valuesSet.add(value)
  348. } else {
  349. if (value) {
  350. fieldsWithValues[field.key].valuesSet.add(value)
  351. }
  352. }
  353. })
  354. // 无过滤
  355. valueNoFilterableFields.forEach(field => {
  356. let value = field.getter(item);
  357. if (value) {
  358. fieldsWithValues[field.key].valuesFiltered.add(value)
  359. }
  360. })
  361. })
  362. // Creates values sorted from valuesSet
  363. valueFilterableFields.forEach(field => {
  364. fieldsWithValues[field.key].values = Array.from(fieldsWithValues[field.key].valuesSet).sort(field.sort || naturalSort)
  365. // 给optionsValue赋值
  366. fieldsWithValues[field.key].optionValues = fieldsWithValues[field.key].values.map(it => {
  367. return {
  368. key: it
  369. }
  370. })
  371. })
  372. // console.log('fieldsWithValues', fieldsWithValues)
  373. return fieldsWithValues
  374. },
  375. // Fields selected values as set
  376. valuesFiltered: function () {
  377. const valuesFiltered = {}
  378. let arr = Object.entries(this.fieldValues);
  379. if (arr && arr.length > 0) {
  380. for (let [key, valuesObject] of arr) {
  381. if (valuesFiltered) {
  382. valuesFiltered[key] = new Set()
  383. valuesObject.forEach(valueObject => {
  384. if (valueObject.checked) {
  385. valuesFiltered[key].add(valueObject.value)
  386. }
  387. })
  388. }
  389. }
  390. }
  391. return valuesFiltered
  392. },
  393. // Pivot table props from Pivot props & data
  394. rowFields: function () {
  395. const rowFields = []
  396. this.rowKeys = []
  397. this.internal.rowFieldKeys.forEach(key => {
  398. const field = this.fields.find(field => field.key === key)
  399. // Generate headerSlotNames from headers
  400. if (field.headers) {
  401. field.headerSlotNames = field.headers
  402. .filter(header => header.checked)
  403. .map(header => header.slotName)
  404. }
  405. // Add selected values
  406. if (field.valueFilter) {
  407. field.valuesFiltered = this.valuesFiltered[field.key]
  408. }
  409. this.rowKeys.push(key);
  410. rowFields.push(field)
  411. })
  412. // 更新数据
  413. this.$nextTick(() => {
  414. this.updateValues();
  415. })
  416. return rowFields
  417. },
  418. colFields: function () {
  419. this.colFlag = false;
  420. const colFields = []
  421. this.internal.colFieldKeys.forEach(key => {
  422. const field = this.fields.find(field => field.key === key)
  423. // Generate headerSlotNames from headers
  424. if (field.headers) {
  425. field.headerSlotNames = field.headers
  426. .filter(header => header.checked)
  427. .map(header => header.slotName)
  428. }
  429. // Add selected values
  430. if (field.valueFilter) {
  431. field.valuesFiltered = this.valuesFiltered[field.key]
  432. }
  433. colFields.push(field)
  434. })
  435. // 说明多个值列,要展开
  436. if (this.dataFields && this.dataFields.length > 1) {
  437. this.groupCols = colFields.filter((a, b) => b !== 0)
  438. } else {
  439. this.groupCols = colFields.filter((a, b) => b !== colFields.length - 1 && b !== 0)
  440. }
  441. setTimeout(() => {
  442. this.colFlag = true;
  443. }, 300)
  444. // 更新数据
  445. this.$nextTick(() => {
  446. this.updateValues();
  447. })
  448. return colFields
  449. },
  450. dataFields: function () {
  451. const dataFields = []
  452. this.internal.dataFieldKeys.forEach(key => {
  453. const field = this.fields.find(field => field.key === key)
  454. // Generate headerSlotNames from headers
  455. if (field.headers) {
  456. field.headerSlotNames = field.headers
  457. .filter(header => header.checked)
  458. .map(header => header.slotName)
  459. }
  460. // Add selected values
  461. if (field.valueFilter) {
  462. field.valuesFiltered = this.valuesFiltered[field.key]
  463. }
  464. dataFields.push(field)
  465. })
  466. return dataFields
  467. },
  468. // Drag area class
  469. dragAreaClass: function () {
  470. return this.dragging ? 'drag-area-highlight' : null
  471. },
  472. // Table wrapper style
  473. tableWrapperStyle: function () {
  474. const maxWidth = this.showSettings ? 'calc(100% - 200px - 2rem)' : '100%'
  475. return `max-width: ${maxWidth};`
  476. }
  477. },
  478. methods: {
  479. // Toggle settings
  480. toggleShowSettings() {
  481. this.showSettings = !this.showSettings
  482. },
  483. // Update dragging boolean
  484. start() {
  485. this.dragging = true
  486. },
  487. end() {
  488. this.dragging = false
  489. },
  490. /**
  491. * @desc : 操作字段列表
  492. * @author : 周兴
  493. * @date : 2023/3/23 21:32
  494. */
  495. handleTools() {
  496. this.hideFlag = !this.hideFlag;
  497. if (this.hideFlag) {
  498. this.$refs['toolSplit'].hidePane();
  499. ``
  500. }
  501. },
  502. /**
  503. * @desc : 选择数据
  504. * @author : 周兴
  505. * @date : 2023/3/23 11:09
  506. */
  507. handleSelect(e) {
  508. if (this.fieldValues && this.fieldValues[e.key]) {
  509. this.fieldValues[e.key].forEach(it => {
  510. if (e.model && e.model.includes(it.value)) {
  511. it.checked = true;
  512. } else {
  513. it.checked = false;
  514. }
  515. })
  516. }
  517. },
  518. /**
  519. * @desc : 获取标题
  520. * @author : 周兴
  521. * @date : 2023/3/21 13:34
  522. */
  523. getField(groupCols, index) {
  524. let titles = []
  525. if (groupCols && groupCols.length > 0) {
  526. groupCols.forEach(it => {
  527. if (it.valuesFiltered) {
  528. let values = [...it.valuesFiltered]
  529. if (values[index]) {
  530. titles.push(values[index]);
  531. }
  532. }
  533. })
  534. return titles.join('_') + '_';
  535. }
  536. return '';
  537. },
  538. /**
  539. * @desc : 要处理boolean的显示
  540. * @author : 周兴
  541. * @date : 2023/4/6 15:36
  542. */
  543. getTitle(value) {
  544. if (typeof value === 'boolean') {
  545. return value ? vm.$t('yes') : vm.$t('no')
  546. } else {
  547. return value + '';
  548. }
  549. },
  550. /**
  551. * @desc : 更新表头列
  552. * @author : 周兴
  553. * @date : 2023/3/21 9:02
  554. */
  555. updateFieldValues() {
  556. return new Promise(resolve => {
  557. for (let [key, field] of Object.entries(this.fieldsWithValues)) {
  558. if (field.valueFilter) {
  559. this.fieldValues[key] = []
  560. field.values.forEach(value => {
  561. this.fieldValues[key].push({value, checked: true})
  562. })
  563. }
  564. }
  565. resolve();
  566. })
  567. },
  568. /**
  569. * @desc : 更新数据
  570. * @author : 周兴
  571. * @date : 2023/3/21 9:03
  572. */
  573. updateValues(updateValuesHashTable = true) {
  574. this.isDataComputing = true;
  575. // Start a task to avoid blocking the page
  576. clearInterval(this.computingInterval)
  577. this.computingInterval = setTimeout(() => {
  578. // this.tableData = this.data.filterColumns(this.rowKeys).unique();
  579. this.tableData = []
  580. const valuesHashTable = new HashTable()
  581. const fields = [...this.rowFields, ...this.colFields]
  582. const dataKey = []
  583. this.data.forEach(it => {
  584. const rowKey = []
  585. const colKey = []
  586. let filtered = false
  587. // Check if item passes fields value filters
  588. for (let field of fields) {
  589. if (field.valuesFiltered && !field.valuesFiltered.has(field.getter(it))) {
  590. filtered = true
  591. break
  592. }
  593. }
  594. // Build item rowKey/colKey if necessary
  595. if (!filtered) {
  596. let rowColumns = {}
  597. this.rowFields.forEach(field => {
  598. rowKey.push(field.getter(it) + '')
  599. rowColumns[field.key] = field.getter(it) + ''
  600. })
  601. if (rowColumns && Object.keys(rowColumns).length > 0) {
  602. this.tableData.pushNoRepeat(rowColumns)
  603. }
  604. this.colFields.forEach(field => {
  605. colKey.push(field.getter(it) + '')
  606. })
  607. if (this.dataFields && this.dataFields.length > 1) {
  608. this.dataFields.forEach(field => {
  609. dataKey.push(field.key)
  610. })
  611. }
  612. }
  613. // Update valuesHashTable
  614. // if (updateValuesHashTable) {
  615. if (dataKey && dataKey.length > 0) {
  616. dataKey.forEach(item => {
  617. // 计算公式
  618. this.reducer = (sum, itm) => sum + itm[item];
  619. const key = [...rowKey, ...colKey, item]
  620. const previousValue = valuesHashTable.get(key) || 0
  621. // console.log('key', key, previousValue, this.reducer(previousValue, it))
  622. valuesHashTable.set(key, this.reducer(previousValue, it))
  623. })
  624. } else {
  625. // 说明值域小于等于1个
  626. if (this.dataFields && this.dataFields.length > 0) {
  627. let item = this.dataFields[0].key
  628. // 计算公式
  629. this.reducer = (sum, itm) => sum + itm[item];
  630. const key = [...rowKey, ...colKey]
  631. const previousValue = valuesHashTable.get(key) || 0
  632. // console.log('key', key, previousValue, this.reducer(previousValue, it))
  633. valuesHashTable.set(key, this.reducer(previousValue, it))
  634. }
  635. }
  636. // }
  637. })
  638. this.$nextTick(() => {
  639. // 是否包括值域
  640. let hasData = this.dataFields.length > 0 ? true : false;
  641. // 把数据装入table
  642. if (this.tableData && this.$refs.col && this.$refs.col.length > 0) {
  643. this.tableData.forEach(item => {
  644. let rowKeys = []
  645. this.rowKeys.forEach(k => {
  646. rowKeys.push(item[k]);
  647. })
  648. // 循环列
  649. this.$refs.col.forEach(col => {
  650. let headerFields = col.field.split('_')
  651. // 如果只有一个值域,是不要显示
  652. if (this.dataFields.length == 1) {
  653. let index = headerFields.findIndex(it => it == this.dataFields[0].key)
  654. // 删除掉
  655. if (index >= 0) {
  656. headerFields.splice(index, 1)
  657. }
  658. }
  659. let key = [...rowKeys, ...headerFields]
  660. if(hasData){
  661. let digits = col.digits;
  662. let value = valuesHashTable.get(key);
  663. item[col.field] = value ? value.toThousandth(digits) : null;
  664. // console.log('col.field', this.dataFields, headerFields, valuesHashTable, key,col)
  665. }else{
  666. item[col.field] = null;
  667. }
  668. })
  669. })
  670. }
  671. this.isDataLoading = false;
  672. })
  673. }, 300)
  674. this.isDataComputing = false
  675. // this.updateInternalFields()
  676. },
  677. /**
  678. * @desc : 复制列
  679. * @author : 周兴
  680. * @date : 2023/3/21 9:04
  681. */
  682. updateInternalFields() {
  683. // this.internalRowFields = cloneDeep(this.rowFields)
  684. // this.internalColFields = cloneDeep(this.colFields)
  685. }
  686. },
  687. watch: {
  688. data() {
  689. this.isDataLoading = true;
  690. this.updateFieldValues().then(res => {
  691. this.updateValues(); // 更新数据
  692. }); // 更新列
  693. },
  694. dataFields(n, o) {
  695. this.isDataLoading = true;
  696. this.updateValues(); // 更新数据
  697. },
  698. },
  699. mounted() {
  700. },
  701. created() {
  702. this.showSettings = this.defaultShowSettings
  703. this.updateFieldValues().then(res => {
  704. this.updateValues(); // 更新数据
  705. }); // 更新列
  706. }
  707. }
  708. </script>
  709. <style lang="scss" scoped>
  710. /* Grid with gutter */
  711. .gutter, .gutter-y {
  712. margin-top: -1rem;
  713. > * {
  714. margin-top: 1rem;
  715. }
  716. }
  717. .gutter-x, .gutter {
  718. margin-left: -1rem;
  719. > * {
  720. margin-left: 1rem;
  721. }
  722. }
  723. .gutter-sm, .gutter-y-sm {
  724. margin-top: -.2rem;
  725. > * {
  726. margin-top: .2rem;
  727. }
  728. }
  729. .gutter-x-sm, .gutter-sm {
  730. margin-left: -.5rem;
  731. > * {
  732. margin-left: .5rem;
  733. }
  734. }
  735. /* Drag & drop stuff */
  736. .drag-area {
  737. width: 49%;
  738. border: 1px dashed #ccc;
  739. padding: 0.75rem;
  740. transition: background-color 0.4s;
  741. .drag-area-title {
  742. line-height: 1;
  743. }
  744. .drag-area-zone {
  745. min-width: 10rem;
  746. min-height: 46px;
  747. height: calc(100% - 20px);
  748. overflow-y: auto;
  749. overflow-x: hidden;
  750. }
  751. }
  752. .drag-area-highlight {
  753. background-color: #f3f3f3;
  754. }
  755. .sortable-ghost {
  756. opacity: 0.4;
  757. }
  758. .field {
  759. position: relative;
  760. width: 100% !important;
  761. }
  762. .btn-class {
  763. width: 100%;
  764. text-align: left;
  765. }
  766. /deep/ .ivu-btn .ivu-icon-md-arrow-dropleft {
  767. display: inline-block;
  768. overflow: hidden;
  769. position: absolute;
  770. top: 0;
  771. right: 10px;
  772. cursor: pointer;
  773. font-size: 22px;
  774. color: #C2C2C2;
  775. }
  776. /deep/ .ivu-btn .ivu-icon-md-arrow-dropright {
  777. display: inline-block;
  778. overflow: hidden;
  779. position: absolute;
  780. top: 0;
  781. right: 10px;
  782. cursor: pointer;
  783. font-size: 22px;
  784. color: #C2C2C2;
  785. }
  786. </style>