2021-01-24 19:23:05 +08:00

230 lines
5.5 KiB
Vue

<template>
<div
class="static-table"
:style="{ width: totalWidth + 'px' }"
>
<table
:class="{
'theme': theme,
'row-header': theme?.rowHeader,
'row-footer': theme?.rowFooter,
'col-header': theme?.colHeader,
'col-footer': theme?.colFooter,
}"
:style="`--themeColor: ${theme?.color}; --subThemeColor1: ${subThemeColor[0]}; --subThemeColor2: ${subThemeColor[1]}`"
>
<colgroup>
<col span="1" v-for="(width, index) in colSizeList" :key="index" :width="width">
</colgroup>
<tbody>
<tr
v-for="(rowCells, rowIndex) in data"
:key="rowIndex"
>
<td
class="cell"
:style="{
borderStyle: outline.style,
borderColor: outline.color,
borderWidth: outline.width + 'px',
...getTextStyle(cell.style),
}"
v-for="(cell, colIndex) in rowCells"
:key="cell.id"
:rowspan="cell.rowspan"
:colspan="cell.colspan"
v-show="!hideCells.includes(`${rowIndex}_${colIndex}`)"
>
<div class="cell-text" v-html="cell.text" />
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref, watch } from 'vue'
import tinycolor from 'tinycolor2'
import { PPTElementOutline, TableCell, TableCellStyle, TableTheme } from '@/types/slides'
export default defineComponent({
name: 'static-table',
props: {
data: {
type: Array as PropType<TableCell[][]>,
required: true,
},
width: {
type: Number,
required: true,
},
colWidths: {
type: Array as PropType<number[]>,
required: true,
},
outline: {
type: Object as PropType<PPTElementOutline>,
required: true,
},
theme: {
type: Object as PropType<TableTheme>,
},
editable: {
type: Boolean,
default: true,
},
},
setup(props) {
const colSizeList = ref<number[]>([])
const totalWidth = computed(() => colSizeList.value.reduce((a, b) => a + b))
watch([
() => props.colWidths,
() => props.width,
], () => {
colSizeList.value = props.colWidths.map(item => item * props.width)
}, { immediate: true })
const hideCells = computed(() => {
const hideCells = []
for(let i = 0; i < props.data.length; i++) {
const rowCells = props.data[i]
for(let j = 0; j < rowCells.length; j++) {
const cell = rowCells[j]
if(cell.colspan > 1 || cell.rowspan > 1) {
for(let row = i; row < i + cell.rowspan; row++) {
for(let col = row === i ? j + 1 : j; col < j + cell.colspan; col++) {
hideCells.push(`${row}_${col}`)
}
}
}
}
}
return hideCells
})
const subThemeColor = ref(['', ''])
watch(() => props.theme, () => {
if(props.theme) {
const rgba = tinycolor(props.theme.color).toRgb()
const subRgba1 = { r: rgba.r, g: rgba.g, b: rgba.b, a: rgba.a * 0.3 }
const subRgba2 = { r: rgba.r, g: rgba.g, b: rgba.b, a: rgba.a * 0.1 }
subThemeColor.value = [
`rgba(${[subRgba1.r, subRgba1.g, subRgba1.b, subRgba1.a].join(',')})`,
`rgba(${[subRgba2.r, subRgba2.g, subRgba2.b, subRgba2.a].join(',')})`,
]
}
}, { immediate: true })
const getTextStyle = (style?: TableCellStyle) => {
if(!style) return {}
const {
bold,
em,
underline,
strikethrough,
color,
backcolor,
fontsize,
fontname,
align,
} = style
return {
fontWeight: bold ? 'bold' : 'normal',
fontStyle: em ? 'italic' : 'normal',
textDecoration: `${underline ? 'underline' : ''} ${strikethrough ? 'line-through' : ''}`,
color: color || '#000',
backgroundColor: backcolor || '',
fontSize: fontsize || '14px',
fontFamily: fontname || '微软雅黑',
textAlign: align || 'left',
}
}
return {
colSizeList,
totalWidth,
hideCells,
getTextStyle,
subThemeColor,
}
},
})
</script>
<style lang="scss" scoped>
.static-table {
position: relative;
user-select: none;
}
table {
width: 100%;
position: relative;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
border: 0;
word-wrap: break-word;
user-select: none;
--themeColor: $themeColor;
--subThemeColor1: $themeColor;
--subThemeColor2: $themeColor;
&.theme {
tr:nth-child(2n) .cell {
background-color: var(--subThemeColor1);
}
tr:nth-child(2n + 1) .cell {
background-color: var(--subThemeColor2);
}
&.row-header {
tr:first-child .cell {
background-color: var(--themeColor);
}
}
&.row-footer {
tr:last-child .cell {
background-color: var(--themeColor);
}
}
&.col-header {
tr .cell:first-child {
background-color: var(--themeColor);
}
}
&.col-footer {
tr .cell:last-child {
background-color: var(--themeColor);
}
}
}
tr {
height: 36px;
}
.cell {
position: relative;
white-space: normal;
word-wrap: break-word;
vertical-align: middle;
}
.cell-text {
min-height: 32px;
padding: 5px;
border: 0;
outline: 0;
line-height: 1.5;
font-size: 14px;
user-select: none;
}
}
</style>