feat(视图): 明细表/汇总表支持按列设置宽度,拖拽调整宽度。#7707

This commit is contained in:
wisonic-s 2024-02-26 17:42:53 +08:00
parent a78b57d3c1
commit 000b4acd96
12 changed files with 261 additions and 35 deletions

View File

@ -865,6 +865,7 @@ export default {
table_config: '表格配置',
table_column_width_config: '列宽调整',
table_column_adapt: '自适应',
table_column_fixed: '固定列宽',
table_column_custom: '自定义',
chart_table_pivot: '透视表',
table_pivot_row: '数据行',
@ -1005,7 +1006,7 @@ export default {
table_scroll_tip: '明细表仅在分页模式为"下拉"时生效',
table_threshold_tip: '提示请勿重复选择字段若同一字段重复配置则只有最后的字段配置生效',
table_column_width_tip:
'列宽并非任何时候都能生效<br/>容器宽度优先级高于列宽(表格容器宽度 / 列数 > 指定列宽)则列宽优先取(容器宽度 / 列数)',
'固定列宽并非任何时候都能生效<br/>容器宽度优先级高于列宽(表格容器宽度 / 列数 > 指定列宽)则列宽优先取(容器宽度 / 列数)',
reference_field_tip:
'引用字段以 "[" 开始 "]" 结束<br/>请勿修改引用内容否则将引用失败<br/>若输入与引用字段相同格式的内容将被当作引用字段处理',
scatter_tip: '该指标生效时样式大小中的气泡大小属性将失效',

View File

@ -54,11 +54,28 @@ declare interface ChartBasicStyle {
/**
* 表格列宽模式: 自适应和自定义
*/
tableColumnMode: 'adapt' | 'custom'
tableColumnMode: 'adapt' | 'custom' | 'field'
/**
* 表格列宽
*/
tableColumnWidth: number
/**
* 表格字段列宽
*/
tableFieldWidth: {
/**
* 字段ID
*/
fieldId: string,
/**
* 字段名称
*/
name: string,
/**
* 字段宽度占比
*/
width: number
}[]
/**
* 表格分页模式
*/

View File

@ -26,7 +26,7 @@ const sort = type => {
}
</script>
<template>
<el-col>
<el-col style="color: black">
<el-row align="middle">
<el-icon><SortUp /></el-icon>
<span class="sort-btn" @click="() => sort('asc')">{{ t('chart.asc') }}</span>

View File

@ -3,6 +3,8 @@ import { onMounted, PropType, reactive, watch } from 'vue'
import { COLOR_PANEL, DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart'
import { useI18n } from '@/hooks/web/useI18n'
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
import { cloneDeep, defaultsDeep } from 'lodash-es'
import { SERIES_NUMBER_FIELD } from '@antv/s2'
const { t } = useI18n()
const props = defineProps({
@ -23,10 +25,19 @@ const predefineColors = COLOR_PANEL
const state = reactive({
basicStyleForm: JSON.parse(JSON.stringify(DEFAULT_BASIC_STYLE)) as ChartBasicStyle,
customColor: null,
colorIndex: 0
colorIndex: 0,
fieldColumnWidth: {
fieldId: '',
width: 0
}
})
watch(
() => props.chart.customAttr.basicStyle,
[
() => props.chart.customAttr.basicStyle,
() => props.chart.customAttr.tableHeader,
() => props.chart.xAxis,
() => props.chart.yAxis
],
() => {
init()
},
@ -37,14 +48,95 @@ const changeBasicStyle = (prop?: string, requestData = false) => {
emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop)
}
const init = () => {
const basicStyle = JSON.parse(JSON.stringify(props.chart.customAttr.basicStyle))
state.basicStyleForm = basicStyle as ChartBasicStyle
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
if (!state.customColor) {
state.customColor = state.basicStyleForm.colors[0]
state.colorIndex = 0
}
initTableColumnWidth()
}
const COLUMN_WIDTH_TYPE = ['table-info', 'table-normal']
const initTableColumnWidth = () => {
if (!COLUMN_WIDTH_TYPE.includes(props.chart.type)) {
return
}
let { xAxis, yAxis, customAttr } = JSON.parse(JSON.stringify(props.chart))
let allAxis = xAxis
if (props.chart.type === 'table-normal') {
allAxis = allAxis.concat(yAxis)
}
const { tableHeader } = customAttr
if (allAxis.length && tableHeader.showIndex) {
const indexColumn = {
dataeaseName: SERIES_NUMBER_FIELD,
name: tableHeader.indexLabel
} as unknown as Axis
allAxis.unshift(indexColumn)
}
if (!allAxis.length) {
state.basicStyleForm.tableFieldWidth?.splice(0)
state.fieldColumnWidth.fieldId = ''
state.fieldColumnWidth.width = 0
} else {
if (!state.basicStyleForm.tableFieldWidth.length) {
state.basicStyleForm.tableFieldWidth.splice(0)
const defaultWidth = parseFloat((100 / allAxis.length).toFixed(2))
allAxis.forEach(item => {
state.basicStyleForm.tableFieldWidth.push({
fieldId: item.dataeaseName,
name: item.name,
width: defaultWidth
})
})
} else {
const fieldMap = state.basicStyleForm.tableFieldWidth.reduce((p, n) => {
p[n.fieldId] = n
return p
}, {})
state.basicStyleForm.tableFieldWidth.splice(0)
allAxis.forEach(item => {
let width = 10
if (fieldMap[item.dataeaseName]) {
width = fieldMap[item.dataeaseName].width
}
state.basicStyleForm.tableFieldWidth.push({
fieldId: item.dataeaseName,
name: item.name,
width
})
})
}
let selectedField = state.basicStyleForm.tableFieldWidth[0]
const curFieldIndex = state.basicStyleForm.tableFieldWidth.findIndex(
i => i.fieldId === state.fieldColumnWidth.fieldId
)
if (curFieldIndex !== -1) {
selectedField = state.basicStyleForm.tableFieldWidth[curFieldIndex]
}
state.fieldColumnWidth.fieldId = selectedField.fieldId
state.fieldColumnWidth.width = selectedField.width
}
}
const changeFieldColumn = () => {
const { basicStyleForm, fieldColumnWidth } = state
const fieldWidth = basicStyleForm.tableFieldWidth?.find(
i => i.fieldId === fieldColumnWidth.fieldId
)
if (fieldWidth) {
fieldColumnWidth.width = fieldWidth.width
}
}
const changeFieldColumnWidth = () => {
const { basicStyleForm, fieldColumnWidth } = state
const fieldWidth = basicStyleForm.tableFieldWidth?.find(
i => i.fieldId === fieldColumnWidth.fieldId
)
if (fieldWidth) {
fieldWidth.width = fieldColumnWidth.width
changeBasicStyle('tableFieldWidth')
}
}
const pageSizeOptions = [
{ name: '10' + t('chart.table_page_size_unit'), value: 10 },
{ name: '20' + t('chart.table_page_size_unit'), value: 20 },
@ -265,7 +357,7 @@ onMounted(() => {
v-model="state.basicStyleForm.alpha"
:min="0"
:max="100"
class="alpha-input-number"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('alpha')"
>
@ -286,15 +378,16 @@ onMounted(() => {
<el-radio-group
v-model="state.basicStyleForm.tableColumnMode"
@change="changeBasicStyle('tableColumnMode')"
class="table-column-mode"
>
<el-radio label="adapt" :effect="themes">
{{ t('chart.table_column_adapt') }}
</el-radio>
<el-radio label="custom" :effect="themes">
{{ t('chart.table_column_fixed') }}
</el-radio>
<el-radio v-show="chart.type !== 'table-pivot'" label="field" :effect="themes">
{{ t('chart.table_column_custom') }}
<el-tooltip placement="bottom" :content="t('chart.table_column_width_tip')" raw-content>
<el-icon><InfoFilled /></el-icon>
</el-tooltip>
</el-radio>
</el-radio-group>
</el-form-item>
@ -311,6 +404,35 @@ onMounted(() => {
@change="changeBasicStyle('tableColumnWidth')"
/>
</el-form-item>
<el-form-item
v-if="showProperty('tableColumnMode') && state.basicStyleForm.tableColumnMode === 'field'"
label=""
class="form-item table-field-width-config"
>
<el-select
v-model="state.fieldColumnWidth.fieldId"
:effect="themes"
@change="changeFieldColumn()"
>
<el-option
v-for="item in state.basicStyleForm.tableFieldWidth"
:key="item.fieldId"
:label="item.name"
:value="item.fieldId"
/>
</el-select>
<el-input
v-model.number="state.fieldColumnWidth.width"
type="number"
class="basic-input-number"
:min="0"
:max="100"
:effect="themes"
@change="changeFieldColumnWidth()"
>
<template #append>%</template>
</el-input>
</el-form-item>
<!--table2 end-->
<!--gauge start-->
<el-form-item
@ -610,7 +732,7 @@ onMounted(() => {
v-model="state.basicStyleForm.innerRadius"
:min="1"
:max="100"
class="alpha-input-number"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('innerRadius')"
>
@ -645,7 +767,7 @@ onMounted(() => {
v-model="state.basicStyleForm.radius"
:min="1"
:max="100"
class="alpha-input-number"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('radius')"
>
@ -695,15 +817,37 @@ onMounted(() => {
color: #a6a6a6;
}
}
.alpha-input-number {
:deep(input) {
-webkit-appearance: none;
-moz-appearance: textfield;
}
.table-field-width-config {
.ed-select {
width: 100px !important;
:deep(.ed-input__wrapper) {
border-radius: 4px 0 0 4px !important;
}
}
.ed-input-group {
width: 124px;
:deep(.ed-input__wrapper) {
border-radius: 0 !important;
}
:deep(.ed-input-group__append) {
padding: 0 8px;
}
}
}
.table-column-mode {
:deep(.ed-radio) {
margin-right: 12px !important;
}
}
.basic-input-number {
:deep(input) {
-webkit-appearance: none;
-moz-appearance: textfield;
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
}
&::-webkit-inner-spin-button,
&::-webkit-outer-spin-button {
-webkit-appearance: none;
}
}
}

View File

@ -54,7 +54,7 @@ import ChartTemplateInfo from '@/views/chart/components/editor/common/ChartTempl
const snapshotStore = snapshotStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
const { canvasCollapse, curComponent, componentData } = storeToRefs(dvMainStore)
const { canvasCollapse, curComponent, componentData, editMode } = storeToRefs(dvMainStore)
const router = useRouter()
const { t } = useI18n()
@ -64,7 +64,10 @@ const tabActiveVQuery = ref('style')
const datasetSelector = ref(null)
const curDatasetWeight = ref(0)
const renameForm = ref<FormInstance>()
const { emitter } = useEmitt()
const { emitter } = useEmitt({
name: 'set-table-column-width',
callback: args => onTableColumnWidthChange(args)
})
const props = defineProps({
view: {
type: Object as PropType<ChartObj>,
@ -787,6 +790,14 @@ const onScrollCfgChange = val => {
renderChart(view.value)
}
const onTableColumnWidthChange = val => {
if (editMode.value !== 'edit') {
return
}
view.value.customAttr.basicStyle.tableFieldWidth = val
snapshotStore.recordSnapshotCache('renderChart', view.value.id)
}
const onExtTooltipChange = val => {
view.value.extTooltip = val
}

View File

@ -1239,6 +1239,7 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)',
tableColumnMode: 'adapt',
tableColumnWidth: 100,
tableFieldWidth: [],
tablePageMode: 'page',
tablePageSize: 20,
gaugeStyle: 'default',

View File

@ -31,7 +31,7 @@ export class TableInfo extends S2ChartView<TableSheet> {
}
public drawChart(drawOption: S2DrawOptions<TableSheet>): TableSheet {
const { container, chart, pageInfo, action } = drawOption
const { container, chart, pageInfo, action, resizeAction } = drawOption
const containerDom = document.getElementById(container)
// fields
@ -159,6 +159,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
action(param)
})
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })

View File

@ -32,7 +32,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
}
drawChart(drawOption: S2DrawOptions<TableSheet>): TableSheet {
const { container, chart, action } = drawOption
const { container, chart, action, resizeAction } = drawOption
const containerDom = document.getElementById(container)
if (!containerDom) return
@ -153,6 +153,8 @@ export class TableNormal extends S2ChartView<TableSheet> {
}
action(param)
})
// header resize
newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
// theme
const customTheme = this.configTheme(chart)
newChart.setThemeCfg({ theme: customTheme })

View File

@ -294,6 +294,14 @@ export class TablePivot extends S2ChartView<PivotSheet> {
return theme
}
setupDefaultOptions(chart: ChartObj): ChartObj {
const { customAttr } = chart
if (customAttr.basicStyle.tableColumnMode === 'field') {
customAttr.basicStyle.tableColumnMode = 'custom'
}
return chart
}
constructor() {
super('table-pivot', [])
}

View File

@ -311,12 +311,35 @@ export function getStyle(chart: Chart): Style {
style.cellCfg = {
height: tableCell.tableItemHeight
}
if (basicStyle.tableColumnMode === 'adapt') {
delete style.colCfg.width
style.layoutWidthType = 'adaptive'
} else {
delete style.layoutWidthType
style.colCfg.width = basicStyle.tableColumnWidth
switch (basicStyle.tableColumnMode) {
case 'adapt': {
delete style.cellCfg.width
style.layoutWidthType = 'compact'
break
}
case 'field': {
delete style.layoutWidthType
const fieldMap =
basicStyle.tableFieldWidth?.reduce((p, n) => {
p[n.fieldId] = n
return p
}, {}) || {}
style.colCfg.width = node => {
const width = node.spreadsheet.container.cfg.el.offsetWidth
if (!basicStyle.tableFieldWidth?.length) {
const fieldsSize = chart.data.fields.length
const columnCount = tableHeader.showIndex ? fieldsSize + 1 : fieldsSize
return width / columnCount
}
const baseWidth = width / 100
return fieldMap[node.field] ? fieldMap[node.field].width * baseWidth : baseWidth * 10
}
break
}
default: {
delete style.layoutWidthType
style.colCfg.width = basicStyle.tableColumnWidth
}
}
}
@ -573,7 +596,7 @@ class SortTooltip extends BaseTooltip {
this.spreadsheet.tooltip.container.innerHTML = ''
const childElement = document.createElement('div')
this.spreadsheet.tooltip.container.appendChild(childElement)
render(vNode, childElement, false)
render(vNode, childElement)
const { x, y } = getAutoAdjustPosition({
spreadsheet: this.spreadsheet,

View File

@ -21,6 +21,7 @@ declare interface PageInfo {
export interface S2DrawOptions<O> extends AntVDrawOptions<O> {
pageInfo?: PageInfo
resizeAction?: (...args: any) => void
}
export abstract class S2ChartView<P extends SpreadSheet> extends AntVAbstractChartView {
public abstract drawChart(drawOption: S2DrawOptions<P>): P

View File

@ -26,9 +26,11 @@ import { defaultsDeep, cloneDeep } from 'lodash-es'
import { BASE_VIEW_CONFIG } from '../../editor/util/chart'
import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/canvasStyle'
import { deepCopy } from '@/utils/utils'
import { useEmitt } from '@/hooks/web/useEmitt'
const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo } = storeToRefs(dvMainStore)
const { emitter } = useEmitt()
const props = defineProps({
view: {
@ -71,7 +73,6 @@ const state = reactive({
},
linkageActiveParam: null,
pointParam: null,
myChart: null,
loading: false,
data: { fields: [] }, //
pageInfo: {
@ -145,7 +146,8 @@ const renderChart = (viewInfo: Chart, resetPageInfo: boolean) => {
chart: toRaw(chart),
chartObj: myChart,
pageInfo: state.pageInfo,
action
action,
resizeAction
})
myChart?.render()
initScroll()
@ -296,6 +298,20 @@ const trackMenu = computed(() => {
return trackMenuInfo
})
const resizeAction = resizeColumn => {
if (showPosition.value !== 'canvas') {
return
}
const fieldId = resizeColumn.info.meta.field
const { basicStyle } = view.value.customAttr
const containerWidth = document.getElementById(containerId).offsetWidth
basicStyle.tableFieldWidth?.forEach(item => {
if (item.fieldId === fieldId) {
item.width = parseFloat(((resizeColumn.info.resizedWidth / containerWidth) * 100).toFixed(2))
}
})
emitter.emit('set-table-column-width', basicStyle.tableFieldWidth)
}
defineExpose({
calcData,
renderChart,