forked from github/dataease
feat(视图): 明细表/汇总表支持按列设置宽度,拖拽调整宽度。#7707
This commit is contained in:
parent
a78b57d3c1
commit
000b4acd96
@ -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: '该指标生效时,样式大小中的气泡大小属性将失效',
|
||||
|
@ -54,11 +54,28 @@ declare interface ChartBasicStyle {
|
||||
/**
|
||||
* 表格列宽模式: 自适应和自定义
|
||||
*/
|
||||
tableColumnMode: 'adapt' | 'custom'
|
||||
tableColumnMode: 'adapt' | 'custom' | 'field'
|
||||
/**
|
||||
* 表格列宽
|
||||
*/
|
||||
tableColumnWidth: number
|
||||
/**
|
||||
* 表格字段列宽
|
||||
*/
|
||||
tableFieldWidth: {
|
||||
/**
|
||||
* 字段ID
|
||||
*/
|
||||
fieldId: string,
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* 字段宽度占比
|
||||
*/
|
||||
width: number
|
||||
}[]
|
||||
/**
|
||||
* 表格分页模式
|
||||
*/
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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 })
|
||||
|
@ -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 })
|
||||
|
@ -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', [])
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user