2022-10-12 21:57:30 +08:00

389 lines
9.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="dv-scroll-board">
<div
class="header"
v-if="status.header.length && status.mergedConfig"
:style="`background-color: ${status.mergedConfig.headerBGC};`"
>
<div
class="header-item"
v-for="(headerItem, i) in status.header"
:key="`${headerItem}${i}`"
:style="`
height: ${status.mergedConfig.headerHeight}px;
line-height: ${status.mergedConfig.headerHeight}px;
width: ${status.widths[i]}px;
`"
:align="status.aligns[i]"
v-html="headerItem"
/>
</div>
<div
v-if="status.mergedConfig"
class="rows"
:style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`"
>
<div
class="row-item"
v-for="(row, ri) in status.rows"
:key="`${row.toString()}${row.scroll}`"
:style="`
height: ${status.heights[ri]}px;
line-height: ${status.heights[ri]}px;
background-color: ${status.mergedConfig[row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};
`"
>
<div
class="ceil"
v-for="(ceil, ci) in row.ceils"
:key="`${ceil}${ri}${ci}`"
:style="`width: ${status.widths[ci]}px;`"
:align="status.aligns[ci]"
v-html="ceil"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { PropType, onUnmounted, reactive, toRefs, watch, onMounted } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import merge from 'lodash/merge'
import cloneDeep from 'lodash/cloneDeep'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
// 这里能拿到图表宽高等
const { w, h } = toRefs(props.chartConfig.attr)
// 这里能拿到上面 config.ts 里的 option 数据
// const { rowNum, headerHeight, index, backgroundColor } = toRefs(props.chartConfig.option)
const status = reactive({
defaultConfig: {
/**
* @description Board header
* @type {Array<String>}
* @default header = []
* @example header = ['column1', 'column2', 'column3']
*/
header: [],
/**
* @description Board dataset
* @type {Array<Array>}
* @default dataset = []
*/
dataset: [],
/**
* @description Row num
* @type {Number}
* @default rowNum = 5
*/
rowNum: 5,
/**
* @description Header background color
* @type {String}
* @default headerBGC = '#00BAFF'
*/
headerBGC: '#00BAFF',
/**
* @description Odd row background color
* @type {String}
* @default oddRowBGC = '#003B51'
*/
oddRowBGC: '#003B51',
/**
* @description Even row background color
* @type {String}
* @default evenRowBGC = '#003B51'
*/
evenRowBGC: '#0A2732',
/**
* @description Scroll wait time
* @type {Number}
* @default waitTime = 2
*/
waitTime: 2,
/**
* @description Header height
* @type {Number}
* @default headerHeight = 35
*/
headerHeight: 35,
/**
* @description Column width
* @type {Array<Number>}
* @default columnWidth = []
*/
columnWidth: [],
/**
* @description Column align
* @type {Array<String>}
* @default align = []
* @example align = ['left', 'center', 'right']
*/
align: [],
/**
* @description Show index
* @type {Boolean}
* @default index = false
*/
index: false,
/**
* @description index Header
* @type {String}
* @default indexHeader = '#'
*/
indexHeader: '#',
/**
* @description Carousel type
* @type {String}
* @default carousel = 'single'
* @example carousel = 'single' | 'page'
*/
carousel: 'single',
/**
* @description Pause scroll when mouse hovered
* @type {Boolean}
* @default hoverPause = true
* @example hoverPause = true | false
*/
hoverPause: true
},
mergedConfig: props.chartConfig.option,
header: [],
rowsData: [],
rows: [
{
ceils: [],
rowIndex: 0,
scroll: 0
}
],
widths: [],
heights: [0],
avgHeight: 0,
aligns: [],
animationIndex: 0,
animationHandler: 0,
updater: 0,
needCalc: false
})
const calcData = () => {
mergeConfig()
calcHeaderData()
calcRowsData()
calcWidths()
calcHeights()
calcAligns()
animation(true)
}
onMounted(() => {
calcData()
})
const mergeConfig = () => {
status.mergedConfig = merge(cloneDeep(status.defaultConfig), props.chartConfig.option)
}
const calcHeaderData = () => {
let { header, index, indexHeader } = status.mergedConfig
if (!header.length) {
status.header = []
return
}
header = [...header]
if (index) header.unshift(indexHeader)
status.header = header
}
const calcRowsData = () => {
let { dataset, index, headerBGC, rowNum } = status.mergedConfig
if (index) {
dataset = dataset.map((row: any, i: number) => {
row = [...row]
const indexTag = `<span class="index" style="background-color: ${headerBGC};border-radius: 3px;padding: 0px 3px;">${
i + 1
}</span>`
row.unshift(indexTag)
return row
})
}
dataset = dataset.map((ceils: any, i: number) => ({ ceils, rowIndex: i }))
const rowLength = dataset.length
if (rowLength > rowNum && rowLength < 2 * rowNum) {
dataset = [...dataset, ...dataset]
}
dataset = dataset.map((d: any, i: number) => ({ ...d, scroll: i }))
status.rowsData = dataset
status.rows = dataset
}
const calcWidths = () => {
const { mergedConfig, rowsData } = status
const { columnWidth, header } = mergedConfig
const usedWidth = columnWidth.reduce((all: any, ws: number) => all + ws, 0)
let columnNum = 0
if (rowsData[0]) {
columnNum = (rowsData[0] as any).ceils.length
} else if (header.length) {
columnNum = header.length
}
const avgWidth = (w.value - usedWidth) / (columnNum - columnWidth.length)
const widths = new Array(columnNum).fill(avgWidth)
status.widths = merge(widths, columnWidth)
}
const calcHeights = (onresize = false) => {
const { mergedConfig, header } = status
const { headerHeight, rowNum, dataset } = mergedConfig
let allHeight = h.value
if (header.length) allHeight -= headerHeight
const avgHeight = allHeight / rowNum
status.avgHeight = avgHeight
if (!onresize) status.heights = new Array(dataset.length).fill(avgHeight)
}
const calcAligns = () => {
const { header, mergedConfig } = status
const columnNum = header.length
let aligns = new Array(columnNum).fill('left')
const { align } = mergedConfig
status.aligns = merge(aligns, align)
}
const animation = async (start = false) => {
const { needCalc } = status
if (needCalc) {
calcRowsData()
calcHeights()
status.needCalc = false
}
let { avgHeight, animationIndex, mergedConfig, rowsData, updater } = status
const { waitTime, carousel, rowNum } = mergedConfig
const rowLength = rowsData.length
if (rowNum >= rowLength) return
if (start) {
await new Promise(resolve => setTimeout(resolve, waitTime * 1000))
if (updater !== status.updater) return
}
const animationNum = carousel === 'single' ? 1 : rowNum
let rows = rowsData.slice(animationIndex)
rows.push(...rowsData.slice(0, animationIndex))
status.rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1)
status.heights = new Array(rowLength).fill(avgHeight)
await new Promise(resolve => setTimeout(resolve, 300))
if (updater !== status.updater) return
status.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))
animationIndex += animationNum
const back = animationIndex - rowLength
if (back >= 0) animationIndex = back
status.animationIndex = animationIndex
status.animationHandler = setTimeout(animation, waitTime * 1000 - 300) as any
}
const stopAnimation = () => {
status.updater = (status.updater + 1) % 999999
if (!status.animationHandler) return
clearTimeout(status.animationHandler)
}
const onRestart = async () => {
try {
if (!status.mergedConfig) return
stopAnimation()
calcData()
} catch (error) {
console.log(error)
}
}
watch(
() => w.value,
() => {
onRestart()
}
)
watch(
() => h.value,
() => {
onRestart()
}
)
// 数据更新
watch(
() => props.chartConfig.option,
() => {
onRestart()
},
{ deep: true }
)
// 数据更新 (默认更新 dataset若更新之后有其它操作可添加回调函数)
useChartDataFetch(props.chartConfig, useChartEditStore, (resData: any[]) => {
props.chartConfig.option.dataset = resData
onRestart()
})
onUnmounted(() => {
stopAnimation()
})
</script>
<style lang="scss" scoped>
.dv-scroll-board {
position: relative;
width: 100%;
height: 100%;
color: #fff;
.text {
padding: 0 10px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.header {
display: flex;
flex-direction: row;
font-size: 15px;
.header-item {
transition: all 0.3s;
}
}
.rows {
overflow: hidden;
.row-item {
display: flex;
font-size: 14px;
transition: all 0.3s;
overflow: hidden;
}
}
}
</style>