Commit ddca17e6 by yuwei

项目初始化

parent ea4049f4
<template>
<div class="table-responsive">
<table>
<thead>
<tr v-for="(tr, index) in combineHeads" :key="index">
<th
v-for="cell in tr"
:key="cell.__index"
:rowspan="cell.rowspan"
:colspan="cell.colspan"
>
<div
:class="{ 'col-corner-bg': cell.isCorner }"
:style="{ 'min-height': _getMinHeightByRowCount(cell.rowspan) }"
>
{{ cell.isCorner ? (rowPaths.length + ' x ' + colPaths.length) : cell.value }}
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(tr, index) in combineValues" :key="tr.__index">
<td
v-for="cell in tr.data"
:key="cell.__index"
:rowspan="cell.rowspan"
:colspan="cell.colspan"
>
<div
:class="{ 'col-corner-bg': cell.isCorner }"
:style="{ 'min-height': _getMinHeightByRowCount(cell.rowspan) }"
>
{{ cell.value }}
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import {
mergeBaseInfo,
convertPathToMap,
getHeightByCount
} from './utils'
import { SEPARATOR } from './utils/constants'
export default {
name: 'PivotTable',
props: {
data: {
type: Array,
default: () => []
},
rows: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
values: {
type: Array,
default: () => []
},
summary: {
type: Array,
default: () => []
}
},
data: () => ({
localRows: [],
localColumns: [],
localValues: [],
localData: [],
// 计算列的数据
calcData: [],
// Separator
Separator: SEPARATOR,
// 合并后的表头
combineHeads: [],
// 合并后的单元格
combineValues: []
}),
computed: {
rowPaths() {
const _paths = this._combinePaths(
...this.localRows.map(({ values }) => values)
)
return _paths
},
colPaths() {
const keys = this.localColumns.map(({ values }) => values)
if (this.localValues.length) {
keys.push(this.localValues.map(({ key }) => key))
}
const _paths = this._combinePaths(...keys)
return _paths
},
// 列的表头
colHeads() {
// 共有多少行
const _rows = this.localColumns.map(() => [])
// 有几个值
const valuesLen = this.localValues.length
if (valuesLen) {
_rows.push([])
}
// todo: 仅有 values 的时候会有一个空的path
this.colPaths.forEach((path, pathIndex) => {
// 条件值
const pathValues = path.split(this.Separator)
// 存储路径
const currPath = []
_rows.forEach((row, rowIndex) => {
const cellData = {}
const currVal = pathValues[rowIndex] || ''
// 是否为 values 行
const isLastRow = rowIndex === _rows.length - 1
// 存储路径
currPath.push(currVal)
const baseX = rowIndex
const baseY = this.localRows.length + pathIndex
row.push(
Object.assign(
cellData,
mergeBaseInfo({
__index: `${baseX}-${baseY}`,
x: baseX,
y: baseY,
path: currPath.filter((item) => !!item),
// 最后一行是 values,替换显示文本
value: valuesLen && isLastRow && currVal ? this.localValues.find(({ key }) => key === currVal).label : currVal
})
)
)
})
})
return _rows
},
// 行的表头
rowHeads() {
// 共有多少列
const _columns = []
// 左上角特殊处理
_columns.push(mergeBaseInfo({
__index: `0-0`,
colspan: this.localRows.length,
rowspan: this.localColumns.length,
// 左上角标记
isCorner: true
}))
this.localRows.forEach(({ label }, index) => {
_columns.push(mergeBaseInfo({
__index: `${this.localColumns.length}-${index}`,
value: label,
x: this.localColumns.length,
y: index
}))
})
return _columns
},
// 行对应的值
rowHeadValues() {
return this.localRows.length
? this.rowPaths.map((path, index) => {
const values = path.split(this.Separator)
const currPath = []
return this.localRows.map((item, rowIndex) => {
const _index = rowIndex
const _currVal = values[_index] || ''
const baseX = this.localColumns.length + +Boolean(this.localValues.length) + index
const baseY = _index
currPath.push(_currVal)
return mergeBaseInfo({
__index: `${baseX}-${baseY}`,
value: _currVal,
x: baseX,
y: baseY,
path: currPath.filter((item) => !!item)
})
})
})
: []
},
// 计算所有对应条件的值
dataValues() {
// 列对应的条件
const colConditions = convertPathToMap(
this.colPaths,
this.localColumns.map(({ key }) => key).concat(this.localValues.length ? ['value'] : [])
)
// 行对应的条件
const rowConditions = convertPathToMap(
this.rowPaths,
this.localRows.map(({ key }) => key)
)
// console.log('colConditions', colConditions)
// console.log('rowConditions', rowConditions)
// 针对没传入行或列的处理
// !colConditions.length && colConditions.push({});
// !rowConditions.length && rowConditions.push({});
// 过滤数据, 遍历行以及遍历行对应的列
return rowConditions.map((rowCondition, rowConditionIndex) => {
const _data = colConditions.map((colCondition, colConditionIndex) => {
// 存储当前单元对应的数据
const cellData = {}
// 当前单元对应的条件
const conditions = Object.assign({}, rowCondition, colCondition)
const _filterConditions = Object.fromEntries(
Object.entries(conditions).filter(
(item) => item[0] !== 'value'
)
)
// 通过当前单元对应的条件,过滤数据
const filterData = this._filterData(_filterConditions, this.localData)
// 对应表格的坐标位置
const baseX = this.localColumns.length + +Boolean(this.localValues.length) + rowConditionIndex
const baseY = this.localRows.length + colConditionIndex
Object.assign(
cellData,
mergeBaseInfo({
conditions,
x: baseX,
y: baseY,
__index: `${baseX}-${baseY}`
})
)
// 针对为指定值 props.values 的空处理(绘制空表格)
const isEmptyValues = this.localColumns.length && this.localRows.length && !this.localValues.length
if (isEmptyValues) {
Object.assign(cellData, { value: '' })
} else {
// 从 props.values 中找出对应的值 handle
// 注意:this.localValues 通过 JSON.xxx 序列化后,handle 会被忽略
const _value = this.values.find(({ key }) => key === conditions.value)
Object.assign(cellData, { value: _value && _value.key ? this._reduceValue(filterData, _value.key) : '' })
}
return cellData
}
)
return {
__index: _data[0].x,
data: _data
}
})
}
},
created: function() {
this.init()
},
methods: {
init() {
if (this.rows.length || this.columns.length || this.values) {
this.handleDataClone()
this.setValuesToColAndRow()
this.handleCalcData()
this.handleCombineHeads()
this.handleCombineValues()
} else {
console.warn(
'[Warn]: props.rows, props.columns, props.values at least one is not empty.'
)
}
},
// clone data
handleDataClone() {
this.localRows = JSON.parse(JSON.stringify(this.rows))
this.localColumns = JSON.parse(JSON.stringify(this.columns))
this.localValues = JSON.parse(JSON.stringify(this.values))
this.localData = Object.freeze(this.data)
},
// set the `values` attribute to rows and columns
setValuesToColAndRow() {
const rowKeys = this.localRows.map(({ key }) => key)
const columnKeys = this.localColumns.map(({ key }) => key)
const rowValues = this._findCategory(rowKeys, this.localData)
const columnValues = this._findCategory(columnKeys, this.localData)
this.localRows.forEach((row) => {
const { key, values } = row
this.$set(row, 'values', values || rowValues[key] || [])
})
this.localColumns.forEach((column) => {
const { key, values } = column
this.$set(column, 'values', values || columnValues[key] || [])
})
},
// 合并表头
handleCombineHeads() {
let combineColHeads = JSON.parse(JSON.stringify(this.colHeads))
combineColHeads[0] = combineColHeads[0] || []
combineColHeads[0].unshift(...this.rowHeads.filter((item) => item.isCorner))
combineColHeads[combineColHeads.length - 1].unshift(...this.rowHeads.filter((item) => !item.isCorner))
combineColHeads = combineColHeads.filter((item) => item.length)
this.combineHeads = combineColHeads
},
// 合并值
handleCombineValues() {
// values
const combineValues = []
const valueRowCount = this.dataValues.length || this.rowHeadValues.length
for (let i = 0; i < valueRowCount; i++) {
const _currRowHeadValue = this.rowHeadValues[i] || []
const _currValue = this.dataValues[i] || {}
const _row = [..._currRowHeadValue, ...(_currValue.data || [])]
combineValues.push(
Object.assign({}, _currValue, { data: _row })
)
}
this.combineValues = combineValues
},
// 初始计算值
handleCalcData() {
if (!this.localValues.length) return
const _rowPaths = this._combinePaths(
...this.localRows.map(({ values }) => values)
)
const _rowKeys = this.localRows.map(({ key }) => key)
const _colPaths = this._combinePaths(
...this.localColumns.map(({ values }) => values)
)
const _colKeys = this.localColumns.map(({ key }) => key)
// conditions of col head
const colConditions = convertPathToMap(_colPaths, _colKeys)
// conditions of row-head
const rowConditions = convertPathToMap(_rowPaths, _rowKeys)
// Note: if there are no props.rows or props.column, push an empty object
!colConditions.length && colConditions.push({})
!rowConditions.length && rowConditions.push({})
// draw data
this.calcData = Object.freeze(
rowConditions
.map((rowCondition, rowConditionIndex) =>
colConditions
.map((colCondition, colConditionIndex) => {
// the condition of current cell
const conditions = Object.assign({}, rowCondition, colCondition)
// filter the data
const filterData = this._filterData(conditions, this.localData)
// empty cell
const isEmptyCell = this.localRows.length && this.localColumns.length && !this.localValues.length
const _values = {}
// 多个值,多条数据
this.values.forEach(({ key, handle }) => {
_values[key] = isEmptyCell ? '' : this._reduceValue(filterData, key)
})
return Object.assign({}, conditions, _values)
})
.flat()
)
.filter((item) => item.length)
.flat()
)
},
_combinePaths(...arrays) {
return arrays.length ? arrays.reduce((prev, curr) => {
const arr = []
prev.forEach(_prevEl => {
curr.forEach(_currEl => {
arr.push(_prevEl + SEPARATOR + _currEl)
})
})
return arr
}) : arrays
},
_findCategory(keys = [], data = []) {
const _result = {}
data.forEach(item => {
keys.forEach(key => {
// Remove duplicates
_result[key] = _result[key] || []
_result[key].push(item[key])
_result[key] = [...new Set(_result[key])]
})
})
return _result
},
_reduceValue(data, key) {
if (!data.length) return ''
return data.reduce((sum, item) => { return sum + Number(item[key]) }, 0)
},
_filterData(conditions, data) {
return data.filter((data) => {
let status = true
for (const key in conditions) {
if (conditions[key] !== data[key]) {
status = false
return
}
}
return status
})
},
// get min height by rowspan
_getMinHeightByRowCount(count) {
return getHeightByCount(count)
}
}
}
</script>
<style lang="scss" scoped>
table {
border-collapse: collapse;
border-spacing: 0;
border: none;
thead th {
background-color: #CCE8EB;
}
tbody {
tr:nth-child(odd) {
background: #fff;
}
tr:nth-child(even) {
background: #F5FAFA;
}
}
td, th {
border: 1px solid #ccc;
padding: 0;
vertical-align: middle;
box-sizing: border-box;
> div {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 5px;
text-align: center;
white-space: nowrap;
width: 100%;
height: 100%;
min-height: 36px;
cursor: default;
&.col-corner-bg {
background: #f1f7fd;
}
}
}
}
</style>
export const CELL_MIN_HEIGHT = 38
export const SEPARATOR = ':'
import { SEPARATOR } from './constants'
// Convert path to object
export default (paths, keys) => {
return paths.map(path => {
const pathArr = path.split(SEPARATOR)
const obj = {}
keys.forEach((key, index) => {
if (pathArr[index]) {
obj[key] = pathArr[index]
}
})
return obj
})
}
import { CELL_MIN_HEIGHT } from './constants'
export default count => count * CELL_MIN_HEIGHT + 'px'
import mergeBaseInfo from './merge_base_info'
import convertPathToMap from './convert_path_to_map'
import getHeightByCount from './get_height_by_count'
export {
mergeBaseInfo,
convertPathToMap,
getHeightByCount
}
// merge base info
export default function mergeBaseInfo(info = {}) {
const _baseCellInfo = {
value: '',
x: 0,
y: 0,
colspan: 1,
rowspan: 1
}
return Object.assign({}, _baseCellInfo, info)
}
......@@ -89,7 +89,7 @@
<el-tabs type="card">
<el-tab-pane label="图表预览">
<div class="widget-center-pane-chart">
<chart-panel v-if="visible" id="chartPanel" ref="chartPanel" :chart-schema="widget" :chart-data="[]" />
<chart-panel v-if="visible" id="chartPanel" ref="chartPanel" :chart-schema="widget" :chart-data="chartData.data" />
</div>
</el-tab-pane>
<el-tab-pane label="查询脚本">
......
<template>
<div ref="chart" :style="chartStyle">
<my-chart-table :col="tableCol" :data="tableData" />
<div ref="chart" :style="chartStyle" style="overflow: auto;">
<pivot-table
ref="pivottable"
:data="data"
:rows="rows"
:columns="columns"
:values="values"
/>
</div>
</template>
<script>
import MyChartTable from './charttable/MyChartTable'
import PivotTable from '@/components/PivotTable'
export default {
name: 'ChartTable',
components: {
MyChartTable
PivotTable
},
props: {
data: {
......@@ -38,60 +44,34 @@ export default {
}
},
computed: {
tableCol() {
return [
{
prop: 'date',
label: '日期'
},
{
label: '配送信息',
children: [
{
prop: 'name',
label: '姓名'
},
{
label: '地址',
children: [
{
prop: 'province',
label: '省份'
},
{
prop: 'city',
label: '市区'
},
{
prop: 'address',
label: '地址'
}
]
}
]
rows() {
return this.chartSchema.rows.map((row, index, arr) => {
return {
key: `${row.col}`,
label: `${row.col}`
}
]
})
},
tableData() {
return [
{
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
columns() {
return this.chartSchema.columns.map((column, index, arr) => {
return {
key: `${column.col}`,
label: `${column.col}`
}
})
},
{
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
values() {
return this.chartSchema.measures.map((measure, index, arr) => {
return {
key: `${measure.col}`,
label: `${measure.col}`
}
]
})
}
},
created() {
console.log(this.data)
console.log(this.chartSchema)
}
}
</script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment